Использование инструкций 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-инструкций.
1 2 3 4 5
8 8 8
|