diff --git a/libfreerdp/primitives/prim_YUV.c b/libfreerdp/primitives/prim_YUV.c index e3dfb2e46..27e3bab5b 100644 --- a/libfreerdp/primitives/prim_YUV.c +++ b/libfreerdp/primitives/prim_YUV.c @@ -280,47 +280,6 @@ static pstatus_t general_YUV444SplitToYUV420( return PRIMITIVES_SUCCESS; } -/** - * | R | ( | 256 0 403 | | Y | ) - * | G | = ( | 256 -48 -120 | | U - 128 | ) >> 8 - * | B | ( | 256 475 0 | | V - 128 | ) - */ -static INLINE INT32 C(INT32 Y) -{ - return (Y) - 0L; -} - -static INLINE INT32 D(INT32 U) -{ - return (U) - 128L; -} - -static INLINE INT32 E(INT32 V) -{ - return (V) - 128L; -} - -static INLINE BYTE YUV2R(INT32 Y, INT32 U, INT32 V) -{ - const INT32 r = (256L * C(Y) + 0L * D(U) + 403L * E(V)); - const INT32 r8 = r >> 8L; - return CLIP(r8); -} - -static INLINE BYTE YUV2G(INT32 Y, INT32 U, INT32 V) -{ - const INT32 g = (256L * C(Y) - 48L * D(U) - 120L * E(V)); - const INT32 g8 = g >> 8L; - return CLIP(g8); -} - -static INLINE BYTE YUV2B(INT32 Y, INT32 U, INT32 V) -{ - const INT32 b = (256L * C(Y) + 475L * D(U) + 0L * E(V)); - const INT32 b8 = b >> 8L; - return CLIP(b8); -} - static pstatus_t general_YUV444ToRGB_8u_P3AC4R_general( const BYTE* pSrc[3], const UINT32 srcStep[3], BYTE* pDst, UINT32 dstStep, UINT32 DstFormat, @@ -343,8 +302,8 @@ static pstatus_t general_YUV444ToRGB_8u_P3AC4R_general( for (x = 0; x < nWidth; x++) { const BYTE Y = pY[x]; - const INT32 U = pU[x]; - const INT32 V = pV[x]; + const BYTE U = pU[x]; + const BYTE V = pV[x]; const BYTE r = YUV2R(Y, U, V); const BYTE g = YUV2G(Y, U, V); const BYTE b = YUV2B(Y, U, V); @@ -376,8 +335,8 @@ static pstatus_t general_YUV444ToRGB_8u_P3AC4R_BGRX( for (x = 0; x < nWidth; x++) { const BYTE Y = pY[x]; - const INT32 U = pU[x]; - const INT32 V = pV[x]; + const BYTE U = pU[x]; + const BYTE V = pV[x]; const BYTE r = YUV2R(Y, U, V); const BYTE g = YUV2G(Y, U, V); const BYTE b = YUV2B(Y, U, V); @@ -589,7 +548,7 @@ static INLINE pstatus_t general_RGBToYUV420_BGRX( const BYTE* pSrc, UINT32 srcStep, BYTE* pDst[3], UINT32 dstStep[3], const prim_size_t* roi) { - UINT32 x, y, i, j; + UINT32 x, y, i; size_t x1 = 0, x2 = 4, x3 = srcStep, x4 = srcStep + 4; size_t y1 = 0, y2 = 1, y3 = dstStep[0], y4 = dstStep[0] + 1; UINT32 max_x = roi->width - 1; @@ -606,7 +565,6 @@ static INLINE pstatus_t general_RGBToYUV420_BGRX( { BYTE R, G, B; INT32 Ra, Ga, Ba; - UINT32 color; /* row 1, pixel 1 */ Ba = B = *(src + x1 + 0); Ga = G = *(src + x1 + 1); @@ -658,7 +616,7 @@ static INLINE pstatus_t general_RGBToYUV420_ANY( BYTE* pDst[3], UINT32 dstStep[3], const prim_size_t* roi) { const UINT32 bpp = GetBytesPerPixel(srcFormat); - UINT32 x, y, i, j; + UINT32 x, y, i; size_t x1 = 0, x2 = bpp, x3 = srcStep, x4 = srcStep + bpp; size_t y1 = 0, y2 = 1, y3 = dstStep[0], y4 = dstStep[0] + 1; UINT32 max_x = roi->width - 1; diff --git a/libfreerdp/primitives/prim_YUV_opt.c b/libfreerdp/primitives/prim_YUV_opt.c index f42d07ee4..6b1be6d54 100644 --- a/libfreerdp/primitives/prim_YUV_opt.c +++ b/libfreerdp/primitives/prim_YUV_opt.c @@ -31,22 +31,25 @@ #include "prim_internal.h" -static primitives_t* generic = NULL; - #ifdef WITH_SSE2 #include #include +#elif defined(WITH_NEON) +#include +#endif /* WITH_SSE2 else WITH_NEON */ +static primitives_t* generic = NULL; +#ifdef WITH_SSE2 /****************************************************************************/ /* SSSE3 YUV420 -> RGB conversion */ /****************************************************************************/ static pstatus_t ssse3_YUV420ToRGB_BGRX( - const BYTE** pSrc, const UINT32* srcStep, - BYTE* pDst, UINT32 dstStep, UINT32 dstFormat, - const prim_size_t* roi) + const BYTE** pSrc, const UINT32* srcStep, + BYTE* pDst, UINT32 dstStep, UINT32 dstFormat, + const prim_size_t* roi) { UINT32 lastRow, lastCol; BYTE* UData, *VData, *YData; @@ -341,9 +344,9 @@ static pstatus_t ssse3_YUV420ToRGB_BGRX( } static pstatus_t ssse3_YUV420ToRGB( - const BYTE** pSrc, const UINT32* srcStep, - BYTE* pDst, UINT32 dstStep, UINT32 DstFormat, - const prim_size_t* roi) + const BYTE** pSrc, const UINT32* srcStep, + BYTE* pDst, UINT32 dstStep, UINT32 DstFormat, + const prim_size_t* roi) { switch (DstFormat) { @@ -356,8 +359,115 @@ static pstatus_t ssse3_YUV420ToRGB( } } +static pstatus_t ssse3_YUV444ToRGB_8u_P3AC4R_BGRX( + const BYTE** pSrc, const UINT32* srcStep, + BYTE* pDst, UINT32 dstStep, + const prim_size_t* roi) +{ + const UINT32 nWidth = roi->width; + const UINT32 nHeight = roi->height; + const __m128i c128 = _mm_set1_epi16(128); + const __m128i mapY = _mm_set_epi32(0x80800380, 0x80800280, 0x80800180, 0x80800080); + const __m128i map = _mm_set_epi32(0x80038002, 0x80018000, 0x80808080, 0x80808080); + UINT32 y; + for (y = 0; y < nHeight; y++) + { + UINT32 x; + __m128i* dst = (__m128i*)(pDst + dstStep * y); + const BYTE* YData = pSrc[0] + y * srcStep[0]; + const BYTE* UData = pSrc[1] + y * srcStep[1]; + const BYTE* VData = pSrc[2] + y * srcStep[2]; + for (x = 0; x < nWidth; x += 4) + { + __m128i BGRX = _mm_set_epi32(0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000); + { + __m128i C, D, E; + /* Load Y values and expand to 32 bit */ + { + const __m128i Yraw = _mm_loadu_si128((__m128i*)YData); + C = _mm_shuffle_epi8(Yraw, mapY); /* Reorder and multiply by 256 */ + } + /* Load U values and expand to 32 bit */ + { + const __m128i Uraw = _mm_loadu_si128((__m128i*)UData); + const __m128i U = _mm_shuffle_epi8(Uraw, map); /* Reorder dcba */ + D = _mm_sub_epi16(U, c128); /* D = U - 128 */ + } + /* Load V values and expand to 32 bit */ + { + const __m128i Vraw = _mm_loadu_si128((__m128i*)VData); + const __m128i V = _mm_shuffle_epi8(Vraw, map); /* Reorder dcba */ + E = _mm_sub_epi16(V, c128); /* E = V - 128 */ + } + /* Get the R value */ + { + const __m128i c403 = _mm_set1_epi16(403); + const __m128i e403 = _mm_unpackhi_epi16(_mm_mullo_epi16(E, c403), _mm_mulhi_epi16(E, c403)); + const __m128i Rs = _mm_add_epi32(C, e403); + const __m128i R32 = _mm_srai_epi32(Rs, 8); + const __m128i R16 = _mm_packs_epi32(R32, _mm_setzero_si128()); + const __m128i R = _mm_packus_epi16(R16, _mm_setzero_si128()); + const __m128i mask = _mm_set_epi32(0x80038080, 0x80028080, 0x80018080, 0x80008080); + const __m128i packed = _mm_shuffle_epi8(R, mask); + BGRX = _mm_or_si128(BGRX, packed); + } + /* Get the G value */ + { + const __m128i c48 = _mm_set1_epi16(48); + const __m128i d48 = _mm_unpackhi_epi16(_mm_mullo_epi16(D, c48), _mm_mulhi_epi16(D, c48)); + const __m128i c120 = _mm_set1_epi16(120); + const __m128i e120 = _mm_unpackhi_epi16(_mm_mullo_epi16(E, c120), _mm_mulhi_epi16(E, c120)); + const __m128i de = _mm_add_epi32(d48, e120); + const __m128i Gs = _mm_sub_epi32(C, de); + const __m128i G32 = _mm_srai_epi32(Gs, 8); + const __m128i G16 = _mm_packs_epi32(G32, _mm_setzero_si128()); + const __m128i G = _mm_packus_epi16(G16, _mm_setzero_si128()); + const __m128i mask = _mm_set_epi32(0x80800380, 0x80800280, 0x80800180, 0x80800080); + const __m128i packed = _mm_shuffle_epi8(G, mask); + BGRX = _mm_or_si128(BGRX, packed); + } + /* Get the B value */ + { + const __m128i c475 = _mm_set1_epi16(475); + const __m128i d475 = _mm_unpackhi_epi16(_mm_mullo_epi16(D, c475), _mm_mulhi_epi16(D, c475)); + const __m128i Bs = _mm_add_epi32(C, d475); + const __m128i B32 = _mm_srai_epi32(Bs, 8); + const __m128i B16 = _mm_packs_epi32(B32, _mm_setzero_si128()); + const __m128i B = _mm_packus_epi16(B16, _mm_setzero_si128()); + const __m128i mask = _mm_set_epi32(0x80808003, 0x80808002, 0x80808001, 0x80808000); + const __m128i packed = _mm_shuffle_epi8(B, mask); + BGRX = _mm_or_si128(BGRX, packed); + } + } + _mm_storeu_si128(dst++, BGRX); + YData += 4; + UData += 4; + VData += 4; + } + } + + return PRIMITIVES_SUCCESS; +} + +static pstatus_t ssse3_YUV444ToRGB_8u_P3AC4R(const BYTE** pSrc, const UINT32* srcStep, + BYTE* pDst, UINT32 dstStep, UINT32 DstFormat, + const prim_size_t* roi) +{ + if (roi->width % 4 != 0) + return generic->YUV444ToRGB_8u_P3AC4R(pSrc, srcStep, pDst, dstStep, DstFormat, roi); + + switch (DstFormat) + { + case PIXEL_FORMAT_BGRX32: + case PIXEL_FORMAT_BGRA32: + return ssse3_YUV444ToRGB_8u_P3AC4R_BGRX(pSrc, srcStep, pDst, dstStep, roi); + + default: + return generic->YUV444ToRGB_8u_P3AC4R(pSrc, srcStep, pDst, dstStep, DstFormat, roi); + } +} /****************************************************************************/ /* SSSE3 RGB -> YUV420 conversion **/ @@ -383,18 +493,22 @@ static pstatus_t ssse3_YUV420ToRGB( * */ -PRIM_ALIGN_128 static const BYTE bgrx_y_factors[] = { - 9, 92, 27, 0, 9, 92, 27, 0, 9, 92, 27, 0, 9, 92, 27, 0 +PRIM_ALIGN_128 static const BYTE bgrx_y_factors[] = +{ + 9, 92, 27, 0, 9, 92, 27, 0, 9, 92, 27, 0, 9, 92, 27, 0 }; -PRIM_ALIGN_128 static const BYTE bgrx_u_factors[] = { - 64, -49, -15, 0, 64, -49, -15, 0, 64, -49, -15, 0, 64, -49, -15, 0 +PRIM_ALIGN_128 static const BYTE bgrx_u_factors[] = +{ + 64, -49, -15, 0, 64, -49, -15, 0, 64, -49, -15, 0, 64, -49, -15, 0 }; -PRIM_ALIGN_128 static const BYTE bgrx_v_factors[] = { - -6, -58, 64, 0, -6, -58, 64, 0, -6, -58, 64, 0, -6, -58, 64, 0 +PRIM_ALIGN_128 static const BYTE bgrx_v_factors[] = +{ + -6, -58, 64, 0, -6, -58, 64, 0, -6, -58, 64, 0, -6, -58, 64, 0 }; -PRIM_ALIGN_128 static const BYTE const_buf_128b[] = { - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 +PRIM_ALIGN_128 static const BYTE const_buf_128b[] = +{ + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }; /* @@ -418,13 +532,12 @@ PRIM_ALIGN_128 static const BYTE rgbx_v_factors[] = { /* compute the luma (Y) component from a single rgb source line */ static INLINE void ssse3_RGBToYUV420_BGRX_Y( - const BYTE* src, BYTE* dst, UINT32 width) + const BYTE* src, BYTE* dst, UINT32 width) { UINT32 x; __m128i y_factors, x0, x1, x2, x3; const __m128i* argb = (const __m128i*) src; __m128i* ydst = (__m128i*) dst; - y_factors = _mm_load_si128((__m128i*)bgrx_y_factors); for (x = 0; x < width; x += 16) @@ -434,24 +547,19 @@ static INLINE void ssse3_RGBToYUV420_BGRX_Y( x1 = _mm_load_si128(argb++); // 2nd 4 pixels x2 = _mm_load_si128(argb++); // 3rd 4 pixels x3 = _mm_load_si128(argb++); // 4th 4 pixels - /* multiplications and subtotals */ x0 = _mm_maddubs_epi16(x0, y_factors); x1 = _mm_maddubs_epi16(x1, y_factors); x2 = _mm_maddubs_epi16(x2, y_factors); x3 = _mm_maddubs_epi16(x3, y_factors); - /* the total sums */ x0 = _mm_hadd_epi16(x0, x1); x2 = _mm_hadd_epi16(x2, x3); - /* shift the results */ x0 = _mm_srli_epi16(x0, 7); x2 = _mm_srli_epi16(x2, 7); - /* pack the 16 words into bytes */ x0 = _mm_packus_epi16(x0, x2); - /* save to y plane */ _mm_storeu_si128(ydst++, x0); } @@ -460,18 +568,15 @@ static INLINE void ssse3_RGBToYUV420_BGRX_Y( /* compute the chrominance (UV) components from two rgb source lines */ static INLINE void ssse3_RGBToYUV420_BGRX_UV( - const BYTE* src1, const BYTE* src2, - BYTE* dst1, BYTE* dst2, UINT32 width) + const BYTE* src1, const BYTE* src2, + BYTE* dst1, BYTE* dst2, UINT32 width) { UINT32 x; __m128i vector128, u_factors, v_factors, x0, x1, x2, x3, x4, x5; - const __m128i* rgb1 = (const __m128i*)src1; const __m128i* rgb2 = (const __m128i*)src2; - __m64* udst = (__m64*)dst1; __m64* vdst = (__m64*)dst2; - vector128 = _mm_load_si128((__m128i*)const_buf_128b); u_factors = _mm_load_si128((__m128i*)bgrx_u_factors); v_factors = _mm_load_si128((__m128i*)bgrx_v_factors); @@ -482,68 +587,53 @@ static INLINE void ssse3_RGBToYUV420_BGRX_UV( x0 = _mm_load_si128(rgb1++); x4 = _mm_load_si128(rgb2++); x0 = _mm_avg_epu8(x0, x4); - x1 = _mm_load_si128(rgb1++); x4 = _mm_load_si128(rgb2++); x1 = _mm_avg_epu8(x1, x4); - x2 = _mm_load_si128(rgb1++); x4 = _mm_load_si128(rgb2++); x2 = _mm_avg_epu8(x2, x4); - x3 = _mm_load_si128(rgb1++); x4 = _mm_load_si128(rgb2++); x3 = _mm_avg_epu8(x3, x4); - // subsample these 16x1 pixels into 8x1 pixels */ - /** * shuffle controls * c = a[0],a[2],b[0],b[2] == 10 00 10 00 = 0x88 * c = a[1],a[3],b[1],b[3] == 11 01 11 01 = 0xdd */ - - x4 = _mm_castps_si128( _mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x1), 0x88) ); - x0 = _mm_castps_si128( _mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x1), 0xdd) ); + x4 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x1), 0x88)); + x0 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x1), 0xdd)); x0 = _mm_avg_epu8(x0, x4); - - x4 = _mm_castps_si128( _mm_shuffle_ps(_mm_castsi128_ps(x2), _mm_castsi128_ps(x3), 0x88) ); - x1 = _mm_castps_si128( _mm_shuffle_ps(_mm_castsi128_ps(x2), _mm_castsi128_ps(x3), 0xdd) ); + x4 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x2), _mm_castsi128_ps(x3), 0x88)); + x1 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x2), _mm_castsi128_ps(x3), 0xdd)); x1 = _mm_avg_epu8(x1, x4); - /* multiplications and subtotals */ x2 = _mm_maddubs_epi16(x0, u_factors); x3 = _mm_maddubs_epi16(x1, u_factors); - x4 = _mm_maddubs_epi16(x0, v_factors); x5 = _mm_maddubs_epi16(x1, v_factors); - /* the total sums */ x0 = _mm_hadd_epi16(x2, x3); x1 = _mm_hadd_epi16(x4, x5); - /* shift the results */ x0 = _mm_srai_epi16(x0, 7); x1 = _mm_srai_epi16(x1, 7); - /* pack the 16 words into bytes */ x0 = _mm_packs_epi16(x0, x1); - /* add 128 */ x0 = _mm_add_epi8(x0, vector128); - /* the lower 8 bytes go to the u plane */ _mm_storel_pi(udst++, _mm_castsi128_ps(x0)); - /* the upper 8 bytes go to the v plane */ _mm_storeh_pi(vdst++, _mm_castsi128_ps(x0)); } } static pstatus_t ssse3_RGBToYUV420_BGRX( - const BYTE* pSrc, UINT32 srcFormat, UINT32 srcStep, - BYTE* pDst[3], UINT32 dstStep[3], - const prim_size_t* roi) + const BYTE* pSrc, UINT32 srcFormat, UINT32 srcStep, + BYTE* pDst[3], UINT32 dstStep[3], + const prim_size_t* roi) { UINT32 y; const BYTE* argb = pSrc; @@ -561,15 +651,13 @@ static pstatus_t ssse3_RGBToYUV420_BGRX( return generic->RGBToYUV420_8u_P3AC4R(pSrc, srcFormat, srcStep, pDst, dstStep, roi); } - for (y = 0; y < roi->height-1; y+=2) + for (y = 0; y < roi->height - 1; y += 2) { const BYTE* line1 = argb; const BYTE* line2 = argb + srcStep; - ssse3_RGBToYUV420_BGRX_UV(line1, line2, udst, vdst, roi->width); ssse3_RGBToYUV420_BGRX_Y(line1, ydst, roi->width); ssse3_RGBToYUV420_BGRX_Y(line2, ydst + dstStep[0], roi->width); - argb += 2 * srcStep; ydst += 2 * dstStep[0]; udst += 1 * dstStep[1]; @@ -587,21 +675,569 @@ static pstatus_t ssse3_RGBToYUV420_BGRX( } static pstatus_t ssse3_RGBToYUV420( - const BYTE* pSrc, UINT32 srcFormat, UINT32 srcStep, - BYTE* pDst[3], UINT32 dstStep[3], - const prim_size_t* roi) + const BYTE* pSrc, UINT32 srcFormat, UINT32 srcStep, + BYTE* pDst[3], UINT32 dstStep[3], + const prim_size_t* roi) { switch (srcFormat) { case PIXEL_FORMAT_BGRX32: case PIXEL_FORMAT_BGRA32: return ssse3_RGBToYUV420_BGRX(pSrc, srcFormat, srcStep, pDst, dstStep, roi); + default: return generic->RGBToYUV420_8u_P3AC4R(pSrc, srcFormat, srcStep, pDst, dstStep, roi); } } +#elif defined(WITH_NEON) +static INLINE uint8x8_t neon_YUV2R(int32x4_t Ch, int32x4_t Cl, + int16x4_t Dh, int16x4_t Dl, + int16x4_t Eh, int16x4_t El) +{ + /* R = (256 * Y + 403 * (V - 128)) >> 8 */ + const int16x4_t c403 = vdup_n_s16(403); + const int32x4_t CEh = vmlal_s16(Ch, Eh, c403); + const int32x4_t CEl = vmlal_s16(Cl, El, c403); + const int32x4_t Rh = vrshrq_n_s32(CEh, 8); + const int32x4_t Rl = vrshrq_n_s32(CEl, 8); + const int16x8_t R = vcombine_s16(vqmovn_s32(Rl), vqmovn_s32(Rh)); + return vqmovun_s16(R); +} + +static INLINE uint8x8_t neon_YUV2G(int32x4_t Ch, int32x4_t Cl, + int16x4_t Dh, int16x4_t Dl, + int16x4_t Eh, int16x4_t El) +{ + /* G = (256L * Y - 48 * (U - 128) - 120 * (V - 128)) >> 8 */ + const int16x4_t c48 = vdup_n_s16(48); + const int16x4_t c120 = vdup_n_s16(120); + const int32x4_t CDh = vmlsl_s16(Ch, Dh, c48); + const int32x4_t CDl = vmlsl_s16(Cl, Dl, c48); + const int32x4_t CDEh = vmlsl_s16(CDh, Eh, c120); + const int32x4_t CDEl = vmlsl_s16(CDl, El, c120); + const int32x4_t Gh = vrshrq_n_s32(CDEh, 8); + const int32x4_t Gl = vrshrq_n_s32(CDEl, 8); + const int16x8_t G = vcombine_s16(vqmovn_s32(Gl), vqmovn_s32(Gh)); + return vqmovun_s16(G); +} + +static INLINE uint8x8_t neon_YUV2B(int32x4_t Ch, int32x4_t Cl, + int16x4_t Dh, int16x4_t Dl, + int16x4_t Eh, int16x4_t El) +{ + /* B = (256L * Y + 475 * (U - 128)) >> 8*/ + const int16x4_t c475 = vdup_n_s16(475); + const int32x4_t CDh = vmlal_s16(Ch, Dh, c475); + const int32x4_t CDl = vmlal_s16(Ch, Dl, c475); + const int32x4_t Bh = vrshrq_n_s32(CDh, 8); + const int32x4_t Bl = vrshrq_n_s32(CDl, 8); + const int16x8_t B = vcombine_s16(vqmovn_s32(Bl), vqmovn_s32(Bh)); + return vqmovun_s16(B); +} + +static INLINE pstatus_t neon_YUV420ToX( + const BYTE* pSrc[3], const UINT32 srcStep[3], + BYTE* pDst, UINT32 dstStep, + const prim_size_t* roi, const uint8_t rPos, const uint8_t gPos, + const uint8_t bPos, const uint8_t aPos) +{ + UINT32 y; + const UINT32 nWidth = roi->width; + const UINT32 nHeight = roi->height; + const int16x8_t c128 = vdupq_n_s16(128); + + for (y = 0; y < nHeight; y += 2) + { + const uint8_t* pY1 = pSrc[0] + y * srcStep[0]; + const uint8_t* pY2 = pY1 + srcStep[0]; + const uint8_t* pU = pSrc[1] + (y / 2) * srcStep[1]; + const uint8_t* pV = pSrc[2] + (y / 2) * srcStep[2]; + uint8_t* pRGB1 = pDst + y * dstStep; + uint8_t* pRGB2 = pRGB1 + dstStep; + UINT32 x; + const BOOL lastY = y >= nHeight - 1; + + for (x = 0; x < nWidth;) + { + const BOOL lastX = (nWidth - x) < 16; + const uint8x8_t Uraw = vld1_u8(pU); + const uint8x8x2_t Uu = vzip_u8(Uraw, Uraw); + const int16x8_t U1 = vreinterpretq_s16_u16(vmovl_u8(Uu.val[0])); + const int16x8_t U2 = vreinterpretq_s16_u16(vmovl_u8(Uu.val[1])); + const uint8x8_t Vraw = vld1_u8(pV); + const uint8x8x2_t Vu = vzip_u8(Vraw, Vraw); + const int16x8_t V1 = vreinterpretq_s16_u16(vmovl_u8(Vu.val[0])); + const int16x8_t V2 = vreinterpretq_s16_u16(vmovl_u8(Vu.val[1])); + const int16x8_t D1 = vsubq_s16(U1, c128); + const int16x4_t D1h = vget_high_s16(D1); + const int16x4_t D1l = vget_low_s16(D1); + const int16x8_t E1 = vsubq_s16(V1, c128); + const int16x4_t E1h = vget_high_s16(E1); + const int16x4_t E1l = vget_low_s16(E1); + const int16x8_t D2 = vsubq_s16(U2, c128); + const int16x4_t D2h = vget_high_s16(D2); + const int16x4_t D2l = vget_low_s16(D2); + const int16x8_t E2 = vsubq_s16(V2, c128); + const int16x4_t E2h = vget_high_s16(E2); + const int16x4_t E2l = vget_low_s16(E2); + uint8x8x4_t bgrx; + bgrx.val[aPos] = vdup_n_u8(0xFF); + { + const uint8x8_t Y1u = vld1_u8(pY1); + const int16x8_t Y1 = vreinterpretq_s16_u16(vmovl_u8(Y1u)); + const int32x4_t Ch = vmulq_n_s32(vmovl_s16(vget_high_s16(Y1)), 256); /* Y * 256 */ + const int32x4_t Cl = vmulq_n_s32(vmovl_s16(vget_low_s16(Y1)), 256); /* Y * 256 */ + bgrx.val[rPos] = neon_YUV2R(Ch, Cl, D1h, D1l, E1h, E1l); + bgrx.val[gPos] = neon_YUV2G(Ch, Cl, D1h, D1l, E1h, E1l); + bgrx.val[bPos] = neon_YUV2B(Ch, Cl, D1h, D1l, E1h, E1l); + vst4_u8(pRGB1, bgrx); + pRGB1 += 32; + pY1 += 8; + x += 8; + } + + if (!lastX) + { + const uint8x8_t Y1u = vld1_u8(pY1); + const int16x8_t Y1 = vreinterpretq_s16_u16(vmovl_u8(Y1u)); + const int32x4_t Ch = vmulq_n_s32(vmovl_s16(vget_high_s16(Y1)), 256); /* Y * 256 */ + const int32x4_t Cl = vmulq_n_s32(vmovl_s16(vget_low_s16(Y1)), 256); /* Y * 256 */ + bgrx.val[rPos] = neon_YUV2R(Ch, Cl, D2h, D2l, E2h, E2l); + bgrx.val[gPos] = neon_YUV2G(Ch, Cl, D2h, D2l, E2h, E2l); + bgrx.val[bPos] = neon_YUV2B(Ch, Cl, D2h, D2l, E2h, E2l); + vst4_u8(pRGB1, bgrx); + pRGB1 += 32; + pY1 += 8; + x += 8; + } + + if (!lastY) + { + const uint8x8_t Y2u = vld1_u8(pY2); + const int16x8_t Y2 = vreinterpretq_s16_u16(vmovl_u8(Y2u)); + const int32x4_t Ch = vmulq_n_s32(vmovl_s16(vget_high_s16(Y2)), 256); /* Y * 256 */ + const int32x4_t Cl = vmulq_n_s32(vmovl_s16(vget_low_s16(Y2)), 256); /* Y * 256 */ + bgrx.val[rPos] = neon_YUV2R(Ch, Cl, D1h, D1l, E1h, E1l); + bgrx.val[gPos] = neon_YUV2G(Ch, Cl, D1h, D1l, E1h, E1l); + bgrx.val[bPos] = neon_YUV2B(Ch, Cl, D1h, D1l, E1h, E1l); + vst4_u8(pRGB2, bgrx); + pRGB2 += 32; + pY2 += 8; + + if (!lastX) + { + const uint8x8_t Y2u = vld1_u8(pY2); + const int16x8_t Y2 = vreinterpretq_s16_u16(vmovl_u8(Y2u)); + const int32x4_t Ch = vmulq_n_s32(vmovl_s16(vget_high_s16(Y2)), 256); /* Y * 256 */ + const int32x4_t Cl = vmulq_n_s32(vmovl_s16(vget_low_s16(Y2)), 256); /* Y * 256 */ + bgrx.val[rPos] = neon_YUV2R(Ch, Cl, D2h, D2l, E2h, E2l); + bgrx.val[gPos] = neon_YUV2G(Ch, Cl, D2h, D2l, E2h, E2l); + bgrx.val[bPos] = neon_YUV2B(Ch, Cl, D2h, D2l, E2h, E2l); + vst4_u8(pRGB2, bgrx); + pRGB2 += 32; + pY2 += 8; + } + } + + pU += 8; + pV += 8; + } + } + + return PRIMITIVES_SUCCESS; +} + +static pstatus_t neon_YUV420ToRGB_8u_P3AC4R( + const BYTE* pSrc[3], const UINT32 srcStep[3], + BYTE* pDst, UINT32 dstStep, UINT32 DstFormat, + const prim_size_t* roi) +{ + switch (DstFormat) + { + case PIXEL_FORMAT_BGRA32: + case PIXEL_FORMAT_BGRX32: + return neon_YUV420ToX(pSrc, srcStep, pDst, dstStep, roi, 2, 1, 0, 3); + + case PIXEL_FORMAT_RGBA32: + case PIXEL_FORMAT_RGBX32: + return neon_YUV420ToX(pSrc, srcStep, pDst, dstStep, roi, 0, 1, 2, 3); + + case PIXEL_FORMAT_ARGB32: + case PIXEL_FORMAT_XRGB32: + return neon_YUV420ToX(pSrc, srcStep, pDst, dstStep, roi, 1, 2, 3, 0); + + case PIXEL_FORMAT_ABGR32: + case PIXEL_FORMAT_XBGR32: + return neon_YUV420ToX(pSrc, srcStep, pDst, dstStep, roi, 3, 2, 1, 0); + + default: + return generic->YUV420ToRGB_8u_P3AC4R(pSrc, srcStep, pDst, dstStep, DstFormat, roi); + } +} + +static INLINE pstatus_t neon_YUV444ToX( + const BYTE* pSrc[3], const UINT32 srcStep[3], + BYTE* pDst, UINT32 dstStep, + const prim_size_t* roi, const uint8_t rPos, const uint8_t gPos, + const uint8_t bPos, const uint8_t aPos) +{ + UINT32 y; + const UINT32 nWidth = roi->width; + const UINT32 nHeight = roi->height; + const UINT32 yPad = srcStep[0] - roi->width; + const UINT32 uPad = srcStep[1] - roi->width; + const UINT32 vPad = srcStep[2] - roi->width; + const UINT32 dPad = dstStep - roi->width * 4; + const uint8_t* pY = pSrc[0]; + const uint8_t* pU = pSrc[1]; + const uint8_t* pV = pSrc[2]; + uint8_t* pRGB = pDst; + const int16x8_t c128 = vdupq_n_s16(128); + const int16x4_t c48 = vdup_n_s16(48); + const int16x4_t c120 = vdup_n_s16(120); + const int16x4_t c403 = vdup_n_s16(403); + const int16x4_t c475 = vdup_n_s16(475); + const DWORD pad = nWidth % 8; + + for (y = 0; y < nHeight; y++) + { + UINT32 x; + + for (x = 0; x < nWidth - pad; x += 8) + { + const uint8x8_t Yu = vld1_u8(pY); + const int16x8_t Y = vreinterpretq_s16_u16(vmovl_u8(Yu)); + const uint8x8_t Uu = vld1_u8(pU); + const int16x8_t U = vreinterpretq_s16_u16(vmovl_u8(Uu)); + const uint8x8_t Vu = vld1_u8(pV); + const int16x8_t V = vreinterpretq_s16_u16(vmovl_u8(Vu)); + /* Do the calculations on Y in 32bit width, the result of 255 * 256 does not fit + * a signed 16 bit value. */ + const int32x4_t Ch = vmulq_n_s32(vmovl_s16(vget_high_s16(Y)), 256); /* Y * 256 */ + const int32x4_t Cl = vmulq_n_s32(vmovl_s16(vget_low_s16(Y)), 256); /* Y * 256 */ + const int16x8_t D = vsubq_s16(U, c128); + const int16x4_t Dh = vget_high_s16(D); + const int16x4_t Dl = vget_low_s16(D); + const int16x8_t E = vsubq_s16(V, c128); + const int16x4_t Eh = vget_high_s16(E); + const int16x4_t El = vget_low_s16(E); + uint8x8x4_t bgrx; + { + /* B = (256L * Y + 475 * (U - 128)) >> 8*/ + const int32x4_t CDh = vmlal_s16(Ch, Dh, c475); + const int32x4_t CDl = vmlal_s16(Cl, Dl, c475); + const int32x4_t Bh = vrshrq_n_s32(CDh, 8); + const int32x4_t Bl = vrshrq_n_s32(CDl, 8); + const int16x8_t B = vcombine_s16(vqmovn_s32(Bl), vqmovn_s32(Bh)); + bgrx.val[bPos] = vqmovun_s16(B); + } + { + /* G = (256L * Y - 48 * (U - 128) - 120 * (V - 128)) >> 8 */ + const int32x4_t CDh = vmlsl_s16(Ch, Dh, c48); + const int32x4_t CDl = vmlsl_s16(Cl, Dl, c48); + const int32x4_t CDEh = vmlsl_s16(CDh, Eh, c120); + const int32x4_t CDEl = vmlsl_s16(CDl, El, c120); + const int32x4_t Gh = vrshrq_n_s32(CDEh, 8); + const int32x4_t Gl = vrshrq_n_s32(CDEl, 8); + const int16x8_t G = vcombine_s16(vqmovn_s32(Gl), vqmovn_s32(Gh)); + bgrx.val[gPos] = vqmovun_s16(G); + } + { + /* R = (256 * Y + 403 * (V - 128)) >> 8 */ + const int32x4_t CEh = vmlal_s16(Ch, Eh, c403); + const int32x4_t CEl = vmlal_s16(Cl, El, c403); + const int32x4_t Rh = vrshrq_n_s32(CEh, 8); + const int32x4_t Rl = vrshrq_n_s32(CEl, 8); + const int16x8_t R = vcombine_s16(vqmovn_s32(Rl), vqmovn_s32(Rh)); + bgrx.val[rPos] = vqmovun_s16(R); + } + { + /* A */ + bgrx.val[aPos] = vdup_n_u8(0xFF); + } + vst4_u8(pRGB, bgrx); + pRGB += 32; + pY += 8; + pU += 8; + pV += 8; + } + + for (x = 0; x < pad; x++) + { + const BYTE Y = *pY++; + const BYTE U = *pU++; + const BYTE V = *pV++; + const BYTE r = YUV2R(Y, U, V); + const BYTE g = YUV2G(Y, U, V); + const BYTE b = YUV2B(Y, U, V); + pRGB[aPos] = 0xFF; + pRGB[rPos] = r; + pRGB[gPos] = g; + pRGB[bPos] = b; + pRGB += 4; + } + + pRGB += dPad; + pY += yPad; + pU += uPad; + pV += vPad; + } + + return PRIMITIVES_SUCCESS; +} + +static pstatus_t neon_YUV444ToRGB_8u_P3AC4R( + const BYTE* pSrc[3], const UINT32 srcStep[3], + BYTE* pDst, UINT32 dstStep, UINT32 DstFormat, + const prim_size_t* roi) +{ + switch (DstFormat) + { + case PIXEL_FORMAT_BGRA32: + case PIXEL_FORMAT_BGRX32: + return neon_YUV444ToX(pSrc, srcStep, pDst, dstStep, roi, 2, 1, 0, 3); + + case PIXEL_FORMAT_RGBA32: + case PIXEL_FORMAT_RGBX32: + return neon_YUV444ToX(pSrc, srcStep, pDst, dstStep, roi, 0, 1, 2, 3); + + case PIXEL_FORMAT_ARGB32: + case PIXEL_FORMAT_XRGB32: + return neon_YUV444ToX(pSrc, srcStep, pDst, dstStep, roi, 1, 2, 3, 0); + + case PIXEL_FORMAT_ABGR32: + case PIXEL_FORMAT_XBGR32: + return neon_YUV444ToX(pSrc, srcStep, pDst, dstStep, roi, 3, 2, 1, 0); + + default: + return generic->YUV444ToRGB_8u_P3AC4R(pSrc, srcStep, pDst, dstStep, DstFormat, roi); + } +} + +static pstatus_t neon_YUV420CombineToYUV444( + const BYTE* pMainSrc[3], const UINT32 srcMainStep[3], + const BYTE* pAuxSrc[3], const UINT32 srcAuxStep[3], + BYTE* pDst[3], const UINT32 dstStep[3], + const prim_size_t* roi) +{ + UINT32 x, y; + const UINT32 nWidth = roi->width; + const UINT32 nHeight = roi->height; + const UINT32 halfWidth = nWidth / 2, halfHeight = nHeight / 2; + const UINT32 oddY = 1; + const UINT32 evenY = 0; + /* The auxilary frame is aligned to multiples of 16x16. + * We need the padded height for B4 and B5 conversion. */ + const UINT32 padHeigth = roi->height + 16 - roi->height % 16; + + if (pMainSrc && pMainSrc[0] && pMainSrc[1] && pMainSrc[2]) + { + /* Y data is already here... */ + /* B1 */ + for (y = 0; y < nHeight; y++) + { + const BYTE* Ym = pMainSrc[0] + srcMainStep[0] * y; + BYTE* pY = pDst[0] + dstStep[0] * y; + memcpy(pY, Ym, nWidth); + } + + /* The first half of U, V are already here part of this frame. */ + /* B2 and B3 */ + for (y = 0; y < halfHeight; y++) + { + const UINT32 val2y = (2 * y + evenY); + const BYTE* Um = pMainSrc[1] + srcMainStep[1] * y; + const BYTE* Vm = pMainSrc[2] + srcMainStep[2] * y; + BYTE* pU = pDst[1] + dstStep[1] * val2y; + BYTE* pV = pDst[2] + dstStep[2] * val2y; + BYTE* pU1 = pU + dstStep[1]; + BYTE* pV1 = pV + dstStep[2]; + + for (x = 0; x + 16 < halfWidth; x += 16) + { + { + const uint8x16_t u = vld1q_u8(Um); + uint8x16x2_t u2x; + u2x.val[0] = u; + u2x.val[1] = u; + vst2q_u8(pU, u2x); + vst2q_u8(pU1, u2x); + Um += 16; + pU += 32; + pU1 += 32; + } + { + const uint8x16_t v = vld1q_u8(Vm); + uint8x16x2_t v2x; + v2x.val[0] = v; + v2x.val[1] = v; + vst2q_u8(pV, v2x); + vst2q_u8(pV1, v2x); + Vm += 16; + pV += 32; + pV1 += 32; + } + } + + for (; x < halfWidth; x++) + { + const BYTE u = *Um++; + const BYTE v = *Vm++; + *pU++ = u; + *pU++ = u; + *pU1++ = u; + *pU1++ = u; + *pV++ = v; + *pV++ = v; + *pV1++ = v; + *pV1++ = v; + } + } + } + + if (!pAuxSrc || !pAuxSrc[0] || !pAuxSrc[1] || !pAuxSrc[2]) + return PRIMITIVES_SUCCESS; + + /* The second half of U and V is a bit more tricky... */ + /* B4 and B5 */ + for (y = 0; y < padHeigth; y += 16) + { + const BYTE* Ya = pAuxSrc[0] + srcAuxStep[0] * y; + UINT32 x; + BYTE* pU = pDst[1] + dstStep[1] * (y + 1); + BYTE* pV = pDst[2] + dstStep[2] * (y + 1); + + for (x = 0; x < 8; x++) + { + if (y + x >= nHeight) + continue; + + memcpy(pU, Ya, nWidth); + pU += dstStep[1] * 2; + Ya += srcAuxStep[0]; + } + + for (x = 0; x < 8; x++) + { + if (y + x >= nHeight) + continue; + + memcpy(pV, Ya, nWidth); + pV += dstStep[1] * 2; + Ya += srcAuxStep[0]; + } + } + + /* B6 and B7 */ + for (y = 0; y < halfHeight; y++) + { + const UINT32 val2y = (y * 2 + evenY); + const BYTE* Ua = pAuxSrc[1] + srcAuxStep[1] * y; + const BYTE* Va = pAuxSrc[2] + srcAuxStep[2] * y; + BYTE* pU = pDst[1] + dstStep[1] * val2y; + BYTE* pV = pDst[2] + dstStep[2] * val2y; + + for (x = 0; x + 16 < halfWidth; x += 16) + { + { + const uint8x16_t u = vld1q_u8(Ua); + uint8x16x2_t uu = vld2q_u8(pU); + uu.val[1] = u; + vst2q_u8(pU, uu); + Ua += 16; + pU += 32; + } + { + const uint8x16_t v = vld1q_u8(Va); + uint8x16x2_t vv = vld2q_u8(pV); + vv.val[1] = v; + vst2q_u8(pV, vv); + Va += 16; + pV += 32; + } + } + + for (; x < halfWidth; x++) + { + pU++; + *pU++ = *Ua++; + pV++; + *pV++ = *Va++; + } + } + + /* Filter */ + for (y = 0; y < halfHeight; y++) + { + const UINT32 val2y = (y * 2 + evenY); + const UINT32 val2y1 = val2y + oddY; + BYTE* pU = pDst[1] + dstStep[1] * val2y; + BYTE* pV = pDst[2] + dstStep[2] * val2y; + BYTE* pU1 = pU + dstStep[1]; + BYTE* pV1 = pV + dstStep[2]; + + if (val2y1 > nHeight) + continue; + + for (x = 0; x + 16 < halfWidth; x += 16) + { + { + /* U = (U2x,2y << 2) - U2x1,2y - U2x,2y1 - U2x1,2y1 */ + uint8x8x2_t u = vld2_u8(pU); + const int16x8_t up = vreinterpretq_s16_u16(vshll_n_u8(u.val[0], 2)); /* Ux2,2y << 2 */ + const uint8x8x2_t u1 = vld2_u8(pU1); + const uint16x8_t usub = vaddl_u8(u1.val[1], u1.val[0]); /* U2x,2y1 + U2x1,2y1 */ + const int16x8_t us = vreinterpretq_s16_u16(vaddw_u8(usub, + u.val[1])); /* U2x1,2y + U2x,2y1 + U2x1,2y1 */ + const int16x8_t un = vsubq_s16(up, us); + const uint8x8_t u8 = vqmovun_s16(un); /* CLIP(un) */ + u.val[0] = u8; + vst2_u8(pU, u); + pU += 16; + pU1 += 16; + } + { + /* V = (V2x,2y << 2) - V2x1,2y - V2x,2y1 - V2x1,2y1 */ + uint8x8x2_t v = vld2_u8(pV); + const int16x8_t vp = vreinterpretq_s16_u16(vshll_n_u8(v.val[0], 2)); /* Vx2,2y << 2 */ + const uint8x8x2_t v1 = vld2_u8(pV1); + const uint16x8_t vsub = vaddl_u8(v1.val[1], v1.val[0]); /* V2x,2y1 + V2x1,2y1 */ + const int16x8_t vs = vreinterpretq_s16_u16(vaddw_u8(vsub, + v.val[1])); /* V2x1,2y + V2x,2y1 + V2x1,2y1 */ + const int16x8_t vn = vsubq_s16(vp, vs); + const uint8x8_t v8 = vqmovun_s16(vn); /* CLIP(vn) */ + v.val[0] = v8; + vst2_u8(pV, v); + pV += 16; + pV1 += 16; + } + } + + for (; x < halfWidth; x++) + { + const UINT32 val2x = (x * 2); + const UINT32 val2x1 = val2x + 1; + const INT32 up = pU[val2x] * 4; + const INT32 vp = pV[val2x] * 4; + INT32 u2020; + INT32 v2020; + + if (val2x1 > nWidth) + continue; + + u2020 = up - pU[val2x1] - pU1[val2x] - pU1[val2x1]; + v2020 = vp - pV[val2x1] - pV1[val2x] - pV1[val2x1]; + *pU = CLIP(u2020); + *pV = CLIP(v2020); + pU += 2; + pV += 2; + } + } + + return PRIMITIVES_SUCCESS; +} #endif void primitives_init_YUV_opt(primitives_t* prims) @@ -615,6 +1251,16 @@ void primitives_init_YUV_opt(primitives_t* prims) { prims->RGBToYUV420_8u_P3AC4R = ssse3_RGBToYUV420; prims->YUV420ToRGB_8u_P3AC4R = ssse3_YUV420ToRGB; + prims->YUV444ToRGB_8u_P3AC4R = ssse3_YUV444ToRGB_8u_P3AC4R; + } + +#elif defined(WITH_NEON) + + if (IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE)) + { + prims->YUV420ToRGB_8u_P3AC4R = neon_YUV420ToRGB_8u_P3AC4R; + prims->YUV444ToRGB_8u_P3AC4R = neon_YUV444ToRGB_8u_P3AC4R; + prims->YUV420CombineToYUV444 = neon_YUV420CombineToYUV444; } #endif diff --git a/libfreerdp/primitives/prim_internal.h b/libfreerdp/primitives/prim_internal.h index 1bf4cd342..f931212e3 100644 --- a/libfreerdp/primitives/prim_internal.h +++ b/libfreerdp/primitives/prim_internal.h @@ -129,6 +129,47 @@ static INLINE BYTE CLIP(INT32 X) return X; } +/** + * | R | ( | 256 0 403 | | Y | ) + * | G | = ( | 256 -48 -120 | | U - 128 | ) >> 8 + * | B | ( | 256 475 0 | | V - 128 | ) + */ +static INLINE INT32 C(INT32 Y) +{ + return (Y) - 0L; +} + +static INLINE INT32 D(INT32 U) +{ + return (U) - 128L; +} + +static INLINE INT32 E(INT32 V) +{ + return (V) - 128L; +} + +static INLINE BYTE YUV2R(INT32 Y, INT32 U, INT32 V) +{ + const INT32 r = (256L * C(Y) + 0L * D(U) + 403L * E(V)); + const INT32 r8 = r >> 8L; + return CLIP(r8); +} + +static INLINE BYTE YUV2G(INT32 Y, INT32 U, INT32 V) +{ + const INT32 g = (256L * C(Y) - 48L * D(U) - 120L * E(V)); + const INT32 g8 = g >> 8L; + return CLIP(g8); +} + +static INLINE BYTE YUV2B(INT32 Y, INT32 U, INT32 V) +{ + const INT32 b = (256L * C(Y) + 475L * D(U) + 0L * E(V)); + const INT32 b8 = b >> 8L; + return CLIP(b8); +} + /* Function prototypes for all the init/deinit routines. */ FREERDP_LOCAL void primitives_init_copy(primitives_t* prims); FREERDP_LOCAL void primitives_init_set(primitives_t* prims); diff --git a/libfreerdp/primitives/test/TestPrimitivesYUV.c b/libfreerdp/primitives/test/TestPrimitivesYUV.c index 310bd3e2c..67599362d 100644 --- a/libfreerdp/primitives/test/TestPrimitivesYUV.c +++ b/libfreerdp/primitives/test/TestPrimitivesYUV.c @@ -22,7 +22,7 @@ static BOOL similar(const BYTE* src, const BYTE* dst, size_t size) { int diff = src[x] - dst[x]; - if (abs(diff) > 2) + if (abs(diff) > 4) { fprintf(stderr, "%"PRIuz" %02"PRIX8" : %02"PRIX8" diff=%d\n", x, src[x], dst[x], abs(diff)); return FALSE; @@ -78,13 +78,14 @@ static BOOL similarRGB(const BYTE* src, const BYTE* dst, size_t size, UINT32 for return TRUE; } -static void get_size(UINT32* width, UINT32* height) +static void get_size(BOOL large, UINT32* width, UINT32* height) { + UINT32 shift = large ? 8 : 1; winpr_RAND((BYTE*)width, sizeof(*width)); winpr_RAND((BYTE*)height, sizeof(*height)); // TODO: Algorithm only works on even resolutions... - *width = (*width % 64 + 1) << 1; - *height = (*height % 64 + 1) << 1; + *width = (*width % 64 + 1) << shift; + *height = (*height % 64 + 1) << shift; } static BOOL check_padding(const BYTE* psrc, size_t size, size_t padding, @@ -170,7 +171,7 @@ static void free_padding(void* src, size_t padding) /* Create 2 pseudo YUV420 frames of same size. * Combine them and check, if the data is at the expected position. */ -static BOOL TestPrimitiveYUVCombine(void) +static BOOL TestPrimitiveYUVCombine(primitives_t* prims, prim_size_t roi) { UINT32 x, y, i; UINT32 awidth, aheight; @@ -186,9 +187,6 @@ static BOOL TestPrimitiveYUVCombine(void) size_t padding = 10000; PROFILER_DEFINE(yuvCombine); PROFILER_DEFINE(yuvSplit); - prim_size_t roi; - primitives_t* prims = primitives_get(); - get_size(&roi.width, &roi.height); awidth = roi.width + 16 - roi.width % 16; aheight = roi.height + 16 - roi.height % 16; fprintf(stderr, "Running YUVCombine on frame size %"PRIu32"x%"PRIu32" [%"PRIu32"x%"PRIu32"]\n", @@ -360,18 +358,16 @@ fail: return rc; } -static BOOL TestPrimitiveYUV(BOOL use444) +static BOOL TestPrimitiveYUV(primitives_t* prims, prim_size_t roi, BOOL use444) { BOOL rc = FALSE; UINT32 x, y; UINT32 awidth, aheight; BYTE* yuv[3] = {0}; UINT32 yuv_step[3]; - prim_size_t roi; BYTE* rgb = NULL; BYTE* rgb_dst = NULL; size_t size; - primitives_t* prims = primitives_get(); size_t uvsize, uvwidth; size_t padding = 10000; size_t stride; @@ -390,7 +386,6 @@ static BOOL TestPrimitiveYUV(BOOL use444) PROFILER_DEFINE(rgbToYUV444); PROFILER_DEFINE(yuv420ToRGB); PROFILER_DEFINE(yuv444ToRGB); - get_size(&roi.width, &roi.height); /* Buffers need to be 16x16 aligned. */ awidth = roi.width + 16 - roi.width % 16; aheight = roi.height + 16 - roi.height % 16; @@ -457,6 +452,7 @@ static BOOL TestPrimitiveYUV(BOOL use444) for (x = 0; x < sizeof(formats) / sizeof(formats[0]); x++) { const UINT32 DstFormat = formats[x]; + printf("Testing destination color format %s\n", GetColorFormatName(DstFormat)); if (use444) { @@ -561,26 +557,73 @@ fail: int TestPrimitivesYUV(int argc, char* argv[]) { + BOOL large = (argc > 1); UINT32 x; int rc = -1; prim_test_setup(FALSE); + primitives_t* prims = primitives_get(); + primitives_t* generic = primitives_get_generic(); for (x = 0; x < 10; x++) { - if (!TestPrimitiveYUV(TRUE)) { + prim_size_t roi; + get_size(large, &roi.width, &roi.height); + printf("-------------------- GENERIC ------------------------\n"); + + if (!TestPrimitiveYUV(generic, roi, TRUE)) + { printf("TestPrimitiveYUV (444) failed.\n"); goto end; } - if (!TestPrimitiveYUV(FALSE)) { + printf("---------------------- END --------------------------\n"); +#if 1 + printf("------------------- OPTIMIZED -----------------------\n"); + + if (!TestPrimitiveYUV(prims, roi, TRUE)) + { + printf("TestPrimitiveYUV (444) failed.\n"); + goto end; + } + + printf("---------------------- END --------------------------\n"); +#endif + printf("-------------------- GENERIC ------------------------\n"); + + if (!TestPrimitiveYUV(generic, roi, FALSE)) + { printf("TestPrimitiveYUV (420) failed.\n"); goto end; } - if (!TestPrimitiveYUVCombine()) { + printf("---------------------- END --------------------------\n"); + printf("------------------- OPTIMIZED -----------------------\n"); + + if (!TestPrimitiveYUV(prims, roi, FALSE)) + { + printf("TestPrimitiveYUV (420) failed.\n"); + goto end; + } + + printf("---------------------- END --------------------------\n"); + printf("-------------------- GENERIC ------------------------\n"); + + if (!TestPrimitiveYUVCombine(generic, roi)) + { printf("TestPrimitiveYUVCombine failed.\n"); goto end; } + + printf("---------------------- END --------------------------\n"); + printf("------------------- OPTIMIZED -----------------------\n"); + + if (!TestPrimitiveYUVCombine(prims, roi)) + { + printf("TestPrimitiveYUVCombine failed.\n"); + goto end; + } + + printf("---------------------- END --------------------------\n"); } rc = 0;