Перейти к содержанию
    

Графический фильтр на Cortex-M7

Бьюсь над реализацией графического фильтра HQ2X на STM32H743 (Cortex-M7). Фильтр работает, но притормаживает, когда в кадре много мелких деталей.

 

Была предпринята оптимизация: Switch/Case из 256 значений был заменён на JumpTable. Не помогло. Исходный код фильтра (Keil ARM MDK):

 

HQ2x.rar

 

Требуется растянуть кадр в 2 раза по обеим осям.

 

Есть другие фильтры Scale2x, SaI2x , LQ2x - с ними проблем нет, на STM32H743 они идут довольно шустро(написанные на C, без Asm-а).

 

Вот тут чувак заточил под NEON и DSP фильтр HQnX (что не годится для Cortex-M7): https://pyra-handheld.com/boards/threads/ru...sp.69047/page-5

 

Существуют ли аналогичные графические фильтры (в частности HQ 2x), оптимизированные на ассемблере для ядер ARM Cortex-M7?

 

Работа фильтра пояснена на рисунке:

 

post-99126-1531719269_thumb.png

Изменено пользователем repstosw

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А попорбовать вместо ассемблерных вставок (которые обычно сбивают оптимизатор) использовть встроенные инлайны компилятора? В случае GCC, к примеру, могут оптимизатором варьироваться регистры, содержащие исходные значения/результаты...

Кстати, сравнение для четырех восьмибитных чисел должно быть тоже в SIMD... скорее всего. Вы его делаете "в ручную".

Searching for 'SSUB8'...
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_armcc.h(803):#define __SSUB8                           __ssub8
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_armclang.h(1381):__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2)
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_armclang.h(1385):  __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) );
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_gcc.h(1590):__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2)
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_gcc.h(1594):  __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) );
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_iccarm.h(397):    #define __SSUB8   __iar_builtin_SSUB8
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core_A\Include\cmsis_iccarm.h(310):  #define __SSUB8   __iar_builtin_SSUB8
7 occurrence(s) have been found.

 

И самый главный вопрос - DATA CACHE включен в процессоре?

Изменено пользователем Genadi Zawidowski

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Рисунок врет. Например, глаза лягушонка он размыл (округлил), а такой же черный квадрат недалеко оставил, как есть. И диагональные линии выглядят слишком красиво.

Растянуть в 2 раза - половина пикселей (по одной оси) уже есть, а половину всунуть кубической интерполяцией. Еще есть пиксели по диагонали, те нужно интерполировать побеим осям.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Рисунок врет. Например, глаза лягушонка он размыл (округлил), а такой же черный квадрат недалеко оставил, как есть. И диагональные линии выглядят слишком красиво.

Просто это не совсем фильтр в привычном понимании. Алгоритм.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А попорбовать вместо ассемблерных вставок (которые обычно сбивают оптимизатор) использовть встроенные инлайны компилятора? В случае GCC, к примеру, могут оптимизатором варьироваться регистры, содержащие исходные значения/результаты...

Кстати, сравнение для четырех восьмибитных чисел должно быть тоже в SIMD... скорее всего. Вы его делаете "в ручную".

 

Наскоряк не нашёл CMSIS-овский хедер, поэтому на ассемблере написал. Попробуем использовать инлайны.

 

И самый главный вопрос - DATA CACHE включен в процессоре?

 

Да. Без него всё очень сильно медленно.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

CMSIS обычно подключен через соответствующий процессорный header - например, в моем случае:

через stm32h7xx.h включен stm32h743xx.h - а в нем в районе 240 строки core_cm7.h:

 

#define __CM7_REV               0x0100U   /*!< Cortex-M7 revision r1p0                       */
#define __MPU_PRESENT             1       /*!< CM7 provides an MPU                           */
#define __NVIC_PRIO_BITS          4       /*!< CM7 uses 4 Bits for the Priority Levels       */
#define __Vendor_SysTickConfig    0       /*!< Set to 1 if different SysTick Config is used  */
#define __FPU_PRESENT             1       /*!< FPU present                                   */
#define __ICACHE_PRESENT          1       /*!< CM7 instruction cache present                 */
#define __DCACHE_PRESENT          1       /*!< CM7 data cache present                        */
#include "core_cm7.h"                     /*!< Cortex-M7 processor and core peripherals      */

Изменено пользователем Genadi Zawidowski

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Просто это не совсем фильтр в привычном понимании. Алгоритм.

Неужели глаза размером в пиксель отличает от просто кубика?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Рисунок врет. Например, глаза лягушонка он размыл (округлил), а такой же черный квадрат недалеко оставил, как есть. И диагональные линии выглядят слишком красиво.

Растянуть в 2 раза - половина пикселей (по одной оси) уже есть, а половину всунуть кубической интерполяцией. Еще есть пиксели по диагонали, те нужно интерполировать побеим осям.

 

Не врёт. Это волшебный фильтр. Не просто билинейная-бикубическая фильтрация , а смарт-фильтр, заточенный под пиксель-арт.

 

В доказательство прикрепляю программу (под винду) вместе с исходником и мейкфайлом. Фильтр LQ2x (практически результат схож с HQ2x, но быстрее и легче для STM32):

 

LQ2x_WIN32.rar

 

Входные данные: файл test.raw, 160x102 пикселя RGB 8:8:8

Выходные данные после отработки программы : LQ2x.raw 204x320 пикселей RGB 8:8:8

В программе фильтр делает ещё поворот на 90 градусов и работает в цветовом пространстве RGB 5:6:5 (для моих целей).

 

RAW смотреть к примеру IrfanView, выставив длину, ширину и пиксель-формат.

 

Ну и ниже картинка с результатами фильтров (с википедии):

 

post-99126-1531727785_thumb.png

Изменено пользователем repstosw

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Была предпринята оптимизация: Switch/Case из 256 значений был заменён на JumpTable. Не помогло. Исходный код фильтра (Keil ARM MDK):

Уже писали много раз и тут в том числе: все эти рукопашные inline - мёртвому припарки. При включённой оптимизации компилятор сам заинлайнит что нужно.

И тем более - какие-то таблицы переходов: компилятор сам прекрасно умеет их делать если switch/case удовлетворяет некоторым условиям: достаточно большое число case и последовательное расположение case-значений. Прежде чем пытаться что-то делать в подобном духе, надо хотя-бы листинг при полной оптимизации посмотреть и подумать.

А ускорить тут можно только ассемблером.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Уже писали много раз и тут в том числе: все эти рукопашные inline - мёртвому припарки. При включённой оптимизации компилятор сам заинлайнит что нужно.

И тем более - какие-то таблицы переходов: компилятор сам прекрасно умеет их делать если switch/case удовлетворяет некоторым условиям: достаточно большое число case и последовательное расположение case-значений. Прежде чем пытаться что-то делать в подобном духе, надо хотя-бы листинг при полной оптимизации посмотреть и подумать.

А ускорить тут можно только ассемблером.

 

При -O3 -Otime switch/case заменяется на Jumptable при условии, что есть некая упорядоченность в проверяемых значениях. А в фильтре она идёт в разнобой. Максимум на что можно надеяться при таком подходе - это на поиск с бинарным разделением

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При -O3 -Otime switch/case заменяется на Jumptable при условии, что есть некая упорядоченность в проверяемых значениях. А в фильтре она идёт в разнобой. Максимум на что можно надеяться при таком подходе - это на поиск с бинарным разделением

Ещё и не факт что подобная раскрутка циклов:

   if ((w1 != w5) && (Diff(y, RGBtoYUV[w1]))) pattern |= (1 << 0);
   if ((w2 != w5) && (Diff(y, RGBtoYUV[w2]))) pattern |= (1 << 1);
   if ((w3 != w5) && (Diff(y, RGBtoYUV[w3]))) pattern |= (1 << 2);
   if ((w4 != w5) && (Diff(y, RGBtoYUV[w4]))) pattern |= (1 << 3);
   if ((w6 != w5) && (Diff(y, RGBtoYUV[w6]))) pattern |= (1 << 4);
   if ((w7 != w5) && (Diff(y, RGBtoYUV[w7]))) pattern |= (1 << 5);
   if ((w8 != w5) && (Diff(y, RGBtoYUV[w8]))) pattern |= (1 << 6);
   if ((w9 != w5) && (Diff(y, RGBtoYUV[w9]))) pattern |= (1 << 7);

даст ускорение. Тело цикла достаточно большое, чтобы одна команда перехода в конце незначительно влияла на скорость. Зато кеша надо намного меньше.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот тут чувак заточил под NEON и DSP фильтр HQnX (что не годится для Cortex-M7)

NEON имеет множество пробелов по сравнению с возможностями Cortex-M7. И уж точно Cortex-M7 может выполнять всё что есть в NEON.

Нужно просто перелопатить код, удаляя и заменяя вставки NEON функций.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Там тоже ассемблер с "неоном". Неон умеет по 16 байт за один раз обсчитывать, Cortex-M7 только по четыре.

лично я делал FIR фильтры (многоканальные, для float) с импользованием NEON. Получилось.

 

 

ps: "там" это тут

Изменено пользователем Genadi Zawidowski

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я так понимаю - Вы сами пытались оптимизировать функцию Diff() asm-вставками? И в её начале - закомментаренное си-тело?

Если так, то действие:

  int c1v = (c1 & Vmask) - (c2 & Vmask);
  if (Absolute(c1v) > trV)

совсем не аналогично варианту на асме ниже.

Подумайте, что будет если к примеру c1==0x81, c2==0 в первом и во втором случае.

Если конечно в реализации изначально не заложено какое-то ограничение по количеству цветов (диапазону значений байтов).

Но в любом случае: взятие модуля от числа - не аналогично операции x=-x.

Да и вычитать SSUB8 c1,c2 чтобы потом находить NEG от каждого байта - это как-то бессмысленно. Почему бы тогда сразу не сделать SSUB8 c2,c1 ?

 

PS: Так что похоже у Вас ещё и реализация кривая.... :laughing:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не врёт. Это волшебный фильтр. Не просто билинейная-бикубическая фильтрация , а смарт-фильтр, заточенный под пиксель-арт.

Фильтр LQ2x (практически результат схож с HQ2x, но быстрее и легче для STM32):

ОТ: Back to 80's? Не в смысле фильтров, а моды на 8-битные игры? Интересно узнать, каков back ground изысканий...

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...