0

私は RGB565/RGB555 アルファ ブレンドの SSE2 実装に取り​​組んでいますが、頭を包むことができなかった問題に遭遇しました。これは C++ のアルファ ブレンドです。

#define ALPHA_BLEND_X_W(dst, src, alpha)\
    ts = src; td = dst;\
    td = ((td | (td << 16)) & RGBMask); ts = ((ts | (ts << 16)) & RGBMask);\
    td = (((((ts - td) * alpha + RGBrndX) >> 5) + td) & RGBMask);\
    dst= (td | (td >> 16));

これは、VBA-M および Kega Fusion エミュレーター用のフィルター プラグイン用です。これはすでに非常に高速で正確なブレンドですが、フィルター プラグインに実装する予定のすべての機能を実装するには、速度が重要です。ts と td は 32 ビットの INT で、緑をシフトアウトし、一度にブレンドを計算してから、緑を所定の位置に戻すことができます。

これは、SSE 実装のためにこれまでに得たものです。

#define AlphaBlendX(s, d0, d1, d2, d3, v0, v1, v2, v3)\
    D = _mm_set_epi32(d0, d1, d2, d3);\
    S = _mm_set1_epi32(s);\
    V = _mm_set_epi16(v0, v0, v1, v1, v2, v2, v3, v3);\
    sD = _mm_slli_si128(D, 2);\
    sS = _mm_slli_si128(S, 2);\
    oD = _mm_or_si128(D, sD);\
    oS = _mm_or_si128(S, sS);\
    mD = _mm_and_si128(oD, RGB);\
    mS = _mm_and_si128(oS, RGB);\
    sub = _mm_sub_epi32(mS, mD);\
    hi = _mm_mulhi_epu16(sub, V);\
    lo = _mm_mullo_epi16(sub, V);\
    mul = _mm_or_si128(_mm_slli_si128(hi, 2), lo);\
    rnd = _mm_add_epi64(mul, RND);\
    div = _mm_srli_epi32(rnd, 5);\
    add = _mm_add_epi64(div, mD);\
    D = _mm_and_si128(add, RGB);\
    DD = _mm_srli_si128(D, 2);\
    DDD = _mm_or_si128(D, DD);\
    d0 = _mm_extract_epi16(DDD, 1); d1 = _mm_extract_epi16(DDD, 3); d2 = _mm_extract_epi16(DDD, 5); d3 = _mm_extract_epi16(DDD, 7);

恐ろしく最適化されていない状態でも、顕著なパフォーマンスの向上です (各算術演算で D から DD にスワップするのではなく、すべての異なる変数)。ただし、間違った値を返しています! 問題を抱えている最初の領域は減算であると確信しています。その減算操作から負の値を取得することは間違いなく可能です。

私が計画している解決策は、4 つの 32 ビット値を比較し、減算の前にそれらをその場で交換して、減算の絶対値を取得することです。_mm_cmpgt/_mm_cmplt 組み込み関数とそれらがどのように機能するかは認識していますが、それらが出力するビットマスクを使用して必要なことを行う方法はわかりません。

ソースと宛先の DWORDS をその場所に保持しながら、絶対値を取得する方法について考えられる解決策は、大歓迎です。このコードの最適化に関するヒントもいいでしょう。

4

1 に答える 1

1

SSE2 を使用して 16 (または 32 ビット) 値の絶対値を取得する方法は次のとおりです。

2 の補数の否定は、1 の補数の後にインクリメントが続きます。

-A == (A ^ -1) + 1;

__m128i xmmOriginal, xmmZero, xmmMask, xmmAbsolute;

// xmmOriginal is assumed to be initialized to positive/negative values

xmmZero = _mm_setzero_si128();
xmmMask = _mm_cmplt_epi16(xmmOriginal, xmmZero); // mask = FFFF where negative values are
xmmAbsolute = _mm_xor_si128(xmmMask, xmmOriginal); // bitwise invert the negative values
xmmMask = _mm_srli_epi16(xmmMask, 15); // convert mask FFFF's into 1's
xmmAbsolute = _mm_add_epi16(xmmAbsolute, xmmMask); // done
于 2013-08-05T19:27:01.227 に答える