2013-08-02 2 views
0

Я работаю над внедрением 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 в своих местах, будет с благодарностью. Советы по оптимизации этого кода также будут приятными.

+0

Могу ли я предположить, что вы пишете, как вместо макроса. Компилятор почти наверняка встроит его в любом случае, но функцию можно отладить, пройдя через нее, чтобы вы могли видеть, какие значения у вас есть в том, что SSE-регистр. Я понятия не имею, почему вы используете запятую в последней строке ... –

+0

Возможно, я должен был упомянуть об этом, но все параметры dn являются указателями на массив. Если я напишу его как встроенную функцию, мне придется иметь дело с возвращаемыми значениями для смешанных цветов и затем устанавливать их отдельно. Я уверен, что первая проблемная область - это вычитание. Вторая возможная проблемная область - в умножении, но я уже знаю, как я буду иметь дело с этим, если я все еще получаю неправильные результаты после фиксации вычитания. Изменены запятые к точкам с запятой. – user2645004

+0

Я уверен, что вы можете решить все проблемы макроса -> inline, используя ссылки. И компилятор, скорее всего, будет генерировать эквивалентный код в любом случае. –

ответ

0

Вот как получить абсолютное значение 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 
Смежные вопросы