前回予告した通り、今回は64 bit 分のデータを mmx のレジスタを 8 つ( 実際には 7つですが)使って一気に処理する方法を書いてみます。 その処理を素直に書くと下の様になります。 ただし 64bit 分を一回で処理しますので画像のサイズは 8 byte 境界に 合わせなければなりません。
// α合成 MMX で 2+2/3 画素まとめて計算版(64 bit)
// ただしサイズが 8 の倍数である必要あり
//
// コピー先 = コピー元1 * α + コピー元1 * (1-α)
//
VOID AlphaBlending(LPBYTE lpDest, // コピー先
LPBYTE lpSrc1, // コピー元1
LPBYTE lpSrc2, // コピー元2
DWORD dwLength, // コピーする長さ
WORD wAlpha){ // α値
DWORD dwAlpha = (DWORD)wAlpha;
DWORD dwAlpha2 = (256 - dwAlpha);
_asm{
mov eax,dwLength // eax = カウンタ
mov ebx,lpDest // ebx = コピー先アドレス
mov ecx,lpSrc1 // ecx = コピー元1 アドレス
mov edx,lpSrc2 // edx = コピー元2 アドレス
shr eax,3 // eax/=8
movd mm4,dwAlpha // mm4 = 0α0α0α0α
punpcklwd mm4,mm4
punpckldq mm4,mm4
movd mm5,dwAlpha2 // mm5 = 0(1-α)0(1-α)0(1-α)0(1-α)
punpcklwd mm5,mm5
punpckldq mm5,mm5
pxor mm6,mm6 // mm6 = パック用ダミー = 0
LOOP1:
// データ読み込み(64 bit 分)
movq mm0,[ecx]
movq mm1,[edx]
// コピー
movq mm2,mm0
movq mm3,mm1
// byte -> word にアンパック
punpckhbw mm0,mm6 // High
punpckhbw mm1,mm6
punpcklbw mm2,mm6 // Low
punpcklbw mm3,mm6
// パック掛け算
pmullw mm0,mm4
pmullw mm1,mm5
pmullw mm2,mm4
pmullw mm3,mm5
// パック足し算して256で割る
paddw mm0,mm1
paddw mm2,mm3
psrlw mm0,8
psrlw mm2,8
// word->byte にパックして転送
packuswb mm2,mm0
movq [ebx],mm2
// ポインタアップ
add ecx,8
add edx,8
add ebx,8
// カウンタダウンしてループ
dec eax
jne LOOP1
emms
}
}
さて結果ですが、MMX ペンティアム 150 で 460x320 のビットマップ
2 枚を混合させる計算速度は平均 60 回/秒 になりました。前回は
49 回/秒でしたので速くなったことが分かります。もちろん今回も上のソースは
全く最適化を行っていないので最適化を行えばもう少し速くなると思います。
という訳で MMX を使ってα合成をする方法を書いてきましたが、最終的には 60 回/秒までは計算できるようになりました。ただ 30 FPS 位のゲームならこれ 位の計算速度でも良いと思いますが 60 FPS まで出そうとするときついと思います。 結局、リアルタイムで半透明を実現するには Direct X を使ってビデオボードに やってもらうのが一番てっとりばやいでしょうね(^^;