・MMXで半透明(4) : MMX 編(その 2) (99/6/27)

前回予告した通り、今回は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 を使ってビデオボードに やってもらうのが一番てっとりばやいでしょうね(^^;