
p210 is a planar format with a Y plane followed an interleaved downsampled UV plane
conceptually current implementation is:
[*] Загрузите 2 блока из 4 слов в один __M256i
[*] Маска и сдвиг для извлечения каждого компонента в отдельный __m256i
[*] Упакуйте 32 -битные значения вниз до 16 бит
shipuffe & blend, чтобы Generation out out out out out out out out out out out out out out out out out out out out out out out out out out out out out out out ou и yv. Lane) < /li>
Сдвиньте до 16bit & write на выход < /li>
< /ul>
, которые могут быть представлены визуально, как это (светло -желтые не имеют значений ярко -желтого цвета. src = "https://i.sstatic.net/udm2qsge.png"/>
Фактический код
void convert(const uint8_t* src, int srcStride, uint8_t* dstY, uint8_t* dstUV, int width, int height)
{
const int groupsPerLine = width / 12;
// Pre-compute constants once outside the loops
const __m256i mask10 = _mm256_set1_epi32(0x3FF);
const __m256i zeroes = _mm256_setzero_si256();
const __m256i s2_shuffleMask = _mm256_setr_epi8(
-1, -1, 0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1, -1, -1,
-1, -1, 0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1, -1, -1
);
const __m256i s1_shuffleMask = _mm256_setr_epi8(
0, 1, -1, -1, 2, 3, 4, 5, -1, -1, 6, 7, -1, -1, -1, -1,
0, 1, -1, -1, 2, 3, 4, 5, -1, -1, 6, 7, -1, -1, -1, -1
);
const __m256i s0_shuffleMask = _mm256_setr_epi8(
0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, -1, -1, 4, 5, 6, 7, -1, -1, -1, -1, -1, -1
);
const uint8_t y_blend_mask_1 = 0b00001001;
const uint8_t y_blend_mask_2 = 0b00011011;
const uint8_t uv_blend_mask_1 = 0b00110110;
const uint8_t uv_blend_mask_2 = 0b00101101;
// Process all lines with a single loop implementation
for (int lineNo = 0; lineNo < height; ++lineNo)
{
const uint32_t* srcLine = reinterpret_cast(src + lineNo * srcStride);
uint16_t* dstLineY = reinterpret_cast(dstY + lineNo * width * 2);
uint16_t* dstLineUV = reinterpret_cast(dstUV + lineNo * width * 2);
// Process all complete groups
int g = 0;
for (; g < groupsPerLine - (lineNo == height - 1 ? 1 : 0); ++g)
{
// Load 8 dwords
__m256i dwords = _mm256_loadu_si256(reinterpret_cast(srcLine));
// transpose to extract a 10-bit component into a 32bit slot spread across 3 registers
__m256i s0_32 = _mm256_and_si256(dwords, mask10); // bits 0–9
__m256i s1_32 = _mm256_and_si256(_mm256_srli_epi32(dwords, 10), mask10); // bits 10–19
__m256i s2_32 = _mm256_and_si256(_mm256_srli_epi32(dwords, 20), mask10); // bits 20–29
// pack down to 16bit and fill the remainder with zeroes
__m256i s0_16 = _mm256_packs_epi32(s0_32, zeroes);
__m256i s1_16 = _mm256_packs_epi32(s1_32, zeroes);
__m256i s2_16 = _mm256_packs_epi32(s2_32, zeroes);
// shuffle to prepare for blending
__m256i s0_16_shuffled = _mm256_shuffle_epi8(s0_16, s0_shuffleMask);
__m256i s1_16_shuffled = _mm256_shuffle_epi8(s1_16, s1_shuffleMask);
__m256i s2_16_shuffled = _mm256_shuffle_epi8(s2_16, s2_shuffleMask);
// blend to y and uv
__m256i y_tmp = _mm256_blend_epi16(s0_16_shuffled, s1_16_shuffled, y_blend_mask_1);
__m256i uv_tmp = _mm256_blend_epi16(s0_16_shuffled, s1_16_shuffled, uv_blend_mask_1);
__m256i y = _mm256_blend_epi16(s2_16_shuffled, y_tmp, y_blend_mask_2);
__m256i uv = _mm256_blend_epi16(s2_16_shuffled, uv_tmp, uv_blend_mask_2);
// scale
__m256i y_scaled = _mm256_slli_epi16(y, 6);
// write 96 bits from each lane
__m128i y_lo = _mm256_extracti128_si256(y_scaled, 0);
__m128i y_hi = _mm256_extracti128_si256(y_scaled, 1);
_mm_storeu_si128(reinterpret_cast(dstLineY), y_lo);
_mm_storeu_si128(reinterpret_cast(dstLineY + 6), y_hi);
// scale
__m256i uv_scaled = _mm256_slli_epi16(uv, 6);
// write 96 bits from each lane
__m128i uv_lo = _mm256_extracti128_si256(uv_scaled, 0);
__m128i uv_hi = _mm256_extracti128_si256(uv_scaled, 1);
_mm_storeu_si128(reinterpret_cast(dstLineUV), uv_lo);
_mm_storeu_si128(reinterpret_cast(dstLineUV + 6), uv_hi);
dstLineY += 12;
dstLineUV += 12;
srcLine += 8;
}
// Handle last group for the last line as the m128i can overflow
if (lineNo == height - 1 && g < groupsPerLine)
{
// Load 8 dwords
__m256i dwords = _mm256_loadu_si256(reinterpret_cast(srcLine));
// transpose to extract a 10-bit component into a 32bit slot spread across 3 registers
__m256i s0_32 = _mm256_and_si256(dwords, mask10); // bits 0–9
__m256i s1_32 = _mm256_and_si256(_mm256_srli_epi32(dwords, 10), mask10); // bits 10–19
__m256i s2_32 = _mm256_and_si256(_mm256_srli_epi32(dwords, 20), mask10); // bits 20–29
// pack down to 16bit and fill the remainder with zeroes
__m256i s0_16 = _mm256_packs_epi32(s0_32, zeroes);
__m256i s1_16 = _mm256_packs_epi32(s1_32, zeroes);
__m256i s2_16 = _mm256_packs_epi32(s2_32, zeroes);
// shuffle to prepare for blending
__m256i s0_16_shuffled = _mm256_shuffle_epi8(s0_16, s0_shuffleMask);
__m256i s1_16_shuffled = _mm256_shuffle_epi8(s1_16, s1_shuffleMask);
__m256i s2_16_shuffled = _mm256_shuffle_epi8(s2_16, s2_shuffleMask);
// blend to y and uv
__m256i y_tmp = _mm256_blend_epi16(s0_16_shuffled, s1_16_shuffled, y_blend_mask_1);
__m256i uv_tmp = _mm256_blend_epi16(s0_16_shuffled, s1_16_shuffled, uv_blend_mask_1);
__m256i y = _mm256_blend_epi16(s2_16_shuffled, y_tmp, y_blend_mask_2);
__m256i uv = _mm256_blend_epi16(s2_16_shuffled, uv_tmp, uv_blend_mask_2);
// scale
__m256i y_scaled = _mm256_slli_epi16(y, 6);
// write 96 bits from each lane
__m128i y_lo = _mm256_extracti128_si256(y_scaled, 0);
__m128i y_hi = _mm256_extracti128_si256(y_scaled, 1);
alignas(32) uint16_t tmpY[16] = { 0 };
_mm_storeu_si128(reinterpret_cast(tmpY), y_lo);
_mm_storeu_si128(reinterpret_cast(tmpY + 6), y_hi);
// scale
__m256i uv_scaled = _mm256_slli_epi16(uv, 6);
// write 96 bits from each lane
__m128i uv_lo = _mm256_extracti128_si256(uv_scaled, 0);
__m128i uv_hi = _mm256_extracti128_si256(uv_scaled, 1);
alignas(32) uint16_t tmpUV[16] = { 0 };
_mm_storeu_si128(reinterpret_cast(tmpUV), uv_lo);
_mm_storeu_si128(reinterpret_cast(tmpUV + 6), uv_hi);
// Calculate remaining pixels to avoid writing past the end of the buffer
const int remainingPixels = width - g * 12;
const size_t bytesToCopy = std::min(24, remainingPixels * 2); // 2 bytes per pixel
std::memcpy(dstLineY, tmpY, bytesToCopy);
std::memcpy(dstLineUV, tmpUV, bytesToCopy);
dstLineY += 12;
dstLineUV += 12;
srcLine += 8;
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/796 ... using-avx2