3回目のMMX
なんとなく遥か昔に24bitカラー(888)から16bitカラー(565)に落とす話題があったのを思い出してこういうのってMMXで速く出来るんじゃないかとまたまた勉強ついでにやってみることにしました(^^;
24bitから16bitに落とすには、厳密にはそれぞれの色をそのままの割合で落とすのですが、どうせ下位bitは無視しても問題ないだろうということで、上位ビットだけ持ってくると速いです。
WORD RGB888TO565( const DWORD rgb888 ) { const WORD wr = static_cast< WORD >( ( rgb888 >> 8 ) & 0x0000f800 ); const WORD wg = static_cast< WORD >( ( rgb888 >> 5 ) & 0x000007e0 ); const WORD wb = static_cast< WORD >( ( rgb888 >> 3 ) & 0x0000001f ); return ( wr | wg | wb ); }
これを全ピクセルについてやるわけです。MMXの利点は64bitを1命令でやってしまうところなので、うまく2ピクセルずつやれば単純計算で2倍の速度がでるはずです。
void RGB888to565( BYTE* pbyColor565, const BYTE* pbyColor888, unsigned int nX, unsigned int nY ) { const __int64 maskR = 0x000000f80000f800; const __int64 maskG = 0x00000007e00007e0; const __int64 maskB = 0x000000001f00001f; const unsigned int nSize = nX * nY; _asm{ mov eax, pbyColor565 mov ebx, pbyColor888 mov ecx, nSize shr ecx, 1 jz BYTE0 movq mm0, maskR movq mm1, maskG movq mm2, maskB LOOP1: movq mm7, [ebx] //R movq mm3, mm7 psrl mm3, 8 pand mm3, mm0 //G movq mm4, mm7 psrl mm4, 5 pand mm4, mm1 //B movq mm5, mm7 psrl mm5, 3 pand mm5, mm2 //まぜまぜ por mm3, mm4 por mm3, mm5 //上のピクセルがずれているので補正 movq mm6, mm3 psrl mm6, 24 punpcklwd mm3, mm6 // psll mm6, 16 // por mm3,mm6 //転送 movd [eax], mm3 //次の2ピクセルに移動 add eax, 4 add ebx, 6 dec ecx jnz LOOP1 BYTE0: mov ecx, nSize and ecx, 1 jz END mov ecx, [ebx] mov edx, ecx shr edx, 8 and edx, 0000f800h mov [eax], dx mov edx, ecx shr edx, 5 and edx, 000007e0h or [eax], dx mov edx, ecx shr edx, 3 and edx, 0000001fh or [eax], dx END: emms } return; }
一応、テクスチャが相手だということでXとYに分けてますが一緒です(^^; 今回はSrcとDestでサイズが違うので普通にbyte数にすると誤用される可能性があるということで分けました(ていうか使うのかこれ?)
ようやくMMXらしくなってきました(何) レジスタがたくさんあるのがいいですね。あまり考える必要がない(^^;
今回、勉強のために無駄にpunpcklwdという命令を使ってます。これはアンパック命令というやつらしくて二つの変数の下位(上位)半分を交互に入れるという命令です(詳しくは適当なMMXのページでも見てください)。今回は下位4バイトしか使ってないのでその威力は微々たるものですがまあ勉強のためということで。
んで結果なんですが、普通にコンパイルしたらかなり速かったんですが、最適化コンパイルだと残念ながら殆どかわりませんね。やっぱりちゃんとuとかvとか考えなきゃいけないのかな?
うーん、先は長そうだ(^^;