Coverage Report

Created: 2026-09-07 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/aom/av1/encoder/x86/pickrst_avx2.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2018, Alliance for Open Media. All rights reserved.
3
 *
4
 * This source code is subject to the terms of the BSD 2 Clause License and
5
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6
 * was not distributed with this source code in the LICENSE file, you can
7
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8
 * Media Patent License 1.0 was not distributed with this source code in the
9
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10
 */
11
12
#include <immintrin.h>  // AVX2
13
#include "aom_dsp/x86/mem_sse2.h"
14
#include "aom_dsp/x86/synonyms.h"
15
#include "aom_dsp/x86/synonyms_avx2.h"
16
#include "aom_dsp/x86/transpose_sse2.h"
17
18
#include "config/av1_rtcd.h"
19
#include "av1/common/restoration.h"
20
#include "av1/encoder/pickrst.h"
21
22
#if CONFIG_AV1_HIGHBITDEPTH
23
static inline void acc_stat_highbd_avx2(int64_t *dst, const uint16_t *dgd,
24
                                        const __m256i *shuffle,
25
0
                                        const __m256i *dgd_ijkl) {
26
  // Load two 128-bit chunks from dgd
27
0
  const __m256i s0 = _mm256_inserti128_si256(
28
0
      _mm256_castsi128_si256(_mm_loadu_si128((__m128i *)dgd)),
29
0
      _mm_loadu_si128((__m128i *)(dgd + 4)), 1);
30
  // s0 = [11 10 9 8 7 6 5 4] [7 6 5 4 3 2 1 0] as u16 (values are dgd indices)
31
  // The weird order is so the shuffle stays within 128-bit lanes
32
33
  // Shuffle 16x u16 values within lanes according to the mask:
34
  // [0 1 1 2 2 3 3 4] [0 1 1 2 2 3 3 4]
35
  // (Actually we shuffle u8 values as there's no 16-bit shuffle)
36
0
  const __m256i s1 = _mm256_shuffle_epi8(s0, *shuffle);
37
  // s1 = [8 7 7 6 6 5 5 4] [4 3 3 2 2 1 1 0] as u16 (values are dgd indices)
38
39
  // Multiply 16x 16-bit integers in dgd_ijkl and s1, resulting in 16x 32-bit
40
  // integers then horizontally add pairs of these integers resulting in 8x
41
  // 32-bit integers
42
0
  const __m256i d0 = _mm256_madd_epi16(*dgd_ijkl, s1);
43
  // d0 = [a b c d] [e f g h] as u32
44
45
  // Take the lower-half of d0, extend to u64, add it on to dst (H)
46
0
  const __m256i d0l = _mm256_cvtepu32_epi64(_mm256_extracti128_si256(d0, 0));
47
  // d0l = [a b] [c d] as u64
48
0
  const __m256i dst0 = yy_load_256(dst);
49
0
  yy_store_256(dst, _mm256_add_epi64(d0l, dst0));
50
51
  // Take the upper-half of d0, extend to u64, add it on to dst (H)
52
0
  const __m256i d0h = _mm256_cvtepu32_epi64(_mm256_extracti128_si256(d0, 1));
53
  // d0h = [e f] [g h] as u64
54
0
  const __m256i dst1 = yy_load_256(dst + 4);
55
0
  yy_store_256(dst + 4, _mm256_add_epi64(d0h, dst1));
56
0
}
57
58
static inline void acc_stat_highbd_win7_one_line_avx2(
59
    const uint16_t *dgd, const uint16_t *src, int h_start, int h_end,
60
    int dgd_stride, const __m256i *shuffle, int32_t *sumX,
61
    int32_t sumY[WIENER_WIN][WIENER_WIN], int64_t M_int[WIENER_WIN][WIENER_WIN],
62
0
    int64_t H_int[WIENER_WIN2][WIENER_WIN * 8]) {
63
0
  int j, k, l;
64
0
  const int wiener_win = WIENER_WIN;
65
  // Main loop handles two pixels at a time
66
  // We can assume that h_start is even, since it will always be aligned to
67
  // a tile edge + some number of restoration units, and both of those will
68
  // be 64-pixel aligned.
69
  // However, at the edge of the image, h_end may be odd, so we need to handle
70
  // that case correctly.
71
0
  assert(h_start % 2 == 0);
72
0
  const int h_end_even = h_end & ~1;
73
0
  const int has_odd_pixel = h_end & 1;
74
0
  for (j = h_start; j < h_end_even; j += 2) {
75
0
    const uint16_t X1 = src[j];
76
0
    const uint16_t X2 = src[j + 1];
77
0
    *sumX += X1 + X2;
78
0
    const uint16_t *dgd_ij = dgd + j;
79
0
    for (k = 0; k < wiener_win; k++) {
80
0
      const uint16_t *dgd_ijk = dgd_ij + k * dgd_stride;
81
0
      for (l = 0; l < wiener_win; l++) {
82
0
        int64_t *H_ = &H_int[(l * wiener_win + k)][0];
83
0
        const uint16_t D1 = dgd_ijk[l];
84
0
        const uint16_t D2 = dgd_ijk[l + 1];
85
0
        sumY[k][l] += D1 + D2;
86
0
        M_int[k][l] += D1 * X1 + D2 * X2;
87
88
        // Load two u16 values from dgd_ijkl combined as a u32,
89
        // then broadcast to 8x u32 slots of a 256
90
0
        const __m256i dgd_ijkl = _mm256_set1_epi32(loadu_int32(dgd_ijk + l));
91
        // dgd_ijkl = [y x y x y x y x] [y x y x y x y x] where each is a u16
92
93
0
        acc_stat_highbd_avx2(H_ + 0 * 8, dgd_ij + 0 * dgd_stride, shuffle,
94
0
                             &dgd_ijkl);
95
0
        acc_stat_highbd_avx2(H_ + 1 * 8, dgd_ij + 1 * dgd_stride, shuffle,
96
0
                             &dgd_ijkl);
97
0
        acc_stat_highbd_avx2(H_ + 2 * 8, dgd_ij + 2 * dgd_stride, shuffle,
98
0
                             &dgd_ijkl);
99
0
        acc_stat_highbd_avx2(H_ + 3 * 8, dgd_ij + 3 * dgd_stride, shuffle,
100
0
                             &dgd_ijkl);
101
0
        acc_stat_highbd_avx2(H_ + 4 * 8, dgd_ij + 4 * dgd_stride, shuffle,
102
0
                             &dgd_ijkl);
103
0
        acc_stat_highbd_avx2(H_ + 5 * 8, dgd_ij + 5 * dgd_stride, shuffle,
104
0
                             &dgd_ijkl);
105
0
        acc_stat_highbd_avx2(H_ + 6 * 8, dgd_ij + 6 * dgd_stride, shuffle,
106
0
                             &dgd_ijkl);
107
0
      }
108
0
    }
109
0
  }
110
  // If the width is odd, add in the final pixel
111
0
  if (has_odd_pixel) {
112
0
    const uint16_t X1 = src[j];
113
0
    *sumX += X1;
114
0
    const uint16_t *dgd_ij = dgd + j;
115
0
    for (k = 0; k < wiener_win; k++) {
116
0
      const uint16_t *dgd_ijk = dgd_ij + k * dgd_stride;
117
0
      for (l = 0; l < wiener_win; l++) {
118
0
        int64_t *H_ = &H_int[(l * wiener_win + k)][0];
119
0
        const uint16_t D1 = dgd_ijk[l];
120
0
        sumY[k][l] += D1;
121
0
        M_int[k][l] += D1 * X1;
122
123
        // The `acc_stat_highbd_avx2` function wants its input to have
124
        // interleaved copies of two pixels, but we only have one. However, the
125
        // pixels are (effectively) used as inputs to a multiply-accumulate. So
126
        // if we set the extra pixel slot to 0, then it is effectively ignored.
127
0
        const __m256i dgd_ijkl = _mm256_set1_epi32((int)D1);
128
129
0
        acc_stat_highbd_avx2(H_ + 0 * 8, dgd_ij + 0 * dgd_stride, shuffle,
130
0
                             &dgd_ijkl);
131
0
        acc_stat_highbd_avx2(H_ + 1 * 8, dgd_ij + 1 * dgd_stride, shuffle,
132
0
                             &dgd_ijkl);
133
0
        acc_stat_highbd_avx2(H_ + 2 * 8, dgd_ij + 2 * dgd_stride, shuffle,
134
0
                             &dgd_ijkl);
135
0
        acc_stat_highbd_avx2(H_ + 3 * 8, dgd_ij + 3 * dgd_stride, shuffle,
136
0
                             &dgd_ijkl);
137
0
        acc_stat_highbd_avx2(H_ + 4 * 8, dgd_ij + 4 * dgd_stride, shuffle,
138
0
                             &dgd_ijkl);
139
0
        acc_stat_highbd_avx2(H_ + 5 * 8, dgd_ij + 5 * dgd_stride, shuffle,
140
0
                             &dgd_ijkl);
141
0
        acc_stat_highbd_avx2(H_ + 6 * 8, dgd_ij + 6 * dgd_stride, shuffle,
142
0
                             &dgd_ijkl);
143
0
      }
144
0
    }
145
0
  }
146
0
}
147
148
static inline void compute_stats_highbd_win7_opt_avx2(
149
    const uint8_t *dgd8, const uint8_t *src8, int h_start, int h_end,
150
    int v_start, int v_end, int dgd_stride, int src_stride, int64_t *M,
151
0
    int64_t *H, aom_bit_depth_t bit_depth) {
152
0
  int i, j, k, l, m, n;
153
0
  const int wiener_win = WIENER_WIN;
154
0
  const int pixel_count = (h_end - h_start) * (v_end - v_start);
155
0
  const int wiener_win2 = wiener_win * wiener_win;
156
0
  const int wiener_halfwin = (wiener_win >> 1);
157
0
  const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
158
0
  const uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
159
0
  const uint16_t avg =
160
0
      find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride);
161
162
0
  int64_t M_int[WIENER_WIN][WIENER_WIN] = { { 0 } };
163
0
  DECLARE_ALIGNED(32, int64_t, H_int[WIENER_WIN2][WIENER_WIN * 8]) = { { 0 } };
164
0
  int32_t sumY[WIENER_WIN][WIENER_WIN] = { { 0 } };
165
0
  int32_t sumX = 0;
166
0
  const uint16_t *dgd_win = dgd - wiener_halfwin * dgd_stride - wiener_halfwin;
167
168
0
  const __m256i shuffle = yy_loadu_256(g_shuffle_stats_highbd_data);
169
0
  for (j = v_start; j < v_end; j += 64) {
170
0
    const int vert_end = AOMMIN(64, v_end - j) + j;
171
0
    for (i = j; i < vert_end; i++) {
172
0
      acc_stat_highbd_win7_one_line_avx2(
173
0
          dgd_win + i * dgd_stride, src + i * src_stride, h_start, h_end,
174
0
          dgd_stride, &shuffle, &sumX, sumY, M_int, H_int);
175
0
    }
176
0
  }
177
178
0
  uint8_t bit_depth_divider = 1;
179
0
  if (bit_depth == AOM_BITS_12)
180
0
    bit_depth_divider = 16;
181
0
  else if (bit_depth == AOM_BITS_10)
182
0
    bit_depth_divider = 4;
183
184
0
  const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
185
0
  for (k = 0; k < wiener_win; k++) {
186
0
    for (l = 0; l < wiener_win; l++) {
187
0
      const int32_t idx0 = l * wiener_win + k;
188
0
      M[idx0] = (M_int[k][l] +
189
0
                 (avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]))) /
190
0
                bit_depth_divider;
191
0
      int64_t *H_ = H + idx0 * wiener_win2;
192
0
      int64_t *H_int_ = &H_int[idx0][0];
193
0
      for (m = 0; m < wiener_win; m++) {
194
0
        for (n = 0; n < wiener_win; n++) {
195
0
          H_[m * wiener_win + n] =
196
0
              (H_int_[n * 8 + m] +
197
0
               (avg_square_sum - (int64_t)avg * (sumY[k][l] + sumY[n][m]))) /
198
0
              bit_depth_divider;
199
0
        }
200
0
      }
201
0
    }
202
0
  }
203
0
}
204
205
static inline void acc_stat_highbd_win5_one_line_avx2(
206
    const uint16_t *dgd, const uint16_t *src, int h_start, int h_end,
207
    int dgd_stride, const __m256i *shuffle, int32_t *sumX,
208
    int32_t sumY[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA],
209
    int64_t M_int[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA],
210
0
    int64_t H_int[WIENER_WIN2_CHROMA][WIENER_WIN_CHROMA * 8]) {
211
0
  int j, k, l;
212
0
  const int wiener_win = WIENER_WIN_CHROMA;
213
  // Main loop handles two pixels at a time
214
  // We can assume that h_start is even, since it will always be aligned to
215
  // a tile edge + some number of restoration units, and both of those will
216
  // be 64-pixel aligned.
217
  // However, at the edge of the image, h_end may be odd, so we need to handle
218
  // that case correctly.
219
0
  assert(h_start % 2 == 0);
220
0
  const int h_end_even = h_end & ~1;
221
0
  const int has_odd_pixel = h_end & 1;
222
0
  for (j = h_start; j < h_end_even; j += 2) {
223
0
    const uint16_t X1 = src[j];
224
0
    const uint16_t X2 = src[j + 1];
225
0
    *sumX += X1 + X2;
226
0
    const uint16_t *dgd_ij = dgd + j;
227
0
    for (k = 0; k < wiener_win; k++) {
228
0
      const uint16_t *dgd_ijk = dgd_ij + k * dgd_stride;
229
0
      for (l = 0; l < wiener_win; l++) {
230
0
        int64_t *H_ = &H_int[(l * wiener_win + k)][0];
231
0
        const uint16_t D1 = dgd_ijk[l];
232
0
        const uint16_t D2 = dgd_ijk[l + 1];
233
0
        sumY[k][l] += D1 + D2;
234
0
        M_int[k][l] += D1 * X1 + D2 * X2;
235
236
        // Load two u16 values from dgd_ijkl combined as a u32,
237
        // then broadcast to 8x u32 slots of a 256
238
0
        const __m256i dgd_ijkl = _mm256_set1_epi32(loadu_int32(dgd_ijk + l));
239
        // dgd_ijkl = [x y x y x y x y] [x y x y x y x y] where each is a u16
240
241
0
        acc_stat_highbd_avx2(H_ + 0 * 8, dgd_ij + 0 * dgd_stride, shuffle,
242
0
                             &dgd_ijkl);
243
0
        acc_stat_highbd_avx2(H_ + 1 * 8, dgd_ij + 1 * dgd_stride, shuffle,
244
0
                             &dgd_ijkl);
245
0
        acc_stat_highbd_avx2(H_ + 2 * 8, dgd_ij + 2 * dgd_stride, shuffle,
246
0
                             &dgd_ijkl);
247
0
        acc_stat_highbd_avx2(H_ + 3 * 8, dgd_ij + 3 * dgd_stride, shuffle,
248
0
                             &dgd_ijkl);
249
0
        acc_stat_highbd_avx2(H_ + 4 * 8, dgd_ij + 4 * dgd_stride, shuffle,
250
0
                             &dgd_ijkl);
251
0
      }
252
0
    }
253
0
  }
254
  // If the width is odd, add in the final pixel
255
0
  if (has_odd_pixel) {
256
0
    const uint16_t X1 = src[j];
257
0
    *sumX += X1;
258
0
    const uint16_t *dgd_ij = dgd + j;
259
0
    for (k = 0; k < wiener_win; k++) {
260
0
      const uint16_t *dgd_ijk = dgd_ij + k * dgd_stride;
261
0
      for (l = 0; l < wiener_win; l++) {
262
0
        int64_t *H_ = &H_int[(l * wiener_win + k)][0];
263
0
        const uint16_t D1 = dgd_ijk[l];
264
0
        sumY[k][l] += D1;
265
0
        M_int[k][l] += D1 * X1;
266
267
        // The `acc_stat_highbd_avx2` function wants its input to have
268
        // interleaved copies of two pixels, but we only have one. However, the
269
        // pixels are (effectively) used as inputs to a multiply-accumulate. So
270
        // if we set the extra pixel slot to 0, then it is effectively ignored.
271
0
        const __m256i dgd_ijkl = _mm256_set1_epi32((int)D1);
272
273
0
        acc_stat_highbd_avx2(H_ + 0 * 8, dgd_ij + 0 * dgd_stride, shuffle,
274
0
                             &dgd_ijkl);
275
0
        acc_stat_highbd_avx2(H_ + 1 * 8, dgd_ij + 1 * dgd_stride, shuffle,
276
0
                             &dgd_ijkl);
277
0
        acc_stat_highbd_avx2(H_ + 2 * 8, dgd_ij + 2 * dgd_stride, shuffle,
278
0
                             &dgd_ijkl);
279
0
        acc_stat_highbd_avx2(H_ + 3 * 8, dgd_ij + 3 * dgd_stride, shuffle,
280
0
                             &dgd_ijkl);
281
0
        acc_stat_highbd_avx2(H_ + 4 * 8, dgd_ij + 4 * dgd_stride, shuffle,
282
0
                             &dgd_ijkl);
283
0
      }
284
0
    }
285
0
  }
286
0
}
287
288
static inline void compute_stats_highbd_win5_opt_avx2(
289
    const uint8_t *dgd8, const uint8_t *src8, int h_start, int h_end,
290
    int v_start, int v_end, int dgd_stride, int src_stride, int64_t *M,
291
0
    int64_t *H, aom_bit_depth_t bit_depth) {
292
0
  int i, j, k, l, m, n;
293
0
  const int wiener_win = WIENER_WIN_CHROMA;
294
0
  const int pixel_count = (h_end - h_start) * (v_end - v_start);
295
0
  const int wiener_win2 = wiener_win * wiener_win;
296
0
  const int wiener_halfwin = (wiener_win >> 1);
297
0
  const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
298
0
  const uint16_t *dgd = CONVERT_TO_SHORTPTR(dgd8);
299
0
  const uint16_t avg =
300
0
      find_average_highbd(dgd, h_start, h_end, v_start, v_end, dgd_stride);
301
302
0
  int64_t M_int64[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
303
0
  DECLARE_ALIGNED(
304
0
      32, int64_t,
305
0
      H_int64[WIENER_WIN2_CHROMA][WIENER_WIN_CHROMA * 8]) = { { 0 } };
306
0
  int32_t sumY[WIENER_WIN_CHROMA][WIENER_WIN_CHROMA] = { { 0 } };
307
0
  int32_t sumX = 0;
308
0
  const uint16_t *dgd_win = dgd - wiener_halfwin * dgd_stride - wiener_halfwin;
309
310
0
  const __m256i shuffle = yy_loadu_256(g_shuffle_stats_highbd_data);
311
0
  for (j = v_start; j < v_end; j += 64) {
312
0
    const int vert_end = AOMMIN(64, v_end - j) + j;
313
0
    for (i = j; i < vert_end; i++) {
314
0
      acc_stat_highbd_win5_one_line_avx2(
315
0
          dgd_win + i * dgd_stride, src + i * src_stride, h_start, h_end,
316
0
          dgd_stride, &shuffle, &sumX, sumY, M_int64, H_int64);
317
0
    }
318
0
  }
319
320
0
  uint8_t bit_depth_divider = 1;
321
0
  if (bit_depth == AOM_BITS_12)
322
0
    bit_depth_divider = 16;
323
0
  else if (bit_depth == AOM_BITS_10)
324
0
    bit_depth_divider = 4;
325
326
0
  const int64_t avg_square_sum = (int64_t)avg * (int64_t)avg * pixel_count;
327
0
  for (k = 0; k < wiener_win; k++) {
328
0
    for (l = 0; l < wiener_win; l++) {
329
0
      const int32_t idx0 = l * wiener_win + k;
330
0
      M[idx0] = (M_int64[k][l] +
331
0
                 (avg_square_sum - (int64_t)avg * (sumX + sumY[k][l]))) /
332
0
                bit_depth_divider;
333
0
      int64_t *H_ = H + idx0 * wiener_win2;
334
0
      int64_t *H_int_ = &H_int64[idx0][0];
335
0
      for (m = 0; m < wiener_win; m++) {
336
0
        for (n = 0; n < wiener_win; n++) {
337
0
          H_[m * wiener_win + n] =
338
0
              (H_int_[n * 8 + m] +
339
0
               (avg_square_sum - (int64_t)avg * (sumY[k][l] + sumY[n][m]))) /
340
0
              bit_depth_divider;
341
0
        }
342
0
      }
343
0
    }
344
0
  }
345
0
}
346
347
void av1_compute_stats_highbd_avx2(int wiener_win, const uint8_t *dgd8,
348
                                   const uint8_t *src8, int16_t *dgd_avg,
349
                                   int16_t *src_avg, int h_start, int h_end,
350
                                   int v_start, int v_end, int dgd_stride,
351
                                   int src_stride, int64_t *M, int64_t *H,
352
0
                                   aom_bit_depth_t bit_depth) {
353
0
  if (wiener_win == WIENER_WIN) {
354
0
    (void)dgd_avg;
355
0
    (void)src_avg;
356
0
    compute_stats_highbd_win7_opt_avx2(dgd8, src8, h_start, h_end, v_start,
357
0
                                       v_end, dgd_stride, src_stride, M, H,
358
0
                                       bit_depth);
359
0
  } else if (wiener_win == WIENER_WIN_CHROMA) {
360
0
    (void)dgd_avg;
361
0
    (void)src_avg;
362
0
    compute_stats_highbd_win5_opt_avx2(dgd8, src8, h_start, h_end, v_start,
363
0
                                       v_end, dgd_stride, src_stride, M, H,
364
0
                                       bit_depth);
365
0
  } else {
366
0
    av1_compute_stats_highbd_c(wiener_win, dgd8, src8, dgd_avg, src_avg,
367
0
                               h_start, h_end, v_start, v_end, dgd_stride,
368
0
                               src_stride, M, H, bit_depth);
369
0
  }
370
0
}
371
#endif  // CONFIG_AV1_HIGHBITDEPTH
372
373
0
static inline void madd_and_accum_avx2(__m256i src, __m256i dgd, __m256i *sum) {
374
0
  *sum = _mm256_add_epi32(*sum, _mm256_madd_epi16(src, dgd));
375
0
}
376
377
0
static inline __m256i convert_and_add_avx2(__m256i src) {
378
0
  const __m256i s0 = _mm256_cvtepi32_epi64(_mm256_castsi256_si128(src));
379
0
  const __m256i s1 = _mm256_cvtepi32_epi64(_mm256_extracti128_si256(src, 1));
380
0
  return _mm256_add_epi64(s0, s1);
381
0
}
382
383
static inline __m256i hadd_four_32_to_64_avx2(__m256i src0, __m256i src1,
384
0
                                              __m256i *src2, __m256i *src3) {
385
  // 00 01 10 11 02 03 12 13
386
0
  const __m256i s_0 = _mm256_hadd_epi32(src0, src1);
387
  // 20 21 30 31 22 23 32 33
388
0
  const __m256i s_1 = _mm256_hadd_epi32(*src2, *src3);
389
  // 00+01 10+11 20+21 30+31 02+03 12+13 22+23 32+33
390
0
  const __m256i s_2 = _mm256_hadd_epi32(s_0, s_1);
391
0
  return convert_and_add_avx2(s_2);
392
0
}
393
394
0
static inline __m128i add_64bit_lvl_avx2(__m256i src0, __m256i src1) {
395
  // 00 10 02 12
396
0
  const __m256i t0 = _mm256_unpacklo_epi64(src0, src1);
397
  // 01 11 03 13
398
0
  const __m256i t1 = _mm256_unpackhi_epi64(src0, src1);
399
  // 00+01 10+11 02+03 12+13
400
0
  const __m256i sum = _mm256_add_epi64(t0, t1);
401
  // 00+01 10+11
402
0
  const __m128i sum0 = _mm256_castsi256_si128(sum);
403
  // 02+03 12+13
404
0
  const __m128i sum1 = _mm256_extracti128_si256(sum, 1);
405
  // 00+01+02+03 10+11+12+13
406
0
  return _mm_add_epi64(sum0, sum1);
407
0
}
408
409
0
static inline __m128i convert_32_to_64_add_avx2(__m256i src0, __m256i src1) {
410
  // 00 01 02 03
411
0
  const __m256i s0 = convert_and_add_avx2(src0);
412
  // 10 11 12 13
413
0
  const __m256i s1 = convert_and_add_avx2(src1);
414
0
  return add_64bit_lvl_avx2(s0, s1);
415
0
}
416
417
0
static inline int32_t calc_sum_of_register(__m256i src) {
418
0
  const __m128i src_l = _mm256_castsi256_si128(src);
419
0
  const __m128i src_h = _mm256_extracti128_si256(src, 1);
420
0
  const __m128i sum = _mm_add_epi32(src_l, src_h);
421
0
  const __m128i dst0 = _mm_add_epi32(sum, _mm_srli_si128(sum, 8));
422
0
  const __m128i dst1 = _mm_add_epi32(dst0, _mm_srli_si128(dst0, 4));
423
0
  return _mm_cvtsi128_si32(dst1);
424
0
}
425
426
static inline void transpose_64bit_4x4_avx2(const __m256i *const src,
427
0
                                            __m256i *const dst) {
428
  // Unpack 64 bit elements. Goes from:
429
  // src[0]: 00 01 02 03
430
  // src[1]: 10 11 12 13
431
  // src[2]: 20 21 22 23
432
  // src[3]: 30 31 32 33
433
  // to:
434
  // reg0:    00 10 02 12
435
  // reg1:    20 30 22 32
436
  // reg2:    01 11 03 13
437
  // reg3:    21 31 23 33
438
0
  const __m256i reg0 = _mm256_unpacklo_epi64(src[0], src[1]);
439
0
  const __m256i reg1 = _mm256_unpacklo_epi64(src[2], src[3]);
440
0
  const __m256i reg2 = _mm256_unpackhi_epi64(src[0], src[1]);
441
0
  const __m256i reg3 = _mm256_unpackhi_epi64(src[2], src[3]);
442
443
  // Unpack 64 bit elements resulting in:
444
  // dst[0]: 00 10 20 30
445
  // dst[1]: 01 11 21 31
446
  // dst[2]: 02 12 22 32
447
  // dst[3]: 03 13 23 33
448
0
  dst[0] = _mm256_inserti128_si256(reg0, _mm256_castsi256_si128(reg1), 1);
449
0
  dst[1] = _mm256_inserti128_si256(reg2, _mm256_castsi256_si128(reg3), 1);
450
0
  dst[2] = _mm256_inserti128_si256(reg1, _mm256_extracti128_si256(reg0, 1), 0);
451
0
  dst[3] = _mm256_inserti128_si256(reg3, _mm256_extracti128_si256(reg2, 1), 0);
452
0
}
453
454
// When we load 32 values of int8_t type and need less than 32 values for
455
// processing, the below mask is used to make the extra values zero.
456
static const int8_t mask_8bit[32] = {
457
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  // 16 bytes
458
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   // 16 bytes
459
};
460
461
// When we load 16 values of int16_t type and need less than 16 values for
462
// processing, the below mask is used to make the extra values zero.
463
static const int16_t mask_16bit[32] = {
464
  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  // 16 bytes
465
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   // 16 bytes
466
};
467
468
static inline uint8_t calc_dgd_buf_avg_avx2(const uint8_t *src, int32_t h_start,
469
                                            int32_t h_end, int32_t v_start,
470
0
                                            int32_t v_end, int32_t stride) {
471
0
  const uint8_t *src_temp = src + v_start * stride + h_start;
472
0
  const __m256i zero = _mm256_setzero_si256();
473
0
  const int32_t width = h_end - h_start;
474
0
  const int32_t height = v_end - v_start;
475
0
  const int32_t wd_beyond_mul32 = width & 31;
476
0
  const int32_t wd_mul32 = width - wd_beyond_mul32;
477
0
  __m128i mask_low, mask_high;
478
0
  __m256i ss = zero;
479
480
  // When width is not multiple of 32, it still loads 32 and to make the data
481
  // which is extra (beyond required) as zero using the below mask.
482
0
  if (wd_beyond_mul32 >= 16) {
483
0
    mask_low = _mm_set1_epi8(-1);
484
0
    mask_high = _mm_loadu_si128((__m128i *)(&mask_8bit[32 - wd_beyond_mul32]));
485
0
  } else {
486
0
    mask_low = _mm_loadu_si128((__m128i *)(&mask_8bit[16 - wd_beyond_mul32]));
487
0
    mask_high = _mm_setzero_si128();
488
0
  }
489
0
  const __m256i mask =
490
0
      _mm256_inserti128_si256(_mm256_castsi128_si256(mask_low), mask_high, 1);
491
492
0
  int32_t proc_ht = 0;
493
0
  do {
494
    // Process width in multiple of 32.
495
0
    int32_t proc_wd = 0;
496
0
    while (proc_wd < wd_mul32) {
497
0
      const __m256i s_0 = _mm256_loadu_si256((__m256i *)(src_temp + proc_wd));
498
0
      const __m256i sad_0 = _mm256_sad_epu8(s_0, zero);
499
0
      ss = _mm256_add_epi32(ss, sad_0);
500
0
      proc_wd += 32;
501
0
    }
502
503
    // Process the remaining width.
504
0
    if (wd_beyond_mul32) {
505
0
      const __m256i s_0 = _mm256_loadu_si256((__m256i *)(src_temp + proc_wd));
506
0
      const __m256i s_m_0 = _mm256_and_si256(s_0, mask);
507
0
      const __m256i sad_0 = _mm256_sad_epu8(s_m_0, zero);
508
0
      ss = _mm256_add_epi32(ss, sad_0);
509
0
    }
510
0
    src_temp += stride;
511
0
    proc_ht++;
512
0
  } while (proc_ht < height);
513
514
0
  const uint32_t sum = calc_sum_of_register(ss);
515
0
  const uint8_t avg = sum / (width * height);
516
0
  return avg;
517
0
}
518
519
// Fill (src-avg) or (dgd-avg) buffers. Note that when n = (width % 16) is not
520
// 0, it writes (16 - n) more data than required.
521
static inline void sub_avg_block_avx2(const uint8_t *src, int32_t src_stride,
522
                                      uint8_t avg, int32_t width,
523
                                      int32_t height, int16_t *dst,
524
                                      int32_t dst_stride,
525
0
                                      int use_downsampled_wiener_stats) {
526
0
  const __m256i avg_reg = _mm256_set1_epi16(avg);
527
528
0
  int32_t proc_ht = 0;
529
0
  do {
530
0
    int ds_factor =
531
0
        use_downsampled_wiener_stats ? WIENER_STATS_DOWNSAMPLE_FACTOR : 1;
532
0
    if (use_downsampled_wiener_stats &&
533
0
        (height - proc_ht < WIENER_STATS_DOWNSAMPLE_FACTOR)) {
534
0
      ds_factor = height - proc_ht;
535
0
    }
536
537
0
    int32_t proc_wd = 0;
538
0
    while (proc_wd < width) {
539
0
      const __m128i s = _mm_loadu_si128((__m128i *)(src + proc_wd));
540
0
      const __m256i ss = _mm256_cvtepu8_epi16(s);
541
0
      const __m256i d = _mm256_sub_epi16(ss, avg_reg);
542
0
      _mm256_storeu_si256((__m256i *)(dst + proc_wd), d);
543
0
      proc_wd += 16;
544
0
    }
545
546
0
    src += ds_factor * src_stride;
547
0
    dst += ds_factor * dst_stride;
548
0
    proc_ht += ds_factor;
549
0
  } while (proc_ht < height);
550
0
}
551
552
// Fills lower-triangular elements of H buffer from upper triangular elements of
553
// the same
554
static inline void fill_lower_triag_elements_avx2(const int32_t wiener_win2,
555
0
                                                  int64_t *const H) {
556
0
  for (int32_t i = 0; i < wiener_win2 - 1; i += 4) {
557
0
    __m256i in[4], out[4];
558
559
0
    in[0] = _mm256_loadu_si256((__m256i *)(H + (i + 0) * wiener_win2 + i + 1));
560
0
    in[1] = _mm256_loadu_si256((__m256i *)(H + (i + 1) * wiener_win2 + i + 1));
561
0
    in[2] = _mm256_loadu_si256((__m256i *)(H + (i + 2) * wiener_win2 + i + 1));
562
0
    in[3] = _mm256_loadu_si256((__m256i *)(H + (i + 3) * wiener_win2 + i + 1));
563
564
0
    transpose_64bit_4x4_avx2(in, out);
565
566
0
    _mm_storel_epi64((__m128i *)(H + (i + 1) * wiener_win2 + i),
567
0
                     _mm256_castsi256_si128(out[0]));
568
0
    _mm_storeu_si128((__m128i *)(H + (i + 2) * wiener_win2 + i),
569
0
                     _mm256_castsi256_si128(out[1]));
570
0
    _mm256_storeu_si256((__m256i *)(H + (i + 3) * wiener_win2 + i), out[2]);
571
0
    _mm256_storeu_si256((__m256i *)(H + (i + 4) * wiener_win2 + i), out[3]);
572
573
0
    for (int32_t j = i + 5; j < wiener_win2; j += 4) {
574
0
      in[0] = _mm256_loadu_si256((__m256i *)(H + (i + 0) * wiener_win2 + j));
575
0
      in[1] = _mm256_loadu_si256((__m256i *)(H + (i + 1) * wiener_win2 + j));
576
0
      in[2] = _mm256_loadu_si256((__m256i *)(H + (i + 2) * wiener_win2 + j));
577
0
      in[3] = _mm256_loadu_si256((__m256i *)(H + (i + 3) * wiener_win2 + j));
578
579
0
      transpose_64bit_4x4_avx2(in, out);
580
581
0
      _mm256_storeu_si256((__m256i *)(H + (j + 0) * wiener_win2 + i), out[0]);
582
0
      _mm256_storeu_si256((__m256i *)(H + (j + 1) * wiener_win2 + i), out[1]);
583
0
      _mm256_storeu_si256((__m256i *)(H + (j + 2) * wiener_win2 + i), out[2]);
584
0
      _mm256_storeu_si256((__m256i *)(H + (j + 3) * wiener_win2 + i), out[3]);
585
0
    }
586
0
  }
587
0
}
588
589
// Fill H buffer based on loop_count.
590
#define INIT_H_VALUES(d, loop_count)                           \
591
0
  for (int g = 0; g < (loop_count); g++) {                     \
592
0
    const __m256i dgd0 =                                       \
593
0
        _mm256_loadu_si256((__m256i *)((d) + (g * d_stride))); \
594
0
    madd_and_accum_avx2(dgd_mul_df, dgd0, &sum_h[g]);          \
595
0
  }
596
597
// Fill M & H buffer.
598
#define INIT_MH_VALUES(d)                                      \
599
0
  for (int g = 0; g < wiener_win; g++) {                       \
600
0
    const __m256i dgds_0 =                                     \
601
0
        _mm256_loadu_si256((__m256i *)((d) + (g * d_stride))); \
602
0
    madd_and_accum_avx2(src_mul_df, dgds_0, &sum_m[g]);        \
603
0
    madd_and_accum_avx2(dgd_mul_df, dgds_0, &sum_h[g]);        \
604
0
  }
605
606
// Update the dgd pointers appropriately.
607
#define INITIALIZATION(wiener_window_sz)                                 \
608
0
  j = i / (wiener_window_sz);                                            \
609
0
  const int16_t *d_window = d + j;                                       \
610
0
  const int16_t *d_current_row =                                         \
611
0
      d + j + ((i % (wiener_window_sz)) * d_stride);                     \
612
0
  int proc_ht = v_start;                                                 \
613
0
  downsample_factor =                                                    \
614
0
      use_downsampled_wiener_stats ? WIENER_STATS_DOWNSAMPLE_FACTOR : 1; \
615
0
  __m256i sum_h[wiener_window_sz];                                       \
616
0
  memset(sum_h, 0, sizeof(sum_h));
617
618
// Update the downsample factor appropriately.
619
#define UPDATE_DOWNSAMPLE_FACTOR                              \
620
0
  int proc_wd = 0;                                            \
621
0
  if (use_downsampled_wiener_stats &&                         \
622
0
      ((v_end - proc_ht) < WIENER_STATS_DOWNSAMPLE_FACTOR)) { \
623
0
    downsample_factor = v_end - proc_ht;                      \
624
0
  }                                                           \
625
0
  const __m256i df_reg = _mm256_set1_epi16(downsample_factor);
626
627
#define CALCULATE_REMAINING_H_WIN5                                             \
628
0
  while (j < wiener_win) {                                                     \
629
0
    d_window = d;                                                              \
630
0
    d_current_row = d + (i / wiener_win) + ((i % wiener_win) * d_stride);      \
631
0
    const __m256i zero = _mm256_setzero_si256();                               \
632
0
    sum_h[0] = zero;                                                           \
633
0
    sum_h[1] = zero;                                                           \
634
0
    sum_h[2] = zero;                                                           \
635
0
    sum_h[3] = zero;                                                           \
636
0
    sum_h[4] = zero;                                                           \
637
0
                                                                               \
638
0
    proc_ht = v_start;                                                         \
639
0
    downsample_factor =                                                        \
640
0
        use_downsampled_wiener_stats ? WIENER_STATS_DOWNSAMPLE_FACTOR : 1;     \
641
0
    do {                                                                       \
642
0
      UPDATE_DOWNSAMPLE_FACTOR;                                                \
643
0
                                                                               \
644
0
      /* Process the amount of width multiple of 16.*/                         \
645
0
      while (proc_wd < wd_mul16) {                                             \
646
0
        const __m256i dgd =                                                    \
647
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));          \
648
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);            \
649
0
        INIT_H_VALUES(d_window + j + proc_wd, 5)                               \
650
0
                                                                               \
651
0
        proc_wd += 16;                                                         \
652
0
      };                                                                       \
653
0
                                                                               \
654
0
      /* Process the remaining width here. */                                  \
655
0
      if (wd_beyond_mul16) {                                                   \
656
0
        const __m256i dgd =                                                    \
657
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));          \
658
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);                  \
659
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);       \
660
0
        INIT_H_VALUES(d_window + j + proc_wd, 5)                               \
661
0
      }                                                                        \
662
0
      proc_ht += downsample_factor;                                            \
663
0
      d_window += downsample_factor * d_stride;                                \
664
0
      d_current_row += downsample_factor * d_stride;                           \
665
0
    } while (proc_ht < v_end);                                                 \
666
0
    const __m256i s_h0 =                                                       \
667
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);     \
668
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + (wiener_win * j)), \
669
0
                        s_h0);                                                 \
670
0
    const __m256i s_m_h = convert_and_add_avx2(sum_h[4]);                      \
671
0
    const __m128i s_m_h0 = add_64bit_lvl_avx2(s_m_h, s_m_h);                   \
672
0
    _mm_storel_epi64(                                                          \
673
0
        (__m128i *)(H + (i * wiener_win2) + (wiener_win * j) + 4), s_m_h0);    \
674
0
    j++;                                                                       \
675
0
  }
676
677
#define CALCULATE_REMAINING_H_WIN7                                             \
678
0
  while (j < wiener_win) {                                                     \
679
0
    d_window = d;                                                              \
680
0
    d_current_row = d + (i / wiener_win) + ((i % wiener_win) * d_stride);      \
681
0
    const __m256i zero = _mm256_setzero_si256();                               \
682
0
    sum_h[0] = zero;                                                           \
683
0
    sum_h[1] = zero;                                                           \
684
0
    sum_h[2] = zero;                                                           \
685
0
    sum_h[3] = zero;                                                           \
686
0
    sum_h[4] = zero;                                                           \
687
0
    sum_h[5] = zero;                                                           \
688
0
    sum_h[6] = zero;                                                           \
689
0
                                                                               \
690
0
    proc_ht = v_start;                                                         \
691
0
    downsample_factor =                                                        \
692
0
        use_downsampled_wiener_stats ? WIENER_STATS_DOWNSAMPLE_FACTOR : 1;     \
693
0
    do {                                                                       \
694
0
      UPDATE_DOWNSAMPLE_FACTOR;                                                \
695
0
                                                                               \
696
0
      /* Process the amount of width multiple of 16.*/                         \
697
0
      while (proc_wd < wd_mul16) {                                             \
698
0
        const __m256i dgd =                                                    \
699
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));          \
700
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);            \
701
0
        INIT_H_VALUES(d_window + j + proc_wd, 7)                               \
702
0
                                                                               \
703
0
        proc_wd += 16;                                                         \
704
0
      };                                                                       \
705
0
                                                                               \
706
0
      /* Process the remaining width here. */                                  \
707
0
      if (wd_beyond_mul16) {                                                   \
708
0
        const __m256i dgd =                                                    \
709
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));          \
710
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);                  \
711
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);       \
712
0
        INIT_H_VALUES(d_window + j + proc_wd, 7)                               \
713
0
      }                                                                        \
714
0
      proc_ht += downsample_factor;                                            \
715
0
      d_window += downsample_factor * d_stride;                                \
716
0
      d_current_row += downsample_factor * d_stride;                           \
717
0
    } while (proc_ht < v_end);                                                 \
718
0
    const __m256i s_h1 =                                                       \
719
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);     \
720
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + (wiener_win * j)), \
721
0
                        s_h1);                                                 \
722
0
    const __m256i s_h2 =                                                       \
723
0
        hadd_four_32_to_64_avx2(sum_h[4], sum_h[5], &sum_h[6], &sum_h[6]);     \
724
0
    _mm256_storeu_si256(                                                       \
725
0
        (__m256i *)(H + (i * wiener_win2) + (wiener_win * j) + 4), s_h2);      \
726
0
    j++;                                                                       \
727
0
  }
728
729
// The buffers H(auto-covariance) and M(cross-correlation) are used to estimate
730
// the filter tap values required for wiener filtering. Here, the buffer H is of
731
// size ((wiener_window_size^2)*(wiener_window_size^2)) and M is of size
732
// (wiener_window_size*wiener_window_size). H is a symmetric matrix where the
733
// value above the diagonal (upper triangle) are equal to the values below the
734
// diagonal (lower triangle). The calculation of elements/stats of H(upper
735
// triangle) and M is done in steps as described below where each step fills
736
// specific values of H and M.
737
// Once the upper triangular elements of H matrix are derived, the same will be
738
// copied to lower triangular using the function
739
// fill_lower_triag_elements_avx2().
740
// Example: Wiener window size =
741
// WIENER_WIN_CHROMA (5) M buffer = [M0 M1 M2 ---- M23 M24] H buffer = Hxy
742
// (x-row, y-column) [H00 H01 H02 ---- H023 H024] [H10 H11 H12 ---- H123 H124]
743
// [H30 H31 H32 ---- H323 H324]
744
// [H40 H41 H42 ---- H423 H424]
745
// [H50 H51 H52 ---- H523 H524]
746
// [H60 H61 H62 ---- H623 H624]
747
//            ||
748
//            ||
749
// [H230 H231 H232 ---- H2323 H2324]
750
// [H240 H241 H242 ---- H2423 H2424]
751
// In Step 1, whole M buffers (i.e., M0 to M24) and the first row of H (i.e.,
752
// H00 to H024) is filled. The remaining rows of H buffer are filled through
753
// steps 2 to 6.
754
static void compute_stats_win5_avx2(const int16_t *const d, int32_t d_stride,
755
                                    const int16_t *const s, int32_t s_stride,
756
                                    int32_t width, int v_start, int v_end,
757
                                    int64_t *const M, int64_t *const H,
758
0
                                    int use_downsampled_wiener_stats) {
759
0
  const int32_t wiener_win = WIENER_WIN_CHROMA;
760
0
  const int32_t wiener_win2 = wiener_win * wiener_win;
761
  // Amount of width which is beyond multiple of 16. This case is handled
762
  // appropriately to process only the required width towards the end.
763
0
  const int32_t wd_mul16 = width & ~15;
764
0
  const int32_t wd_beyond_mul16 = width - wd_mul16;
765
0
  const __m256i mask =
766
0
      _mm256_loadu_si256((__m256i *)(&mask_16bit[16 - wd_beyond_mul16]));
767
0
  int downsample_factor;
768
769
  // Step 1: Full M (i.e., M0 to M24) and first row H (i.e., H00 to H024)
770
  // values are filled here. Here, the loop over 'j' is executed for values 0
771
  // to 4 (wiener_win-1). When the loop executed for a specific 'j', 5 values of
772
  // M and H are filled as shown below.
773
  // j=0: M0-M4 and H00-H04, j=1: M5-M9 and H05-H09 are filled etc,.
774
0
  int j = 0;
775
0
  do {
776
0
    const int16_t *s_t = s;
777
0
    const int16_t *d_t = d;
778
0
    __m256i sum_m[WIENER_WIN_CHROMA] = { _mm256_setzero_si256() };
779
0
    __m256i sum_h[WIENER_WIN_CHROMA] = { _mm256_setzero_si256() };
780
0
    downsample_factor =
781
0
        use_downsampled_wiener_stats ? WIENER_STATS_DOWNSAMPLE_FACTOR : 1;
782
0
    int proc_ht = v_start;
783
0
    do {
784
0
      UPDATE_DOWNSAMPLE_FACTOR
785
786
      // Process the amount of width multiple of 16.
787
0
      while (proc_wd < wd_mul16) {
788
0
        const __m256i src = _mm256_loadu_si256((__m256i *)(s_t + proc_wd));
789
0
        const __m256i dgd = _mm256_loadu_si256((__m256i *)(d_t + proc_wd));
790
0
        const __m256i src_mul_df = _mm256_mullo_epi16(src, df_reg);
791
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
792
0
        INIT_MH_VALUES(d_t + j + proc_wd)
793
794
0
        proc_wd += 16;
795
0
      }
796
797
      // Process the remaining width here.
798
0
      if (wd_beyond_mul16) {
799
0
        const __m256i src = _mm256_loadu_si256((__m256i *)(s_t + proc_wd));
800
0
        const __m256i dgd = _mm256_loadu_si256((__m256i *)(d_t + proc_wd));
801
0
        const __m256i src_mask = _mm256_and_si256(src, mask);
802
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
803
0
        const __m256i src_mul_df = _mm256_mullo_epi16(src_mask, df_reg);
804
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
805
0
        INIT_MH_VALUES(d_t + j + proc_wd)
806
0
      }
807
0
      proc_ht += downsample_factor;
808
0
      s_t += downsample_factor * s_stride;
809
0
      d_t += downsample_factor * d_stride;
810
0
    } while (proc_ht < v_end);
811
812
0
    const __m256i s_m =
813
0
        hadd_four_32_to_64_avx2(sum_m[0], sum_m[1], &sum_m[2], &sum_m[3]);
814
0
    const __m128i s_m_h = convert_32_to_64_add_avx2(sum_m[4], sum_h[4]);
815
0
    _mm256_storeu_si256((__m256i *)(M + wiener_win * j), s_m);
816
0
    _mm_storel_epi64((__m128i *)&M[wiener_win * j + 4], s_m_h);
817
818
0
    const __m256i s_h =
819
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
820
0
    _mm256_storeu_si256((__m256i *)(H + wiener_win * j), s_h);
821
0
    _mm_storeh_epi64((__m128i *)&H[wiener_win * j + 4], s_m_h);
822
0
  } while (++j < wiener_win);
823
824
  // The below steps are designed to fill remaining rows of H buffer. Here, aim
825
  // is to fill only upper triangle elements correspond to each row and lower
826
  // triangle elements are copied from upper-triangle elements. Also, as
827
  // mentioned in Step 1, the core function is designed to fill 5
828
  // elements/stats/values of H buffer.
829
  //
830
  // Step 2: Here, the rows 1, 6, 11, 16 and 21 are filled. As we need to fill
831
  // only upper-triangle elements, H10 from row1, H60-H64 and H65 from row6,etc,
832
  // are need not be filled. As the core function process 5 values, in first
833
  // iteration of 'j' only 4 values to be filled i.e., H11-H14 from row1,H66-H69
834
  // from row6, etc.
835
0
  for (int i = 1; i < wiener_win2; i += wiener_win) {
836
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
837
    // from where the H buffer filling needs to be started.
838
0
    INITIALIZATION(WIENER_WIN_CHROMA)
839
840
0
    do {
841
0
      UPDATE_DOWNSAMPLE_FACTOR
842
843
      // Process the amount of width multiple of 16.
844
0
      while (proc_wd < wd_mul16) {
845
0
        const __m256i dgd =
846
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
847
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
848
0
        INIT_H_VALUES(d_window + proc_wd + (1 * d_stride), 4)
849
850
0
        proc_wd += 16;
851
0
      }
852
853
      // Process the remaining width here.
854
0
      if (wd_beyond_mul16) {
855
0
        const __m256i dgd =
856
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
857
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
858
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
859
0
        INIT_H_VALUES(d_window + proc_wd + (1 * d_stride), 4)
860
0
      }
861
0
      proc_ht += downsample_factor;
862
0
      d_window += downsample_factor * d_stride;
863
0
      d_current_row += downsample_factor * d_stride;
864
0
    } while (proc_ht < v_end);
865
0
    const __m256i s_h =
866
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
867
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + i), s_h);
868
869
    // process the remaining 'j' iterations.
870
0
    j++;
871
0
    CALCULATE_REMAINING_H_WIN5
872
0
  }
873
874
  // Step 3: Here, the rows 2, 7, 12, 17 and 22 are filled. As we need to fill
875
  // only upper-triangle elements, H20-H21 from row2, H70-H74 and H75-H76 from
876
  // row7, etc, are need not be filled. As the core function process 5 values,
877
  // in first iteration of 'j' only 3 values to be filled i.e., H22-H24 from
878
  // row2, H77-H79 from row7, etc.
879
0
  for (int i = 2; i < wiener_win2; i += wiener_win) {
880
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
881
    // from where the H buffer filling needs to be started.
882
0
    INITIALIZATION(WIENER_WIN_CHROMA)
883
884
0
    do {
885
0
      UPDATE_DOWNSAMPLE_FACTOR
886
887
      // Process the amount of width multiple of 16.
888
0
      while (proc_wd < wd_mul16) {
889
0
        const __m256i dgd =
890
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
891
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
892
0
        INIT_H_VALUES(d_window + proc_wd + (2 * d_stride), 3)
893
894
0
        proc_wd += 16;
895
0
      }
896
897
      // Process the remaining width here.
898
0
      if (wd_beyond_mul16) {
899
0
        const __m256i dgd =
900
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
901
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
902
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
903
0
        INIT_H_VALUES(d_window + proc_wd + (2 * d_stride), 3)
904
0
      }
905
0
      proc_ht += downsample_factor;
906
0
      d_window += downsample_factor * d_stride;
907
0
      d_current_row += downsample_factor * d_stride;
908
0
    } while (proc_ht < v_end);
909
0
    const __m256i s_h =
910
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
911
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + i), s_h);
912
913
    // process the remaining 'j' iterations.
914
0
    j++;
915
0
    CALCULATE_REMAINING_H_WIN5
916
0
  }
917
918
  // Step 4: Here, the rows 3, 8, 13, 18 and 23 are filled. As we need to fill
919
  // only upper-triangle elements, H30-H32 from row3, H80-H84 and H85-H87 from
920
  // row8, etc, are need not be filled. As the core function process 5 values,
921
  // in first iteration of 'j' only 2 values to be filled i.e., H33-H34 from
922
  // row3, H88-89 from row8, etc.
923
0
  for (int i = 3; i < wiener_win2; i += wiener_win) {
924
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
925
    // from where the H buffer filling needs to be started.
926
0
    INITIALIZATION(WIENER_WIN_CHROMA)
927
928
0
    do {
929
0
      UPDATE_DOWNSAMPLE_FACTOR
930
931
      // Process the amount of width multiple of 16.
932
0
      while (proc_wd < wd_mul16) {
933
0
        const __m256i dgd =
934
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
935
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
936
0
        INIT_H_VALUES(d_window + proc_wd + (3 * d_stride), 2)
937
938
0
        proc_wd += 16;
939
0
      }
940
941
      // Process the remaining width here.
942
0
      if (wd_beyond_mul16) {
943
0
        const __m256i dgd =
944
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
945
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
946
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
947
0
        INIT_H_VALUES(d_window + proc_wd + (3 * d_stride), 2)
948
0
      }
949
0
      proc_ht += downsample_factor;
950
0
      d_window += downsample_factor * d_stride;
951
0
      d_current_row += downsample_factor * d_stride;
952
0
    } while (proc_ht < v_end);
953
0
    const __m128i s_h = convert_32_to_64_add_avx2(sum_h[0], sum_h[1]);
954
0
    _mm_storeu_si128((__m128i *)(H + (i * wiener_win2) + i), s_h);
955
956
    // process the remaining 'j' iterations.
957
0
    j++;
958
0
    CALCULATE_REMAINING_H_WIN5
959
0
  }
960
961
  // Step 5: Here, the rows 4, 9, 14, 19 and 24 are filled. As we need to fill
962
  // only upper-triangle elements, H40-H43 from row4, H90-H94 and H95-H98 from
963
  // row9, etc, are need not be filled. As the core function process 5 values,
964
  // in first iteration of 'j' only 1 values to be filled i.e., H44 from row4,
965
  // H99 from row9, etc.
966
0
  for (int i = 4; i < wiener_win2; i += wiener_win) {
967
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
968
    // from where the H buffer filling needs to be started.
969
0
    INITIALIZATION(WIENER_WIN_CHROMA)
970
0
    do {
971
0
      UPDATE_DOWNSAMPLE_FACTOR
972
973
      // Process the amount of width multiple of 16.
974
0
      while (proc_wd < wd_mul16) {
975
0
        const __m256i dgd =
976
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
977
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
978
0
        INIT_H_VALUES(d_window + proc_wd + (4 * d_stride), 1)
979
980
0
        proc_wd += 16;
981
0
      }
982
983
      // Process the remaining width here.
984
0
      if (wd_beyond_mul16) {
985
0
        const __m256i dgd =
986
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
987
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
988
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
989
0
        INIT_H_VALUES(d_window + proc_wd + (4 * d_stride), 1)
990
0
      }
991
0
      proc_ht += downsample_factor;
992
0
      d_window += downsample_factor * d_stride;
993
0
      d_current_row += downsample_factor * d_stride;
994
0
    } while (proc_ht < v_end);
995
0
    const __m128i s_h = convert_32_to_64_add_avx2(sum_h[0], sum_h[1]);
996
0
    _mm_storeu_si128((__m128i *)(H + (i * wiener_win2) + i), s_h);
997
998
    // process the remaining 'j' iterations.
999
0
    j++;
1000
0
    CALCULATE_REMAINING_H_WIN5
1001
0
  }
1002
1003
  // Step 6: Here, the rows 5, 10, 15 and 20 are filled. As we need to fill only
1004
  // upper-triangle elements, H50-H54 from row5, H100-H104 and H105-H109 from
1005
  // row10,etc, are need not be filled. The first iteration of 'j' fills H55-H59
1006
  // from row5 and H1010-H1014 from row10, etc.
1007
0
  for (int i = 5; i < wiener_win2; i += wiener_win) {
1008
    // Derive j'th iteration from where the H buffer filling needs to be
1009
    // started.
1010
0
    j = i / wiener_win;
1011
0
    int shift = 0;
1012
0
    do {
1013
      // Update the dgd pointers appropriately.
1014
0
      int proc_ht = v_start;
1015
0
      const int16_t *d_window = d + (i / wiener_win);
1016
0
      const int16_t *d_current_row =
1017
0
          d + (i / wiener_win) + ((i % wiener_win) * d_stride);
1018
0
      downsample_factor =
1019
0
          use_downsampled_wiener_stats ? WIENER_STATS_DOWNSAMPLE_FACTOR : 1;
1020
0
      __m256i sum_h[WIENER_WIN_CHROMA] = { _mm256_setzero_si256() };
1021
0
      do {
1022
0
        UPDATE_DOWNSAMPLE_FACTOR
1023
1024
        // Process the amount of width multiple of 16.
1025
0
        while (proc_wd < wd_mul16) {
1026
0
          const __m256i dgd =
1027
0
              _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1028
0
          const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1029
0
          INIT_H_VALUES(d_window + shift + proc_wd, 5)
1030
1031
0
          proc_wd += 16;
1032
0
        }
1033
1034
        // Process the remaining width here.
1035
0
        if (wd_beyond_mul16) {
1036
0
          const __m256i dgd =
1037
0
              _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1038
0
          const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1039
0
          const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1040
0
          INIT_H_VALUES(d_window + shift + proc_wd, 5)
1041
0
        }
1042
0
        proc_ht += downsample_factor;
1043
0
        d_window += downsample_factor * d_stride;
1044
0
        d_current_row += downsample_factor * d_stride;
1045
0
      } while (proc_ht < v_end);
1046
1047
0
      const __m256i s_h =
1048
0
          hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1049
0
      _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + (wiener_win * j)),
1050
0
                          s_h);
1051
0
      const __m256i s_m_h = convert_and_add_avx2(sum_h[4]);
1052
0
      const __m128i s_m_h0 = add_64bit_lvl_avx2(s_m_h, s_m_h);
1053
0
      _mm_storel_epi64(
1054
0
          (__m128i *)(H + (i * wiener_win2) + (wiener_win * j) + 4), s_m_h0);
1055
0
      shift++;
1056
0
    } while (++j < wiener_win);
1057
0
  }
1058
1059
0
  fill_lower_triag_elements_avx2(wiener_win2, H);
1060
0
}
1061
1062
// The buffers H(auto-covariance) and M(cross-correlation) are used to estimate
1063
// the filter tap values required for wiener filtering. Here, the buffer H is of
1064
// size ((wiener_window_size^2)*(wiener_window_size^2)) and M is of size
1065
// (wiener_window_size*wiener_window_size). H is a symmetric matrix where the
1066
// value above the diagonal (upper triangle) are equal to the values below the
1067
// diagonal (lower triangle). The calculation of elements/stats of H(upper
1068
// triangle) and M is done in steps as described below where each step fills
1069
// specific values of H and M.
1070
// Example:
1071
// Wiener window size = WIENER_WIN (7)
1072
// M buffer = [M0 M1 M2 ---- M47 M48]
1073
// H buffer = Hxy (x-row, y-column)
1074
// [H00 H01 H02 ---- H047 H048]
1075
// [H10 H11 H12 ---- H147 H148]
1076
// [H30 H31 H32 ---- H347 H348]
1077
// [H40 H41 H42 ---- H447 H448]
1078
// [H50 H51 H52 ---- H547 H548]
1079
// [H60 H61 H62 ---- H647 H648]
1080
//            ||
1081
//            ||
1082
// [H470 H471 H472 ---- H4747 H4748]
1083
// [H480 H481 H482 ---- H4847 H4848]
1084
// In Step 1, whole M buffers (i.e., M0 to M48) and the first row of H (i.e.,
1085
// H00 to H048) is filled. The remaining rows of H buffer are filled through
1086
// steps 2 to 8.
1087
static void compute_stats_win7_avx2(const int16_t *const d, int32_t d_stride,
1088
                                    const int16_t *const s, int32_t s_stride,
1089
                                    int32_t width, int v_start, int v_end,
1090
                                    int64_t *const M, int64_t *const H,
1091
0
                                    int use_downsampled_wiener_stats) {
1092
0
  const int32_t wiener_win = WIENER_WIN;
1093
0
  const int32_t wiener_win2 = wiener_win * wiener_win;
1094
  // Amount of width which is beyond multiple of 16. This case is handled
1095
  // appropriately to process only the required width towards the end.
1096
0
  const int32_t wd_mul16 = width & ~15;
1097
0
  const int32_t wd_beyond_mul16 = width - wd_mul16;
1098
0
  const __m256i mask =
1099
0
      _mm256_loadu_si256((__m256i *)(&mask_16bit[16 - wd_beyond_mul16]));
1100
0
  int downsample_factor;
1101
1102
  // Step 1: Full M (i.e., M0 to M48) and first row H (i.e., H00 to H048)
1103
  // values are filled here. Here, the loop over 'j' is executed for values 0
1104
  // to 6. When the loop executed for a specific 'j', 7 values of M and H are
1105
  // filled as shown below.
1106
  // j=0: M0-M6 and H00-H06, j=1: M7-M13 and H07-H013 are filled etc,.
1107
0
  int j = 0;
1108
0
  do {
1109
0
    const int16_t *s_t = s;
1110
0
    const int16_t *d_t = d;
1111
0
    __m256i sum_m[WIENER_WIN] = { _mm256_setzero_si256() };
1112
0
    __m256i sum_h[WIENER_WIN] = { _mm256_setzero_si256() };
1113
0
    downsample_factor =
1114
0
        use_downsampled_wiener_stats ? WIENER_STATS_DOWNSAMPLE_FACTOR : 1;
1115
0
    int proc_ht = v_start;
1116
0
    do {
1117
0
      UPDATE_DOWNSAMPLE_FACTOR
1118
1119
      // Process the amount of width multiple of 16.
1120
0
      while (proc_wd < wd_mul16) {
1121
0
        const __m256i src = _mm256_loadu_si256((__m256i *)(s_t + proc_wd));
1122
0
        const __m256i dgd = _mm256_loadu_si256((__m256i *)(d_t + proc_wd));
1123
0
        const __m256i src_mul_df = _mm256_mullo_epi16(src, df_reg);
1124
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1125
0
        INIT_MH_VALUES(d_t + j + proc_wd)
1126
1127
0
        proc_wd += 16;
1128
0
      }
1129
1130
0
      if (wd_beyond_mul16) {
1131
0
        const __m256i src = _mm256_loadu_si256((__m256i *)(s_t + proc_wd));
1132
0
        const __m256i dgd = _mm256_loadu_si256((__m256i *)(d_t + proc_wd));
1133
0
        const __m256i src_mask = _mm256_and_si256(src, mask);
1134
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1135
0
        const __m256i src_mul_df = _mm256_mullo_epi16(src_mask, df_reg);
1136
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1137
0
        INIT_MH_VALUES(d_t + j + proc_wd)
1138
0
      }
1139
0
      proc_ht += downsample_factor;
1140
0
      s_t += downsample_factor * s_stride;
1141
0
      d_t += downsample_factor * d_stride;
1142
0
    } while (proc_ht < v_end);
1143
1144
0
    const __m256i s_m0 =
1145
0
        hadd_four_32_to_64_avx2(sum_m[0], sum_m[1], &sum_m[2], &sum_m[3]);
1146
0
    const __m256i s_m1 =
1147
0
        hadd_four_32_to_64_avx2(sum_m[4], sum_m[5], &sum_m[6], &sum_m[6]);
1148
0
    _mm256_storeu_si256((__m256i *)(M + wiener_win * j + 0), s_m0);
1149
0
    _mm_storeu_si128((__m128i *)(M + wiener_win * j + 4),
1150
0
                     _mm256_castsi256_si128(s_m1));
1151
0
    _mm_storel_epi64((__m128i *)&M[wiener_win * j + 6],
1152
0
                     _mm256_extracti128_si256(s_m1, 1));
1153
1154
0
    const __m256i sh_0 =
1155
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1156
0
    const __m256i sh_1 =
1157
0
        hadd_four_32_to_64_avx2(sum_h[4], sum_h[5], &sum_h[6], &sum_h[6]);
1158
0
    _mm256_storeu_si256((__m256i *)(H + wiener_win * j + 0), sh_0);
1159
0
    _mm_storeu_si128((__m128i *)(H + wiener_win * j + 4),
1160
0
                     _mm256_castsi256_si128(sh_1));
1161
0
    _mm_storel_epi64((__m128i *)&H[wiener_win * j + 6],
1162
0
                     _mm256_extracti128_si256(sh_1, 1));
1163
0
  } while (++j < wiener_win);
1164
1165
  // The below steps are designed to fill remaining rows of H buffer. Here, aim
1166
  // is to fill only upper triangle elements correspond to each row and lower
1167
  // triangle elements are copied from upper-triangle elements. Also, as
1168
  // mentioned in Step 1, the core function is designed to fill 7
1169
  // elements/stats/values of H buffer.
1170
  //
1171
  // Step 2: Here, the rows 1, 8, 15, 22, 29, 36 and 43 are filled. As we need
1172
  // to fill only upper-triangle elements, H10 from row1, H80-H86 and H87 from
1173
  // row8, etc. are need not be filled. As the core function process 7 values,
1174
  // in first iteration of 'j' only 6 values to be filled i.e., H11-H16 from
1175
  // row1 and H88-H813 from row8, etc.
1176
0
  for (int i = 1; i < wiener_win2; i += wiener_win) {
1177
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
1178
    // from where the H buffer filling needs to be started.
1179
0
    INITIALIZATION(WIENER_WIN)
1180
1181
0
    do {
1182
0
      UPDATE_DOWNSAMPLE_FACTOR
1183
1184
      // Process the amount of width multiple of 16.
1185
0
      while (proc_wd < wd_mul16) {
1186
0
        const __m256i dgd =
1187
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1188
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1189
0
        INIT_H_VALUES(d_window + proc_wd + (1 * d_stride), 6)
1190
1191
0
        proc_wd += 16;
1192
0
      }
1193
1194
      // Process the remaining width here.
1195
0
      if (wd_beyond_mul16) {
1196
0
        const __m256i dgd =
1197
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1198
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1199
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1200
0
        INIT_H_VALUES(d_window + proc_wd + (1 * d_stride), 6)
1201
0
      }
1202
0
      proc_ht += downsample_factor;
1203
0
      d_window += downsample_factor * d_stride;
1204
0
      d_current_row += downsample_factor * d_stride;
1205
0
    } while (proc_ht < v_end);
1206
0
    const __m256i s_h =
1207
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1208
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + i), s_h);
1209
0
    const __m128i s_h0 = convert_32_to_64_add_avx2(sum_h[4], sum_h[5]);
1210
0
    _mm_storeu_si128((__m128i *)(H + (i * wiener_win2) + i + 4), s_h0);
1211
1212
    // process the remaining 'j' iterations.
1213
0
    j++;
1214
0
    CALCULATE_REMAINING_H_WIN7
1215
0
  }
1216
1217
  // Step 3: Here, the rows 2, 9, 16, 23, 30, 37 and 44 are filled. As we need
1218
  // to fill only upper-triangle elements, H20-H21 from row2, H90-H96 and
1219
  // H97-H98 from row9, etc. are need not be filled. As the core function
1220
  // process 7 values, in first iteration of 'j' only 5 values to be filled
1221
  // i.e., H22-H26 from row2 and H99-H913 from row9, etc.
1222
0
  for (int i = 2; i < wiener_win2; i += wiener_win) {
1223
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
1224
    // from where the H buffer filling needs to be started.
1225
0
    INITIALIZATION(WIENER_WIN)
1226
0
    do {
1227
0
      UPDATE_DOWNSAMPLE_FACTOR
1228
1229
      // Process the amount of width multiple of 16.
1230
0
      while (proc_wd < wd_mul16) {
1231
0
        const __m256i dgd =
1232
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1233
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1234
0
        INIT_H_VALUES(d_window + proc_wd + (2 * d_stride), 5)
1235
1236
0
        proc_wd += 16;
1237
0
      }
1238
1239
      // Process the remaining width here.
1240
0
      if (wd_beyond_mul16) {
1241
0
        const __m256i dgd =
1242
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1243
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1244
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1245
0
        INIT_H_VALUES(d_window + proc_wd + (2 * d_stride), 5)
1246
0
      }
1247
0
      proc_ht += downsample_factor;
1248
0
      d_window += downsample_factor * d_stride;
1249
0
      d_current_row += downsample_factor * d_stride;
1250
0
    } while (proc_ht < v_end);
1251
0
    const __m256i s_h =
1252
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1253
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + i), s_h);
1254
0
    const __m256i s_m_h = convert_and_add_avx2(sum_h[4]);
1255
0
    const __m128i s_m_h0 = add_64bit_lvl_avx2(s_m_h, s_m_h);
1256
0
    _mm_storel_epi64((__m128i *)(H + (i * wiener_win2) + i + 4), s_m_h0);
1257
1258
    // process the remaining 'j' iterations.
1259
0
    j++;
1260
0
    CALCULATE_REMAINING_H_WIN7
1261
0
  }
1262
1263
  // Step 4: Here, the rows 3, 10, 17, 24, 31, 38 and 45 are filled. As we need
1264
  // to fill only upper-triangle elements, H30-H32 from row3, H100-H106 and
1265
  // H107-H109 from row10, etc. are need not be filled. As the core function
1266
  // process 7 values, in first iteration of 'j' only 4 values to be filled
1267
  // i.e., H33-H36 from row3 and H1010-H1013 from row10, etc.
1268
0
  for (int i = 3; i < wiener_win2; i += wiener_win) {
1269
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
1270
    // from where the H buffer filling needs to be started.
1271
0
    INITIALIZATION(WIENER_WIN)
1272
1273
0
    do {
1274
0
      UPDATE_DOWNSAMPLE_FACTOR
1275
1276
      // Process the amount of width multiple of 16.
1277
0
      while (proc_wd < wd_mul16) {
1278
0
        const __m256i dgd =
1279
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1280
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1281
0
        INIT_H_VALUES(d_window + proc_wd + (3 * d_stride), 4)
1282
1283
0
        proc_wd += 16;
1284
0
      }
1285
1286
      // Process the remaining width here.
1287
0
      if (wd_beyond_mul16) {
1288
0
        const __m256i dgd =
1289
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1290
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1291
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1292
0
        INIT_H_VALUES(d_window + proc_wd + (3 * d_stride), 4)
1293
0
      }
1294
0
      proc_ht += downsample_factor;
1295
0
      d_window += downsample_factor * d_stride;
1296
0
      d_current_row += downsample_factor * d_stride;
1297
0
    } while (proc_ht < v_end);
1298
0
    const __m256i s_h =
1299
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1300
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + i), s_h);
1301
1302
    // process the remaining 'j' iterations.
1303
0
    j++;
1304
0
    CALCULATE_REMAINING_H_WIN7
1305
0
  }
1306
1307
  // Step 5: Here, the rows 4, 11, 18, 25, 32, 39 and 46 are filled. As we need
1308
  // to fill only upper-triangle elements, H40-H43 from row4, H110-H116 and
1309
  // H117-H1110 from row10, etc. are need not be filled. As the core function
1310
  // process 7 values, in first iteration of 'j' only 3 values to be filled
1311
  // i.e., H44-H46 from row4 and H1111-H1113 from row11, etc.
1312
0
  for (int i = 4; i < wiener_win2; i += wiener_win) {
1313
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
1314
    // from where the H buffer filling needs to be started.
1315
0
    INITIALIZATION(WIENER_WIN)
1316
1317
0
    do {
1318
0
      UPDATE_DOWNSAMPLE_FACTOR
1319
1320
      // Process the amount of width multiple of 16.
1321
0
      while (proc_wd < wd_mul16) {
1322
0
        const __m256i dgd =
1323
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1324
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1325
0
        INIT_H_VALUES(d_window + proc_wd + (4 * d_stride), 3)
1326
1327
0
        proc_wd += 16;
1328
0
      }
1329
1330
      // Process the remaining width here.
1331
0
      if (wd_beyond_mul16) {
1332
0
        const __m256i dgd =
1333
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1334
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1335
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1336
0
        INIT_H_VALUES(d_window + proc_wd + (4 * d_stride), 3)
1337
0
      }
1338
0
      proc_ht += downsample_factor;
1339
0
      d_window += downsample_factor * d_stride;
1340
0
      d_current_row += downsample_factor * d_stride;
1341
0
    } while (proc_ht < v_end);
1342
0
    const __m256i s_h =
1343
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1344
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + i), s_h);
1345
1346
    // process the remaining 'j' iterations.
1347
0
    j++;
1348
0
    CALCULATE_REMAINING_H_WIN7
1349
0
  }
1350
1351
  // Step 6: Here, the rows 5, 12, 19, 26, 33, 40 and 47 are filled. As we need
1352
  // to fill only upper-triangle elements, H50-H54 from row5, H120-H126 and
1353
  // H127-H1211 from row12, etc. are need not be filled. As the core function
1354
  // process 7 values, in first iteration of 'j' only 2 values to be filled
1355
  // i.e., H55-H56 from row5 and H1212-H1213 from row12, etc.
1356
0
  for (int i = 5; i < wiener_win2; i += wiener_win) {
1357
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
1358
    // from where the H buffer filling needs to be started.
1359
0
    INITIALIZATION(WIENER_WIN)
1360
0
    do {
1361
0
      UPDATE_DOWNSAMPLE_FACTOR
1362
1363
      // Process the amount of width multiple of 16.
1364
0
      while (proc_wd < wd_mul16) {
1365
0
        const __m256i dgd =
1366
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1367
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1368
0
        INIT_H_VALUES(d_window + proc_wd + (5 * d_stride), 2)
1369
1370
0
        proc_wd += 16;
1371
0
      }
1372
1373
      // Process the remaining width here.
1374
0
      if (wd_beyond_mul16) {
1375
0
        const __m256i dgd =
1376
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1377
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1378
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1379
0
        INIT_H_VALUES(d_window + proc_wd + (5 * d_stride), 2)
1380
0
      }
1381
0
      proc_ht += downsample_factor;
1382
0
      d_window += downsample_factor * d_stride;
1383
0
      d_current_row += downsample_factor * d_stride;
1384
0
    } while (proc_ht < v_end);
1385
0
    const __m256i s_h =
1386
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1387
0
    _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + i), s_h);
1388
1389
    // process the remaining 'j' iterations.
1390
0
    j++;
1391
0
    CALCULATE_REMAINING_H_WIN7
1392
0
  }
1393
1394
  // Step 7: Here, the rows 6, 13, 20, 27, 34, 41 and 48 are filled. As we need
1395
  // to fill only upper-triangle elements, H60-H65 from row6, H130-H136 and
1396
  // H137-H1312 from row13, etc. are need not be filled. As the core function
1397
  // process 7 values, in first iteration of 'j' only 1 value to be filled
1398
  // i.e., H66 from row6 and H1313 from row13, etc.
1399
0
  for (int i = 6; i < wiener_win2; i += wiener_win) {
1400
    // Update the dgd pointers appropriately and also derive the 'j'th iteration
1401
    // from where the H buffer filling needs to be started.
1402
0
    INITIALIZATION(WIENER_WIN)
1403
0
    do {
1404
0
      UPDATE_DOWNSAMPLE_FACTOR
1405
1406
      // Process the amount of width multiple of 16.
1407
0
      while (proc_wd < wd_mul16) {
1408
0
        const __m256i dgd =
1409
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1410
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1411
0
        INIT_H_VALUES(d_window + proc_wd + (6 * d_stride), 1)
1412
1413
0
        proc_wd += 16;
1414
0
      }
1415
1416
      // Process the remaining width here.
1417
0
      if (wd_beyond_mul16) {
1418
0
        const __m256i dgd =
1419
0
            _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1420
0
        const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1421
0
        const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1422
0
        INIT_H_VALUES(d_window + proc_wd + (6 * d_stride), 1)
1423
0
      }
1424
0
      proc_ht += downsample_factor;
1425
0
      d_window += downsample_factor * d_stride;
1426
0
      d_current_row += downsample_factor * d_stride;
1427
0
    } while (proc_ht < v_end);
1428
0
    const __m256i s_h =
1429
0
        hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1430
0
    xx_storel_64(&H[(i * wiener_win2) + i], _mm256_castsi256_si128(s_h));
1431
1432
    // process the remaining 'j' iterations.
1433
0
    j++;
1434
0
    CALCULATE_REMAINING_H_WIN7
1435
0
  }
1436
1437
  // Step 8: Here, the rows 7, 14, 21, 28, 35 and 42 are filled. As we need
1438
  // to fill only upper-triangle elements, H70-H75 from row7, H140-H146 and
1439
  // H147-H1413 from row14, etc. are need not be filled. The first iteration of
1440
  // 'j' fills H77-H713 from row7 and H1414-H1420 from row14, etc.
1441
0
  for (int i = 7; i < wiener_win2; i += wiener_win) {
1442
    // Derive j'th iteration from where the H buffer filling needs to be
1443
    // started.
1444
0
    j = i / wiener_win;
1445
0
    int shift = 0;
1446
0
    do {
1447
      // Update the dgd pointers appropriately.
1448
0
      int proc_ht = v_start;
1449
0
      const int16_t *d_window = d + (i / WIENER_WIN);
1450
0
      const int16_t *d_current_row =
1451
0
          d + (i / WIENER_WIN) + ((i % WIENER_WIN) * d_stride);
1452
0
      downsample_factor =
1453
0
          use_downsampled_wiener_stats ? WIENER_STATS_DOWNSAMPLE_FACTOR : 1;
1454
0
      __m256i sum_h[WIENER_WIN] = { _mm256_setzero_si256() };
1455
0
      do {
1456
0
        UPDATE_DOWNSAMPLE_FACTOR
1457
1458
        // Process the amount of width multiple of 16.
1459
0
        while (proc_wd < wd_mul16) {
1460
0
          const __m256i dgd =
1461
0
              _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1462
0
          const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd, df_reg);
1463
0
          INIT_H_VALUES(d_window + shift + proc_wd, 7)
1464
1465
0
          proc_wd += 16;
1466
0
        }
1467
1468
        // Process the remaining width here.
1469
0
        if (wd_beyond_mul16) {
1470
0
          const __m256i dgd =
1471
0
              _mm256_loadu_si256((__m256i *)(d_current_row + proc_wd));
1472
0
          const __m256i dgd_mask = _mm256_and_si256(dgd, mask);
1473
0
          const __m256i dgd_mul_df = _mm256_mullo_epi16(dgd_mask, df_reg);
1474
0
          INIT_H_VALUES(d_window + shift + proc_wd, 7)
1475
0
        }
1476
0
        proc_ht += downsample_factor;
1477
0
        d_window += downsample_factor * d_stride;
1478
0
        d_current_row += downsample_factor * d_stride;
1479
0
      } while (proc_ht < v_end);
1480
1481
0
      const __m256i sh_0 =
1482
0
          hadd_four_32_to_64_avx2(sum_h[0], sum_h[1], &sum_h[2], &sum_h[3]);
1483
0
      const __m256i sh_1 =
1484
0
          hadd_four_32_to_64_avx2(sum_h[4], sum_h[5], &sum_h[6], &sum_h[6]);
1485
0
      _mm256_storeu_si256((__m256i *)(H + (i * wiener_win2) + (wiener_win * j)),
1486
0
                          sh_0);
1487
0
      _mm_storeu_si128(
1488
0
          (__m128i *)(H + (i * wiener_win2) + (wiener_win * j) + 4),
1489
0
          _mm256_castsi256_si128(sh_1));
1490
0
      _mm_storel_epi64((__m128i *)&H[(i * wiener_win2) + (wiener_win * j) + 6],
1491
0
                       _mm256_extracti128_si256(sh_1, 1));
1492
0
      shift++;
1493
0
    } while (++j < wiener_win);
1494
0
  }
1495
1496
0
  fill_lower_triag_elements_avx2(wiener_win2, H);
1497
0
}
1498
1499
void av1_compute_stats_avx2(int wiener_win, const uint8_t *dgd,
1500
                            const uint8_t *src, int16_t *dgd_avg,
1501
                            int16_t *src_avg, int h_start, int h_end,
1502
                            int v_start, int v_end, int dgd_stride,
1503
                            int src_stride, int64_t *M, int64_t *H,
1504
0
                            int use_downsampled_wiener_stats) {
1505
0
  if (wiener_win != WIENER_WIN && wiener_win != WIENER_WIN_CHROMA) {
1506
    // Currently, libaom supports Wiener filter processing with window sizes as
1507
    // WIENER_WIN_CHROMA(5) and WIENER_WIN(7). For any other window size, SIMD
1508
    // support is not facilitated. Hence, invoke C function for the same.
1509
0
    av1_compute_stats_c(wiener_win, dgd, src, dgd_avg, src_avg, h_start, h_end,
1510
0
                        v_start, v_end, dgd_stride, src_stride, M, H,
1511
0
                        use_downsampled_wiener_stats);
1512
0
    return;
1513
0
  }
1514
1515
0
  const int32_t wiener_halfwin = wiener_win >> 1;
1516
0
  const uint8_t avg =
1517
0
      calc_dgd_buf_avg_avx2(dgd, h_start, h_end, v_start, v_end, dgd_stride);
1518
0
  const int32_t width = h_end - h_start;
1519
0
  const int32_t height = v_end - v_start;
1520
0
  const int32_t d_stride = (width + 2 * wiener_halfwin + 15) & ~15;
1521
0
  const int32_t s_stride = (width + 15) & ~15;
1522
1523
  // Based on the sf 'use_downsampled_wiener_stats', process either once for
1524
  // UPDATE_DOWNSAMPLE_FACTOR or for each row.
1525
0
  sub_avg_block_avx2(src + v_start * src_stride + h_start, src_stride, avg,
1526
0
                     width, height, src_avg, s_stride,
1527
0
                     use_downsampled_wiener_stats);
1528
1529
  // Compute (dgd-avg) buffer here which is used to fill H buffer.
1530
0
  sub_avg_block_avx2(
1531
0
      dgd + (v_start - wiener_halfwin) * dgd_stride + h_start - wiener_halfwin,
1532
0
      dgd_stride, avg, width + 2 * wiener_halfwin, height + 2 * wiener_halfwin,
1533
0
      dgd_avg, d_stride, 0);
1534
0
  if (wiener_win == WIENER_WIN) {
1535
0
    compute_stats_win7_avx2(dgd_avg, d_stride, src_avg, s_stride, width,
1536
0
                            v_start, v_end, M, H, use_downsampled_wiener_stats);
1537
0
  } else if (wiener_win == WIENER_WIN_CHROMA) {
1538
0
    compute_stats_win5_avx2(dgd_avg, d_stride, src_avg, s_stride, width,
1539
0
                            v_start, v_end, M, H, use_downsampled_wiener_stats);
1540
0
  }
1541
0
}
1542
1543
0
static inline __m256i pair_set_epi16(int a, int b) {
1544
0
  return _mm256_set1_epi32(
1545
0
      (int32_t)(((uint16_t)(a)) | (((uint32_t)(uint16_t)(b)) << 16)));
1546
0
}
1547
1548
0
static inline __m256i load_shuffled_u8_to_epi16(const uint8_t *ptr) {
1549
0
  const __m128i raw = xx_loadu_128(ptr);
1550
0
  const __m128i shuffled = _mm_shuffle_epi32(raw, _MM_SHUFFLE(3, 1, 2, 0));
1551
0
  return _mm256_cvtepu8_epi16(shuffled);
1552
0
}
1553
1554
static inline __m256i load_shuffled_u8_dual8_to_epi16(const uint8_t *ptrA,
1555
0
                                                      const uint8_t *ptrB) {
1556
0
  const __m128i rawA = _mm_loadl_epi64((const __m128i *)ptrA);
1557
0
  const __m128i rawB = _mm_loadl_epi64((const __m128i *)ptrB);
1558
0
  const __m128i raw_AB = _mm_unpacklo_epi64(rawA, rawB);
1559
0
  const __m128i shuffled = _mm_shuffle_epi32(raw_AB, _MM_SHUFFLE(3, 1, 2, 0));
1560
0
  return _mm256_cvtepu8_epi16(shuffled);
1561
0
}
1562
1563
static inline __m256i calc_proj_err_r0_r1_avx2(
1564
    const __m256i d0, const __m256i s0, const __m256i flt0_16b,
1565
    const __m256i flt1_16b, const __m256i xq_coeff, const __m256i rounding,
1566
0
    int shift) {
1567
0
  const __m256i u0 = _mm256_slli_epi16(d0, SGRPROJ_RST_BITS);
1568
0
  const __m256i v0 = _mm256_madd_epi16(
1569
0
      xq_coeff, _mm256_unpacklo_epi16(_mm256_sub_epi16(flt0_16b, u0),
1570
0
                                      _mm256_sub_epi16(flt1_16b, u0)));
1571
0
  const __m256i v1 = _mm256_madd_epi16(
1572
0
      xq_coeff, _mm256_unpackhi_epi16(_mm256_sub_epi16(flt0_16b, u0),
1573
0
                                      _mm256_sub_epi16(flt1_16b, u0)));
1574
0
  const __m256i vr = _mm256_packs_epi32(
1575
0
      _mm256_srai_epi32(_mm256_add_epi32(v0, rounding), shift),
1576
0
      _mm256_srai_epi32(_mm256_add_epi32(v1, rounding), shift));
1577
0
  return _mm256_add_epi16(vr, _mm256_sub_epi16(d0, s0));
1578
0
}
1579
1580
static inline __m256i calc_proj_err_r0_or_r1_avx2(
1581
    const __m256i d0, const __m256i s0, const __m256i flt_16b,
1582
0
    const __m256i xq_coeff, const __m256i rounding, int shift) {
1583
0
  const __m256i v0 =
1584
0
      _mm256_madd_epi16(xq_coeff, _mm256_unpacklo_epi16(flt_16b, d0));
1585
0
  const __m256i v1 =
1586
0
      _mm256_madd_epi16(xq_coeff, _mm256_unpackhi_epi16(flt_16b, d0));
1587
0
  const __m256i vr_16b = _mm256_packs_epi32(
1588
0
      _mm256_srai_epi32(_mm256_add_epi32(v0, rounding), shift),
1589
0
      _mm256_srai_epi32(_mm256_add_epi32(v1, rounding), shift));
1590
0
  return _mm256_add_epi16(vr_16b, _mm256_sub_epi16(d0, s0));
1591
0
}
1592
1593
int64_t av1_lowbd_pixel_proj_error_avx2(
1594
    const uint8_t *src8, int width, int height, int src_stride,
1595
    const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride,
1596
0
    int32_t *flt1, int flt1_stride, int xq[2], const sgr_params_type *params) {
1597
0
  int i, j, k;
1598
0
  const int32_t shift = SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS;
1599
0
  const __m256i rounding = _mm256_set1_epi32(1 << (shift - 1));
1600
0
  __m256i sum64 = _mm256_setzero_si256();
1601
0
  const uint8_t *src = src8;
1602
0
  const uint8_t *dat = dat8;
1603
0
  int64_t err = 0;
1604
1605
0
  if (params->r[0] > 0 && params->r[1] > 0) {
1606
0
    __m256i xq_coeff = pair_set_epi16(xq[0], xq[1]);
1607
0
    if (width == 8) {
1608
0
      __m256i sum32 = _mm256_setzero_si256();
1609
0
      const int height_even = height & ~1;
1610
0
      for (i = 0; i < height_even; i += 2) {
1611
0
        const uint8_t *dat_rowB = dat + dat_stride;
1612
0
        const uint8_t *src_rowB = src + src_stride;
1613
0
        const int32_t *flt0_rowB = flt0 + flt0_stride;
1614
0
        const int32_t *flt1_rowB = flt1 + flt1_stride;
1615
1616
0
        const __m256i d0 = load_shuffled_u8_dual8_to_epi16(dat, dat_rowB);
1617
0
        const __m256i s0 = load_shuffled_u8_dual8_to_epi16(src, src_rowB);
1618
0
        const __m256i flt0_16b =
1619
0
            _mm256_packs_epi32(yy_loadu_256(flt0), yy_loadu_256(flt0_rowB));
1620
0
        const __m256i flt1_16b =
1621
0
            _mm256_packs_epi32(yy_loadu_256(flt1), yy_loadu_256(flt1_rowB));
1622
1623
0
        const __m256i e0 = calc_proj_err_r0_r1_avx2(d0, s0, flt0_16b, flt1_16b,
1624
0
                                                    xq_coeff, rounding, shift);
1625
0
        const __m256i err0 = _mm256_madd_epi16(e0, e0);
1626
0
        sum32 = _mm256_add_epi32(sum32, err0);
1627
1628
0
        dat += 2 * dat_stride;
1629
0
        src += 2 * src_stride;
1630
0
        flt0 += 2 * flt0_stride;
1631
0
        flt1 += 2 * flt1_stride;
1632
0
      }
1633
0
      if (i < height) {
1634
0
        for (k = 0; k < 8; ++k) {
1635
0
          const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
1636
0
          int32_t v = xq[0] * (flt0[k] - u) + xq[1] * (flt1[k] - u);
1637
0
          const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
1638
0
          err += ((int64_t)e * e);
1639
0
        }
1640
0
      }
1641
0
      const __m256i sum64_0 =
1642
0
          _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
1643
0
      const __m256i sum64_1 =
1644
0
          _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
1645
0
      sum64 = _mm256_add_epi64(sum64, _mm256_add_epi64(sum64_0, sum64_1));
1646
0
    } else if (width == 16) {
1647
0
      __m256i sum32_A = _mm256_setzero_si256();
1648
0
      __m256i sum32_B = _mm256_setzero_si256();
1649
0
      __m256i sum32_C = _mm256_setzero_si256();
1650
0
      __m256i sum32_D = _mm256_setzero_si256();
1651
0
      const int height_v4 = height & ~3;
1652
0
      for (i = 0; i < height_v4; i += 4) {
1653
0
        const uint8_t *dat_rowB = dat + dat_stride;
1654
0
        const uint8_t *dat_rowC = dat_rowB + dat_stride;
1655
0
        const uint8_t *dat_rowD = dat_rowC + dat_stride;
1656
0
        const uint8_t *src_rowB = src + src_stride;
1657
0
        const uint8_t *src_rowC = src_rowB + src_stride;
1658
0
        const uint8_t *src_rowD = src_rowC + src_stride;
1659
0
        const int32_t *flt0_rowB = flt0 + flt0_stride;
1660
0
        const int32_t *flt0_rowC = flt0_rowB + flt0_stride;
1661
0
        const int32_t *flt0_rowD = flt0_rowC + flt0_stride;
1662
0
        const int32_t *flt1_rowB = flt1 + flt1_stride;
1663
0
        const int32_t *flt1_rowC = flt1_rowB + flt1_stride;
1664
0
        const int32_t *flt1_rowD = flt1_rowC + flt1_stride;
1665
1666
        // Row A
1667
0
        {
1668
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat);
1669
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src);
1670
0
          const __m256i flt0_16b =
1671
0
              _mm256_packs_epi32(yy_loadu_256(flt0), yy_loadu_256(flt0 + 8));
1672
0
          const __m256i flt1_16b =
1673
0
              _mm256_packs_epi32(yy_loadu_256(flt1), yy_loadu_256(flt1 + 8));
1674
0
          const __m256i e = calc_proj_err_r0_r1_avx2(d0, s0, flt0_16b, flt1_16b,
1675
0
                                                     xq_coeff, rounding, shift);
1676
0
          sum32_A = _mm256_add_epi32(sum32_A, _mm256_madd_epi16(e, e));
1677
0
        }
1678
        // Row B
1679
0
        {
1680
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat_rowB);
1681
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src_rowB);
1682
0
          const __m256i flt0_16b = _mm256_packs_epi32(
1683
0
              yy_loadu_256(flt0_rowB), yy_loadu_256(flt0_rowB + 8));
1684
0
          const __m256i flt1_16b = _mm256_packs_epi32(
1685
0
              yy_loadu_256(flt1_rowB), yy_loadu_256(flt1_rowB + 8));
1686
0
          const __m256i e = calc_proj_err_r0_r1_avx2(d0, s0, flt0_16b, flt1_16b,
1687
0
                                                     xq_coeff, rounding, shift);
1688
0
          sum32_B = _mm256_add_epi32(sum32_B, _mm256_madd_epi16(e, e));
1689
0
        }
1690
        // Row C
1691
0
        {
1692
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat_rowC);
1693
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src_rowC);
1694
0
          const __m256i flt0_16b = _mm256_packs_epi32(
1695
0
              yy_loadu_256(flt0_rowC), yy_loadu_256(flt0_rowC + 8));
1696
0
          const __m256i flt1_16b = _mm256_packs_epi32(
1697
0
              yy_loadu_256(flt1_rowC), yy_loadu_256(flt1_rowC + 8));
1698
0
          const __m256i e = calc_proj_err_r0_r1_avx2(d0, s0, flt0_16b, flt1_16b,
1699
0
                                                     xq_coeff, rounding, shift);
1700
0
          sum32_C = _mm256_add_epi32(sum32_C, _mm256_madd_epi16(e, e));
1701
0
        }
1702
        // Row D
1703
0
        {
1704
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat_rowD);
1705
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src_rowD);
1706
0
          const __m256i flt0_16b = _mm256_packs_epi32(
1707
0
              yy_loadu_256(flt0_rowD), yy_loadu_256(flt0_rowD + 8));
1708
0
          const __m256i flt1_16b = _mm256_packs_epi32(
1709
0
              yy_loadu_256(flt1_rowD), yy_loadu_256(flt1_rowD + 8));
1710
0
          const __m256i e = calc_proj_err_r0_r1_avx2(d0, s0, flt0_16b, flt1_16b,
1711
0
                                                     xq_coeff, rounding, shift);
1712
0
          sum32_D = _mm256_add_epi32(sum32_D, _mm256_madd_epi16(e, e));
1713
0
        }
1714
1715
0
        dat += 4 * dat_stride;
1716
0
        src += 4 * src_stride;
1717
0
        flt0 += 4 * flt0_stride;
1718
0
        flt1 += 4 * flt1_stride;
1719
0
      }
1720
0
      for (; i < height; ++i) {
1721
0
        const __m256i d0 = load_shuffled_u8_to_epi16(dat);
1722
0
        const __m256i s0 = load_shuffled_u8_to_epi16(src);
1723
0
        const __m256i flt0_16b =
1724
0
            _mm256_packs_epi32(yy_loadu_256(flt0), yy_loadu_256(flt0 + 8));
1725
0
        const __m256i flt1_16b =
1726
0
            _mm256_packs_epi32(yy_loadu_256(flt1), yy_loadu_256(flt1 + 8));
1727
0
        const __m256i e = calc_proj_err_r0_r1_avx2(d0, s0, flt0_16b, flt1_16b,
1728
0
                                                   xq_coeff, rounding, shift);
1729
0
        sum32_A = _mm256_add_epi32(sum32_A, _mm256_madd_epi16(e, e));
1730
1731
0
        dat += dat_stride;
1732
0
        src += src_stride;
1733
0
        flt0 += flt0_stride;
1734
0
        flt1 += flt1_stride;
1735
0
      }
1736
0
      __m256i sum32 = _mm256_add_epi32(_mm256_add_epi32(sum32_A, sum32_B),
1737
0
                                       _mm256_add_epi32(sum32_C, sum32_D));
1738
0
      const __m256i sum64_0 =
1739
0
          _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
1740
0
      const __m256i sum64_1 =
1741
0
          _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
1742
0
      sum64 = _mm256_add_epi64(sum64, _mm256_add_epi64(sum64_0, sum64_1));
1743
0
    } else if (width >= 32 && (width % 32 == 0)) {
1744
0
      int rows_per_batch = 4096 / width;
1745
0
      if (rows_per_batch < 1) rows_per_batch = 1;
1746
0
      for (i = 0; i < height;) {
1747
0
        int rows_to_do = height - i;
1748
0
        if (rows_to_do > rows_per_batch) rows_to_do = rows_per_batch;
1749
0
        const int next_i = i + rows_to_do;
1750
0
        __m256i sum32_A = _mm256_setzero_si256();
1751
0
        __m256i sum32_B = _mm256_setzero_si256();
1752
0
        for (; i < next_i; ++i) {
1753
0
          for (j = 0; j <= width - 32; j += 32) {
1754
0
            const __m256i d_A = load_shuffled_u8_to_epi16(dat + j);
1755
0
            const __m256i s_A = load_shuffled_u8_to_epi16(src + j);
1756
0
            const __m256i flt0_A = _mm256_packs_epi32(
1757
0
                yy_loadu_256(flt0 + j), yy_loadu_256(flt0 + j + 8));
1758
0
            const __m256i flt1_A = _mm256_packs_epi32(
1759
0
                yy_loadu_256(flt1 + j), yy_loadu_256(flt1 + j + 8));
1760
0
            const __m256i e_A = calc_proj_err_r0_r1_avx2(
1761
0
                d_A, s_A, flt0_A, flt1_A, xq_coeff, rounding, shift);
1762
0
            sum32_A = _mm256_add_epi32(sum32_A, _mm256_madd_epi16(e_A, e_A));
1763
1764
0
            const __m256i d_B = load_shuffled_u8_to_epi16(dat + j + 16);
1765
0
            const __m256i s_B = load_shuffled_u8_to_epi16(src + j + 16);
1766
0
            const __m256i flt0_B = _mm256_packs_epi32(
1767
0
                yy_loadu_256(flt0 + j + 16), yy_loadu_256(flt0 + j + 24));
1768
0
            const __m256i flt1_B = _mm256_packs_epi32(
1769
0
                yy_loadu_256(flt1 + j + 16), yy_loadu_256(flt1 + j + 24));
1770
0
            const __m256i e_B = calc_proj_err_r0_r1_avx2(
1771
0
                d_B, s_B, flt0_B, flt1_B, xq_coeff, rounding, shift);
1772
0
            sum32_B = _mm256_add_epi32(sum32_B, _mm256_madd_epi16(e_B, e_B));
1773
0
          }
1774
0
          dat += dat_stride;
1775
0
          src += src_stride;
1776
0
          flt0 += flt0_stride;
1777
0
          flt1 += flt1_stride;
1778
0
        }
1779
0
        __m256i sum32 = _mm256_add_epi32(sum32_A, sum32_B);
1780
0
        const __m256i sum64_0 =
1781
0
            _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
1782
0
        const __m256i sum64_1 =
1783
0
            _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
1784
0
        sum64 = _mm256_add_epi64(sum64, _mm256_add_epi64(sum64_0, sum64_1));
1785
0
      }
1786
0
    } else {
1787
      // General fallback
1788
0
      for (i = 0; i < height; ++i) {
1789
0
        __m256i sum32 = _mm256_setzero_si256();
1790
0
        for (j = 0; j <= width - 16; j += 16) {
1791
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat + j);
1792
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src + j);
1793
0
          const __m256i flt0_16b = _mm256_packs_epi32(
1794
0
              yy_loadu_256(flt0 + j), yy_loadu_256(flt0 + j + 8));
1795
0
          const __m256i flt1_16b = _mm256_packs_epi32(
1796
0
              yy_loadu_256(flt1 + j), yy_loadu_256(flt1 + j + 8));
1797
0
          const __m256i e0 = calc_proj_err_r0_r1_avx2(
1798
0
              d0, s0, flt0_16b, flt1_16b, xq_coeff, rounding, shift);
1799
0
          sum32 = _mm256_add_epi32(sum32, _mm256_madd_epi16(e0, e0));
1800
0
        }
1801
0
        for (k = j; k < width; ++k) {
1802
0
          const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
1803
0
          int32_t v = xq[0] * (flt0[k] - u) + xq[1] * (flt1[k] - u);
1804
0
          const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
1805
0
          err += ((int64_t)e * e);
1806
0
        }
1807
0
        dat += dat_stride;
1808
0
        src += src_stride;
1809
0
        flt0 += flt0_stride;
1810
0
        flt1 += flt1_stride;
1811
0
        const __m256i sum64_0 =
1812
0
            _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
1813
0
        const __m256i sum64_1 =
1814
0
            _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
1815
0
        sum64 = _mm256_add_epi64(sum64, sum64_0);
1816
0
        sum64 = _mm256_add_epi64(sum64, sum64_1);
1817
0
      }
1818
0
    }
1819
0
  } else if (params->r[0] > 0 || params->r[1] > 0) {
1820
0
    const int xq_active = (params->r[0] > 0) ? xq[0] : xq[1];
1821
0
    const __m256i xq_coeff =
1822
0
        pair_set_epi16(xq_active, -xq_active * (1 << SGRPROJ_RST_BITS));
1823
0
    const int32_t *flt = (params->r[0] > 0) ? flt0 : flt1;
1824
0
    const int flt_stride = (params->r[0] > 0) ? flt0_stride : flt1_stride;
1825
1826
0
    if (width == 8) {
1827
0
      __m256i sum32 = _mm256_setzero_si256();
1828
0
      const int height_even = height & ~1;
1829
0
      for (i = 0; i < height_even; i += 2) {
1830
0
        const uint8_t *dat_rowB = dat + dat_stride;
1831
0
        const uint8_t *src_rowB = src + src_stride;
1832
0
        const int32_t *flt_rowB = flt + flt_stride;
1833
1834
0
        const __m256i d0 = load_shuffled_u8_dual8_to_epi16(dat, dat_rowB);
1835
0
        const __m256i s0 = load_shuffled_u8_dual8_to_epi16(src, src_rowB);
1836
0
        const __m256i flt_16b =
1837
0
            _mm256_packs_epi32(yy_loadu_256(flt), yy_loadu_256(flt_rowB));
1838
1839
0
        const __m256i e0 = calc_proj_err_r0_or_r1_avx2(
1840
0
            d0, s0, flt_16b, xq_coeff, rounding, shift);
1841
0
        const __m256i err0 = _mm256_madd_epi16(e0, e0);
1842
0
        sum32 = _mm256_add_epi32(sum32, err0);
1843
1844
0
        dat += 2 * dat_stride;
1845
0
        src += 2 * src_stride;
1846
0
        flt += 2 * flt_stride;
1847
0
      }
1848
0
      if (i < height) {
1849
0
        for (k = 0; k < 8; ++k) {
1850
0
          const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
1851
0
          int32_t v = xq_active * (flt[k] - u);
1852
0
          const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
1853
0
          err += ((int64_t)e * e);
1854
0
        }
1855
0
      }
1856
0
      const __m256i sum64_0 =
1857
0
          _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
1858
0
      const __m256i sum64_1 =
1859
0
          _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
1860
0
      sum64 = _mm256_add_epi64(sum64, _mm256_add_epi64(sum64_0, sum64_1));
1861
0
    } else if (width == 16) {
1862
0
      __m256i sum32_A = _mm256_setzero_si256();
1863
0
      __m256i sum32_B = _mm256_setzero_si256();
1864
0
      __m256i sum32_C = _mm256_setzero_si256();
1865
0
      __m256i sum32_D = _mm256_setzero_si256();
1866
0
      const int height_v4 = height & ~3;
1867
0
      for (i = 0; i < height_v4; i += 4) {
1868
0
        const uint8_t *dat_rowB = dat + dat_stride;
1869
0
        const uint8_t *dat_rowC = dat_rowB + dat_stride;
1870
0
        const uint8_t *dat_rowD = dat_rowC + dat_stride;
1871
0
        const uint8_t *src_rowB = src + src_stride;
1872
0
        const uint8_t *src_rowC = src_rowB + src_stride;
1873
0
        const uint8_t *src_rowD = src_rowC + src_stride;
1874
0
        const int32_t *flt_rowB = flt + flt_stride;
1875
0
        const int32_t *flt_rowC = flt_rowB + flt_stride;
1876
0
        const int32_t *flt_rowD = flt_rowC + flt_stride;
1877
1878
        // Row A
1879
0
        {
1880
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat);
1881
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src);
1882
0
          const __m256i flt_16b =
1883
0
              _mm256_packs_epi32(yy_loadu_256(flt), yy_loadu_256(flt + 8));
1884
0
          const __m256i e = calc_proj_err_r0_or_r1_avx2(
1885
0
              d0, s0, flt_16b, xq_coeff, rounding, shift);
1886
0
          sum32_A = _mm256_add_epi32(sum32_A, _mm256_madd_epi16(e, e));
1887
0
        }
1888
        // Row B
1889
0
        {
1890
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat_rowB);
1891
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src_rowB);
1892
0
          const __m256i flt_16b = _mm256_packs_epi32(
1893
0
              yy_loadu_256(flt_rowB), yy_loadu_256(flt_rowB + 8));
1894
0
          const __m256i e = calc_proj_err_r0_or_r1_avx2(
1895
0
              d0, s0, flt_16b, xq_coeff, rounding, shift);
1896
0
          sum32_B = _mm256_add_epi32(sum32_B, _mm256_madd_epi16(e, e));
1897
0
        }
1898
        // Row C
1899
0
        {
1900
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat_rowC);
1901
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src_rowC);
1902
0
          const __m256i flt_16b = _mm256_packs_epi32(
1903
0
              yy_loadu_256(flt_rowC), yy_loadu_256(flt_rowC + 8));
1904
0
          const __m256i e = calc_proj_err_r0_or_r1_avx2(
1905
0
              d0, s0, flt_16b, xq_coeff, rounding, shift);
1906
0
          sum32_C = _mm256_add_epi32(sum32_C, _mm256_madd_epi16(e, e));
1907
0
        }
1908
        // Row D
1909
0
        {
1910
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat_rowD);
1911
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src_rowD);
1912
0
          const __m256i flt_16b = _mm256_packs_epi32(
1913
0
              yy_loadu_256(flt_rowD), yy_loadu_256(flt_rowD + 8));
1914
0
          const __m256i e = calc_proj_err_r0_or_r1_avx2(
1915
0
              d0, s0, flt_16b, xq_coeff, rounding, shift);
1916
0
          sum32_D = _mm256_add_epi32(sum32_D, _mm256_madd_epi16(e, e));
1917
0
        }
1918
1919
0
        dat += 4 * dat_stride;
1920
0
        src += 4 * src_stride;
1921
0
        flt += 4 * flt_stride;
1922
0
      }
1923
0
      for (; i < height; ++i) {
1924
0
        const __m256i d0 = load_shuffled_u8_to_epi16(dat);
1925
0
        const __m256i s0 = load_shuffled_u8_to_epi16(src);
1926
0
        const __m256i flt_16b =
1927
0
            _mm256_packs_epi32(yy_loadu_256(flt), yy_loadu_256(flt + 8));
1928
0
        const __m256i e = calc_proj_err_r0_or_r1_avx2(d0, s0, flt_16b, xq_coeff,
1929
0
                                                      rounding, shift);
1930
0
        sum32_A = _mm256_add_epi32(sum32_A, _mm256_madd_epi16(e, e));
1931
1932
0
        dat += dat_stride;
1933
0
        src += src_stride;
1934
0
        flt += flt_stride;
1935
0
      }
1936
0
      __m256i sum32 = _mm256_add_epi32(_mm256_add_epi32(sum32_A, sum32_B),
1937
0
                                       _mm256_add_epi32(sum32_C, sum32_D));
1938
0
      const __m256i sum64_0 =
1939
0
          _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
1940
0
      const __m256i sum64_1 =
1941
0
          _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
1942
0
      sum64 = _mm256_add_epi64(sum64, _mm256_add_epi64(sum64_0, sum64_1));
1943
0
    } else if (width >= 32 && (width % 32 == 0)) {
1944
0
      int rows_per_batch = 4096 / width;
1945
0
      if (rows_per_batch < 1) rows_per_batch = 1;
1946
0
      for (i = 0; i < height;) {
1947
0
        int rows_to_do = height - i;
1948
0
        if (rows_to_do > rows_per_batch) rows_to_do = rows_per_batch;
1949
0
        const int next_i = i + rows_to_do;
1950
0
        __m256i sum32_A = _mm256_setzero_si256();
1951
0
        __m256i sum32_B = _mm256_setzero_si256();
1952
0
        for (; i < next_i; ++i) {
1953
0
          for (j = 0; j <= width - 32; j += 32) {
1954
0
            const __m256i d_A = load_shuffled_u8_to_epi16(dat + j);
1955
0
            const __m256i s_A = load_shuffled_u8_to_epi16(src + j);
1956
0
            const __m256i flt_A = _mm256_packs_epi32(yy_loadu_256(flt + j),
1957
0
                                                     yy_loadu_256(flt + j + 8));
1958
0
            const __m256i e_A = calc_proj_err_r0_or_r1_avx2(
1959
0
                d_A, s_A, flt_A, xq_coeff, rounding, shift);
1960
0
            sum32_A = _mm256_add_epi32(sum32_A, _mm256_madd_epi16(e_A, e_A));
1961
1962
0
            const __m256i d_B = load_shuffled_u8_to_epi16(dat + j + 16);
1963
0
            const __m256i s_B = load_shuffled_u8_to_epi16(src + j + 16);
1964
0
            const __m256i flt_B = _mm256_packs_epi32(
1965
0
                yy_loadu_256(flt + j + 16), yy_loadu_256(flt + j + 24));
1966
0
            const __m256i e_B = calc_proj_err_r0_or_r1_avx2(
1967
0
                d_B, s_B, flt_B, xq_coeff, rounding, shift);
1968
0
            sum32_B = _mm256_add_epi32(sum32_B, _mm256_madd_epi16(e_B, e_B));
1969
0
          }
1970
0
          dat += dat_stride;
1971
0
          src += src_stride;
1972
0
          flt += flt_stride;
1973
0
        }
1974
0
        __m256i sum32 = _mm256_add_epi32(sum32_A, sum32_B);
1975
0
        const __m256i sum64_0 =
1976
0
            _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
1977
0
        const __m256i sum64_1 =
1978
0
            _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
1979
0
        sum64 = _mm256_add_epi64(sum64, _mm256_add_epi64(sum64_0, sum64_1));
1980
0
      }
1981
0
    } else {
1982
      // General fallback
1983
0
      for (i = 0; i < height; ++i) {
1984
0
        __m256i sum32 = _mm256_setzero_si256();
1985
0
        for (j = 0; j <= width - 16; j += 16) {
1986
0
          const __m256i d0 = load_shuffled_u8_to_epi16(dat + j);
1987
0
          const __m256i s0 = load_shuffled_u8_to_epi16(src + j);
1988
0
          const __m256i flt_16b = _mm256_packs_epi32(yy_loadu_256(flt + j),
1989
0
                                                     yy_loadu_256(flt + j + 8));
1990
0
          const __m256i e0 = calc_proj_err_r0_or_r1_avx2(
1991
0
              d0, s0, flt_16b, xq_coeff, rounding, shift);
1992
0
          sum32 = _mm256_add_epi32(sum32, _mm256_madd_epi16(e0, e0));
1993
0
        }
1994
0
        for (k = j; k < width; ++k) {
1995
0
          const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
1996
0
          int32_t v = xq_active * (flt[k] - u);
1997
0
          const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
1998
0
          err += ((int64_t)e * e);
1999
0
        }
2000
0
        dat += dat_stride;
2001
0
        src += src_stride;
2002
0
        flt += flt_stride;
2003
0
        const __m256i sum64_0 =
2004
0
            _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
2005
0
        const __m256i sum64_1 =
2006
0
            _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
2007
0
        sum64 = _mm256_add_epi64(sum64, sum64_0);
2008
0
        sum64 = _mm256_add_epi64(sum64, sum64_1);
2009
0
      }
2010
0
    }
2011
0
  } else {
2012
0
    if (width == 8) {
2013
0
      __m256i sum32 = _mm256_setzero_si256();
2014
0
      const int height_even = height & ~1;
2015
0
      for (i = 0; i < height_even; i += 2) {
2016
0
        const uint8_t *dat_rowB = dat + dat_stride;
2017
0
        const uint8_t *src_rowB = src + src_stride;
2018
2019
0
        const __m128i d_AB =
2020
0
            _mm_unpacklo_epi64(_mm_loadl_epi64((const __m128i *)dat),
2021
0
                               _mm_loadl_epi64((const __m128i *)dat_rowB));
2022
0
        const __m128i s_AB =
2023
0
            _mm_unpacklo_epi64(_mm_loadl_epi64((const __m128i *)src),
2024
0
                               _mm_loadl_epi64((const __m128i *)src_rowB));
2025
0
        const __m256i diff = _mm256_sub_epi16(_mm256_cvtepu8_epi16(d_AB),
2026
0
                                              _mm256_cvtepu8_epi16(s_AB));
2027
0
        sum32 = _mm256_add_epi32(sum32, _mm256_madd_epi16(diff, diff));
2028
0
        dat += 2 * dat_stride;
2029
0
        src += 2 * src_stride;
2030
0
      }
2031
0
      if (i < height) {
2032
0
        const __m128i d_A = _mm_loadl_epi64((const __m128i *)dat);
2033
0
        const __m128i s_A = _mm_loadl_epi64((const __m128i *)src);
2034
0
        const __m256i diff = _mm256_sub_epi16(_mm256_cvtepu8_epi16(d_A),
2035
0
                                              _mm256_cvtepu8_epi16(s_A));
2036
0
        sum32 = _mm256_add_epi32(sum32, _mm256_madd_epi16(diff, diff));
2037
0
      }
2038
0
      const __m256i sum64_0 =
2039
0
          _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
2040
0
      const __m256i sum64_1 =
2041
0
          _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
2042
0
      sum64 = _mm256_add_epi64(sum64_0, sum64_1);
2043
0
    } else if (width >= 32 && (width % 32 == 0)) {
2044
0
      __m256i sum32_A = _mm256_setzero_si256();
2045
0
      __m256i sum32_B = _mm256_setzero_si256();
2046
0
      for (i = 0; i < height; ++i) {
2047
0
        for (j = 0; j <= width - 32; j += 32) {
2048
0
          const __m256i d_A = _mm256_cvtepu8_epi16(xx_loadu_128(dat + j));
2049
0
          const __m256i s_A = _mm256_cvtepu8_epi16(xx_loadu_128(src + j));
2050
0
          const __m256i diff_A = _mm256_sub_epi16(d_A, s_A);
2051
0
          sum32_A =
2052
0
              _mm256_add_epi32(sum32_A, _mm256_madd_epi16(diff_A, diff_A));
2053
2054
0
          const __m256i d_B = _mm256_cvtepu8_epi16(xx_loadu_128(dat + j + 16));
2055
0
          const __m256i s_B = _mm256_cvtepu8_epi16(xx_loadu_128(src + j + 16));
2056
0
          const __m256i diff_B = _mm256_sub_epi16(d_B, s_B);
2057
0
          sum32_B =
2058
0
              _mm256_add_epi32(sum32_B, _mm256_madd_epi16(diff_B, diff_B));
2059
0
        }
2060
0
        dat += dat_stride;
2061
0
        src += src_stride;
2062
0
      }
2063
0
      __m256i sum32 = _mm256_add_epi32(sum32_A, sum32_B);
2064
0
      const __m256i sum64_0 =
2065
0
          _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
2066
0
      const __m256i sum64_1 =
2067
0
          _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
2068
0
      sum64 = _mm256_add_epi64(sum64_0, sum64_1);
2069
0
    } else if (width >= 16) {
2070
0
      __m256i sum32_A = _mm256_setzero_si256();
2071
0
      __m256i sum32_B = _mm256_setzero_si256();
2072
0
      const int height_even = height & ~1;
2073
0
      for (i = 0; i < height_even; i += 2) {
2074
0
        const uint8_t *dat_rowB = dat + dat_stride;
2075
0
        const uint8_t *src_rowB = src + src_stride;
2076
0
        for (j = 0; j <= width - 16; j += 16) {
2077
0
          const __m256i d_A = _mm256_cvtepu8_epi16(xx_loadu_128(dat + j));
2078
0
          const __m256i s_A = _mm256_cvtepu8_epi16(xx_loadu_128(src + j));
2079
0
          const __m256i diff_A = _mm256_sub_epi16(d_A, s_A);
2080
0
          sum32_A =
2081
0
              _mm256_add_epi32(sum32_A, _mm256_madd_epi16(diff_A, diff_A));
2082
2083
0
          const __m256i d_B = _mm256_cvtepu8_epi16(xx_loadu_128(dat_rowB + j));
2084
0
          const __m256i s_B = _mm256_cvtepu8_epi16(xx_loadu_128(src_rowB + j));
2085
0
          const __m256i diff_B = _mm256_sub_epi16(d_B, s_B);
2086
0
          sum32_B =
2087
0
              _mm256_add_epi32(sum32_B, _mm256_madd_epi16(diff_B, diff_B));
2088
0
        }
2089
0
        for (k = j; k < width; ++k) {
2090
0
          const int32_t e_A = (int32_t)dat[k] - src[k];
2091
0
          err += (int64_t)e_A * e_A;
2092
0
          const int32_t e_B = (int32_t)dat_rowB[k] - src_rowB[k];
2093
0
          err += (int64_t)e_B * e_B;
2094
0
        }
2095
0
        dat += 2 * dat_stride;
2096
0
        src += 2 * src_stride;
2097
0
      }
2098
0
      if (i < height) {
2099
0
        for (j = 0; j <= width - 16; j += 16) {
2100
0
          const __m256i d_A = _mm256_cvtepu8_epi16(xx_loadu_128(dat + j));
2101
0
          const __m256i s_A = _mm256_cvtepu8_epi16(xx_loadu_128(src + j));
2102
0
          const __m256i diff_A = _mm256_sub_epi16(d_A, s_A);
2103
0
          sum32_A =
2104
0
              _mm256_add_epi32(sum32_A, _mm256_madd_epi16(diff_A, diff_A));
2105
0
        }
2106
0
        for (k = j; k < width; ++k) {
2107
0
          const int32_t e_A = (int32_t)dat[k] - src[k];
2108
0
          err += (int64_t)e_A * e_A;
2109
0
        }
2110
0
      }
2111
0
      __m256i sum32 = _mm256_add_epi32(sum32_A, sum32_B);
2112
0
      const __m256i sum64_0 =
2113
0
          _mm256_cvtepi32_epi64(_mm256_castsi256_si128(sum32));
2114
0
      const __m256i sum64_1 =
2115
0
          _mm256_cvtepi32_epi64(_mm256_extracti128_si256(sum32, 1));
2116
0
      sum64 = _mm256_add_epi64(sum64_0, sum64_1);
2117
0
    } else {
2118
      // General fallback
2119
0
      for (i = 0; i < height; ++i) {
2120
0
        for (k = 0; k < width; ++k) {
2121
0
          const int32_t e = (int32_t)(dat[k]) - src[k];
2122
0
          err += ((int64_t)e * e);
2123
0
        }
2124
0
        dat += dat_stride;
2125
0
        src += src_stride;
2126
0
      }
2127
0
    }
2128
0
  }
2129
0
  int64_t sum[4];
2130
0
  yy_storeu_256(sum, sum64);
2131
0
  err += sum[0] + sum[1] + sum[2] + sum[3];
2132
0
  return err;
2133
0
}
2134
2135
// When params->r[0] > 0 and params->r[1] > 0. In this case all elements of
2136
// C and H need to be computed.
2137
static inline void calc_proj_params_r0_r1_avx2(
2138
    const uint8_t *src8, int width, int height, int src_stride,
2139
    const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride,
2140
0
    int32_t *flt1, int flt1_stride, int64_t H[2][2], int64_t C[2]) {
2141
0
  const int size = width * height;
2142
0
  const uint8_t *src = src8;
2143
0
  const uint8_t *dat = dat8;
2144
0
  __m256i h00, h01, h11, c0, c1;
2145
0
  const __m256i zero = _mm256_setzero_si256();
2146
0
  h01 = h11 = c0 = c1 = h00 = zero;
2147
2148
0
  for (int i = 0; i < height; ++i) {
2149
0
    for (int j = 0; j < width; j += 8) {
2150
0
      const __m256i u_load = _mm256_cvtepu8_epi32(
2151
0
          _mm_loadl_epi64((__m128i *)(dat + i * dat_stride + j)));
2152
0
      const __m256i s_load = _mm256_cvtepu8_epi32(
2153
0
          _mm_loadl_epi64((__m128i *)(src + i * src_stride + j)));
2154
0
      __m256i f1 = _mm256_loadu_si256((__m256i *)(flt0 + i * flt0_stride + j));
2155
0
      __m256i f2 = _mm256_loadu_si256((__m256i *)(flt1 + i * flt1_stride + j));
2156
0
      __m256i d = _mm256_slli_epi32(u_load, SGRPROJ_RST_BITS);
2157
0
      __m256i s = _mm256_slli_epi32(s_load, SGRPROJ_RST_BITS);
2158
0
      s = _mm256_sub_epi32(s, d);
2159
0
      f1 = _mm256_sub_epi32(f1, d);
2160
0
      f2 = _mm256_sub_epi32(f2, d);
2161
2162
0
      const __m256i h00_even = _mm256_mul_epi32(f1, f1);
2163
0
      const __m256i h00_odd = _mm256_mul_epi32(_mm256_srli_epi64(f1, 32),
2164
0
                                               _mm256_srli_epi64(f1, 32));
2165
0
      h00 = _mm256_add_epi64(h00, h00_even);
2166
0
      h00 = _mm256_add_epi64(h00, h00_odd);
2167
2168
0
      const __m256i h01_even = _mm256_mul_epi32(f1, f2);
2169
0
      const __m256i h01_odd = _mm256_mul_epi32(_mm256_srli_epi64(f1, 32),
2170
0
                                               _mm256_srli_epi64(f2, 32));
2171
0
      h01 = _mm256_add_epi64(h01, h01_even);
2172
0
      h01 = _mm256_add_epi64(h01, h01_odd);
2173
2174
0
      const __m256i h11_even = _mm256_mul_epi32(f2, f2);
2175
0
      const __m256i h11_odd = _mm256_mul_epi32(_mm256_srli_epi64(f2, 32),
2176
0
                                               _mm256_srli_epi64(f2, 32));
2177
0
      h11 = _mm256_add_epi64(h11, h11_even);
2178
0
      h11 = _mm256_add_epi64(h11, h11_odd);
2179
2180
0
      const __m256i c0_even = _mm256_mul_epi32(f1, s);
2181
0
      const __m256i c0_odd =
2182
0
          _mm256_mul_epi32(_mm256_srli_epi64(f1, 32), _mm256_srli_epi64(s, 32));
2183
0
      c0 = _mm256_add_epi64(c0, c0_even);
2184
0
      c0 = _mm256_add_epi64(c0, c0_odd);
2185
2186
0
      const __m256i c1_even = _mm256_mul_epi32(f2, s);
2187
0
      const __m256i c1_odd =
2188
0
          _mm256_mul_epi32(_mm256_srli_epi64(f2, 32), _mm256_srli_epi64(s, 32));
2189
0
      c1 = _mm256_add_epi64(c1, c1_even);
2190
0
      c1 = _mm256_add_epi64(c1, c1_odd);
2191
0
    }
2192
0
  }
2193
2194
0
  __m256i c_low = _mm256_unpacklo_epi64(c0, c1);
2195
0
  const __m256i c_high = _mm256_unpackhi_epi64(c0, c1);
2196
0
  c_low = _mm256_add_epi64(c_low, c_high);
2197
0
  const __m128i c_128bit = _mm_add_epi64(_mm256_extracti128_si256(c_low, 1),
2198
0
                                         _mm256_castsi256_si128(c_low));
2199
2200
0
  __m256i h0x_low = _mm256_unpacklo_epi64(h00, h01);
2201
0
  const __m256i h0x_high = _mm256_unpackhi_epi64(h00, h01);
2202
0
  h0x_low = _mm256_add_epi64(h0x_low, h0x_high);
2203
0
  const __m128i h0x_128bit = _mm_add_epi64(_mm256_extracti128_si256(h0x_low, 1),
2204
0
                                           _mm256_castsi256_si128(h0x_low));
2205
2206
  // Using the symmetric properties of H,  calculations of H[1][0] are not
2207
  // needed.
2208
0
  __m256i h1x_low = _mm256_unpacklo_epi64(zero, h11);
2209
0
  const __m256i h1x_high = _mm256_unpackhi_epi64(zero, h11);
2210
0
  h1x_low = _mm256_add_epi64(h1x_low, h1x_high);
2211
0
  const __m128i h1x_128bit = _mm_add_epi64(_mm256_extracti128_si256(h1x_low, 1),
2212
0
                                           _mm256_castsi256_si128(h1x_low));
2213
2214
0
  xx_storeu_128(C, c_128bit);
2215
0
  xx_storeu_128(H[0], h0x_128bit);
2216
0
  xx_storeu_128(H[1], h1x_128bit);
2217
2218
0
  H[0][0] /= size;
2219
0
  H[0][1] /= size;
2220
0
  H[1][1] /= size;
2221
2222
  // Since H is a symmetric matrix
2223
0
  H[1][0] = H[0][1];
2224
0
  C[0] /= size;
2225
0
  C[1] /= size;
2226
0
}
2227
2228
// When only params->r[0] > 0. In this case only H[0][0] and C[0] are
2229
// non-zero and need to be computed.
2230
static inline void calc_proj_params_r0_avx2(const uint8_t *src8, int width,
2231
                                            int height, int src_stride,
2232
                                            const uint8_t *dat8, int dat_stride,
2233
                                            int32_t *flt0, int flt0_stride,
2234
0
                                            int64_t H[2][2], int64_t C[2]) {
2235
0
  const int size = width * height;
2236
0
  const uint8_t *src = src8;
2237
0
  const uint8_t *dat = dat8;
2238
0
  __m256i h00, c0;
2239
0
  const __m256i zero = _mm256_setzero_si256();
2240
0
  c0 = h00 = zero;
2241
2242
0
  for (int i = 0; i < height; ++i) {
2243
0
    for (int j = 0; j < width; j += 8) {
2244
0
      const __m256i u_load = _mm256_cvtepu8_epi32(
2245
0
          _mm_loadl_epi64((__m128i *)(dat + i * dat_stride + j)));
2246
0
      const __m256i s_load = _mm256_cvtepu8_epi32(
2247
0
          _mm_loadl_epi64((__m128i *)(src + i * src_stride + j)));
2248
0
      __m256i f1 = _mm256_loadu_si256((__m256i *)(flt0 + i * flt0_stride + j));
2249
0
      __m256i d = _mm256_slli_epi32(u_load, SGRPROJ_RST_BITS);
2250
0
      __m256i s = _mm256_slli_epi32(s_load, SGRPROJ_RST_BITS);
2251
0
      s = _mm256_sub_epi32(s, d);
2252
0
      f1 = _mm256_sub_epi32(f1, d);
2253
2254
0
      const __m256i h00_even = _mm256_mul_epi32(f1, f1);
2255
0
      const __m256i h00_odd = _mm256_mul_epi32(_mm256_srli_epi64(f1, 32),
2256
0
                                               _mm256_srli_epi64(f1, 32));
2257
0
      h00 = _mm256_add_epi64(h00, h00_even);
2258
0
      h00 = _mm256_add_epi64(h00, h00_odd);
2259
2260
0
      const __m256i c0_even = _mm256_mul_epi32(f1, s);
2261
0
      const __m256i c0_odd =
2262
0
          _mm256_mul_epi32(_mm256_srli_epi64(f1, 32), _mm256_srli_epi64(s, 32));
2263
0
      c0 = _mm256_add_epi64(c0, c0_even);
2264
0
      c0 = _mm256_add_epi64(c0, c0_odd);
2265
0
    }
2266
0
  }
2267
0
  const __m128i h00_128bit = _mm_add_epi64(_mm256_extracti128_si256(h00, 1),
2268
0
                                           _mm256_castsi256_si128(h00));
2269
0
  const __m128i h00_val =
2270
0
      _mm_add_epi64(h00_128bit, _mm_srli_si128(h00_128bit, 8));
2271
2272
0
  const __m128i c0_128bit = _mm_add_epi64(_mm256_extracti128_si256(c0, 1),
2273
0
                                          _mm256_castsi256_si128(c0));
2274
0
  const __m128i c0_val = _mm_add_epi64(c0_128bit, _mm_srli_si128(c0_128bit, 8));
2275
2276
0
  const __m128i c = _mm_unpacklo_epi64(c0_val, _mm256_castsi256_si128(zero));
2277
0
  const __m128i h0x = _mm_unpacklo_epi64(h00_val, _mm256_castsi256_si128(zero));
2278
2279
0
  xx_storeu_128(C, c);
2280
0
  xx_storeu_128(H[0], h0x);
2281
2282
0
  H[0][0] /= size;
2283
0
  C[0] /= size;
2284
0
}
2285
2286
// When only params->r[1] > 0. In this case only H[1][1] and C[1] are
2287
// non-zero and need to be computed.
2288
static inline void calc_proj_params_r1_avx2(const uint8_t *src8, int width,
2289
                                            int height, int src_stride,
2290
                                            const uint8_t *dat8, int dat_stride,
2291
                                            int32_t *flt1, int flt1_stride,
2292
0
                                            int64_t H[2][2], int64_t C[2]) {
2293
0
  const int size = width * height;
2294
0
  const uint8_t *src = src8;
2295
0
  const uint8_t *dat = dat8;
2296
0
  __m256i h11, c1;
2297
0
  const __m256i zero = _mm256_setzero_si256();
2298
0
  c1 = h11 = zero;
2299
2300
0
  for (int i = 0; i < height; ++i) {
2301
0
    for (int j = 0; j < width; j += 8) {
2302
0
      const __m256i u_load = _mm256_cvtepu8_epi32(
2303
0
          _mm_loadl_epi64((__m128i *)(dat + i * dat_stride + j)));
2304
0
      const __m256i s_load = _mm256_cvtepu8_epi32(
2305
0
          _mm_loadl_epi64((__m128i *)(src + i * src_stride + j)));
2306
0
      __m256i f2 = _mm256_loadu_si256((__m256i *)(flt1 + i * flt1_stride + j));
2307
0
      __m256i d = _mm256_slli_epi32(u_load, SGRPROJ_RST_BITS);
2308
0
      __m256i s = _mm256_slli_epi32(s_load, SGRPROJ_RST_BITS);
2309
0
      s = _mm256_sub_epi32(s, d);
2310
0
      f2 = _mm256_sub_epi32(f2, d);
2311
2312
0
      const __m256i h11_even = _mm256_mul_epi32(f2, f2);
2313
0
      const __m256i h11_odd = _mm256_mul_epi32(_mm256_srli_epi64(f2, 32),
2314
0
                                               _mm256_srli_epi64(f2, 32));
2315
0
      h11 = _mm256_add_epi64(h11, h11_even);
2316
0
      h11 = _mm256_add_epi64(h11, h11_odd);
2317
2318
0
      const __m256i c1_even = _mm256_mul_epi32(f2, s);
2319
0
      const __m256i c1_odd =
2320
0
          _mm256_mul_epi32(_mm256_srli_epi64(f2, 32), _mm256_srli_epi64(s, 32));
2321
0
      c1 = _mm256_add_epi64(c1, c1_even);
2322
0
      c1 = _mm256_add_epi64(c1, c1_odd);
2323
0
    }
2324
0
  }
2325
2326
0
  const __m128i h11_128bit = _mm_add_epi64(_mm256_extracti128_si256(h11, 1),
2327
0
                                           _mm256_castsi256_si128(h11));
2328
0
  const __m128i h11_val =
2329
0
      _mm_add_epi64(h11_128bit, _mm_srli_si128(h11_128bit, 8));
2330
2331
0
  const __m128i c1_128bit = _mm_add_epi64(_mm256_extracti128_si256(c1, 1),
2332
0
                                          _mm256_castsi256_si128(c1));
2333
0
  const __m128i c1_val = _mm_add_epi64(c1_128bit, _mm_srli_si128(c1_128bit, 8));
2334
2335
0
  const __m128i c = _mm_unpacklo_epi64(_mm256_castsi256_si128(zero), c1_val);
2336
0
  const __m128i h1x = _mm_unpacklo_epi64(_mm256_castsi256_si128(zero), h11_val);
2337
2338
0
  xx_storeu_128(C, c);
2339
0
  xx_storeu_128(H[1], h1x);
2340
2341
0
  H[1][1] /= size;
2342
0
  C[1] /= size;
2343
0
}
2344
2345
// AVX2 variant of av1_calc_proj_params_c.
2346
void av1_calc_proj_params_avx2(const uint8_t *src8, int width, int height,
2347
                               int src_stride, const uint8_t *dat8,
2348
                               int dat_stride, int32_t *flt0, int flt0_stride,
2349
                               int32_t *flt1, int flt1_stride, int64_t H[2][2],
2350
0
                               int64_t C[2], const sgr_params_type *params) {
2351
0
  if ((params->r[0] > 0) && (params->r[1] > 0)) {
2352
0
    calc_proj_params_r0_r1_avx2(src8, width, height, src_stride, dat8,
2353
0
                                dat_stride, flt0, flt0_stride, flt1,
2354
0
                                flt1_stride, H, C);
2355
0
  } else if (params->r[0] > 0) {
2356
0
    calc_proj_params_r0_avx2(src8, width, height, src_stride, dat8, dat_stride,
2357
0
                             flt0, flt0_stride, H, C);
2358
0
  } else if (params->r[1] > 0) {
2359
0
    calc_proj_params_r1_avx2(src8, width, height, src_stride, dat8, dat_stride,
2360
0
                             flt1, flt1_stride, H, C);
2361
0
  }
2362
0
}
2363
2364
#if CONFIG_AV1_HIGHBITDEPTH
2365
static inline void calc_proj_params_r0_r1_high_bd_avx2(
2366
    const uint8_t *src8, int width, int height, int src_stride,
2367
    const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride,
2368
0
    int32_t *flt1, int flt1_stride, int64_t H[2][2], int64_t C[2]) {
2369
0
  const int size = width * height;
2370
0
  const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
2371
0
  const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
2372
0
  __m256i h00, h01, h11, c0, c1;
2373
0
  const __m256i zero = _mm256_setzero_si256();
2374
0
  h01 = h11 = c0 = c1 = h00 = zero;
2375
2376
0
  for (int i = 0; i < height; ++i) {
2377
0
    for (int j = 0; j < width; j += 8) {
2378
0
      const __m256i u_load = _mm256_cvtepu16_epi32(
2379
0
          _mm_load_si128((__m128i *)(dat + i * dat_stride + j)));
2380
0
      const __m256i s_load = _mm256_cvtepu16_epi32(
2381
0
          _mm_load_si128((__m128i *)(src + i * src_stride + j)));
2382
0
      __m256i f1 = _mm256_loadu_si256((__m256i *)(flt0 + i * flt0_stride + j));
2383
0
      __m256i f2 = _mm256_loadu_si256((__m256i *)(flt1 + i * flt1_stride + j));
2384
0
      __m256i d = _mm256_slli_epi32(u_load, SGRPROJ_RST_BITS);
2385
0
      __m256i s = _mm256_slli_epi32(s_load, SGRPROJ_RST_BITS);
2386
0
      s = _mm256_sub_epi32(s, d);
2387
0
      f1 = _mm256_sub_epi32(f1, d);
2388
0
      f2 = _mm256_sub_epi32(f2, d);
2389
2390
0
      const __m256i h00_even = _mm256_mul_epi32(f1, f1);
2391
0
      const __m256i h00_odd = _mm256_mul_epi32(_mm256_srli_epi64(f1, 32),
2392
0
                                               _mm256_srli_epi64(f1, 32));
2393
0
      h00 = _mm256_add_epi64(h00, h00_even);
2394
0
      h00 = _mm256_add_epi64(h00, h00_odd);
2395
2396
0
      const __m256i h01_even = _mm256_mul_epi32(f1, f2);
2397
0
      const __m256i h01_odd = _mm256_mul_epi32(_mm256_srli_epi64(f1, 32),
2398
0
                                               _mm256_srli_epi64(f2, 32));
2399
0
      h01 = _mm256_add_epi64(h01, h01_even);
2400
0
      h01 = _mm256_add_epi64(h01, h01_odd);
2401
2402
0
      const __m256i h11_even = _mm256_mul_epi32(f2, f2);
2403
0
      const __m256i h11_odd = _mm256_mul_epi32(_mm256_srli_epi64(f2, 32),
2404
0
                                               _mm256_srli_epi64(f2, 32));
2405
0
      h11 = _mm256_add_epi64(h11, h11_even);
2406
0
      h11 = _mm256_add_epi64(h11, h11_odd);
2407
2408
0
      const __m256i c0_even = _mm256_mul_epi32(f1, s);
2409
0
      const __m256i c0_odd =
2410
0
          _mm256_mul_epi32(_mm256_srli_epi64(f1, 32), _mm256_srli_epi64(s, 32));
2411
0
      c0 = _mm256_add_epi64(c0, c0_even);
2412
0
      c0 = _mm256_add_epi64(c0, c0_odd);
2413
2414
0
      const __m256i c1_even = _mm256_mul_epi32(f2, s);
2415
0
      const __m256i c1_odd =
2416
0
          _mm256_mul_epi32(_mm256_srli_epi64(f2, 32), _mm256_srli_epi64(s, 32));
2417
0
      c1 = _mm256_add_epi64(c1, c1_even);
2418
0
      c1 = _mm256_add_epi64(c1, c1_odd);
2419
0
    }
2420
0
  }
2421
2422
0
  __m256i c_low = _mm256_unpacklo_epi64(c0, c1);
2423
0
  const __m256i c_high = _mm256_unpackhi_epi64(c0, c1);
2424
0
  c_low = _mm256_add_epi64(c_low, c_high);
2425
0
  const __m128i c_128bit = _mm_add_epi64(_mm256_extracti128_si256(c_low, 1),
2426
0
                                         _mm256_castsi256_si128(c_low));
2427
2428
0
  __m256i h0x_low = _mm256_unpacklo_epi64(h00, h01);
2429
0
  const __m256i h0x_high = _mm256_unpackhi_epi64(h00, h01);
2430
0
  h0x_low = _mm256_add_epi64(h0x_low, h0x_high);
2431
0
  const __m128i h0x_128bit = _mm_add_epi64(_mm256_extracti128_si256(h0x_low, 1),
2432
0
                                           _mm256_castsi256_si128(h0x_low));
2433
2434
  // Using the symmetric properties of H,  calculations of H[1][0] are not
2435
  // needed.
2436
0
  __m256i h1x_low = _mm256_unpacklo_epi64(zero, h11);
2437
0
  const __m256i h1x_high = _mm256_unpackhi_epi64(zero, h11);
2438
0
  h1x_low = _mm256_add_epi64(h1x_low, h1x_high);
2439
0
  const __m128i h1x_128bit = _mm_add_epi64(_mm256_extracti128_si256(h1x_low, 1),
2440
0
                                           _mm256_castsi256_si128(h1x_low));
2441
2442
0
  xx_storeu_128(C, c_128bit);
2443
0
  xx_storeu_128(H[0], h0x_128bit);
2444
0
  xx_storeu_128(H[1], h1x_128bit);
2445
2446
0
  H[0][0] /= size;
2447
0
  H[0][1] /= size;
2448
0
  H[1][1] /= size;
2449
2450
  // Since H is a symmetric matrix
2451
0
  H[1][0] = H[0][1];
2452
0
  C[0] /= size;
2453
0
  C[1] /= size;
2454
0
}
2455
2456
static inline void calc_proj_params_r0_high_bd_avx2(
2457
    const uint8_t *src8, int width, int height, int src_stride,
2458
    const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride,
2459
0
    int64_t H[2][2], int64_t C[2]) {
2460
0
  const int size = width * height;
2461
0
  const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
2462
0
  const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
2463
0
  __m256i h00, c0;
2464
0
  const __m256i zero = _mm256_setzero_si256();
2465
0
  c0 = h00 = zero;
2466
2467
0
  for (int i = 0; i < height; ++i) {
2468
0
    for (int j = 0; j < width; j += 8) {
2469
0
      const __m256i u_load = _mm256_cvtepu16_epi32(
2470
0
          _mm_load_si128((__m128i *)(dat + i * dat_stride + j)));
2471
0
      const __m256i s_load = _mm256_cvtepu16_epi32(
2472
0
          _mm_load_si128((__m128i *)(src + i * src_stride + j)));
2473
0
      __m256i f1 = _mm256_loadu_si256((__m256i *)(flt0 + i * flt0_stride + j));
2474
0
      __m256i d = _mm256_slli_epi32(u_load, SGRPROJ_RST_BITS);
2475
0
      __m256i s = _mm256_slli_epi32(s_load, SGRPROJ_RST_BITS);
2476
0
      s = _mm256_sub_epi32(s, d);
2477
0
      f1 = _mm256_sub_epi32(f1, d);
2478
2479
0
      const __m256i h00_even = _mm256_mul_epi32(f1, f1);
2480
0
      const __m256i h00_odd = _mm256_mul_epi32(_mm256_srli_epi64(f1, 32),
2481
0
                                               _mm256_srli_epi64(f1, 32));
2482
0
      h00 = _mm256_add_epi64(h00, h00_even);
2483
0
      h00 = _mm256_add_epi64(h00, h00_odd);
2484
2485
0
      const __m256i c0_even = _mm256_mul_epi32(f1, s);
2486
0
      const __m256i c0_odd =
2487
0
          _mm256_mul_epi32(_mm256_srli_epi64(f1, 32), _mm256_srli_epi64(s, 32));
2488
0
      c0 = _mm256_add_epi64(c0, c0_even);
2489
0
      c0 = _mm256_add_epi64(c0, c0_odd);
2490
0
    }
2491
0
  }
2492
0
  const __m128i h00_128bit = _mm_add_epi64(_mm256_extracti128_si256(h00, 1),
2493
0
                                           _mm256_castsi256_si128(h00));
2494
0
  const __m128i h00_val =
2495
0
      _mm_add_epi64(h00_128bit, _mm_srli_si128(h00_128bit, 8));
2496
2497
0
  const __m128i c0_128bit = _mm_add_epi64(_mm256_extracti128_si256(c0, 1),
2498
0
                                          _mm256_castsi256_si128(c0));
2499
0
  const __m128i c0_val = _mm_add_epi64(c0_128bit, _mm_srli_si128(c0_128bit, 8));
2500
2501
0
  const __m128i c = _mm_unpacklo_epi64(c0_val, _mm256_castsi256_si128(zero));
2502
0
  const __m128i h0x = _mm_unpacklo_epi64(h00_val, _mm256_castsi256_si128(zero));
2503
2504
0
  xx_storeu_128(C, c);
2505
0
  xx_storeu_128(H[0], h0x);
2506
2507
0
  H[0][0] /= size;
2508
0
  C[0] /= size;
2509
0
}
2510
2511
static inline void calc_proj_params_r1_high_bd_avx2(
2512
    const uint8_t *src8, int width, int height, int src_stride,
2513
    const uint8_t *dat8, int dat_stride, int32_t *flt1, int flt1_stride,
2514
0
    int64_t H[2][2], int64_t C[2]) {
2515
0
  const int size = width * height;
2516
0
  const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
2517
0
  const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
2518
0
  __m256i h11, c1;
2519
0
  const __m256i zero = _mm256_setzero_si256();
2520
0
  c1 = h11 = zero;
2521
2522
0
  for (int i = 0; i < height; ++i) {
2523
0
    for (int j = 0; j < width; j += 8) {
2524
0
      const __m256i u_load = _mm256_cvtepu16_epi32(
2525
0
          _mm_load_si128((__m128i *)(dat + i * dat_stride + j)));
2526
0
      const __m256i s_load = _mm256_cvtepu16_epi32(
2527
0
          _mm_load_si128((__m128i *)(src + i * src_stride + j)));
2528
0
      __m256i f2 = _mm256_loadu_si256((__m256i *)(flt1 + i * flt1_stride + j));
2529
0
      __m256i d = _mm256_slli_epi32(u_load, SGRPROJ_RST_BITS);
2530
0
      __m256i s = _mm256_slli_epi32(s_load, SGRPROJ_RST_BITS);
2531
0
      s = _mm256_sub_epi32(s, d);
2532
0
      f2 = _mm256_sub_epi32(f2, d);
2533
2534
0
      const __m256i h11_even = _mm256_mul_epi32(f2, f2);
2535
0
      const __m256i h11_odd = _mm256_mul_epi32(_mm256_srli_epi64(f2, 32),
2536
0
                                               _mm256_srli_epi64(f2, 32));
2537
0
      h11 = _mm256_add_epi64(h11, h11_even);
2538
0
      h11 = _mm256_add_epi64(h11, h11_odd);
2539
2540
0
      const __m256i c1_even = _mm256_mul_epi32(f2, s);
2541
0
      const __m256i c1_odd =
2542
0
          _mm256_mul_epi32(_mm256_srli_epi64(f2, 32), _mm256_srli_epi64(s, 32));
2543
0
      c1 = _mm256_add_epi64(c1, c1_even);
2544
0
      c1 = _mm256_add_epi64(c1, c1_odd);
2545
0
    }
2546
0
  }
2547
2548
0
  const __m128i h11_128bit = _mm_add_epi64(_mm256_extracti128_si256(h11, 1),
2549
0
                                           _mm256_castsi256_si128(h11));
2550
0
  const __m128i h11_val =
2551
0
      _mm_add_epi64(h11_128bit, _mm_srli_si128(h11_128bit, 8));
2552
2553
0
  const __m128i c1_128bit = _mm_add_epi64(_mm256_extracti128_si256(c1, 1),
2554
0
                                          _mm256_castsi256_si128(c1));
2555
0
  const __m128i c1_val = _mm_add_epi64(c1_128bit, _mm_srli_si128(c1_128bit, 8));
2556
2557
0
  const __m128i c = _mm_unpacklo_epi64(_mm256_castsi256_si128(zero), c1_val);
2558
0
  const __m128i h1x = _mm_unpacklo_epi64(_mm256_castsi256_si128(zero), h11_val);
2559
2560
0
  xx_storeu_128(C, c);
2561
0
  xx_storeu_128(H[1], h1x);
2562
2563
0
  H[1][1] /= size;
2564
0
  C[1] /= size;
2565
0
}
2566
2567
// AVX2 variant of av1_calc_proj_params_high_bd_c.
2568
void av1_calc_proj_params_high_bd_avx2(const uint8_t *src8, int width,
2569
                                       int height, int src_stride,
2570
                                       const uint8_t *dat8, int dat_stride,
2571
                                       int32_t *flt0, int flt0_stride,
2572
                                       int32_t *flt1, int flt1_stride,
2573
                                       int64_t H[2][2], int64_t C[2],
2574
0
                                       const sgr_params_type *params) {
2575
0
  if ((params->r[0] > 0) && (params->r[1] > 0)) {
2576
0
    calc_proj_params_r0_r1_high_bd_avx2(src8, width, height, src_stride, dat8,
2577
0
                                        dat_stride, flt0, flt0_stride, flt1,
2578
0
                                        flt1_stride, H, C);
2579
0
  } else if (params->r[0] > 0) {
2580
0
    calc_proj_params_r0_high_bd_avx2(src8, width, height, src_stride, dat8,
2581
0
                                     dat_stride, flt0, flt0_stride, H, C);
2582
0
  } else if (params->r[1] > 0) {
2583
0
    calc_proj_params_r1_high_bd_avx2(src8, width, height, src_stride, dat8,
2584
0
                                     dat_stride, flt1, flt1_stride, H, C);
2585
0
  }
2586
0
}
2587
2588
int64_t av1_highbd_pixel_proj_error_avx2(
2589
    const uint8_t *src8, int width, int height, int src_stride,
2590
    const uint8_t *dat8, int dat_stride, int32_t *flt0, int flt0_stride,
2591
0
    int32_t *flt1, int flt1_stride, int xq[2], const sgr_params_type *params) {
2592
0
  int i, j, k;
2593
0
  const int32_t shift = SGRPROJ_RST_BITS + SGRPROJ_PRJ_BITS;
2594
0
  const __m256i rounding = _mm256_set1_epi32(1 << (shift - 1));
2595
0
  __m256i sum64 = _mm256_setzero_si256();
2596
0
  const uint16_t *src = CONVERT_TO_SHORTPTR(src8);
2597
0
  const uint16_t *dat = CONVERT_TO_SHORTPTR(dat8);
2598
0
  int64_t err = 0;
2599
0
  if (params->r[0] > 0 && params->r[1] > 0) {  // Both filters are enabled
2600
0
    const __m256i xq0 = _mm256_set1_epi32(xq[0]);
2601
0
    const __m256i xq1 = _mm256_set1_epi32(xq[1]);
2602
0
    for (i = 0; i < height; ++i) {
2603
0
      __m256i sum32 = _mm256_setzero_si256();
2604
0
      for (j = 0; j <= width - 16; j += 16) {  // Process 16 pixels at a time
2605
        // Load 16 pixels each from source image and corrupted image
2606
0
        const __m256i s0 = yy_loadu_256(src + j);
2607
0
        const __m256i d0 = yy_loadu_256(dat + j);
2608
        // s0 = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0] as u16 (indices)
2609
2610
        // Shift-up each pixel to match filtered image scaling
2611
0
        const __m256i u0 = _mm256_slli_epi16(d0, SGRPROJ_RST_BITS);
2612
2613
        // Split u0 into two halves and pad each from u16 to i32
2614
0
        const __m256i u0l = _mm256_cvtepu16_epi32(_mm256_castsi256_si128(u0));
2615
0
        const __m256i u0h =
2616
0
            _mm256_cvtepu16_epi32(_mm256_extracti128_si256(u0, 1));
2617
        // u0h, u0l = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as u32
2618
2619
        // Load 16 pixels from each filtered image
2620
0
        const __m256i flt0l = yy_loadu_256(flt0 + j);
2621
0
        const __m256i flt0h = yy_loadu_256(flt0 + j + 8);
2622
0
        const __m256i flt1l = yy_loadu_256(flt1 + j);
2623
0
        const __m256i flt1h = yy_loadu_256(flt1 + j + 8);
2624
        // flt?l, flt?h = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as u32
2625
2626
        // Subtract shifted corrupt image from each filtered image
2627
0
        const __m256i flt0l_subu = _mm256_sub_epi32(flt0l, u0l);
2628
0
        const __m256i flt0h_subu = _mm256_sub_epi32(flt0h, u0h);
2629
0
        const __m256i flt1l_subu = _mm256_sub_epi32(flt1l, u0l);
2630
0
        const __m256i flt1h_subu = _mm256_sub_epi32(flt1h, u0h);
2631
2632
        // Multiply basis vectors by appropriate coefficients
2633
0
        const __m256i v0l = _mm256_mullo_epi32(flt0l_subu, xq0);
2634
0
        const __m256i v0h = _mm256_mullo_epi32(flt0h_subu, xq0);
2635
0
        const __m256i v1l = _mm256_mullo_epi32(flt1l_subu, xq1);
2636
0
        const __m256i v1h = _mm256_mullo_epi32(flt1h_subu, xq1);
2637
2638
        // Add together the contributions from the two basis vectors
2639
0
        const __m256i vl = _mm256_add_epi32(v0l, v1l);
2640
0
        const __m256i vh = _mm256_add_epi32(v0h, v1h);
2641
2642
        // Right-shift v with appropriate rounding
2643
0
        const __m256i vrl =
2644
0
            _mm256_srai_epi32(_mm256_add_epi32(vl, rounding), shift);
2645
0
        const __m256i vrh =
2646
0
            _mm256_srai_epi32(_mm256_add_epi32(vh, rounding), shift);
2647
        // vrh, vrl = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0]
2648
2649
        // Saturate each i32 to an i16 then combine both halves
2650
        // The permute (control=[3 1 2 0]) fixes weird ordering from AVX lanes
2651
0
        const __m256i vr =
2652
0
            _mm256_permute4x64_epi64(_mm256_packs_epi32(vrl, vrh), 0xd8);
2653
        // intermediate = [15 14 13 12 7 6 5 4] [11 10 9 8 3 2 1 0]
2654
        // vr = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0]
2655
2656
        // Add twin-subspace-sgr-filter to corrupt image then subtract source
2657
0
        const __m256i e0 = _mm256_sub_epi16(_mm256_add_epi16(vr, d0), s0);
2658
2659
        // Calculate squared error and add adjacent values
2660
0
        const __m256i err0 = _mm256_madd_epi16(e0, e0);
2661
2662
0
        sum32 = _mm256_add_epi32(sum32, err0);
2663
0
      }
2664
2665
0
      const __m256i sum32l =
2666
0
          _mm256_cvtepu32_epi64(_mm256_castsi256_si128(sum32));
2667
0
      sum64 = _mm256_add_epi64(sum64, sum32l);
2668
0
      const __m256i sum32h =
2669
0
          _mm256_cvtepu32_epi64(_mm256_extracti128_si256(sum32, 1));
2670
0
      sum64 = _mm256_add_epi64(sum64, sum32h);
2671
2672
      // Process remaining pixels in this row (modulo 16)
2673
0
      for (k = j; k < width; ++k) {
2674
0
        const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
2675
0
        int32_t v = xq[0] * (flt0[k] - u) + xq[1] * (flt1[k] - u);
2676
0
        const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
2677
0
        err += ((int64_t)e * e);
2678
0
      }
2679
0
      dat += dat_stride;
2680
0
      src += src_stride;
2681
0
      flt0 += flt0_stride;
2682
0
      flt1 += flt1_stride;
2683
0
    }
2684
0
  } else if (params->r[0] > 0 || params->r[1] > 0) {  // Only one filter enabled
2685
0
    const int32_t xq_on = (params->r[0] > 0) ? xq[0] : xq[1];
2686
0
    const __m256i xq_active = _mm256_set1_epi32(xq_on);
2687
0
    const __m256i xq_inactive =
2688
0
        _mm256_set1_epi32(-xq_on * (1 << SGRPROJ_RST_BITS));
2689
0
    const int32_t *flt = (params->r[0] > 0) ? flt0 : flt1;
2690
0
    const int flt_stride = (params->r[0] > 0) ? flt0_stride : flt1_stride;
2691
0
    for (i = 0; i < height; ++i) {
2692
0
      __m256i sum32 = _mm256_setzero_si256();
2693
0
      for (j = 0; j <= width - 16; j += 16) {
2694
        // Load 16 pixels from source image
2695
0
        const __m256i s0 = yy_loadu_256(src + j);
2696
        // s0 = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0] as u16
2697
2698
        // Load 16 pixels from corrupted image and pad each u16 to i32
2699
0
        const __m256i d0 = yy_loadu_256(dat + j);
2700
0
        const __m256i d0h =
2701
0
            _mm256_cvtepu16_epi32(_mm256_extracti128_si256(d0, 1));
2702
0
        const __m256i d0l = _mm256_cvtepu16_epi32(_mm256_castsi256_si128(d0));
2703
        // d0 = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0] as u16
2704
        // d0h, d0l = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as i32
2705
2706
        // Load 16 pixels from the filtered image
2707
0
        const __m256i flth = yy_loadu_256(flt + j + 8);
2708
0
        const __m256i fltl = yy_loadu_256(flt + j);
2709
        // flth, fltl = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as i32
2710
2711
0
        const __m256i flth_xq = _mm256_mullo_epi32(flth, xq_active);
2712
0
        const __m256i fltl_xq = _mm256_mullo_epi32(fltl, xq_active);
2713
0
        const __m256i d0h_xq = _mm256_mullo_epi32(d0h, xq_inactive);
2714
0
        const __m256i d0l_xq = _mm256_mullo_epi32(d0l, xq_inactive);
2715
2716
0
        const __m256i vh = _mm256_add_epi32(flth_xq, d0h_xq);
2717
0
        const __m256i vl = _mm256_add_epi32(fltl_xq, d0l_xq);
2718
2719
        // Shift this down with appropriate rounding
2720
0
        const __m256i vrh =
2721
0
            _mm256_srai_epi32(_mm256_add_epi32(vh, rounding), shift);
2722
0
        const __m256i vrl =
2723
0
            _mm256_srai_epi32(_mm256_add_epi32(vl, rounding), shift);
2724
        // vrh, vrl = [15 14 13 12] [11 10 9 8], [7 6 5 4] [3 2 1 0] as i32
2725
2726
        // Saturate each i32 to an i16 then combine both halves
2727
        // The permute (control=[3 1 2 0]) fixes weird ordering from AVX lanes
2728
0
        const __m256i vr =
2729
0
            _mm256_permute4x64_epi64(_mm256_packs_epi32(vrl, vrh), 0xd8);
2730
        // intermediate = [15 14 13 12 7 6 5 4] [11 10 9 8 3 2 1 0] as u16
2731
        // vr = [15 14 13 12 11 10 9 8] [7 6 5 4 3 2 1 0] as u16
2732
2733
        // Subtract twin-subspace-sgr filtered from source image to get error
2734
0
        const __m256i e0 = _mm256_sub_epi16(_mm256_add_epi16(vr, d0), s0);
2735
2736
        // Calculate squared error and add adjacent values
2737
0
        const __m256i err0 = _mm256_madd_epi16(e0, e0);
2738
2739
0
        sum32 = _mm256_add_epi32(sum32, err0);
2740
0
      }
2741
2742
0
      const __m256i sum32l =
2743
0
          _mm256_cvtepu32_epi64(_mm256_castsi256_si128(sum32));
2744
0
      sum64 = _mm256_add_epi64(sum64, sum32l);
2745
0
      const __m256i sum32h =
2746
0
          _mm256_cvtepu32_epi64(_mm256_extracti128_si256(sum32, 1));
2747
0
      sum64 = _mm256_add_epi64(sum64, sum32h);
2748
2749
      // Process remaining pixels in this row (modulo 16)
2750
0
      for (k = j; k < width; ++k) {
2751
0
        const int32_t u = (int32_t)(dat[k] << SGRPROJ_RST_BITS);
2752
0
        int32_t v = xq_on * (flt[k] - u);
2753
0
        const int32_t e = ROUND_POWER_OF_TWO(v, shift) + dat[k] - src[k];
2754
0
        err += ((int64_t)e * e);
2755
0
      }
2756
0
      dat += dat_stride;
2757
0
      src += src_stride;
2758
0
      flt += flt_stride;
2759
0
    }
2760
0
  } else {  // Neither filter is enabled
2761
0
    for (i = 0; i < height; ++i) {
2762
0
      __m256i sum32 = _mm256_setzero_si256();
2763
0
      for (j = 0; j <= width - 32; j += 32) {
2764
        // Load 2x16 u16 from source image
2765
0
        const __m256i s0l = yy_loadu_256(src + j);
2766
0
        const __m256i s0h = yy_loadu_256(src + j + 16);
2767
2768
        // Load 2x16 u16 from corrupted image
2769
0
        const __m256i d0l = yy_loadu_256(dat + j);
2770
0
        const __m256i d0h = yy_loadu_256(dat + j + 16);
2771
2772
        // Subtract corrupted image from source image
2773
0
        const __m256i diffl = _mm256_sub_epi16(d0l, s0l);
2774
0
        const __m256i diffh = _mm256_sub_epi16(d0h, s0h);
2775
2776
        // Square error and add adjacent values
2777
0
        const __m256i err0l = _mm256_madd_epi16(diffl, diffl);
2778
0
        const __m256i err0h = _mm256_madd_epi16(diffh, diffh);
2779
2780
0
        sum32 = _mm256_add_epi32(sum32, err0l);
2781
0
        sum32 = _mm256_add_epi32(sum32, err0h);
2782
0
      }
2783
2784
0
      const __m256i sum32l =
2785
0
          _mm256_cvtepu32_epi64(_mm256_castsi256_si128(sum32));
2786
0
      sum64 = _mm256_add_epi64(sum64, sum32l);
2787
0
      const __m256i sum32h =
2788
0
          _mm256_cvtepu32_epi64(_mm256_extracti128_si256(sum32, 1));
2789
0
      sum64 = _mm256_add_epi64(sum64, sum32h);
2790
2791
      // Process remaining pixels (modulu 16)
2792
0
      for (k = j; k < width; ++k) {
2793
0
        const int32_t e = (int32_t)(dat[k]) - src[k];
2794
0
        err += ((int64_t)e * e);
2795
0
      }
2796
0
      dat += dat_stride;
2797
0
      src += src_stride;
2798
0
    }
2799
0
  }
2800
2801
  // Sum 4 values from sum64l and sum64h into err
2802
0
  int64_t sum[4];
2803
0
  yy_storeu_256(sum, sum64);
2804
0
  err += sum[0] + sum[1] + sum[2] + sum[3];
2805
0
  return err;
2806
0
}
2807
#endif  // CONFIG_AV1_HIGHBITDEPTH