Конференция "Media" » Умножение в регистре отдельных байт (ммх) [D6, D7, Win2k, WinXP]
 
  • antonn © (20.11.09 22:39) [0]
    Название темы дурацкое, поясню чего я хотел :)
    Пишу блитинг битмапов методом сложения (высветления, сверху накладывается спрайт огня или дыма без альфы нарисованный на черном фоне, а на бекграунде каждый пиксель спрайта складывается и получается высветленная область (PS в стиле "Капитана Очевидность", но мало ли...:) )), с оптимизацией на ММХ.

    В данный момент есть код просто и без затей выполняющий сложение:
         movq    mm0, [edi] //берем пиксель бекграунда
         movq    mm1, [edi+8]  
         paddusb mm0, [esi]  //берем пиксель спрайта и складываем его с пикселем бекграунда, без переполнений
         paddusb mm1, [esi+8]
         movq    [edi], mm0  //пишем результат обратно в бекграунд
         movq    [edi+8], mm1
         add     esi, edx  //в edx смещение к следующему пикселу (на 4), в общем не важно
         add     edi, edx



    Дикая производительность радует. Но понадобилось теперь выводить спрайт с различной степенью прозрачности, причем не только меньше 100%, но и с превышением (будет засветка спрайта), причем делать это нужно уже на выводе.
    Так вот вопрос - помогите написать это умножение :)
    Регистры ММХ все свободны, eax занят. В принципе полупрозрачность я реализую, но вот как сделать пересвет (прозрачность на 250%) я не пойму :(

    ЗЫ и, походу, в умножении нет такой замечательной функции как рaddusb, с самопроверкой на переполнение :(
  • antonn © (20.11.09 22:42) [1]
    чуть позже выложу тестовый проект
  • antonn © (21.11.09 00:43) [2]
    тестовый проект
    http://desksoft.ru/index.php?downloads=attachments&id=241 (4кб, zip)
  • Sapersky (21.11.09 01:14) [3]
    Битмап забыл в пример положить :)
    Ну ладно, я вроде и так понял, что нужно такое:
    DstColor := DstColor + SrcColor * Alpha
    где Alpha может быть > 1.0 (или 255, если в целых)

    Проблема с MMX-умножением в том, что "вершки" (старшие разряды для всего что больше 65535) мы получаем в одном регистре (командой PMULHW), а "корешки" - в другом (PMULLW). Если ты будешь использовать коэфф. больше 255, у тебя естественно может получиться больше 65535. Как обрезать до 65535? У меня только одна идея - сравнивать старшие разряды с 0 командой сравнения (PCMPEQW или PCMPGTW). Если использовать PCMPEQW, то получается: если старший разряд равен 0 (результат умножения <= 65535), то в элемент маски пишется $FFFF, если не равен (результат умножения > 65535) - $0000.
    Ну и далее:
    Dst := (Low and Mask) or (not Mask)
    фактически эквивалентно
    If Mask = $FFFF then Dst := Low else Dst := $FFFF;
    где Low - младшие разряды. Элементарно, Ватсон? :)
    Или можно использовать PCMPGTW с 0, тогда логика меняется на противоположную - если результат умножения > 65535, то в маске $FFFF и т.д.
  • antonn © (21.11.09 01:28) [4]

    > Битмап забыл в пример положить :)

    вот блин))
    http://desksoft.ru/index.php?downloads=attachments&id=242 (230кб)

    вообще я пока нифига не понял, попробую завтра на свещую голову разобрать :)
  • antonn © (21.11.09 01:56) [5]
    я так понимаю что 65535 у меня получится если я засвечу картинку в 255 раз?
    т.е. можно распаковать в mm2 два пиксела, они займут положение 00FF00FF, далее умножить на маску, и максимум что тут получится FFFFFFFF в случае 255 кратной засветки?

    еще размышляю, а не покроет ли такая "оптимизация" выигрыш от нескольких "простых" блитингов (пока делаю для конкретной цели - огонь) :)
  • Sapersky (21.11.09 03:41) [6]
    я так понимаю что 65535 у меня получится если я засвечу картинку в 255 раз?
    т.е. можно распаковать в mm2 два пиксела, они займут положение 00FF00FF, далее умножить на маску, и максимум что тут получится FFFFFFFF в случае 255 кратной засветки?


    Ну здесь по сути то же самое, что и с обычным альфа-блендингом (и за основу лучше брать функцию с альфой, а не сложение). Макс. значение пикселя = 255, макс. значение альфы = 255, макс. результат... 65025. Гхм. Ладно, погрешность получается небольшая (65025 shr 8 (/ 256) = 254, всего-то 1/255 яркости) и на глаз незаметная.
    Для простоты считаю, что макс. значение от умножения двух пикселей = 65535 = High(Word). С точки зрения возможного переполнения именно эта граница имеет значение.

    еще размышляю, а не покроет ли такая "оптимизация" выигрыш от нескольких "простых" блитингов

    Кстати да, как вариант, если устраивает "грубая" засветка в 2,3 и т.д. раз. Причём можно не делать несколько проходов, а сложить несколько раз в самой ф-ии, тогда однозначно будет быстрее.
  • antonn © (21.11.09 22:14) [7]

    > Кстати да, как вариант, если устраивает "грубая" засветка
    > в 2,3 и т.д. раз. Причём можно не делать несколько проходов,
    >  а сложить несколько раз в самой ф-ии, тогда однозначно
    > будет быстрее.

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

    с альфой у miek'а довольно хитро, там две маски - прямая и обратная, спрайт умножается на одну, а фон на другую, потом складываются, и потому там все в пределах байта получается (насколько я понял :)). Попробую как нить провернуть, но мне кажется не особо стоит рассчитывать на благоприятный исход (хотя бы в плане скорости) :(
  • Sapersky (22.11.09 00:24) [8]
    MMX-умножение работает только с word'ами, так что в пределах байта не получится. Опять же, как ни инвертируй альфу, а всё равно хотя бы в одном из произведений в максимуме получится 255*255. К пределам байта оно приводится потом командой psrlw mm0, 8.
    Вообще-то есть такая штука - View FPU window, Ctrl-Alt-F, правой кнопкой на окно, Show -> MMX registers. Делаешь трассировку и смотришь, что и как меняется. Ну и описание команд посмотреть не мешало бы.

    По скорости альфа-блендинг, конечно, медленнее аддитивного - больше операций, меньше данных за раз обрабатывается - обычно у меня получалось где-то в 1.5 раза, сейчас на P3 намерил в 2 с лишним.
  • Sapersky (24.11.09 20:29) [9]
    Если ещё надо:

    procedure xLine_DrawAddK32_MMX(Src, Dst : Pointer; Count, K : Integer);
    asm
     push esi
     mov esi, eax
     // запихиваем коэфф. во все word'ы MMX-регистра
     // несколько корявый вариант на "чистом" MMX
     imul eax, K, $10001
     movd mm5, eax
     movq mm4, mm5
     psllq mm4, 32
     por mm5, mm4
     // или более изящный на enhanced MMX (P3):
    //  pshufw mm5, [ebp + 8], 0

     pxor mm7, mm7    // mm7 = 0
     pcmpeqb mm6, mm6 // mm6 = $FFFFFFFF.FFFFFFFF
    @inner_loop:
     movd mm0, [esi]
     movd mm1, [edx]
     punpcklbw mm0, mm7
     movq mm2, mm5    // copy Alpha
     pmulhw mm2, mm0  // HiWord(Res)
     pmullw mm0, mm5  // LoWord(Res)
     pcmpeqw mm2, mm7 // HiWord = 0?
     pand mm0, mm2    // да - используем LoWord
     pxor mm2, mm6    // иначе инверсию маски
     por mm0, mm2     // (= $FFFF)
     psrlw mm0, 8      // сокращаем до байтов
     packuswb mm0, mm7 // пакуем
     paddusb mm0, mm1  // складываем
     movd [edx], mm0
     add esi, 4
     add edx, 4
     dec ecx
     jnz @inner_loop
     emms
     pop esi
    end;


    По скорости не блестяще, на 15% медленнее альфа-блендинга и всего на 35% быстрее варианта на Паскале. Хотя можно ещё несколько оптимизировать выполнением 2-х умножений за итерацию.
    Кстати, скорость команд вроде movq mm0, [mem], используемых при обработке 2-х и более пикселей за раз (тот же аддитивный блендинг), сильно зависит от выравнивания (чётная-нечётная x-координата спрайта). Поэтому для нечётных рекомендуется сделать специальную версию спрайта, сдвинутую на 1 вправо, и рисовать её в положении x-1,y.
  • Sapersky (25.11.09 17:29) [10]
    Ещё вполне себе вариант - применение палитровой графики. В небольших спрайтах цветов обычно немного, можно без особых искажений урезать их до 256. Т.е. умножаешь палитру (очень быстро), а потом складываешь MMX-ом. Медленнее, чем сложение 32-битных, но быстрее, чем сложение 32-bpp с умножением.
  • antonn © (25.11.09 22:15) [11]
    Не, я без ограничений юзаю, обычно даже с альфой везде где можно, без искажений (потому не использую jpeg нигде). Гулять так гулять :)
 
Конференция "Media" » Умножение в регистре отдельных байт (ммх) [D6, D7, Win2k, WinXP]
Есть новые Нет новых   [119142   +33][b:0][p:0.002]