FreeRDP
Loading...
Searching...
No Matches
prim_YUV_avx2.c
1
18#include <winpr/sysinfo.h>
19
20#include <freerdp/config.h>
21
22#include <string.h>
23#include <freerdp/types.h>
24#include <freerdp/primitives.h>
25#include <freerdp/log.h>
26
27#include "prim_internal.h"
28#include "prim_YUV.h"
29
30#if defined(SSE_AVX_INTRINSICS_ENABLED)
31#include <immintrin.h>
32
33static primitives_t* generic = nullptr;
34
35/* RGBToI444_8u: one Y, U and V sample per pixel, with the exact integer math of
36 * RGB2Y/RGB2U/RGB2V (16 bit lanes are wide enough for every intermediate value),
37 * so the result matches the generic routine byte for byte. Same method as the
38 * SSE4.1 version, 32 pixels per step. */
39
40/* a vpshufb mask that, in each 128 bit half, moves byte off of each of four 32 bit
41 * pixels into the low byte of 16 bit lane first..first+3 of that half */
42static inline __m256i avx2_I444_mask(size_t off, size_t first)
43{
44 BYTE m[16];
45 memset(m, 0x80, sizeof(m));
46 for (size_t x = 0; x < 4; x++)
47 m[2 * (first + x)] = (BYTE)(4 * x + off);
48 const __m128i half = _mm_loadu_si128((const __m128i*)m);
49 return _mm256_broadcastsi128_si256(half);
50}
51
52/* Y, U and V of sixteen pixels given as 16 bit R, G and B lanes */
53static inline void avx2_I444_YUV(__m256i r, __m256i g, __m256i b, __m256i* WINPR_RESTRICT y,
54 __m256i* WINPR_RESTRICT u, __m256i* WINPR_RESTRICT v)
55{
56 const __m256i c128 = _mm256_set1_epi16(128);
57
58 /* (54 R + 183 G + 18 B) >> 8: at most 65025, exact as unsigned 16 bit */
59 __m256i t = _mm256_add_epi16(_mm256_mullo_epi16(r, _mm256_set1_epi16(54)),
60 _mm256_mullo_epi16(g, _mm256_set1_epi16(183)));
61 t = _mm256_add_epi16(t, _mm256_mullo_epi16(b, _mm256_set1_epi16(18)));
62 *y = _mm256_srli_epi16(t, 8);
63
64 /* ((128 B - (29 R + 99 G)) >> 8) + 128: both terms are at most 32640 */
65 t = _mm256_add_epi16(_mm256_mullo_epi16(r, _mm256_set1_epi16(29)),
66 _mm256_mullo_epi16(g, _mm256_set1_epi16(99)));
67 t = _mm256_sub_epi16(_mm256_slli_epi16(b, 7), t);
68 *u = _mm256_add_epi16(_mm256_srai_epi16(t, 8), c128);
69
70 /* ((128 R - (116 G + 12 B)) >> 8) + 128 */
71 t = _mm256_add_epi16(_mm256_mullo_epi16(g, _mm256_set1_epi16(116)),
72 _mm256_mullo_epi16(b, _mm256_set1_epi16(12)));
73 t = _mm256_sub_epi16(_mm256_slli_epi16(r, 7), t);
74 *v = _mm256_add_epi16(_mm256_srai_epi16(t, 8), c128);
75}
76
77/* sixteen pixels from two loads of eight. vpshufb works within 128 bit halves, so
78 * the 16 bit lanes hold pixels 0-3, 8-11 | 4-7, 12-15 */
79static inline void avx2_I444_16px(const BYTE* WINPR_RESTRICT src, const __m256i masks[6],
80 __m256i* WINPR_RESTRICT y, __m256i* WINPR_RESTRICT u,
81 __m256i* WINPR_RESTRICT v)
82{
83 const __m256i p0 = _mm256_loadu_si256(WINPR_PACKED_ALIGN_CAST(const __m256i*, src));
84 const __m256i p1 = _mm256_loadu_si256(WINPR_PACKED_ALIGN_CAST(const __m256i*, &src[32]));
85 const __m256i r =
86 _mm256_or_si256(_mm256_shuffle_epi8(p0, masks[0]), _mm256_shuffle_epi8(p1, masks[1]));
87 const __m256i g =
88 _mm256_or_si256(_mm256_shuffle_epi8(p0, masks[2]), _mm256_shuffle_epi8(p1, masks[3]));
89 const __m256i b =
90 _mm256_or_si256(_mm256_shuffle_epi8(p0, masks[4]), _mm256_shuffle_epi8(p1, masks[5]));
91 avx2_I444_YUV(r, g, b, y, u, v);
92}
93
94/* pack the 16 bit results of pixels 0-15 and 16-31 into 32 bytes in pixel order.
95 * vpackuswb also works within halves; its four byte groups then hold pixels
96 * 0-3, 8-11, 16-19, 24-27, 4-7, 12-15, 20-23, 28-31 */
97static inline __m256i avx2_I444_pack(__m256i a, __m256i b)
98{
99 const __m256i order = _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7);
100 return _mm256_permutevar8x32_epi32(_mm256_packus_epi16(a, b), order);
101}
102
103static pstatus_t avx2_RGBToI444_32bpp(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcStep,
104 BYTE* WINPR_RESTRICT pDst[3], const UINT32 dstStep[3],
105 const prim_size_t* WINPR_RESTRICT roi, size_t rOff,
106 size_t gOff, size_t bOff)
107{
108 const __m256i masks[6] = { avx2_I444_mask(rOff, 0), avx2_I444_mask(rOff, 4),
109 avx2_I444_mask(gOff, 0), avx2_I444_mask(gOff, 4),
110 avx2_I444_mask(bOff, 0), avx2_I444_mask(bOff, 4) };
111 const size_t width = roi->width;
112
113 for (size_t row = 0; row < roi->height; row++)
114 {
115 const BYTE* src = &pSrc[row * srcStep];
116 BYTE* yd = &pDst[0][row * dstStep[0]];
117 BYTE* ud = &pDst[1][row * dstStep[1]];
118 BYTE* vd = &pDst[2][row * dstStep[2]];
119
120 size_t x = 0;
121 for (; x + 32 <= width; x += 32)
122 {
123 __m256i y0 = _mm256_setzero_si256();
124 __m256i u0 = _mm256_setzero_si256();
125 __m256i v0 = _mm256_setzero_si256();
126 __m256i y1 = _mm256_setzero_si256();
127 __m256i u1 = _mm256_setzero_si256();
128 __m256i v1 = _mm256_setzero_si256();
129 avx2_I444_16px(&src[4 * x], masks, &y0, &u0, &v0);
130 avx2_I444_16px(&src[4 * x + 64], masks, &y1, &u1, &v1);
131
132 __m256i* cyd = WINPR_PACKED_ALIGN_CAST(__m256i*, &yd[x]);
133 __m256i* cud = WINPR_PACKED_ALIGN_CAST(__m256i*, &ud[x]);
134 __m256i* cvd = WINPR_PACKED_ALIGN_CAST(__m256i*, &vd[x]);
135 _mm256_storeu_si256(cyd, avx2_I444_pack(y0, y1));
136 _mm256_storeu_si256(cud, avx2_I444_pack(u0, u1));
137 _mm256_storeu_si256(cvd, avx2_I444_pack(v0, v1));
138 }
139
140 for (; x < width; x++)
141 {
142 const BYTE* px = &src[4 * x];
143 const BYTE R = px[rOff];
144 const BYTE G = px[gOff];
145 const BYTE B = px[bOff];
146 yd[x] = RGB2Y(R, G, B);
147 ud[x] = RGB2U(R, G, B);
148 vd[x] = RGB2V(R, G, B);
149 }
150 }
151
152 return PRIMITIVES_SUCCESS;
153}
154
155static pstatus_t avx2_RGBToI444_8u(const BYTE* WINPR_RESTRICT pSrc, UINT32 SrcFormat,
156 const UINT32 srcStep, BYTE* WINPR_RESTRICT pDst[3],
157 const UINT32 dstStep[3], const prim_size_t* WINPR_RESTRICT roi)
158{
159 /* byte offsets of R, G and B in memory, as in general_RGBToI444_8u */
160 switch (SrcFormat)
161 {
162 case PIXEL_FORMAT_BGRA32:
163 case PIXEL_FORMAT_BGRX32:
164 return avx2_RGBToI444_32bpp(pSrc, srcStep, pDst, dstStep, roi, 2, 1, 0);
165 case PIXEL_FORMAT_RGBA32:
166 case PIXEL_FORMAT_RGBX32:
167 return avx2_RGBToI444_32bpp(pSrc, srcStep, pDst, dstStep, roi, 0, 1, 2);
168 case PIXEL_FORMAT_ARGB32:
169 case PIXEL_FORMAT_XRGB32:
170 return avx2_RGBToI444_32bpp(pSrc, srcStep, pDst, dstStep, roi, 1, 2, 3);
171 case PIXEL_FORMAT_ABGR32:
172 case PIXEL_FORMAT_XBGR32:
173 return avx2_RGBToI444_32bpp(pSrc, srcStep, pDst, dstStep, roi, 3, 2, 1);
174 default:
175 return generic->RGBToI444_8u(pSrc, SrcFormat, srcStep, pDst, dstStep, roi);
176 }
177}
178#endif
179
180void primitives_init_YUV_avx2_int(primitives_t* WINPR_RESTRICT prims)
181{
182#if defined(SSE_AVX_INTRINSICS_ENABLED)
183 generic = primitives_get_generic();
184
185 WLog_VRB(PRIM_TAG, "AVX2 optimizations");
186 prims->RGBToI444_8u = avx2_RGBToI444_8u;
187#else
188 WLog_VRB(PRIM_TAG, "undefined WITH_SIMD or WITH_AVX2 or AVX2 intrinsics not available");
189 WINPR_UNUSED(prims);
190#endif
191}