これ以上の高速化はあんまし思いつかないや、というところまで描画処理をまとめました。一例としてαブレンディングの最適化結果など。
1 画素ずつ処理する場合は、下記の非 SIMD 処理で...
inline const PixPacked32 operator()(const PixPacked32 iDst, const PixPacked32 iSrc, const PixLevel iAlpha) { const auto aSrcAlpha = convertRange255to256(iAlpha); const auto aDstAlpha = 256 - aSrcAlpha; const auto aDst13 = (iDst & 0xff00ff) * aDstAlpha + (iSrc & 0xff00ff) * aSrcAlpha; const auto aDst02 = (iDst & 0x00ff00) * aDstAlpha + (iSrc & 0x00ff00) * aSrcAlpha; return ( (aDst13 & 0xff00ff00) | (aDst02 & 0x00ff0000) ) >> 8; }
デスティネーションが 16 バイトアライメント可能かつ 4 画素ずつ処理できる場合は、下記の SIMD (SSE2) 処理で...
inline const PixPacked128 operator()(const PixPacked128 iDst, const PixPacked128 iSrc, const PixPacked128 iAlpha32x4) { const auto aZero = _mm_setzero_si128(); const auto aSrcAlpha = _mm_srli_epi16(_mm_mullo_epi16(iAlpha32x4, _mm_set1_epi32(129)), 7); // 0-255 -> 0-256 const auto aDstAlpha = _mm_sub_epi32(_mm_set1_epi32(256), aSrcAlpha); // 下位 64-bit auto aSrcAlpha1 = _mm_shufflelo_epi16(aSrcAlpha, _MM_SHUFFLE(2, 2, 0, 0)); auto aDstAlpha1 = _mm_shufflelo_epi16(aDstAlpha, _MM_SHUFFLE(2, 2, 0, 0)); aSrcAlpha1 = _mm_unpacklo_epi16(aSrcAlpha1, aSrcAlpha1); aDstAlpha1 = _mm_unpacklo_epi16(aDstAlpha1, aDstAlpha1); auto aSrc = _mm_unpacklo_epi8(aZero, iSrc); auto aDst = _mm_unpacklo_epi8(aZero, iDst); aSrc = _mm_mulhi_epu16(aSrc, aSrcAlpha1); aDst = _mm_mulhi_epu16(aDst, aDstAlpha1); const auto aLo = _mm_add_epi16(aSrc, aDst); // 上位 64-bit aSrcAlpha1 = _mm_shufflehi_epi16(aSrcAlpha, _MM_SHUFFLE(2, 2, 0, 0)); aDstAlpha1 = _mm_shufflehi_epi16(aDstAlpha, _MM_SHUFFLE(2, 2, 0, 0)); aSrcAlpha1 = _mm_unpackhi_epi16(aSrcAlpha1, aSrcAlpha1); aDstAlpha1 = _mm_unpackhi_epi16(aDstAlpha1, aDstAlpha1); aSrc = _mm_unpackhi_epi8(aZero, iSrc); aDst = _mm_unpackhi_epi8(aZero, iDst); aSrc = _mm_mulhi_epu16(aSrc, aSrcAlpha1); aDst = _mm_mulhi_epu16(aDst, aDstAlpha1); const auto aHi = _mm_add_epi16(aSrc, aDst); return _mm_packus_epi16(aLo, aHi); }
SIMD 版の処理の半分以上がパック (アンパック) とシャッフルというところが泣けます。これでもかなり削ったんですけれど…。
ちょっとだけ工夫があるのは、8-bit の右シフトの回数が減っているところでしょうか。画素を 16-bit にアンパックする際、{ 0xbb00, 0xgg00, 0xrr00, 0xxx00, 0xbb00, 0xgg00, 0xrr00, 0xxx00 } として、α値 (0-256) との乗算結果の上位 16-bit を採用することでシフト不要にしてあります。
SIMD ってもうちょっと使いやすくならないものでしょうか。いくら並列演算がすごくても、並列演算自体が使いにくかったら仕方ないと思うのですが…。
とにもかくにも、今度こそ高速化は終わり。機能作ります!機能!