Я работаю над реализацией SSE2 альфа-смеси RGB565 / RGB555 и столкнулся с проблемой, которую не смог обернуть. Это альфа-смесь в 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 и обратно при каждой арифметической операции). Тем не менее, он возвращает неправильные значения! Я довольно уверен, что первая область, с которой у него проблемы, это вычитание. Определенно возможно получить отрицательное значение от этой операции вычитания.
Моим запланированным решением было бы сравнить четыре 32-битных значения и затем поменять их на месте перед вычитанием, чтобы получить абсолютное значение вычитания. Я знаю о свойствах _mm_cmpgt / _mm_cmplt и о том, как они работают, хотя я не знаю, как использовать битовые маски, которые они выводят, чтобы делать то, что мне нужно.
Будем весьма благодарны за любое возможное решение о том, как я получу абсолютное значение, сохраняя DWORDS источника и назначения на своих местах. Советы по оптимизации этого кода также были бы хорошими.
Вот как можно получить абсолютное значение из 16 (или 32-разрядных) значений с помощью SSE2:
Отрицание дополнения 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
Других решений пока нет …