我正在研究 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 保留在其位置上的同时获得绝对值的任何可能的解决方案,我们将不胜感激。有关优化此代码的提示也很好。