Встроенные функции C ++ SSE2 или AVX2 для преобразования градаций серого в ARGB

Мне было интересно, есть ли целочисленная инструкция SSE2 / AVX2 или последовательность инструкций (или встроенных функций), которые должны быть выполнены для достижения следующего результата:

Дана строка из 8 байтовых пикселей вида:

A = {a, b, c, d, e, f, g, h}

Есть ли способ загрузить эти пиксели в регистр YMM, который содержит 8 32-битных пикселей ARGB, так что начальное значение в градациях серого передается в другие 2 байта каждого соответствующего 32-битного пикселя? Результат должен быть примерно таким: (0 — это альфа-значение)

B = {0aaa, 0bbb, 0ccc, 0ddd, 0eee, 0fff, 0ggg, 0hhh}

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

Любая помощь будет оценена. Спасибо!

Update1

Спасибо за ваши ответы. У меня все еще есть проблема, хотя:

Я собрал этот небольшой пример и скомпилировал с VS2015 на x64.

int main()
{
unsigned char* pixels = (unsigned char*)_aligned_malloc(64, 32);
memset(pixels, 0, 64);

for (unsigned char i = 0; i < 8; i++)
pixels[i] = 0xaa + i;

__m128i grayscalePix = _mm_load_si128((const __m128i*)pixels);
__m256i rgba = _mm256_cvtepu8_epi32(grayscalePix);
__m256i mulOperand = _mm256_set1_epi32(0x00010101);

__m256i result = _mm256_mullo_epi32(rgba, mulOperand);

_aligned_free(pixels);
return 0;
}

Проблема в том, что после выполнения

__m256i rgba = mm256_cvtepu8_epi32(grayscalePix)

RGBA только первые четыре двойных слова установлены. Последние четыре все 0.

Руководство разработчика Intel гласит:

VPMOVZXBD ymm1, xmm2 / m64
Ноль расширяет 8 упакованных 8-битных целых в младшем 8
байтов от xmm2 / m64 до 8 упакованных 32-битных целых чисел в
ymm1.

Я не уверен, является ли это предполагаемым поведением, или я все еще что-то упускаю.

Благодарю.

3

Решение

Начните с PMOVZX, как предлагает Марк.

Но после этого PSHUFB (_mm256_shuffle_epi8) будет намного быстрее, чем PMULLD, за исключением того, что он конкурирует за случайный порт с PMOVZX. (И он работает на линии, так что вам все еще нужен PMOVZX).

Так что, если вы заботитесь только о пропускной способности, а не о задержке, то _mm256_mullo_epi32 это хорошо. Но если задержка имеет значение, или если ваши узкополосные узкие места в чем-то отличны от 2-х команд в случайном порядке на каждый вектор, тогда PSHUFB для дублирования байтов в каждом пикселе должен быть лучшим.

На самом деле, даже для пропускной способности, _mm256_mullo_epi32 плохо для HSW и BDW: это 2 мопа (задержка 10 с) для p0, поэтому это 2 моп для одного порта.

В SKL это 2 мопа (задержка 10 с) для p01, поэтому он может поддерживать такую ​​же пропускную способность на тактовую частоту, что и VPMOVZXBD. Но это дополнительный 1 моп, что делает его более вероятным узким местом.

(VPSHUFB равен 1 моп, задержка 1 с для порта 5 на всех процессорах Intel, поддерживающих AVX2.)

4

Другие решения

Вы можете загрузить упакованные байты в регистр,
call __m256i _mm256_cvtepu8_epi32 (__m128i a) преобразовать в 32-битные значения, а затем умножить на 0x00010101, чтобы воспроизвести серую шкалу в R, G и B.

2

По вопросам рекламы [email protected]