0

我正在研究 RGB565/RGB555 Alpha 混合的 SSE2 实现,我遇到了一个我无法解决的问题。这是 C++ 中的 Alpha 混合:

#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 并返回),这也是一个显着的性能改进。但是,它返回的值不正确!我非常有信心它遇到的第一个问题是减法。绝对有可能从减法运算中得到负值。

我计划的解决方案是比较四个 32 位值,然后在减法之前就地交换它们以获得减法的绝对值。我知道 _mm_cmpgt/_mm_cmplt 内在函数以及它们是如何工作的,尽管我不知道如何使用它们输出的位掩码来做我需要的事情。

对于如何在将源和目标 DWORDS 保留在其位置上的同时获得绝对值的任何可能的解决方案,我们将不胜感激。有关优化此代码的提示也很好。

4

1 回答 1

1

Here's how to get absolute value of 16 (or 32-bit) values using SSE2:

2's complement negation is 1's complement followed by increment

-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 回答