・MMXで半透明(3) : MMX 編(その 1) (99/5/25)

前回アセンブラを使ってα合成を行っても平均 11 回/秒 しか計算することが 出来ませんでした。そこで今回は MMX を使ってα合成をやってみる事にします。

その前に簡単に MMX について説明します。MMX の特徴を簡単に言えば

ということがあげられます。ここで整数パック演算というのは複数の整数の計算を 同時に行えるということで、例えば 16 bit の整数の足し算を同時に 4 回行ったり、 32 bit の整数を同時に 2 回掛け算することができます。またサチレーション(飽和) 処理も行ってくれます。この辺りのことはインテルのチュートリアルに簡単に書いて ありますのでそちらを参照して下さい。ともかく、このパック演算を使えば 掛け算と足し算を 4 回同時に行う事ができますので単純に考えても 前回よりも 4 倍位は計算速度が上がることが予想されます。とりあえず MMX を使って素直に書くと下の様なソースになります。
// α合成 MMX 版(32 bit) 
//
// コピー先 = コピー元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,2        // eax/=4

    movd mm4,dwAlpha 
    punpcklwd mm4,mm4
    punpckldq mm4,mm4 // mm4 =  0α0α0α0α
    movd mm5,dwAlpha2 
    punpcklwd mm5,mm5
    punpckldq mm5,mm5 // mm5 = 0(1-α)0(1-α)0(1-α)0(1-α)
    pxor mm6,mm6 // mm6 = パック用ダミー = 0
    pxor mm7,mm7 // mm7 = パック用ダミー = 0

LOOP1:
        // データ読み込み(32 bit)
	movd mm0,[ecx]
	movd mm1,[edx]

	// byte -> word にアンパック
	punpcklbw mm0,mm6
	punpcklbw mm1,mm7

	// パック掛け算
	pmullw mm0,mm4
	pmullw mm1,mm5

	//  word 単位で足して256で割る
	paddw mm0,mm1
	psrlw mm0,8

	// word->byte にパックして転送
	packuswb mm0,mm6
	movd [ebx],mm0

	// カウンタアップ
	add ecx,4
	add edx,4
	add ebx,4

	dec eax
	jne LOOP1

	emms
	}
}
さて結果ですが、MMX ペンティアム 150 で 460x320 のビットマップ 2 枚を混合させる計算速度は平均 49 回/秒 で予想通り 4 倍以上の 計算速度になりました。ついでに高橋名人も越えました(笑)。 もちろん上のソースは全く最適化を 行っていないので最適化を行えばもう少し速くなると思います。

ところで MMX のレジスタは 64 bit なのでメモリからデータを 64 bit 分を一回で読み書きする事が出来るのですが、上では 32 bit分しか 一度に読み書きしていません。また 8 つあるレジスタのうち、6 つしか 使って無いのでちょっと勿体無い感じがします。そこで次回は 64 bit 分のデータをレジスタを 8 つ使って一気に処理する方法を書いてみようと 思います。

(第4回(多分最終回) MMX編(その 2)に続く)