ОПТИМИЗАЦИЯ
6.3. Использование инструкций MMX
Если вкратце (а по-другому и не выйдет) с помощью MMX можно довольно неплохо
разогнать некоторые медленные операции - например, сделать RGB-освещение. Или
текстурирование с билинейной фильтрацией. Здесь я только продемонстрирую эти
два примера; всяческие дальнейшие применения - на откуп читателю.
Пример внутреннего цикла с освещением через инструкции MMX:
mov eax,u ; 24:8 fixedpoint
mov ebx,v ; 24:8 fixedpoint
mov ecx,length
xor edx,edx
mov esi,texture
mov edi,outputbuffer
movq mm1,light ; RGB-освещенность, qword
; (4 штуки 0:9 fixedpoint)
movq mm2,delta_light ; изменение освещенности
inner:
mov dl,ah ; dl = (u >> 8)
add eax,du ; u += du
mov dh,bh ; dh = (v >> 8)
add ebx,dv ; v += dv
movd mm0,[esi+4*edx] ; грузим пиксел
punpcklbw mm0,mm0 ; распаковываем пиксел
psrlw mm0,1 ; для того, чтобы были
; беззнаковые числа
pmulhw mm0,mm1 ; умножаем RGB на RGB-освещенность
add edi,4
dec ecx
packuswb mm0,mm0 ; пакуем пиксел обратно
paddw mm1,mm2 ; light += delta_light
movd [edi-4],mm0
jnz inner
Этот цикл дает после некоторой дальнейшей оптимизации 7 тактов на
пиксел - зато с текстурированием и полноценным RGB-освещением. Собственно
освещение занимает лишь 2 такта. Не очень плохо.
Пример внутреннего цикла с билинейной фильтрацией через инструкции MMX:
mov eax,u ; 24:8 fixedpoint
mov ebx,v ; 24:8 fixedpoint
mov ebp,length
xor ecx,ecx
xor edx,edx
mov esi,texture
mov edi,outputbuffer
inner:
mov dl,ah ; dl = (u >> 8)
add eax,du ; u += du
mov dh,bh ; dh = (v >> 8)
add ebx,dv ; v += dv
mov cl,al ; ecx = (u & 0xFF) = fu - дробная
; часть u
movd mm0,[esi+4*edx] ; грузим пикселы
movd mm1,[esi+4*edx+4]
movd mm2,[esi+4*edx+4*256]
movd mm3,[esi+4*edx+4*257]
punpcklbw mm0,mm0 ; распаковываем пикселы
punpcklbw mm1,mm1
punpcklbw mm2,mm2
punpcklbw mm3,mm3
psrlw mm0,1 ; для того, чтобы были беззнаковые
psrlw mm1,1 ; числа и pmulhw (знаковое
psrlw mm2,1 ; умножение) работало нормально
psrlw mm3,1
psubw mm1,mm0 ; mm1 = tex[v+1][u]-tex[v][u]
psubw mm3,mm2 ; mm3 = tex[v+1][u+1]-tex[v][u+1]
pmulhw mm1,tab[8*ecx] ; mm1 *= fu
pmulhw mm3,tab[8*ecx] ; mm3 *= fu
add esi,4
add edi,4
psllw mm1,7 ; корректируем результат умножения
psllw mm3,7
paddsw mm0,mm1 ; mm0 = tex[v][u] + mm1
paddsw mm2,mm3 ; mm2 = tex[v][u+1] + mm3
mov cl,bl ; ecx = (v & 0xFF) = fv - дробная
; часть v
psubw mm2,mm0 ; mm2 -= mm0
pmulhw mm2,tab[8*ecx] ; mm2 *= fv
psrlw mm0,7 ; корректируем результат умножения
paddsw mm0,mm2 ; mm0 += mm2 - отфильтрованное
; значение
packuswb mm0,mm0 ; пакуем пиксел
movd [edi-4],mm0 ; записываем его
dec ebp
jnz inner
Отдельного упоминания и разъяснение требует табличка tab. Это просто табличка
дробных частей в готовом для MMX-умножения виде:
tab label qword
dw 0,0,0,0
dw 1,1,1,1
dw 2,2,2,2
; ...
dw 255,255,255,255
То есть в данном примере tab[8*ecx] = [cl, cl, cl, cl] - как раз готовая для
использования в MMX-инструкциях дробная часть.
Здесь получается уже довольно приличное количество тактов на пиксел, порядка
двадцати. Но несмотря на это, вышеприведенный цикл уронил fps на моей любимой
тестовой сцене всего лишь в 1.5 раза по сравнению с обычным текстурированием.
Тоже не очень плохо. В общем, успехов в использовании. Только не забывайте
включать поддержку не-MMX режима для тех, у кого MMX нет, и, соответственно,
детектор наличия MMX-инструкций.