/src/libwebp/src/dsp/lossless_avx2.c
Line | Count | Source |
1 | | // Copyright 2025 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style license |
4 | | // that can be found in the COPYING file in the root of the source |
5 | | // tree. An additional intellectual property rights grant can be found |
6 | | // in the file PATENTS. All contributing project authors may |
7 | | // be found in the AUTHORS file in the root of the source tree. |
8 | | // ----------------------------------------------------------------------------- |
9 | | // |
10 | | // AVX2 variant of methods for lossless decoder |
11 | | // |
12 | | // Author: Vincent Rabaud (vrabaud@google.com) |
13 | | |
14 | | #include "src/dsp/dsp.h" |
15 | | |
16 | | #if defined(WEBP_USE_AVX2) |
17 | | |
18 | | #include <immintrin.h> |
19 | | #include <stddef.h> |
20 | | |
21 | | #include "src/dsp/cpu.h" |
22 | | #include "src/dsp/lossless.h" |
23 | | #include "src/webp/format_constants.h" |
24 | | #include "src/webp/types.h" |
25 | | |
26 | | //------------------------------------------------------------------------------ |
27 | | // Predictor Transform |
28 | | |
29 | | static WEBP_INLINE void Average2_m256i(const __m256i* const a0, |
30 | | const __m256i* const a1, |
31 | 1.24M | __m256i* const avg) { |
32 | | // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1) |
33 | 1.24M | const __m256i ones = _mm256_set1_epi8(1); |
34 | 1.24M | const __m256i avg1 = _mm256_avg_epu8(*a0, *a1); |
35 | 1.24M | const __m256i one = _mm256_and_si256(_mm256_xor_si256(*a0, *a1), ones); |
36 | 1.24M | *avg = _mm256_sub_epi8(avg1, one); |
37 | 1.24M | } |
38 | | |
39 | | // Batch versions of those functions. |
40 | | |
41 | | // Predictor0: ARGB_BLACK. |
42 | | static void PredictorAdd0_AVX2(const uint32_t* in, const uint32_t* upper, |
43 | 4.09M | int num_pixels, uint32_t* WEBP_RESTRICT out) { |
44 | 4.09M | int i; |
45 | 4.09M | const __m256i black = _mm256_set1_epi32((int)ARGB_BLACK); |
46 | 13.1M | for (i = 0; i + 8 <= num_pixels; i += 8) { |
47 | 9.10M | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); |
48 | 9.10M | const __m256i res = _mm256_add_epi8(src, black); |
49 | 9.10M | _mm256_storeu_si256((__m256i*)&out[i], res); |
50 | 9.10M | } |
51 | 4.09M | if (i != num_pixels) { |
52 | 1.37M | VP8LPredictorsAdd_SSE[0](in + i, NULL, num_pixels - i, out + i); |
53 | 1.37M | } |
54 | 4.09M | (void)upper; |
55 | 4.09M | } |
56 | | |
57 | | // Predictor1: left. |
58 | | static void PredictorAdd1_AVX2(const uint32_t* in, const uint32_t* upper, |
59 | 2.90M | int num_pixels, uint32_t* WEBP_RESTRICT out) { |
60 | 2.90M | int i; |
61 | 2.90M | __m256i prev = _mm256_set1_epi32((int)out[-1]); |
62 | 11.8M | for (i = 0; i + 8 <= num_pixels; i += 8) { |
63 | | // h | g | f | e | d | c | b | a |
64 | 8.91M | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); |
65 | | // g | f | e | 0 | c | b | a | 0 |
66 | 8.91M | const __m256i shift0 = _mm256_slli_si256(src, 4); |
67 | | // g + h | f + g | e + f | e | c + d | b + c | a + b | a |
68 | 8.91M | const __m256i sum0 = _mm256_add_epi8(src, shift0); |
69 | | // e + f | e | 0 | 0 | a + b | a | 0 | 0 |
70 | 8.91M | const __m256i shift1 = _mm256_slli_si256(sum0, 8); |
71 | | // e + f + g + h | e + f + g | e + f | e | a + b + c + d | a + b + c | a + b |
72 | | // | a |
73 | 8.91M | const __m256i sum1 = _mm256_add_epi8(sum0, shift1); |
74 | | // Add a + b + c + d to the upper lane. |
75 | 8.91M | const int32_t sum_abcd = _mm256_extract_epi32(sum1, 3); |
76 | 8.91M | const __m256i sum2 = _mm256_add_epi8( |
77 | 8.91M | sum1, |
78 | 8.91M | _mm256_set_epi32(sum_abcd, sum_abcd, sum_abcd, sum_abcd, 0, 0, 0, 0)); |
79 | | |
80 | 8.91M | const __m256i res = _mm256_add_epi8(sum2, prev); |
81 | 8.91M | _mm256_storeu_si256((__m256i*)&out[i], res); |
82 | | // replicate last res output in prev. |
83 | 8.91M | prev = _mm256_permutevar8x32_epi32( |
84 | 8.91M | res, _mm256_set_epi32(7, 7, 7, 7, 7, 7, 7, 7)); |
85 | 8.91M | } |
86 | 2.90M | if (i != num_pixels) { |
87 | 745k | VP8LPredictorsAdd_SSE[1](in + i, upper + i, num_pixels - i, out + i); |
88 | 745k | } |
89 | 2.90M | } |
90 | | |
91 | | // Macro that adds 32-bit integers from IN using mod 256 arithmetic |
92 | | // per 8 bit channel. |
93 | | #define GENERATE_PREDICTOR_1(X, IN) \ |
94 | | static void PredictorAdd##X##_AVX2(const uint32_t* in, \ |
95 | | const uint32_t* upper, int num_pixels, \ |
96 | 10.0k | uint32_t* WEBP_RESTRICT out) { \ |
97 | 10.0k | int i; \ |
98 | 196k | for (i = 0; i + 8 <= num_pixels; i += 8) { \ |
99 | 186k | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ |
100 | 186k | const __m256i other = _mm256_loadu_si256((const __m256i*)&(IN)); \ |
101 | 186k | const __m256i res = _mm256_add_epi8(src, other); \ |
102 | 186k | _mm256_storeu_si256((__m256i*)&out[i], res); \ |
103 | 186k | } \ |
104 | 10.0k | if (i != num_pixels) { \ |
105 | 6.40k | VP8LPredictorsAdd_SSE[(X)](in + i, upper + i, num_pixels - i, out + i); \ |
106 | 6.40k | } \ |
107 | 10.0k | } lossless_avx2.c:PredictorAdd2_AVX2 Line | Count | Source | 96 | 3.66k | uint32_t* WEBP_RESTRICT out) { \ | 97 | 3.66k | int i; \ | 98 | 33.0k | for (i = 0; i + 8 <= num_pixels; i += 8) { \ | 99 | 29.3k | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ | 100 | 29.3k | const __m256i other = _mm256_loadu_si256((const __m256i*)&(IN)); \ | 101 | 29.3k | const __m256i res = _mm256_add_epi8(src, other); \ | 102 | 29.3k | _mm256_storeu_si256((__m256i*)&out[i], res); \ | 103 | 29.3k | } \ | 104 | 3.66k | if (i != num_pixels) { \ | 105 | 2.06k | VP8LPredictorsAdd_SSE[(X)](in + i, upper + i, num_pixels - i, out + i); \ | 106 | 2.06k | } \ | 107 | 3.66k | } |
lossless_avx2.c:PredictorAdd3_AVX2 Line | Count | Source | 96 | 3.40k | uint32_t* WEBP_RESTRICT out) { \ | 97 | 3.40k | int i; \ | 98 | 96.1k | for (i = 0; i + 8 <= num_pixels; i += 8) { \ | 99 | 92.7k | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ | 100 | 92.7k | const __m256i other = _mm256_loadu_si256((const __m256i*)&(IN)); \ | 101 | 92.7k | const __m256i res = _mm256_add_epi8(src, other); \ | 102 | 92.7k | _mm256_storeu_si256((__m256i*)&out[i], res); \ | 103 | 92.7k | } \ | 104 | 3.40k | if (i != num_pixels) { \ | 105 | 2.37k | VP8LPredictorsAdd_SSE[(X)](in + i, upper + i, num_pixels - i, out + i); \ | 106 | 2.37k | } \ | 107 | 3.40k | } |
lossless_avx2.c:PredictorAdd4_AVX2 Line | Count | Source | 96 | 2.99k | uint32_t* WEBP_RESTRICT out) { \ | 97 | 2.99k | int i; \ | 98 | 67.4k | for (i = 0; i + 8 <= num_pixels; i += 8) { \ | 99 | 64.4k | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ | 100 | 64.4k | const __m256i other = _mm256_loadu_si256((const __m256i*)&(IN)); \ | 101 | 64.4k | const __m256i res = _mm256_add_epi8(src, other); \ | 102 | 64.4k | _mm256_storeu_si256((__m256i*)&out[i], res); \ | 103 | 64.4k | } \ | 104 | 2.99k | if (i != num_pixels) { \ | 105 | 1.96k | VP8LPredictorsAdd_SSE[(X)](in + i, upper + i, num_pixels - i, out + i); \ | 106 | 1.96k | } \ | 107 | 2.99k | } |
|
108 | | |
109 | | // Predictor2: Top. |
110 | | GENERATE_PREDICTOR_1(2, upper[i]) |
111 | | // Predictor3: Top-right. |
112 | | GENERATE_PREDICTOR_1(3, upper[i + 1]) |
113 | | // Predictor4: Top-left. |
114 | | GENERATE_PREDICTOR_1(4, upper[i - 1]) |
115 | | #undef GENERATE_PREDICTOR_1 |
116 | | |
117 | | // Due to averages with integers, values cannot be accumulated in parallel for |
118 | | // predictors 5 to 7. |
119 | | |
120 | | #define GENERATE_PREDICTOR_2(X, IN) \ |
121 | | static void PredictorAdd##X##_AVX2(const uint32_t* in, \ |
122 | | const uint32_t* upper, int num_pixels, \ |
123 | 9.34k | uint32_t* WEBP_RESTRICT out) { \ |
124 | 9.34k | int i; \ |
125 | 266k | for (i = 0; i + 8 <= num_pixels; i += 8) { \ |
126 | 256k | const __m256i Tother = _mm256_loadu_si256((const __m256i*)&(IN)); \ |
127 | 256k | const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); \ |
128 | 256k | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ |
129 | 256k | __m256i avg, res; \ |
130 | 256k | Average2_m256i(&T, &Tother, &avg); \ |
131 | 256k | res = _mm256_add_epi8(avg, src); \ |
132 | 256k | _mm256_storeu_si256((__m256i*)&out[i], res); \ |
133 | 256k | } \ |
134 | 9.34k | if (i != num_pixels) { \ |
135 | 5.16k | VP8LPredictorsAdd_SSE[(X)](in + i, upper + i, num_pixels - i, out + i); \ |
136 | 5.16k | } \ |
137 | 9.34k | } lossless_avx2.c:PredictorAdd8_AVX2 Line | Count | Source | 123 | 5.08k | uint32_t* WEBP_RESTRICT out) { \ | 124 | 5.08k | int i; \ | 125 | 144k | for (i = 0; i + 8 <= num_pixels; i += 8) { \ | 126 | 139k | const __m256i Tother = _mm256_loadu_si256((const __m256i*)&(IN)); \ | 127 | 139k | const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); \ | 128 | 139k | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ | 129 | 139k | __m256i avg, res; \ | 130 | 139k | Average2_m256i(&T, &Tother, &avg); \ | 131 | 139k | res = _mm256_add_epi8(avg, src); \ | 132 | 139k | _mm256_storeu_si256((__m256i*)&out[i], res); \ | 133 | 139k | } \ | 134 | 5.08k | if (i != num_pixels) { \ | 135 | 2.77k | VP8LPredictorsAdd_SSE[(X)](in + i, upper + i, num_pixels - i, out + i); \ | 136 | 2.77k | } \ | 137 | 5.08k | } |
lossless_avx2.c:PredictorAdd9_AVX2 Line | Count | Source | 123 | 4.25k | uint32_t* WEBP_RESTRICT out) { \ | 124 | 4.25k | int i; \ | 125 | 121k | for (i = 0; i + 8 <= num_pixels; i += 8) { \ | 126 | 117k | const __m256i Tother = _mm256_loadu_si256((const __m256i*)&(IN)); \ | 127 | 117k | const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); \ | 128 | 117k | const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ | 129 | 117k | __m256i avg, res; \ | 130 | 117k | Average2_m256i(&T, &Tother, &avg); \ | 131 | 117k | res = _mm256_add_epi8(avg, src); \ | 132 | 117k | _mm256_storeu_si256((__m256i*)&out[i], res); \ | 133 | 117k | } \ | 134 | 4.25k | if (i != num_pixels) { \ | 135 | 2.39k | VP8LPredictorsAdd_SSE[(X)](in + i, upper + i, num_pixels - i, out + i); \ | 136 | 2.39k | } \ | 137 | 4.25k | } |
|
138 | | // Predictor8: average TL T. |
139 | | GENERATE_PREDICTOR_2(8, upper[i - 1]) |
140 | | // Predictor9: average T TR. |
141 | | GENERATE_PREDICTOR_2(9, upper[i + 1]) |
142 | | #undef GENERATE_PREDICTOR_2 |
143 | | |
144 | | // Predictor10: average of (average of (L,TL), average of (T, TR)). |
145 | | #define DO_PRED10(OUT) \ |
146 | 464k | do { \ |
147 | 464k | __m256i avgLTL, avg; \ |
148 | 464k | Average2_m256i(&L, &TL, &avgLTL); \ |
149 | 464k | Average2_m256i(&avgTTR, &avgLTL, &avg); \ |
150 | 464k | L = _mm256_add_epi8(avg, src); \ |
151 | 464k | out[i + (OUT)] = (uint32_t)_mm256_cvtsi256_si32(L); \ |
152 | 464k | } while (0) |
153 | | |
154 | | #define DO_PRED10_SHIFT \ |
155 | 464k | do { \ |
156 | 464k | /* Rotate the pre-computed values for the next iteration.*/ \ |
157 | 464k | avgTTR = _mm256_srli_si256(avgTTR, 4); \ |
158 | 464k | TL = _mm256_srli_si256(TL, 4); \ |
159 | 464k | src = _mm256_srli_si256(src, 4); \ |
160 | 464k | } while (0) |
161 | | |
162 | | static void PredictorAdd10_AVX2(const uint32_t* in, const uint32_t* upper, |
163 | 3.51k | int num_pixels, uint32_t* WEBP_RESTRICT out) { |
164 | 3.51k | int i, j; |
165 | 3.51k | __m256i L = _mm256_setr_epi32((int)out[-1], 0, 0, 0, 0, 0, 0, 0); |
166 | 61.5k | for (i = 0; i + 8 <= num_pixels; i += 8) { |
167 | 58.0k | __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); |
168 | 58.0k | __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); |
169 | 58.0k | const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); |
170 | 58.0k | const __m256i TR = _mm256_loadu_si256((const __m256i*)&upper[i + 1]); |
171 | 58.0k | __m256i avgTTR; |
172 | 58.0k | Average2_m256i(&T, &TR, &avgTTR); |
173 | 58.0k | { |
174 | 58.0k | const __m256i avgTTR_bak = avgTTR; |
175 | 58.0k | const __m256i TL_bak = TL; |
176 | 58.0k | const __m256i src_bak = src; |
177 | 290k | for (j = 0; j < 4; ++j) { |
178 | 232k | DO_PRED10(j); |
179 | 232k | DO_PRED10_SHIFT; |
180 | 232k | } |
181 | 58.0k | avgTTR = _mm256_permute2x128_si256(avgTTR_bak, avgTTR_bak, 1); |
182 | 58.0k | TL = _mm256_permute2x128_si256(TL_bak, TL_bak, 1); |
183 | 58.0k | src = _mm256_permute2x128_si256(src_bak, src_bak, 1); |
184 | 290k | for (; j < 8; ++j) { |
185 | 232k | DO_PRED10(j); |
186 | 232k | DO_PRED10_SHIFT; |
187 | 232k | } |
188 | 58.0k | } |
189 | 58.0k | } |
190 | 3.51k | if (i != num_pixels) { |
191 | 2.00k | VP8LPredictorsAdd_SSE[10](in + i, upper + i, num_pixels - i, out + i); |
192 | 2.00k | } |
193 | 3.51k | } |
194 | | #undef DO_PRED10 |
195 | | #undef DO_PRED10_SHIFT |
196 | | |
197 | | // Predictor11: select. |
198 | | #define DO_PRED11(OUT) \ |
199 | 1.47M | do { \ |
200 | 1.47M | const __m256i L_lo = _mm256_unpacklo_epi32(L, T); \ |
201 | 1.47M | const __m256i TL_lo = _mm256_unpacklo_epi32(TL, T); \ |
202 | 1.47M | const __m256i pb = _mm256_sad_epu8(L_lo, TL_lo); /* pb = sum |L-TL|*/ \ |
203 | 1.47M | const __m256i mask = _mm256_cmpgt_epi32(pb, pa); \ |
204 | 1.47M | const __m256i A = _mm256_and_si256(mask, L); \ |
205 | 1.47M | const __m256i B = _mm256_andnot_si256(mask, T); \ |
206 | 1.47M | const __m256i pred = _mm256_or_si256(A, B); /* pred = (pa > b)? L : T*/ \ |
207 | 1.47M | L = _mm256_add_epi8(src, pred); \ |
208 | 1.47M | out[i + (OUT)] = (uint32_t)_mm256_cvtsi256_si32(L); \ |
209 | 1.47M | } while (0) |
210 | | |
211 | | #define DO_PRED11_SHIFT \ |
212 | 1.47M | do { \ |
213 | 1.47M | /* Shift the pre-computed value for the next iteration.*/ \ |
214 | 1.47M | T = _mm256_srli_si256(T, 4); \ |
215 | 1.47M | TL = _mm256_srli_si256(TL, 4); \ |
216 | 1.47M | src = _mm256_srli_si256(src, 4); \ |
217 | 1.47M | pa = _mm256_srli_si256(pa, 4); \ |
218 | 1.47M | } while (0) |
219 | | |
220 | | static void PredictorAdd11_AVX2(const uint32_t* in, const uint32_t* upper, |
221 | 9.76k | int num_pixels, uint32_t* WEBP_RESTRICT out) { |
222 | 9.76k | int i, j; |
223 | 9.76k | __m256i pa; |
224 | 9.76k | __m256i L = _mm256_setr_epi32((int)out[-1], 0, 0, 0, 0, 0, 0, 0); |
225 | 194k | for (i = 0; i + 8 <= num_pixels; i += 8) { |
226 | 184k | __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); |
227 | 184k | __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); |
228 | 184k | __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); |
229 | 184k | { |
230 | | // We can unpack with any value on the upper 32 bits, provided it's the |
231 | | // same on both operands (so that their sum of abs diff is zero). Here we |
232 | | // use T. |
233 | 184k | const __m256i T_lo = _mm256_unpacklo_epi32(T, T); |
234 | 184k | const __m256i TL_lo = _mm256_unpacklo_epi32(TL, T); |
235 | 184k | const __m256i T_hi = _mm256_unpackhi_epi32(T, T); |
236 | 184k | const __m256i TL_hi = _mm256_unpackhi_epi32(TL, T); |
237 | 184k | const __m256i s_lo = _mm256_sad_epu8(T_lo, TL_lo); |
238 | 184k | const __m256i s_hi = _mm256_sad_epu8(T_hi, TL_hi); |
239 | 184k | pa = _mm256_packs_epi32(s_lo, s_hi); // pa = sum |T-TL| |
240 | 184k | } |
241 | 184k | { |
242 | 184k | const __m256i T_bak = T; |
243 | 184k | const __m256i TL_bak = TL; |
244 | 184k | const __m256i src_bak = src; |
245 | 184k | const __m256i pa_bak = pa; |
246 | 924k | for (j = 0; j < 4; ++j) { |
247 | 739k | DO_PRED11(j); |
248 | 739k | DO_PRED11_SHIFT; |
249 | 739k | } |
250 | 184k | T = _mm256_permute2x128_si256(T_bak, T_bak, 1); |
251 | 184k | TL = _mm256_permute2x128_si256(TL_bak, TL_bak, 1); |
252 | 184k | src = _mm256_permute2x128_si256(src_bak, src_bak, 1); |
253 | 184k | pa = _mm256_permute2x128_si256(pa_bak, pa_bak, 1); |
254 | 924k | for (; j < 8; ++j) { |
255 | 739k | DO_PRED11(j); |
256 | 739k | DO_PRED11_SHIFT; |
257 | 739k | } |
258 | 184k | } |
259 | 184k | } |
260 | 9.76k | if (i != num_pixels) { |
261 | 7.73k | VP8LPredictorsAdd_SSE[11](in + i, upper + i, num_pixels - i, out + i); |
262 | 7.73k | } |
263 | 9.76k | } |
264 | | #undef DO_PRED11 |
265 | | #undef DO_PRED11_SHIFT |
266 | | |
267 | | // Predictor12: ClampedAddSubtractFull. |
268 | | #define DO_PRED12(DIFF, OUT) \ |
269 | 1.70M | do { \ |
270 | 1.70M | const __m256i all = _mm256_add_epi16(L, (DIFF)); \ |
271 | 1.70M | const __m256i alls = _mm256_packus_epi16(all, all); \ |
272 | 1.70M | const __m256i res = _mm256_add_epi8(src, alls); \ |
273 | 1.70M | out[i + (OUT)] = (uint32_t)_mm256_cvtsi256_si32(res); \ |
274 | 1.70M | L = _mm256_unpacklo_epi8(res, zero); \ |
275 | 1.70M | } while (0) |
276 | | |
277 | | #define DO_PRED12_SHIFT(DIFF, LANE) \ |
278 | 1.49M | do { \ |
279 | 1.49M | /* Shift the pre-computed value for the next iteration.*/ \ |
280 | 1.49M | if ((LANE) == 0) (DIFF) = _mm256_srli_si256(DIFF, 8); \ |
281 | 1.49M | src = _mm256_srli_si256(src, 4); \ |
282 | 1.49M | } while (0) |
283 | | |
284 | | static void PredictorAdd12_AVX2(const uint32_t* in, const uint32_t* upper, |
285 | 6.36k | int num_pixels, uint32_t* WEBP_RESTRICT out) { |
286 | 6.36k | int i; |
287 | 6.36k | const __m256i zero = _mm256_setzero_si256(); |
288 | 6.36k | const __m256i L8 = _mm256_setr_epi32((int)out[-1], 0, 0, 0, 0, 0, 0, 0); |
289 | 6.36k | __m256i L = _mm256_unpacklo_epi8(L8, zero); |
290 | 219k | for (i = 0; i + 8 <= num_pixels; i += 8) { |
291 | | // Load 8 pixels at a time. |
292 | 213k | __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); |
293 | 213k | const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); |
294 | 213k | const __m256i T_lo = _mm256_unpacklo_epi8(T, zero); |
295 | 213k | const __m256i T_hi = _mm256_unpackhi_epi8(T, zero); |
296 | 213k | const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); |
297 | 213k | const __m256i TL_lo = _mm256_unpacklo_epi8(TL, zero); |
298 | 213k | const __m256i TL_hi = _mm256_unpackhi_epi8(TL, zero); |
299 | 213k | __m256i diff_lo = _mm256_sub_epi16(T_lo, TL_lo); |
300 | 213k | __m256i diff_hi = _mm256_sub_epi16(T_hi, TL_hi); |
301 | 213k | const __m256i diff_lo_bak = diff_lo; |
302 | 213k | const __m256i diff_hi_bak = diff_hi; |
303 | 213k | const __m256i src_bak = src; |
304 | 213k | DO_PRED12(diff_lo, 0); |
305 | 213k | DO_PRED12_SHIFT(diff_lo, 0); |
306 | 213k | DO_PRED12(diff_lo, 1); |
307 | 213k | DO_PRED12_SHIFT(diff_lo, 0); |
308 | 213k | DO_PRED12(diff_hi, 2); |
309 | 213k | DO_PRED12_SHIFT(diff_hi, 0); |
310 | 213k | DO_PRED12(diff_hi, 3); |
311 | 213k | DO_PRED12_SHIFT(diff_hi, 0); |
312 | | |
313 | | // Process the upper lane. |
314 | 213k | diff_lo = _mm256_permute2x128_si256(diff_lo_bak, diff_lo_bak, 1); |
315 | 213k | diff_hi = _mm256_permute2x128_si256(diff_hi_bak, diff_hi_bak, 1); |
316 | 213k | src = _mm256_permute2x128_si256(src_bak, src_bak, 1); |
317 | | |
318 | 213k | DO_PRED12(diff_lo, 4); |
319 | 213k | DO_PRED12_SHIFT(diff_lo, 0); |
320 | 213k | DO_PRED12(diff_lo, 5); |
321 | 213k | DO_PRED12_SHIFT(diff_lo, 1); |
322 | 213k | DO_PRED12(diff_hi, 6); |
323 | 213k | DO_PRED12_SHIFT(diff_hi, 0); |
324 | 213k | DO_PRED12(diff_hi, 7); |
325 | 213k | } |
326 | 6.36k | if (i != num_pixels) { |
327 | 4.45k | VP8LPredictorsAdd_SSE[12](in + i, upper + i, num_pixels - i, out + i); |
328 | 4.45k | } |
329 | 6.36k | } |
330 | | #undef DO_PRED12 |
331 | | #undef DO_PRED12_SHIFT |
332 | | |
333 | | // Due to averages with integers, values cannot be accumulated in parallel for |
334 | | // predictors 13. |
335 | | |
336 | | //------------------------------------------------------------------------------ |
337 | | // Subtract-Green Transform |
338 | | |
339 | | static void AddGreenToBlueAndRed_AVX2(const uint32_t* const src, int num_pixels, |
340 | 7.35k | uint32_t* dst) { |
341 | 7.35k | int i; |
342 | 7.35k | const __m256i kCstShuffle = _mm256_set_epi8( |
343 | 7.35k | -1, 29, -1, 29, -1, 25, -1, 25, -1, 21, -1, 21, -1, 17, -1, 17, -1, 13, |
344 | 7.35k | -1, 13, -1, 9, -1, 9, -1, 5, -1, 5, -1, 1, -1, 1); |
345 | 19.5M | for (i = 0; i + 8 <= num_pixels; i += 8) { |
346 | 19.5M | const __m256i in = _mm256_loadu_si256((const __m256i*)&src[i]); // argb |
347 | 19.5M | const __m256i in_0g0g = _mm256_shuffle_epi8(in, kCstShuffle); // 0g0g |
348 | 19.5M | const __m256i out = _mm256_add_epi8(in, in_0g0g); |
349 | 19.5M | _mm256_storeu_si256((__m256i*)&dst[i], out); |
350 | 19.5M | } |
351 | | // fallthrough and finish off with SSE. |
352 | 7.35k | if (i != num_pixels) { |
353 | 88 | VP8LAddGreenToBlueAndRed_SSE(src + i, num_pixels - i, dst + i); |
354 | 88 | } |
355 | 7.35k | } |
356 | | |
357 | | //------------------------------------------------------------------------------ |
358 | | // Color Transform |
359 | | |
360 | | static void TransformColorInverse_AVX2(const VP8LMultipliers* const m, |
361 | | const uint32_t* const src, |
362 | 8.31M | int num_pixels, uint32_t* dst) { |
363 | | // sign-extended multiplying constants, pre-shifted by 5. |
364 | 24.9M | #define CST(X) (((int16_t)(m->X << 8)) >> 5) // sign-extend |
365 | 8.31M | const __m256i mults_rb = _mm256_set1_epi32( |
366 | 8.31M | (int)((uint32_t)CST(green_to_red) << 16 | (CST(green_to_blue) & 0xffff))); |
367 | 8.31M | const __m256i mults_b2 = _mm256_set1_epi32(CST(red_to_blue)); |
368 | 8.31M | #undef CST |
369 | 8.31M | const __m256i mask_ag = _mm256_set1_epi32((int)0xff00ff00); |
370 | 8.31M | const __m256i perm1 = _mm256_setr_epi8( |
371 | 8.31M | -1, 1, -1, 1, -1, 5, -1, 5, -1, 9, -1, 9, -1, 13, -1, 13, -1, 17, -1, 17, |
372 | 8.31M | -1, 21, -1, 21, -1, 25, -1, 25, -1, 29, -1, 29); |
373 | 8.31M | const __m256i perm2 = _mm256_setr_epi8( |
374 | 8.31M | -1, 2, -1, -1, -1, 6, -1, -1, -1, 10, -1, -1, -1, 14, -1, -1, -1, 18, -1, |
375 | 8.31M | -1, -1, 22, -1, -1, -1, 26, -1, -1, -1, 30, -1, -1); |
376 | 8.31M | int i; |
377 | 20.2M | for (i = 0; i + 8 <= num_pixels; i += 8) { |
378 | 11.8M | const __m256i A = _mm256_loadu_si256((const __m256i*)(src + i)); |
379 | 11.8M | const __m256i B = _mm256_shuffle_epi8(A, perm1); // argb -> g0g0 |
380 | 11.8M | const __m256i C = _mm256_mulhi_epi16(B, mults_rb); |
381 | 11.8M | const __m256i D = _mm256_add_epi8(A, C); |
382 | 11.8M | const __m256i E = _mm256_shuffle_epi8(D, perm2); |
383 | 11.8M | const __m256i F = _mm256_mulhi_epi16(E, mults_b2); |
384 | 11.8M | const __m256i G = _mm256_add_epi8(D, F); |
385 | 11.8M | const __m256i out = _mm256_blendv_epi8(G, A, mask_ag); |
386 | 11.8M | _mm256_storeu_si256((__m256i*)&dst[i], out); |
387 | 11.8M | } |
388 | | // Fall-back to SSE-version for left-overs. |
389 | 8.31M | if (i != num_pixels) { |
390 | 4.41M | VP8LTransformColorInverse_SSE(m, src + i, num_pixels - i, dst + i); |
391 | 4.41M | } |
392 | 8.31M | } |
393 | | |
394 | | //------------------------------------------------------------------------------ |
395 | | // Color-space conversion functions |
396 | | |
397 | | static void ConvertBGRAToRGBA_AVX2(const uint32_t* WEBP_RESTRICT src, |
398 | 1.32M | int num_pixels, uint8_t* WEBP_RESTRICT dst) { |
399 | 1.32M | const __m256i* in = (const __m256i*)src; |
400 | 1.32M | __m256i* out = (__m256i*)dst; |
401 | 173M | while (num_pixels >= 8) { |
402 | 171M | const __m256i A = _mm256_loadu_si256(in++); |
403 | 171M | const __m256i B = _mm256_shuffle_epi8( |
404 | 171M | A, |
405 | 171M | _mm256_set_epi8(15, 12, 13, 14, 11, 8, 9, 10, 7, 4, 5, 6, 3, 0, 1, 2, |
406 | 171M | 15, 12, 13, 14, 11, 8, 9, 10, 7, 4, 5, 6, 3, 0, 1, 2)); |
407 | 171M | _mm256_storeu_si256(out++, B); |
408 | 171M | num_pixels -= 8; |
409 | 171M | } |
410 | | // left-overs |
411 | 1.32M | if (num_pixels > 0) { |
412 | 1.23M | VP8LConvertBGRAToRGBA_SSE((const uint32_t*)in, num_pixels, (uint8_t*)out); |
413 | 1.23M | } |
414 | 1.32M | } |
415 | | |
416 | | //------------------------------------------------------------------------------ |
417 | | // Entry point |
418 | | |
419 | | extern void VP8LDspInitAVX2(void); |
420 | | |
421 | 1 | WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitAVX2(void) { |
422 | 1 | VP8LPredictorsAdd[0] = PredictorAdd0_AVX2; |
423 | 1 | VP8LPredictorsAdd[1] = PredictorAdd1_AVX2; |
424 | 1 | VP8LPredictorsAdd[2] = PredictorAdd2_AVX2; |
425 | 1 | VP8LPredictorsAdd[3] = PredictorAdd3_AVX2; |
426 | 1 | VP8LPredictorsAdd[4] = PredictorAdd4_AVX2; |
427 | 1 | VP8LPredictorsAdd[8] = PredictorAdd8_AVX2; |
428 | 1 | VP8LPredictorsAdd[9] = PredictorAdd9_AVX2; |
429 | 1 | VP8LPredictorsAdd[10] = PredictorAdd10_AVX2; |
430 | 1 | VP8LPredictorsAdd[11] = PredictorAdd11_AVX2; |
431 | 1 | VP8LPredictorsAdd[12] = PredictorAdd12_AVX2; |
432 | | |
433 | 1 | VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_AVX2; |
434 | 1 | VP8LTransformColorInverse = TransformColorInverse_AVX2; |
435 | 1 | VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA_AVX2; |
436 | 1 | } |
437 | | |
438 | | #else // !WEBP_USE_AVX2 |
439 | | |
440 | | WEBP_DSP_INIT_STUB(VP8LDspInitAVX2) |
441 | | |
442 | | #endif // WEBP_USE_AVX2 |