Coverage Report

Created: 2026-09-14 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}