Я пытаюсь выполнить преобразование цвета изображения из YCbCr в BGRA (не спрашивайте об A, такая головная боль).
В любом случае, это должно выполняться как можно быстрее, поэтому я написал его, используя встроенные функции компилятора, чтобы использовать преимущества SSE2. Это мое первое приключение в SIMD, я в основном новичок, и я уверен, что есть много, что я делаю неэффективно.
Мой арифметический код для выполнения фактического преобразования цветов оказывается особенно медленным, и VTune Intel демонстрирует его как существенное узкое место.
Так, как я могу ускорить следующий код? Это делается в 32 бита, 4 пикселя за раз. Первоначально я пытался сделать это в 8 битах по 16 пикселей за раз (как в верхнем цикле), но вычисления вызывают переполнение целых чисел и прерывание преобразования. Весь этот процесс, включая декодирование Intel jpeg, занимает ~ 14 мс для одного поля Full HD. Было бы замечательно, если бы я мог снизить его хотя бы до 12 мс, в идеале до 10 мс.
Любая помощь или советы с благодарностью. Спасибо!
const __m128i s128_8 = _mm_set1_epi8((char)128);
const int nNumPixels = roi.width * roi.height;
for (int i=0; i<nNumPixels; i+=32)
{
// Go ahead and prefetch our packed UV Data.
// As long as the load remains directly next, this saves us time.
_mm_prefetch((const char*)&pSrc8u[2][i],_MM_HINT_T0);
// We need to fetch and blit out our k before we write over it with UV data.
__m128i sK1 = _mm_load_si128((__m128i*)&pSrc8u[2][i]);
__m128i sK2 = _mm_load_si128((__m128i*)&pSrc8u[2][i+16]);
// Using the destination buffer temporarily here so we don't need to waste time doing a memory allocation.
_mm_store_si128 ((__m128i*)&m_pKBuffer[i], sK1);
_mm_store_si128 ((__m128i*)&m_pKBuffer[i+16], sK2);
// In theory, this prefetch needs to be some cycles ahead of the first read. It isn't, yet it does appear to save us time. Worth investigating.
_mm_prefetch((const char*)&pSrc8u[1][i],_MM_HINT_T0);
__m128i sUVI1 = _mm_load_si128((__m128i*)&pSrc8u[1][i]);
__m128i sUVI2 = _mm_load_si128((__m128i*)&pSrc8u[1][i+16]);
// Subtract the 128 here ahead of our YCbCr -> BGRA conversion so we can go 16 pixels at a time rather than 4.
sUVI1 = _mm_sub_epi8(sUVI1, s128_8);
sUVI2 = _mm_sub_epi8(sUVI2, s128_8);
// Swizzle and double up UV data from interleaved 8x1 byte blocks into planar
__m128i sU1 = _mm_unpacklo_epi8(sUVI1, sUVI1);
__m128i sV1 = _mm_unpackhi_epi8(sUVI1, sUVI1);
__m128i sU2 = _mm_unpacklo_epi8(sUVI2, sUVI2);
__m128i sV2 = _mm_unpackhi_epi8(sUVI2, sUVI2);
_mm_store_si128((__m128i*)&pSrc8u[1][i], sU1);
_mm_store_si128((__m128i*)&pSrc8u[1][i+16], sU2);
_mm_store_si128((__m128i*)&pSrc8u[2][i], sV1);
_mm_store_si128((__m128i*)&pSrc8u[2][i+16], sV2);
}
const __m128i s16 = _mm_set1_epi32(16);
const __m128i s299 = _mm_set1_epi32(299);
const __m128i s410 = _mm_set1_epi32(410);
const __m128i s518 = _mm_set1_epi32(518);
const __m128i s101 = _mm_set1_epi32(101);
const __m128i s209 = _mm_set1_epi32(209);
Ipp8u* pDstP = pDst8u;
for (int i=0; i<nNumPixels; i+=4, pDstP+=16)
{
__m128i sK = _mm_set_epi32(m_pKBuffer[i], m_pKBuffer[i+1], m_pKBuffer[i+2], m_pKBuffer[i+3]);
__m128i sY = _mm_set_epi32(pSrc8u[0][i], pSrc8u[0][i+1], pSrc8u[0][i+2], pSrc8u[0][i+3]);
__m128i sU = _mm_set_epi32((char)pSrc8u[1][i], (char)pSrc8u[1][i+1], (char)pSrc8u[1][i+2], (char)pSrc8u[1][i+3]);
__m128i sV = _mm_set_epi32((char)pSrc8u[2][i], (char)pSrc8u[2][i+1], (char)pSrc8u[2][i+2], (char)pSrc8u[2][i+3]);
// N.b. - Attempted to do the sub 16 in 8 bits similar to the sub 128 for U and V - however doing it here is quicker
// as the time saved on the arithmetic is less than the time taken by the additional loads/stores needed in the swizzle loop
sY = _mm_mullo_epi32(_mm_sub_epi32(sY, s16), s299);
__m128i sR = _mm_srli_epi32(_mm_add_epi32(sY,_mm_mullo_epi32(s410, sV)), 8);
__m128i sG = _mm_srli_epi32(_mm_sub_epi32(_mm_sub_epi32(sY, _mm_mullo_epi32(s101, sU)),_mm_mullo_epi32(s209, sV)), 8);
__m128i sB = _mm_srli_epi32(_mm_add_epi32(sY, _mm_mullo_epi32(s518, sU)), 8);
//Microsoft's YUV Conversion
//__m128i sC = _mm_sub_epi32(sY, s16);
//__m128i sD = _mm_sub_epi32(sU, s128);
//__m128i sE = _mm_sub_epi32(sV, s128);
//
//__m128i sR = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(_mm_mullo_epi32(s298, sC), _mm_mullo_epi32(s409, sE)), s128), 8);
//__m128i sG = _mm_srli_epi32(_mm_add_epi32(_mm_sub_epi32(_mm_mullo_epi32(s298, sC), _mm_sub_epi32(_mm_mullo_epi32(s100, sD), _mm_mullo_epi32(s208, sE))), s128), 8);
//__m128i sB = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(_mm_mullo_epi32(s298, sC), _mm_mullo_epi32(s516, sD)), s128), 8);
__m128i sKGl = _mm_unpacklo_epi32(sK, sG);
__m128i sKGh = _mm_unpackhi_epi32(sK, sG);
__m128i sRBl = _mm_unpacklo_epi32(sR, sB);
__m128i sRBh = _mm_unpackhi_epi32(sR, sB);
__m128i sKRGB1 = _mm_unpackhi_epi32(sKGh,sRBh);
__m128i sKRGB2 = _mm_unpacklo_epi32(sKGh,sRBh);
__m128i sKRGB3 = _mm_unpackhi_epi32(sKGl,sRBl);
__m128i sKRGB4 = _mm_unpacklo_epi32(sKGl,sRBl);
__m128i p1 = _mm_packus_epi16(sKRGB1, sKRGB2);
__m128i p2 = _mm_packus_epi16(sKRGB3, sKRGB4);
__m128i po = _mm_packus_epi16(p1, p2);
_mm_store_si128((__m128i*)pDstP, po);
}