/src/libvpx/vpx_dsp/x86/deblock_sse2.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2018 The WebM project authors. All Rights Reserved. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license |
5 | | * that can be found in the LICENSE file in the root of the source |
6 | | * tree. An additional intellectual property rights grant can be found |
7 | | * in the file PATENTS. All contributing project authors may |
8 | | * be found in the AUTHORS file in the root of the source tree. |
9 | | */ |
10 | | |
11 | | #include <assert.h> |
12 | | #include <emmintrin.h> |
13 | | #include <stdlib.h> |
14 | | |
15 | | #include "./vpx_dsp_rtcd.h" |
16 | | #include "vpx/vpx_integer.h" |
17 | | #include "vpx_dsp/x86/mem_sse2.h" |
18 | | |
19 | | extern const int16_t vpx_rv[]; |
20 | | |
21 | | static INLINE __m128i filter_16_sse2(__m128i v, __m128i p_above2, |
22 | | __m128i p_above1, __m128i p_below1, |
23 | | __m128i p_below2, __m128i flimit, |
24 | 0 | __m128i zero) { |
25 | 0 | const __m128i k1 = _mm_avg_epu8(p_above2, p_above1); |
26 | 0 | const __m128i k2 = _mm_avg_epu8(p_below2, p_below1); |
27 | 0 | const __m128i k3 = _mm_avg_epu8(k1, k2); |
28 | 0 | const __m128i filtered = _mm_avg_epu8(k3, v); |
29 | |
|
30 | 0 | const __m128i diff_b1 = |
31 | 0 | _mm_or_si128(_mm_subs_epu8(v, p_below1), _mm_subs_epu8(p_below1, v)); |
32 | 0 | const __m128i diff_b2 = |
33 | 0 | _mm_or_si128(_mm_subs_epu8(v, p_below2), _mm_subs_epu8(p_below2, v)); |
34 | 0 | const __m128i diff_a1 = |
35 | 0 | _mm_or_si128(_mm_subs_epu8(v, p_above1), _mm_subs_epu8(p_above1, v)); |
36 | 0 | const __m128i diff_a2 = |
37 | 0 | _mm_or_si128(_mm_subs_epu8(v, p_above2), _mm_subs_epu8(p_above2, v)); |
38 | |
|
39 | 0 | const __m128i mask_b1 = _mm_cmpeq_epi8(_mm_subs_epu8(flimit, diff_b1), zero); |
40 | 0 | const __m128i mask_b2 = _mm_cmpeq_epi8(_mm_subs_epu8(flimit, diff_b2), zero); |
41 | 0 | const __m128i mask_a1 = _mm_cmpeq_epi8(_mm_subs_epu8(flimit, diff_a1), zero); |
42 | 0 | const __m128i mask_a2 = _mm_cmpeq_epi8(_mm_subs_epu8(flimit, diff_a2), zero); |
43 | |
|
44 | 0 | const __m128i fail_mask = _mm_or_si128(_mm_or_si128(mask_b1, mask_b2), |
45 | 0 | _mm_or_si128(mask_a1, mask_a2)); |
46 | |
|
47 | 0 | return _mm_or_si128(_mm_and_si128(fail_mask, v), |
48 | 0 | _mm_andnot_si128(fail_mask, filtered)); |
49 | 0 | } |
50 | | |
51 | | static INLINE unsigned char filter_scalar( |
52 | | unsigned char v, unsigned char p_above2, unsigned char p_above1, |
53 | 0 | unsigned char p_below1, unsigned char p_below2, unsigned char flimit) { |
54 | 0 | if ((abs(v - p_above2) < flimit) && (abs(v - p_above1) < flimit) && |
55 | 0 | (abs(v - p_below1) < flimit) && (abs(v - p_below2) < flimit)) { |
56 | 0 | const unsigned char k1 = (p_above2 + p_above1 + 1) >> 1; |
57 | 0 | const unsigned char k2 = (p_below2 + p_below1 + 1) >> 1; |
58 | 0 | const unsigned char k3 = (k1 + k2 + 1) >> 1; |
59 | 0 | return (k3 + v + 1) >> 1; |
60 | 0 | } |
61 | 0 | return v; |
62 | 0 | } |
63 | | |
64 | | void vpx_post_proc_down_and_across_mb_row_sse2(unsigned char *src, |
65 | | unsigned char *dst, |
66 | | int src_pitch, int dst_pitch, |
67 | | int cols, unsigned char *flimits, |
68 | 0 | int size) { |
69 | 0 | int row, col; |
70 | 0 | const __m128i zero = _mm_setzero_si128(); |
71 | |
|
72 | 0 | for (row = 0; row < size; row++) { |
73 | 0 | const int aligned_cols = cols & ~15; |
74 | 0 | for (col = 0; col < aligned_cols; col += 16) { |
75 | 0 | const __m128i v = _mm_loadu_si128((const __m128i *)(src + col)); |
76 | 0 | const __m128i p_below1 = |
77 | 0 | _mm_loadu_si128((const __m128i *)(src + src_pitch + col)); |
78 | 0 | const __m128i p_below2 = |
79 | 0 | _mm_loadu_si128((const __m128i *)(src + 2 * src_pitch + col)); |
80 | 0 | const __m128i p_above1 = |
81 | 0 | _mm_loadu_si128((const __m128i *)(src - src_pitch + col)); |
82 | 0 | const __m128i p_above2 = |
83 | 0 | _mm_loadu_si128((const __m128i *)(src - 2 * src_pitch + col)); |
84 | 0 | const __m128i f = _mm_loadu_si128((const __m128i *)(flimits + col)); |
85 | |
|
86 | 0 | const __m128i out = |
87 | 0 | filter_16_sse2(v, p_above2, p_above1, p_below1, p_below2, f, zero); |
88 | 0 | _mm_storeu_si128((__m128i *)(dst + col), out); |
89 | 0 | } |
90 | |
|
91 | 0 | for (; col < cols; col++) { |
92 | 0 | const unsigned char p_above2 = src[col - 2 * src_pitch]; |
93 | 0 | const unsigned char p_above1 = src[col - src_pitch]; |
94 | 0 | const unsigned char p_below1 = src[col + src_pitch]; |
95 | 0 | const unsigned char p_below2 = src[col + 2 * src_pitch]; |
96 | 0 | const unsigned char v = src[col]; |
97 | |
|
98 | 0 | dst[col] = filter_scalar(v, p_above2, p_above1, p_below1, p_below2, |
99 | 0 | flimits[col]); |
100 | 0 | } |
101 | |
|
102 | 0 | dst[-2] = dst[-1] = dst[0]; |
103 | 0 | dst[cols] = dst[cols + 1] = dst[cols - 1]; |
104 | |
|
105 | 0 | __m128i prev_out = _mm_setzero_si128(); |
106 | 0 | for (col = 0; col < aligned_cols; col += 16) { |
107 | 0 | const __m128i v = _mm_loadu_si128((const __m128i *)(dst + col)); |
108 | 0 | const __m128i p_left2 = _mm_loadu_si128((const __m128i *)(dst + col - 2)); |
109 | 0 | const __m128i p_left1 = _mm_loadu_si128((const __m128i *)(dst + col - 1)); |
110 | 0 | const __m128i p_right1 = |
111 | 0 | _mm_loadu_si128((const __m128i *)(dst + col + 1)); |
112 | 0 | const __m128i p_right2 = |
113 | 0 | _mm_loadu_si128((const __m128i *)(dst + col + 2)); |
114 | 0 | const __m128i f = _mm_loadu_si128((const __m128i *)(flimits + col)); |
115 | |
|
116 | 0 | if (col > 0) { |
117 | 0 | _mm_storeu_si128((__m128i *)(dst + col - 16), prev_out); |
118 | 0 | } |
119 | 0 | prev_out = |
120 | 0 | filter_16_sse2(v, p_left2, p_left1, p_right1, p_right2, f, zero); |
121 | 0 | } |
122 | |
|
123 | 0 | const int last_sse_col = col; |
124 | 0 | const unsigned char prev_p2 = dst[col - 2]; |
125 | 0 | const unsigned char prev_p1 = dst[col - 1]; |
126 | 0 | unsigned char d[4] = { 0 }; |
127 | |
|
128 | 0 | if (col > 0) { |
129 | 0 | _mm_storeu_si128((__m128i *)(dst + col - 16), prev_out); |
130 | 0 | } |
131 | |
|
132 | 0 | for (; col < cols; col++) { |
133 | 0 | const unsigned char p_left2 = |
134 | 0 | (col == last_sse_col) |
135 | 0 | ? prev_p2 |
136 | 0 | : ((col == last_sse_col + 1) ? prev_p1 : dst[col - 2]); |
137 | 0 | const unsigned char p_left1 = |
138 | 0 | (col == last_sse_col) ? prev_p1 : dst[col - 1]; |
139 | 0 | const unsigned char p_right1 = dst[col + 1]; |
140 | 0 | const unsigned char p_right2 = dst[col + 2]; |
141 | 0 | const unsigned char v = dst[col]; |
142 | |
|
143 | 0 | d[col & 3] = |
144 | 0 | filter_scalar(v, p_left2, p_left1, p_right1, p_right2, flimits[col]); |
145 | |
|
146 | 0 | if (col >= last_sse_col + 2) dst[col - 2] = d[(col - 2) & 3]; |
147 | 0 | } |
148 | |
|
149 | 0 | if (cols - last_sse_col >= 2) { |
150 | 0 | dst[cols - 2] = d[(cols - 2) & 3]; |
151 | 0 | dst[cols - 1] = d[(cols - 1) & 3]; |
152 | 0 | } else if (cols - last_sse_col == 1) { |
153 | 0 | dst[cols - 1] = d[(cols - 1) & 3]; |
154 | 0 | } |
155 | |
|
156 | 0 | src += src_pitch; |
157 | 0 | dst += dst_pitch; |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | static INLINE __m128i filter_4_across(const uint8_t *s_c, __m128i left4_u8, |
162 | | __m128i right4_u8, int *sum, int *sumsq, |
163 | | __m128i f_vec, __m128i eight, |
164 | 0 | __m128i zero) { |
165 | 0 | const __m128i left16 = _mm_unpacklo_epi8(left4_u8, zero); |
166 | 0 | const __m128i right16 = _mm_unpacklo_epi8(right4_u8, zero); |
167 | |
|
168 | 0 | const __m128i x16 = _mm_sub_epi16(right16, left16); |
169 | 0 | const __m128i y16 = _mm_add_epi16(right16, left16); |
170 | |
|
171 | 0 | const __m128i d_sum = _mm_srai_epi32(_mm_unpacklo_epi16(zero, x16), 16); |
172 | |
|
173 | 0 | const __m128i xy_lo = _mm_mullo_epi16(x16, y16); |
174 | 0 | const __m128i xy_hi = _mm_mulhi_epi16(x16, y16); |
175 | 0 | const __m128i d_sumsq = _mm_unpacklo_epi16(xy_lo, xy_hi); |
176 | |
|
177 | 0 | const __m128i t1 = _mm_slli_si128(d_sum, 4); |
178 | 0 | const __m128i s1 = _mm_add_epi32(d_sum, t1); |
179 | 0 | const __m128i t2 = _mm_slli_si128(s1, 8); |
180 | 0 | const __m128i pref_sum = _mm_add_epi32(s1, t2); |
181 | 0 | const __m128i sum_vec = _mm_add_epi32(pref_sum, _mm_set1_epi32(*sum)); |
182 | |
|
183 | 0 | const __m128i u1 = _mm_slli_si128(d_sumsq, 4); |
184 | 0 | const __m128i q1 = _mm_add_epi32(d_sumsq, u1); |
185 | 0 | const __m128i u2 = _mm_slli_si128(q1, 8); |
186 | 0 | const __m128i pref_sumsq = _mm_add_epi32(q1, u2); |
187 | 0 | const __m128i sumsq_vec = _mm_add_epi32(pref_sumsq, _mm_set1_epi32(*sumsq)); |
188 | |
|
189 | 0 | *sum = _mm_cvtsi128_si32(_mm_shuffle_epi32(sum_vec, 3)); |
190 | 0 | *sumsq = _mm_cvtsi128_si32(_mm_shuffle_epi32(sumsq_vec, 3)); |
191 | |
|
192 | 0 | const __m128i sumsq15 = |
193 | 0 | _mm_sub_epi32(_mm_slli_epi32(sumsq_vec, 4), sumsq_vec); |
194 | 0 | const __m128i sum_sq = _mm_madd_epi16(sum_vec, sum_vec); |
195 | 0 | const __m128i diff = _mm_sub_epi32(_mm_sub_epi32(sumsq15, sum_sq), f_vec); |
196 | 0 | const __m128i mask32 = _mm_srai_epi32(diff, 31); |
197 | |
|
198 | 0 | const __m128i sc4 = load_unaligned_u32(s_c); |
199 | 0 | const __m128i sc16 = _mm_unpacklo_epi8(sc4, zero); |
200 | 0 | const __m128i sc32 = _mm_unpacklo_epi16(sc16, zero); |
201 | |
|
202 | 0 | const __m128i filt32 = |
203 | 0 | _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(sum_vec, sc32), eight), 4); |
204 | 0 | return _mm_or_si128(_mm_and_si128(mask32, filt32), |
205 | 0 | _mm_andnot_si128(mask32, sc32)); |
206 | 0 | } |
207 | | |
208 | | void vpx_mbpost_proc_across_ip_sse2(unsigned char *src, int pitch, int rows, |
209 | 0 | int cols, int flimit) { |
210 | 0 | int r, c, i; |
211 | 0 | const __m128i zero = _mm_setzero_si128(); |
212 | 0 | const __m128i f_vec = _mm_set1_epi32(flimit); |
213 | 0 | const __m128i eight = _mm_set1_epi32(8); |
214 | |
|
215 | 0 | if (cols < 8 || (cols & 7)) { |
216 | 0 | vpx_mbpost_proc_across_ip_c(src, pitch, rows, cols, flimit); |
217 | 0 | return; |
218 | 0 | } |
219 | | |
220 | 0 | for (r = 0; r < rows; r++) { |
221 | 0 | unsigned char *s = src + r * pitch; |
222 | 0 | int sumsq = 16; |
223 | 0 | int sum = 0; |
224 | |
|
225 | 0 | for (i = -8; i < 0; i++) s[i] = s[0]; |
226 | 0 | for (i = 0; i < 17; i++) s[i + cols] = s[cols - 1]; |
227 | |
|
228 | 0 | for (i = -8; i <= 6; i++) { |
229 | 0 | sumsq += s[i] * s[i]; |
230 | 0 | sum += s[i]; |
231 | 0 | } |
232 | |
|
233 | 0 | __m128i prev_out = _mm_setzero_si128(); |
234 | 0 | for (c = 0; c < cols + 8; c += 8) { |
235 | 0 | __m128i res8 = _mm_setzero_si128(); |
236 | 0 | if (c < cols) { |
237 | 0 | const __m128i left_lo = load_unaligned_u32(s + c - 8); |
238 | 0 | const __m128i right_lo = load_unaligned_u32(s + c + 7); |
239 | 0 | const __m128i res32_lo = filter_4_across(s + c, left_lo, right_lo, &sum, |
240 | 0 | &sumsq, f_vec, eight, zero); |
241 | |
|
242 | 0 | const __m128i left_hi = load_unaligned_u32(s + c - 4); |
243 | 0 | const __m128i right_hi = load_unaligned_u32(s + c + 11); |
244 | 0 | const __m128i res32_hi = filter_4_across( |
245 | 0 | s + c + 4, left_hi, right_hi, &sum, &sumsq, f_vec, eight, zero); |
246 | |
|
247 | 0 | const __m128i res16 = _mm_packs_epi32(res32_lo, res32_hi); |
248 | 0 | res8 = _mm_packus_epi16(res16, zero); |
249 | 0 | } |
250 | |
|
251 | 0 | if (c >= 8) { |
252 | 0 | _mm_storel_epi64((__m128i *)(s + c - 8), prev_out); |
253 | 0 | } |
254 | 0 | prev_out = res8; |
255 | 0 | } |
256 | 0 | } |
257 | 0 | } |
258 | | |
259 | | void vpx_mbpost_proc_down_sse2(unsigned char *dst, int pitch, int rows, |
260 | 0 | int cols, int flimit) { |
261 | 0 | int col; |
262 | 0 | const __m128i zero = _mm_setzero_si128(); |
263 | 0 | const __m128i f = _mm_set1_epi32(flimit); |
264 | 0 | DECLARE_ALIGNED(16, int16_t, above_context[8 * 8]); |
265 | | |
266 | | // 8 columns are processed at a time. |
267 | | // If rows is less than 8 the bottom border extension fails. |
268 | 0 | assert(cols % 8 == 0); |
269 | 0 | assert(rows >= 8); |
270 | |
|
271 | 0 | for (col = 0; col < cols; col += 8) { |
272 | 0 | int row, i; |
273 | 0 | __m128i s = _mm_loadl_epi64((__m128i *)dst); |
274 | 0 | __m128i sum, sumsq_0, sumsq_1; |
275 | 0 | __m128i tmp_0, tmp_1; |
276 | 0 | __m128i below_context = _mm_setzero_si128(); |
277 | |
|
278 | 0 | s = _mm_unpacklo_epi8(s, zero); |
279 | |
|
280 | 0 | for (i = 0; i < 8; ++i) { |
281 | 0 | _mm_store_si128((__m128i *)above_context + i, s); |
282 | 0 | } |
283 | | |
284 | | // sum *= 9 |
285 | 0 | sum = _mm_slli_epi16(s, 3); |
286 | 0 | sum = _mm_add_epi16(s, sum); |
287 | | |
288 | | // sum^2 * 9 == (sum * 9) * sum |
289 | 0 | tmp_0 = _mm_mullo_epi16(sum, s); |
290 | 0 | tmp_1 = _mm_mulhi_epi16(sum, s); |
291 | |
|
292 | 0 | sumsq_0 = _mm_unpacklo_epi16(tmp_0, tmp_1); |
293 | 0 | sumsq_1 = _mm_unpackhi_epi16(tmp_0, tmp_1); |
294 | | |
295 | | // Prime sum/sumsq |
296 | 0 | for (i = 1; i <= 6; ++i) { |
297 | 0 | __m128i a = _mm_loadl_epi64((__m128i *)(dst + i * pitch)); |
298 | 0 | a = _mm_unpacklo_epi8(a, zero); |
299 | 0 | sum = _mm_add_epi16(sum, a); |
300 | 0 | a = _mm_mullo_epi16(a, a); |
301 | 0 | sumsq_0 = _mm_add_epi32(sumsq_0, _mm_unpacklo_epi16(a, zero)); |
302 | 0 | sumsq_1 = _mm_add_epi32(sumsq_1, _mm_unpackhi_epi16(a, zero)); |
303 | 0 | } |
304 | |
|
305 | 0 | for (row = 0; row < rows + 8; row++) { |
306 | 0 | const __m128i above = |
307 | 0 | _mm_load_si128((__m128i *)above_context + (row & 7)); |
308 | 0 | __m128i this_row = _mm_loadl_epi64((__m128i *)(dst + row * pitch)); |
309 | 0 | __m128i above_sq, below_sq; |
310 | 0 | __m128i mask_0, mask_1; |
311 | 0 | __m128i multmp_0, multmp_1; |
312 | 0 | __m128i rv; |
313 | 0 | __m128i out; |
314 | |
|
315 | 0 | this_row = _mm_unpacklo_epi8(this_row, zero); |
316 | |
|
317 | 0 | if (row + 7 < rows) { |
318 | | // Instead of copying the end context we just stop loading when we get |
319 | | // to the last one. |
320 | 0 | below_context = _mm_loadl_epi64((__m128i *)(dst + (row + 7) * pitch)); |
321 | 0 | below_context = _mm_unpacklo_epi8(below_context, zero); |
322 | 0 | } |
323 | |
|
324 | 0 | sum = _mm_sub_epi16(sum, above); |
325 | 0 | sum = _mm_add_epi16(sum, below_context); |
326 | | |
327 | | // context^2 fits in 16 bits. Don't need to mulhi and combine. Just zero |
328 | | // extend. Unfortunately we can't do below_sq - above_sq in 16 bits |
329 | | // because x86 does not have unpack with sign extension. |
330 | 0 | above_sq = _mm_mullo_epi16(above, above); |
331 | 0 | sumsq_0 = _mm_sub_epi32(sumsq_0, _mm_unpacklo_epi16(above_sq, zero)); |
332 | 0 | sumsq_1 = _mm_sub_epi32(sumsq_1, _mm_unpackhi_epi16(above_sq, zero)); |
333 | |
|
334 | 0 | below_sq = _mm_mullo_epi16(below_context, below_context); |
335 | 0 | sumsq_0 = _mm_add_epi32(sumsq_0, _mm_unpacklo_epi16(below_sq, zero)); |
336 | 0 | sumsq_1 = _mm_add_epi32(sumsq_1, _mm_unpackhi_epi16(below_sq, zero)); |
337 | | |
338 | | // sumsq * 16 - sumsq == sumsq * 15 |
339 | 0 | mask_0 = _mm_slli_epi32(sumsq_0, 4); |
340 | 0 | mask_0 = _mm_sub_epi32(mask_0, sumsq_0); |
341 | 0 | mask_1 = _mm_slli_epi32(sumsq_1, 4); |
342 | 0 | mask_1 = _mm_sub_epi32(mask_1, sumsq_1); |
343 | |
|
344 | 0 | multmp_0 = _mm_mullo_epi16(sum, sum); |
345 | 0 | multmp_1 = _mm_mulhi_epi16(sum, sum); |
346 | |
|
347 | 0 | mask_0 = _mm_sub_epi32(mask_0, _mm_unpacklo_epi16(multmp_0, multmp_1)); |
348 | 0 | mask_1 = _mm_sub_epi32(mask_1, _mm_unpackhi_epi16(multmp_0, multmp_1)); |
349 | | |
350 | | // mask - f gives a negative value when mask < f |
351 | 0 | mask_0 = _mm_sub_epi32(mask_0, f); |
352 | 0 | mask_1 = _mm_sub_epi32(mask_1, f); |
353 | | |
354 | | // Shift the sign bit down to create a mask |
355 | 0 | mask_0 = _mm_srai_epi32(mask_0, 31); |
356 | 0 | mask_1 = _mm_srai_epi32(mask_1, 31); |
357 | |
|
358 | 0 | mask_0 = _mm_packs_epi32(mask_0, mask_1); |
359 | |
|
360 | 0 | rv = _mm_loadu_si128((__m128i const *)(vpx_rv + (row & 127))); |
361 | |
|
362 | 0 | mask_1 = _mm_add_epi16(rv, sum); |
363 | 0 | mask_1 = _mm_add_epi16(mask_1, this_row); |
364 | 0 | mask_1 = _mm_srai_epi16(mask_1, 4); |
365 | |
|
366 | 0 | mask_1 = _mm_and_si128(mask_0, mask_1); |
367 | 0 | mask_0 = _mm_andnot_si128(mask_0, this_row); |
368 | 0 | out = _mm_or_si128(mask_1, mask_0); |
369 | |
|
370 | 0 | _mm_storel_epi64((__m128i *)(dst + row * pitch), |
371 | 0 | _mm_packus_epi16(out, zero)); |
372 | |
|
373 | 0 | _mm_store_si128((__m128i *)above_context + ((row + 8) & 7), this_row); |
374 | 0 | } |
375 | |
|
376 | 0 | dst += 8; |
377 | 0 | } |
378 | 0 | } |