Я работаю над внедрением SSE2 альфа-смеси RGB565/RGB555, и у меня возникла проблема, с которой я не смог обернуть голову вокруг. Это смесь альфа в C++:Мне нужна обычная процедура SSE, чтобы избежать отрицательных чисел при параллельном вычитании
#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 в своих местах, будет с благодарностью. Советы по оптимизации этого кода также будут приятными.
Могу ли я предположить, что вы пишете, как вместо макроса. Компилятор почти наверняка встроит его в любом случае, но функцию можно отладить, пройдя через нее, чтобы вы могли видеть, какие значения у вас есть в том, что SSE-регистр. Я понятия не имею, почему вы используете запятую в последней строке ... –
Возможно, я должен был упомянуть об этом, но все параметры dn являются указателями на массив. Если я напишу его как встроенную функцию, мне придется иметь дело с возвращаемыми значениями для смешанных цветов и затем устанавливать их отдельно. Я уверен, что первая проблемная область - это вычитание. Вторая возможная проблемная область - в умножении, но я уже знаю, как я буду иметь дело с этим, если я все еще получаю неправильные результаты после фиксации вычитания. Изменены запятые к точкам с запятой. – user2645004
Я уверен, что вы можете решить все проблемы макроса -> inline, используя ссылки. И компилятор, скорее всего, будет генерировать эквивалентный код в любом случае. –