repstosw 18 16 июля, 2018 Опубликовано 16 июля, 2018 · Жалоба Я так понимаю - Вы сами пытались оптимизировать функцию 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: Это не реализация!!! Это драфт, не претендующий на конечный вариант. За ошибку спасибо! Нашёл с того форума(про фильтр на NEON) реализацию Диффа. Но я не силён в GCC-asm и его синтаксис ставит меня немного в ступор: static inline int Diff(u32 yuv1, u32 yuv2) { register u32 tmp1, tmp2; asm( "usub8 %[tmp1], %[value1], %[value2] \n\t" // tmp1 = value1 - value2 // tmp1 = yuv1 - yuv2 "usub8 %[value2], %[value2], %[value1] \n\t" // value2 = value2 - value1 // value2 = yuv2 - yuv1 "mov %[tmp2], #0 \n\t" // tmp2 = 0 "movw %[value1], #0x0706 \n\t" "movt %[value1], #0x30 \n\t" // value1 = 0x300706 "sel %[tmp1], %[value2], %[tmp1] \n\t" // tmp1 = (value2 >= 0)?value2:tmp1 // tmp1 = abs(yuv1 - yuv2) "usub8 %[value2], %[value1], %[tmp1] \n\t" // value2 = value1 - tmp1 // value2 = 0x300706 - abs(yuv1 - yuv2) "sel %[value1], %[tmp2], %[value1] " // value1 = (value2 >= 0)?tmp2:value1 // value1 = (0x300706 >= abs(yuv1 - yuv2))?0:0x300706 : [value1] "+r" (yuv1), [value2] "+r" (yuv2), [tmp1] "=r" (tmp1), [tmp2] "=r" (tmp2) : : "cc" ); return yuv1; Начал переделывать под Keil Asm (или ARM Asm): static inline int Diff(u32 yuv1, u32 yuv2) { register u32 tmp1,tmp2; register u32 value1=yuv1,value2=yuv2; __asm { usub8 tmp1,value1,value2 // tmp1 = value1 - value2 // tmp1 = yuv1 - yuv2 usub8 value2,value2,value1 // value2 = value2 - value1 // value2 = yuv2 - yuv1 mov tmp2,#0 // tmp2 = 0 movw value1,#0x0706 movt value1,#0x30 // value1 = 0x300706 sel tmp1,value2,tmp1 // tmp1 = (value2 >= 0)?value2:tmp1 // tmp1 = abs(yuv1 - yuv2) usub8 value2,value1,tmp1 // value2 = value1 - tmp1 // value2 = 0x300706 - abs(yuv1 - yuv2) sel value1,tmp2,value1 // value1 = (value2 >= 0)?tmp2:value1 // value1 = (0x300706 >= abs(yuv1 - yuv2))?0:0x300706 } return yuv1; // ??? } Знающие GCC ASM, подскажите пожалуйста, по переделке на ARM ASM, что загоняется в value1, value 2? И куда возвращаемый результат кладётся? И что означает в конце: : [value1] "+r" (yuv1), [value2] "+r" (yuv2), [tmp1] "=r" (tmp1), [tmp2] "=r" (tmp2) : : "cc" ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Obam 31 16 июля, 2018 Опубликовано 16 июля, 2018 · Жалоба Т.к. кортекс-М умеет только команды Tumb-2, то загрузка 32битной константы выполняется за 2 команды: movw (младшее полуслово) и movt (старшее). А смысл 0x00300706 вам знать. LDR value1, =_00300706 одной командой не будет быстрее? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 16 июля, 2018 Опубликовано 16 июля, 2018 · Жалоба Т.к. кортекс-М умеет только команды Tumb-2, то загрузка 32битной константы выполняется за 2 команды: movw (младшее полуслово) и movt (старшее). А смысл 0x00300706 вам знать. LDR value1, =_00300706 одной командой не будет быстрее? Обращение к ячейке памяти. Предположим, что это STM32H743 и переменная в DTCM и адрес выровнен на 4 байта. Что будет быстрее: один LDR или 2 MOV ? К тому же LDR загрузит адрес, а не значение, а это уже 2 LDR: LDR r0, =DATA00300706 LDR r1,[r0] или у меня недопонятки? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 187 16 июля, 2018 Опубликовано 16 июля, 2018 · Жалоба Что будет быстрее: один LDR или 2 MOV ? К тому же LDR загрузит адрес, а не значение, а это уже 2 LDR: LDR r0, =DATA00300706 LDR r1,[r0] Это не так. LDR Rx, =0x300706 говорит транслятору расположить вблизи этого места в кодовом сегменте (после LTORG) константу 0x300706 и считать её в Rx через косвенную адресацию по [PC, #...]. Типа сделать как: LDR Rx, ptrVal ... DATA ptrVal DC32 0x300706 Команда будет одна. Но вот быстрее она будет двух MOVW/MOVT - это ещё вопрос. По идее можно попробовать написать просто: MOV Rx, #0x300706. Такой команды конечно нет, но некоторые трансляторы умные и заменяют такое на требуемую последовательность команд, возможно - наиболее оптимальную по их мнению. Знающие GCC ASM, подскажите пожалуйста, по переделке на ARM ASM, что загоняется в value1, value 2? И куда возвращаемый результат кладётся? Я не знаток GCC ASM, но вроде там всё интуитивно понятно: register u32 value1=yuv1,value2=yuv2; - эта запись явно назначает алиасные имена регистрам, которые содержат данные аргументы. Просто замените эти value1, value2, ... на регистры какие больше нравятся, и с которыми команды покороче будут. И аргументы поступают в функцию и результат должен класться согласно соглашениям вызова используемым вашим компилятором. Прочитайте их в доке на компилятор. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 187 17 июля, 2018 Опубликовано 17 июля, 2018 · Жалоба Кстати - по этой найденной Вами ассемблерной Diff() уже видно как значительно можно выиграть, написав весь блок вызовов Diff() в виде асм-функции и заинлайнив туда саму Diff(). Из неё сразу выпадают: mov tmp2,#0 // tmp2 = 0 movw value1,#0x0706 movt value1,#0x30 // value1 = 0x300706 Которые запросто выносятся за рамки цикла. И вызовов/возвратов не нужно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 17 июля, 2018 Опубликовано 17 июля, 2018 (изменено) · Жалоба Проделал несколько экспериментов. 1) Переписал Diff на ARM ASM: static inline int Diff(u32 yuv1,u32 yuv2) { register u32 t,z; __asm { USUB8 t,yuv1,yuv2 // t=yuv1-yuv2 USUB8 yuv2,yuv2,yuv1 // yuv2=yuv2-yuv1 SEL t,yuv2,t // t=abs(yuv1-yuv2) MOV yuv1,#YUVMASK // yuv1=YUVMASK MOV z,#0 // z=0 USUB8 yuv2,yuv1,t // yuv2=YUVMASK-abs(yuv1-yuv2) SEL yuv1,z,yuv1 // yuv1=(YUVMASK>=abs(yuv1-yuv2))?0:YUVMASK } return yuv1; } Фильтр работает, причем быстрее, чем с Си-шной реализацией Diff(). 2) Сравнил быстродействие фильтра с вариантом JumpTable и Case/Switch. Как это ни странно, вариант с Case/Switch быстрее! Оптимизация Keil CC : -O3 -Otime. Приемлемость работы фильтра оцениваю по косвенному признаку: задержки обновления кольцевого буфера DMA для воспроизведения звука - если звук хрипит, значит данные не успевают обновляться, значит фильтр нужно ещё более оптимизировать! С фильтрами SaI, LQ2x, Scale2x звук не хрипит. 3) Возникла идея откомпилировать код фильтра на GCC и полученный объектник подстыковать к Keil. Думал, что невозможно, а оказалось - возможно! Откомпилировал фильтр GCC тулчейн arm-eabi-gcc6.2.0-r4.exe : C:\arm-eabi\bin\arm-eabi-gcc.exe -fshort-wchar -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -O3 -Ofast -c -I ../Port HQ2x.cpp -o HQ2x.obj Построил либу для Кейла: C:\arm-eabi\bin\arm-eabi-ar.exe cru HQ2x.lib HQ2x.obj и подстыковал её в проект Keil. Результаты приятно удивили - звук идёт ровно без лагов, фильтр работает! Выходит, что GCC лучше заоптимизировал? Ещё заметил, что ASM-листинг, даваемый компилятором Keil какой-то избыточный. А в GCC листинг достаточно прозрачен : применение инструкций вполне очевиден. Связано ли это с тем, что Keil таблеточный? И производитель делает флуд АСМ-инструкций в генерируемый код в случае таблетки? 4) Вернул JumpTable вместо Case/Switch и скомпилировал в GCC. Результаты лучше, чем с Keil, но и тут Case/Switch также победил. 5) Заменил своппинг байтов на аппаратный: //#define rev16(x) (((x)>>8)|((x)<<8)) static u32 inline rev16(u32 x) { asm( "rev16 %[value],%[value]" : [value] "+r" (x) : : "cc" ); return x; } Стало ещё лучше - в GCC. А вот в Keil - наоборот было хуже. Это странно. Асм-листинг в GCC смотрю: C:\arm-eabi\bin\arm-eabi-gcc.exe -fshort-wchar -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -O3 -Ofast -S -I ../Port HQ2x.cpp -o HQ2x.S А теперь вопросы, попрошу ответить на них: 1) Можно ли в GCC выставить ещё более сильные флаги оптимизации по скорости, кроме тех что есть -O3 - Ofast ? 2) Оправдан ли переход на более новую версию тулчейна GCC с целью получить более производительный код? 3) Может ли ядро Cortex-M7 посчитать интерполяцию быстрее. Такого типа: pixel =( (pixel1 * 3) + pixel2) / 4 где pixel - это пиксели 0:8:8:8 bit 0:R:G:B 4) Спуфит ли Keil лишними ASM-инструкциями для намерянного снижения производительности в вылеченных версиях? 5) Хочется избавиться от байт-своппинга REV16. Необходимость продиктована использованием DMA - дисплей требует занос старшего байта вначале, что приводит к некорректному отображения цвета (так как DMA передаёт вначале младший байт по дефолту). 6) В STM32H743 есть Master DMA и битовое поле Little/Big endianess для байтов/полуслов/слов. Но он у меня не заработал (трансфер Memory to Peripheral). А должен ли? Рабочий код фильтра прикрепляю (и батник для сборки либы для Keil-ы под GCC ): HQ2x_GCC_Opt_SRC.rar Кстати - по этой найденной Вами ассемблерной Diff() уже видно как значительно можно выиграть, написав весь блок вызовов Diff() в виде асм-функции и заинлайнив туда саму Diff(). Из неё сразу выпадают: mov tmp2,#0 // tmp2 = 0 movw value1,#0x0706 movt value1,#0x30 // value1 = 0x300706 Которые запросто выносятся за рамки цикла. И вызовов/возвратов не нужно. Попытаюсь осмыслить Вами сказанное и испытать. По идее можно попробовать написать просто: MOV Rx, #0x300706. Такой команды конечно нет, но некоторые трансляторы умные и заменяют такое на требуемую последовательность команд, возможно - наиболее оптимальную по их мнению. Проверил: GCC делает 2 MOV, а Keil - LDR память Изменено 17 июля, 2018 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 17 июля, 2018 Опубликовано 17 июля, 2018 · Жалоба Кстати - по этой найденной Вами ассемблерной Diff() уже видно как значительно можно выиграть, написав весь блок вызовов Diff() в виде асм-функции и заинлайнив туда саму Diff(). Из неё сразу выпадают: mov tmp2,#0 // tmp2 = 0 movw value1,#0x0706 movt value1,#0x30 // value1 = 0x300706 Которые запросто выносятся за рамки цикла. И вызовов/возвратов не нужно. Пытался обдумать, не выходит. Там слева более простое выражение, которое если "ложь", то Diff() не вычисляется. Может вычислять все Diff не надо? В цикле строки: 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); Если я Вас не понял, то можете более подробнее изложить суть предлагаемых изменений? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AVI-crak 0 17 июля, 2018 Опубликовано 17 июля, 2018 · Жалоба 3) Может ли ядро Cortex-M7 посчитать интерполяцию быстрее. Такого типа: pixel =( (pixel1 * 3) + pixel2) / 4 где pixel - это пиксели 0:8:8:8 bit 0:R:G:B Это-ж dma2d, смешивание двух слоёв. Эту операцию можно полностью аппаратно выполнять. Прогресс будет даже на небольших блоках. По самому алгоритму. Честно говорю - пытался понять и нишмог. Блочную схему, ну или хотя-бы точку входа в алгоритм... А то там столько напечатано, что ногу свернуть можно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 187 17 июля, 2018 Опубликовано 17 июля, 2018 · Жалоба Если я Вас не понял, то можете более подробнее изложить суть предлагаемых изменений? Ну что непонятного? int w[8], w5; int *p = &w[0], i; u32 pattern = 0, c = 1 << 23; do if ((i = *p++) != w5) if (Diff(y, RGBtoYUV[i])) pattern |= c; while ((s32)(c <<= 1) >= 0); pattern &= 255; Типа так. Вместо w1-w9 - w[8], а w5 - отдельно. Изменив в остальных местах соответственно. Подставьте вместо вызова Diff() само тело функции в цикл (она больше нигде не вызывается у Вас) если сам компилятор не заинлайнит. Скомпилите, а затем возьмите полученный asm-листинг и сделайте из него функцию и оптимизируйте её (раз сами не можете сразу асм-функцию написать). Вот как раз одна из оптимизаций будет - вынести те три команды наружу цикла, отдав под них пару регистров. Операцию pattern &= 255 можно выкинуть, если сделать c = 1 << 24 и условие завершения цикла - перенос из старшего бита во флаг переноса по команде LSLS (не знаю как в си такое сделать). Когда будет асм-функция там думаю можно будет и другие возможности оптимизации увидеть. PS: Глядя на SEL yuv1,z,yuv1 // yuv1=(YUVMASK>=abs(yuv1-yuv2))?0:YUVMASK сдаётся мне что там вообще можно MOV yuv1,#YUVMASK // yuv1=YUVMASK MOV z,#0 выкинуть, если Diff() будет встроена в тело цикла. Как я понимаю - в зависимости от результата предыдущей команды SEL выбирает один из своих регистров-аргументов и копирует его в целевой регистр. Но потом это используется только как флаг. Так что можно заменить z и yuv1 в ней на другие подходящие регистры, выкинув вообще пару команд указанную выше. Надо читать описание команды SEL - не использовал её никогда, не уверен. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 18 июля, 2018 Опубликовано 18 июля, 2018 (изменено) · Жалоба int w[8], w5; int *p = &w[0], i; u32 pattern = 0, c = 1 << 23; do if ((i = *p++) != w5) if (Diff(y, RGBtoYUV[i])) pattern |= c; while ((s32)(c <<= 1) >= 0); pattern &= 255; Типа так. Вместо w1-w9 - w[8], а w5 - отдельно. Изменив в остальных местах соответственно. Подставьте вместо вызова Diff() само тело функции в цикл (она больше нигде не вызывается у Вас) если сам компилятор не заинлайнит. Скомпилите, а затем возьмите полученный asm-листинг и сделайте из него функцию и оптимизируйте её (раз сами не можете сразу асм-функцию написать). Вот как раз одна из оптимизаций будет - вынести те три команды наружу цикла, отдав под них пару регистров. Операцию pattern &= 255 можно выкинуть, если сделать c = 1 << 24 и условие завершения цикла - перенос из старшего бита во флаг переноса по команде LSLS (не знаю как в си такое сделать). Когда будет асм-функция там думаю можно будет и другие возможности оптимизации увидеть. PS: Глядя на SEL yuv1,z,yuv1 // yuv1=(YUVMASK>=abs(yuv1-yuv2))?0:YUVMASK сдаётся мне что там вообще можно MOV yuv1,#YUVMASK // yuv1=YUVMASK MOV z,#0 выкинуть, если Diff() будет встроена в тело цикла. Как я понимаю - в зависимости от результата предыдущей команды SEL выбирает один из своих регистров-аргументов и копирует его в целевой регистр. Но потом это используется только как флаг. Так что можно заменить z и yuv1 в ней на другие подходящие регистры, выкинув вообще пару команд указанную выше. Надо читать описание команды SEL - не использовал её никогда, не уверен. Попробовал. Стало хуже. Но это по вине компилятора: 1) int w[8] - это обращение к памяти, в то время как w1..w9 - очевидно регистры 2) Цикл разворачивается 8 раз. Никакие #pragma GCC optimize ("no-unroll-loops") и #pragma GCC optimize ("no-peel-loops") не помогают. Только -Ofast глобально убирать. 3) Одна итерация цикла строится от 11 до 13 инструкций. Зависит от настроения компилятора. 4) Не получается гарантировать сохранность регистров(для 0 и YUVMASK) внутри цикла -компилятор их внаглую затирает. Если объявить пару регистров глобально или -ffixed-reg, то работает, но эти регистры более нигде не применяются за пределами цикла(что тоже плохо). 5) И ещё вызов Diff() идёт в Case/Switch, а не только где сжали циклом: case 255: { if (Diff(RGBtoYUV[w[3]], RGBtoYUV[w[1]])) { X2PIXEL00_0 } else ................ Делал так: //Diff: static inline int Diff(u32 yuv1,u32 yuv2) { register u32 tmp1; asm( "usub8 %[tmp1], %[value1], %[value2] \n\t" "usub8 %[value2], %[value2], %[value1] \n\t" "sel %[tmp1], %[value2], %[tmp1] \n\t" "usub8 %[value2], r6, %[tmp1] \n\t" "sel %[value1], r7, %[value1] " : [value1] "+r" (yuv1), [value2] "+r" (yuv2), [tmp1] "=r" (tmp1) : : "cc" ); return yuv1; } //В цикле строки: u32 *p=&w[0],i; u32 c=1; register u32 r6 asm("r6")=YUVMASK; register u32 r7 asm("r7")=0; do if((i=*p++)!=w5)if(Diff(y,RGBtoYUV[i]))pattern|=c; while((c<<=1)<256); Тут только на ассемблере писать, иначе компилятор фигню будет строить. Изменено 18 июля, 2018 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 187 18 июля, 2018 Опубликовано 18 июля, 2018 · Жалоба Попробовал. Стало хуже. Но это по вине компилятора: 1) int w[8] - это обращение к памяти, в то время как w1..w9 - очевидно регистры 2) Цикл разворачивается 8 раз. Никакие #pragma GCC optimize ("no-unroll-loops") и #pragma GCC optimize ("no-peel-loops") не помогают. Только -Ofast глобально убирать. ... Тут только на ассемблере писать, иначе компилятор фигню будет строить. Я вообще-то Вам и говорил, что это надо на асме писать. А код привёл для иллюстрации примерного алгоритма (на асме было писать дольше - лень). Вы уже дошли до пределов возможностей оптимизатора си, дальше только брать всё в свои руки - писать на чистом асме. Как бы ни были хороши современные оптимизаторы, а если взять листинг какой-либо функции скомпилённой IAR-ом на самой максимальной оптимизации, то там как правило сразу видно много путей оптимизации. Да и во многих местах даже последний IAR очень сильно лажает, добавляя кучу бессмысленных команд даже на макс. оптимизации. Так что: компилите функцию, берёте листинг, берёте даташит на систему команд Cortex-M и - вперёд! :rolleyes: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 21 июля, 2018 Опубликовано 21 июля, 2018 · Жалоба Я вообще-то Вам и говорил, что это надо на асме писать. А код привёл для иллюстрации примерного алгоритма (на асме было писать дольше - лень). Вы уже дошли до пределов возможностей оптимизатора си, дальше только брать всё в свои руки - писать на чистом асме. Как бы ни были хороши современные оптимизаторы, а если взять листинг какой-либо функции скомпилённой IAR-ом на самой максимальной оптимизации, то там как правило сразу видно много путей оптимизации. Да и во многих местах даже последний IAR очень сильно лажает, добавляя кучу бессмысленных команд даже на макс. оптимизации. Так что: компилите функцию, берёте листинг, берёте даташит на систему команд Cortex-M и - вперёд! :rolleyes: Попробовал вручную оптимизировать ASM-листинг с GCC и потом его в объектник с помощью ассемблера. Что заметил: 1) Много лишних переприсавиваний (верхние 2 строки закомментировал, ниже свой вариант): @ ldr r3, [r8, r2, lsl #2] @ mov ip, r3 ldr ip, [r8, r2, lsl #2] .syntax unified @ 128 "HQ2x.cpp" 1 usub8 r3, lr, ip usub8 ip, r5, r3 sel ip, ip, r3 usub8 ip, r7, ip sel ip, r5, r7 Причем избавиться от этого не вышло, манипулируя разными комбинациями в объявлении входных/выходных/clobber- переменных 2) Цикл, переходы на метки - всё разворачивает. Объявление переменной границы цикла как volatile не даёт развернуть цикл, и он работает медленее, чем когда развёрнуто. Для моих целей оказался лучше другой фильтр - SaI. Он лучше сглаживает края (антиалиасинг), чем HQ2x и LQ2x. Исходники SaI фильтра + makefile + бинарник под Win32: SaI2x_Win32.rar Входные данные: test.raw 160x144 RGB 8:8:8 Выходные: test2x.raw 288x320 RGB 8:8:8 (разворот на 90 градусов!) Картинка, иллюстрирующая работу фильтров: BSpline - это "обычный фотошопный" фильтр (изображение размыто) SaI 320x240 - попытка втиснуть 320x288 в дисплей 320x240. Каждая 6-я строка выходного буфера пропускается, получается 240 линий вместо 288. Вроде б неплохо, если сравнивать с 320x240. И что самое главное, алгоритм менее ресурсозатратный , чем HQ2x : нет переходов и считываний из лукапов :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 21 июля, 2018 Опубликовано 21 июля, 2018 (изменено) · Жалоба ОТ: Back to 80's? Не в смысле фильтров, а моды на 8-битные игры? Интересно узнать, каков back ground изысканий... Цель - прощупать новый флагман от STM: Cortex-M7 @ 400MHz H743 в мультимедийном контексте. Just for fun & Proof concept. Если подробнее, то веду тему здесь (там же демонстрация работы, исходники и многое другое): http://vrtp.ru/index.php?showtopic=30174&st=0 Эмуляторы игровых приставок/консолей Все фильтры, которые тут обсуждались успешно применил (HQ2x в стадии отладки и оптимизации) :rolleyes: Изменено 21 июля, 2018 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 21 июля, 2018 Опубликовано 21 июля, 2018 · Жалоба Все фильтры, которые тут обсуждались успешно применил (HQ2x в стадии отладки и оптимизации) :rolleyes: А зачем? У меня вот, в детстве была Sega. А у друзей были Sega Mega Drive 16bit, Dendi... И там особо не было никаких фильтров - резкие картинки и четкие силуэты. Вот как на оригинале у Вас. И ИМХО именно это сейчас бы вселило в меня куда бОльшую настольгию, чем красочные и сглаженные картинки :rolleyes: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 21 июля, 2018 Опубликовано 21 июля, 2018 · Жалоба А зачем? У меня вот, в детстве была Sega. А у друзей были Sega Mega Drive 16bit, Dendi... И там особо не было никаких фильтров - резкие картинки и четкие силуэты. Вот как на оригинале у Вас. И ИМХО именно это сейчас бы вселило в меня куда бОльшую настольгию, чем красочные и сглаженные картинки :rolleyes: Дело в том что у SEGA MegDrive, о которй Вы тут написали видеоконтроллер поддерживает разрешение до 320x240 пикселей. И в QVGA LCD, который я применяю, оно очень вписывается. Тут фильтры НЕ нужны и не используются. То же касается NES и SNES (256x240 , 256x256). Но в Atari Lynx экран всего 160x102, в ГеймБой: 160x144. На QVGA-шном дисплее это всего 1/4 площади. Картинка маленькая, неудобно смотреть (особенно когда нужно в формактор кредитки уложить все устройство). И если с бордюрами в NES,SNES ещё как-то можно смириться, то тут стретч 2x как бы и напрашивается автоматически. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться