Быстрый 32-битный массив -> 24-битное преобразование массива в SSE3?(RGB32 -> RGB24) - PullRequest
3 голосов
/ 03 апреля 2012

Этот вопрос относится к ранее отвеченному вопросу: Быстрый 24-битный массив -> 32-битное преобразование массива? В одном ответе interjay любезно разместил код SSE3 для преобразования RGB24 -> RGB32, однако мне также нужно обратное преобразование (RGB32 -> RGB24). Я сделал это (см. Ниже), и мой код определенно работает, но он сложнее, чем код Interjay, и заметно медленнее. Я не мог понять, как точно изменить инструкции: _mm_alignr_epi8 в этом случае не кажется полезным, но я не так знаком с SSE3, как следовало бы. Является ли асимметрия неизбежной или есть более быстрая замена смен и ИЛИ?

RGB32 -> RGB24:

__m128i *src = ...
__m128i *dst = ...
__m128i mask = _mm_setr_epi8(0,1,2,4, 5,6,8,9, 10,12,13,14, -1,-1,-1,-1);
for (UINT i = 0; i < Pixels; i += 16) {
    __m128i sa = _mm_shuffle_epi8(_mm_load_si128(src), mask);
    __m128i sb = _mm_shuffle_epi8(_mm_load_si128(src + 1), mask);
    __m128i sc = _mm_shuffle_epi8(_mm_load_si128(src + 2), mask);
    __m128i sd = _mm_shuffle_epi8(_mm_load_si128(src + 3), mask);
    _mm_store_si128(dst, _mm_or_si128(sa, _mm_slli_si128(sb, 12)));
    _mm_store_si128(dst + 1, _mm_or_si128(_mm_srli_si128(sb, 4), _mm_slli_si128(sc, 8)));
    _mm_store_si128(dst + 2, _mm_or_si128(_mm_srli_si128(sc, 8), _mm_slli_si128(sd, 4)));
    src += 4;
    dst += 3;
}

RGB24 -> RGB32 (вежливость):

__m128i *src = ...
__m128i *dst = ...
__m128i mask = _mm_setr_epi8(0,1,2,-1, 3,4,5,-1, 6,7,8,-1, 9,10,11,-1);
for (UINT i = 0; i < Pixels; i += 16) {
    __m128i sa = _mm_load_si128(src);
    __m128i sb = _mm_load_si128(src + 1);
    __m128i sc = _mm_load_si128(src + 2);
    __m128i val = _mm_shuffle_epi8(sa, mask);
    _mm_store_si128(dst, val);
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sb, sa, 12), mask);
    _mm_store_si128(dst + 1, val);
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sc, sb, 8), mask);
    _mm_store_si128(dst + 2, val);
    val = _mm_shuffle_epi8(_mm_alignr_epi8(sc, sc, 4), mask);
    _mm_store_si128(dst + 3, val);
    src += 3;
    dst += 4;
}

Ответы [ 2 ]

0 голосов
/ 20 июня 2014

Старый вопрос, но я пытался решить ту же проблему, так что ...

Вы можете использовать palignr, если выровняете по правому краю его второй операнд, то есть нули в младших байтах. Вам нужна выровненная по левому краю версия второго, третьего и четвертого слова, а также выровненная по правому краю версия первого, второго и третьего слова.

Что касается второго и третьего слова, GCC будет немного счастливее, если я использую сдвиги для вычисления выравнивания по правому краю из выравнивания по левому краю. Если я использую два разных pshufb, он генерирует 3 ненужных хода.

Вот код. Он использует ровно 8 регистров; если вы находитесь в 64-битном режиме, вы можете попробовать развернуть его на два.

    __m128i mask_right = _mm_set_epi8(14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0, 0x80, 0x80, 0x80, 0x80);
    __m128i mask = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 14, 13, 12, 10, 9, 8, 6, 5, 4, 2, 1, 0);

    for (; n; n -= 16, d += 48, s += 64) {
            __m128i v0 = _mm_load_si128((__m128i *) &s[0]);
            __m128i v1 = _mm_load_si128((__m128i *) &s[16]);
            __m128i v2 = _mm_load_si128((__m128i *) &s[32]);
            __m128i v3 = _mm_load_si128((__m128i *) &s[48]);

            v0 = _mm_shuffle_epi8(v0, mask_right);
            v1 = _mm_shuffle_epi8(v1, mask);
            v2 = _mm_shuffle_epi8(v2, mask);
            v3 = _mm_shuffle_epi8(v3, mask);

            v0 = _mm_alignr_epi8(v1, v0, 4);
            v1 = _mm_slli_si128(v1, 4);       // mask -> mask_right
            v1 = _mm_alignr_epi8(v2, v1, 8);
            v2 = _mm_slli_si128(v2, 4);       // mask -> mask_right
            v2 = _mm_alignr_epi8(v3, v2, 12);

            _mm_store_si128((__m128i *) &d[0], v0);
            _mm_store_si128((__m128i *) &d[16], v1);
            _mm_store_si128((__m128i *) &d[32], v2);
    }

Центральная часть также может быть написана так. Компилятор производит на одну инструкцию меньше, и похоже, что он имеет немного больше параллелизма, но для правильного ответа необходим сравнительный анализ:

            v0 = _mm_shuffle_epi8(v0, mask_right);
            v1 = _mm_shuffle_epi8(v1, mask);
            v2 = _mm_shuffle_epi8(v2, mask_right);
            v3 = _mm_shuffle_epi8(v3, mask);

            __m128i v2l = v2;
            v0 = _mm_alignr_epi8(v1, v0, 4);
            v1 = _mm_slli_si128(v1, 4);             // mask -> mask_right
            v2 = _mm_alignr_epi8(v3, v2, 12);
            v2l = _mm_srli_si128(v2l, 4);           // mask_right -> mask
            v1 = _mm_alignr_epi8(v2l, v1, 8);
0 голосов
/ 03 апреля 2012

Вы можете взять этот ответ и изменить маску перемешивания для перехода с RGB32 на RGB24.

Большая разница заключается в непосредственном вычислении перемешиваний и использовании побитовых операций, чтобы избежать смещения.Кроме того, использование выровненной потоковой записи вместо выровненной записи не портит кеш.

...