Быстро 24-разрядный массив-> 32-разрядное преобразование массива?

Быстрая сводка:

У меня есть массив 24-разрядных значений. Какое-либо предложение о том, как быстро развернуть отдельные 24-разрядные элементы массива в 32-разрядные элементы?

Подробнее:

Я обрабатываю входящие видеокадры в Пиксельных шейдерах использования в реальном времени в DirectX 10. Камень преткновения - то, что мои кадры входят от аппаратных средств получения с 24-разрядными пикселями (или как YUV или как изображения RGB), но DX10 берет 32-разрядные пиксельные структуры. Так, я должен развернуть 24-разрядные значения до 32 битов, прежде чем я смогу загрузить их в GPU.

Я действительно не забочусь о том, что я установил остающиеся 8 битов на, или где входящие 24 бита находятся в том 32-разрядном значении - я могу зафиксировать все это в пиксельном шейдере. Но я должен сделать преобразование от 24-разрядного до 32-разрядного действительно быстро.

Я не ужасно знаком с операциями SSE SIMD, но от моего поверхностного взгляда не похоже, что я могу сделать, расширение с помощью них, учитывая мои чтения и записи не является тем же размером. Какие-либо предложения? Или застревают я последовательно массажирующий этот набор данных?

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

13
задан Clippy 4 June 2010 в 11:37
поделиться

2 ответа

Код ниже должен быть довольно быстрым. Он копирует 4 пикселя в каждой итерации, используя только 32-битные инструкции чтения/записи. Указатели источника и назначения должны быть выровнены по 32 битам.

uint32_t *src = ...;
uint32_t *dst = ...;

for (int i=0; i<num_pixels; i+=4) {
    uint32_t sa = src[0];
    uint32_t sb = src[1];
    uint32_t sc = src[2];

    dst[i+0] = sa;
    dst[i+1] = (sa>>24) | (sb<<8);
    dst[i+2] = (sb>>16) | (sc<<16);
    dst[i+3] = sc>>8;

    src += 3;
}

Edit:

Вот способ сделать это с помощью SSSE3-инструкций PSHUFB и PALIGNR. Код написан с использованием интринсиков компилятора, но при необходимости его несложно перевести на ассемблер. В каждой итерации копируется 16 пикселей. Указатели источника и назначения Должны быть выровнены по 16 байтам, иначе произойдет сбой. Если они не выровнены, вы можете заставить его работать, заменив _mm_load_si128 на _mm_loadu_si128 и _mm_store_si128 на _mm_storeu_si128, но это будет медленнее.

#include <emmintrin.h>
#include <tmmintrin.h>

__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 (int i=0; i<num_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;
}

SSSE3 (не путать с SSE3) потребует относительно нового процессора: Core 2 или новее, и я полагаю, что AMD пока не поддерживает его. Выполнение этой задачи только с инструкциями SSE2 потребует гораздо больше операций, и, возможно, оно того не стоит.

22
ответ дан 1 December 2019 в 21:11
поделиться

Различные размеры ввода/вывода не являются препятствием для использования simd, это просто ограничение скорости. Вам нужно будет разбить данные так, чтобы чтение и запись осуществлялись полными словами simd (16 байт).

В этом случае вы прочитаете 3 слова SIMD (48 байт == 16 пикселей rgb), выполните расширение, а затем запишите 4 слова SIMD.

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

1
ответ дан 1 December 2019 в 21:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: