2013/01/06

アルファブレンド

__declspec(align(16)) unsigned int erase_alpha[4] = 
{
    0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000
};
__declspec(align(16)) unsigned char alpha_mask[16] = 
{
    0x06, 0x80, 0x06, 0x80, 0x06, 0x80, 0x06, 0x80,
    0x0e, 0x80, 0x0e, 0x80, 0x0e, 0x80, 0x0e, 0x80,
};
__declspec(align(16)) unsigned char swap_endian_bl[16] = 
{
    0x80, 0x00, 0x80, 0x02, 0x80, 0x04, 0x80, 0x06,
    0x80, 0x08, 0x80, 0x0a, 0x80, 0x0c, 0x80, 0x0e,
};
__declspec(align(16)) unsigned char swap_endian_lb[16] = 
{
    0x01, 0x80, 0x03, 0x80, 0x05, 0x80, 0x07, 0x80,
    0x09, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x0f, 0x80,
};

void alpha_blend_sse4(void* background, void* foreground, int count)
{
    __asm 
    {
        mov ecx, count
        mov esi, dword ptr background
        mov edi, dword ptr foreground
        
        movdqa xmm6, dword ptr swap_endian_bl
        movdqa xmm7, dword ptr alpha_mask

L0:
        //-------------------------------------- Get B.rgb
        movdqa xmm0, [esi]    // B.argb
        movhlps xmm1, xmm0

        pmovzxbw xmm2, xmm0 // B.rgb
        pmovzxbw xmm3, xmm1 // B.rgb

        // using: xmm2, xmm3

        //-------------------------------------- Get F.rgb
        movdqa xmm4, [edi] // F.argb
        movhlps xmm5, xmm4

        // escape swap_endian_bl and alpha_mask to xmm0 and xmm1
        movdqa xmm0, xmm6
        movdqa xmm1, xmm7

        pmovzxbw xmm6, xmm4 // F.rgb
        pmovzxbw xmm7, xmm5 // F.rgb

        // using: xmm2, xmm3, xmm6, xmm7

        //-------------------------------------- Get F.a
        movdqa xmm4, xmm6
        movdqa xmm5, xmm7
        pshufb xmm4, xmm1 // F.a   xmm1 = alpha_mask
        pshufb xmm5, xmm1 // F.a
        
        // using: xmm2, xmm3, xmm4, xmm5, xmm6, xmm7

        //-------------------------------------- Flip endianness
        pshufb xmm2, xmm0 // xmm0 = swap_endian_bl
        pshufb xmm3, xmm0
        pshufb xmm4, xmm0
        pshufb xmm5, xmm0
        pshufb xmm6, xmm0
        pshufb xmm7, xmm0

        
        //-------------------------------------- Calc F.rgb * F.a
        pmulhuw xmm6, xmm4
        pmulhuw xmm7, xmm5
        
        //-------------------------------------- Calc B.rgb * F.a
        pmulhuw xmm4, xmm2
        pmulhuw xmm5, xmm3

        //-------------------------------------- Calc (F.rgb * F.a) + B.rgb
        paddq xmm6, xmm2
        paddq xmm7, xmm3

        //-------------------------------------- Calc ((F.rgb * F.a) + B.rgb) - (B.rgb * F.a)
        psubq xmm4, xmm6
        psubq xmm5, xmm7

        //-------------------------------------- Flip endianness
        movdqa xmm2, dword ptr swap_endian_lb
        pshufb xmm4, xmm2
        pshufb xmm5, xmm2

        //-------------------------------------- Pack values
        packuswb xmm4, xmm5
        por xmm4, dword ptr erase_alpha

        //-------------------------------------- Write result
        movdqa [esi], xmm4

        
        //-------------------------------------- Restore escaped values
        movdqa xmm6, xmm0
        movdqa xmm7, xmm1
        
        //-------------------------------------- 
        add esi, 16
        add edi, 16
        sub ecx, 1
        jnz L0
    };
}

初のインラインアセンブラで頑張ったけどまだ色がおかしい……。

処理時間は Core i7 920 上で 1280 x 720 の画像を 2 枚重ねると 1.2 msec くらい。普通にやるより 5 倍近く速い。

AVX っていう命令系も使えればメモリへアクセスしてる部分が減りそうなんだけど、Sandy 以降の CPU じゃないと使えないらしい。残念。

0 件のコメント:

コメントを投稿