Coverage Report

Created: 2026-09-07 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/aom/aom_dsp/x86/adaptive_quantize_avx2.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2019, 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>
13
#include "config/aom_dsp_rtcd.h"
14
#include "aom/aom_integer.h"
15
#include "aom_dsp/quantize.h"
16
#include "aom_dsp/x86/quantize_x86.h"
17
18
static inline void load_b_values_avx2(const int16_t *zbin_ptr, __m256i *zbin,
19
                                      const int16_t *round_ptr, __m256i *round,
20
                                      const int16_t *quant_ptr, __m256i *quant,
21
                                      const int16_t *dequant_ptr,
22
                                      __m256i *dequant,
23
                                      const int16_t *shift_ptr,
24
0
                                      __m256i *shift) {
25
0
  *zbin = _mm256_castsi128_si256(_mm_load_si128((const __m128i *)zbin_ptr));
26
0
  *zbin = _mm256_permute4x64_epi64(*zbin, 0x54);
27
0
  *zbin = _mm256_sub_epi16(*zbin, _mm256_set1_epi16(1));
28
0
  *round = _mm256_castsi128_si256(_mm_load_si128((const __m128i *)round_ptr));
29
0
  *round = _mm256_permute4x64_epi64(*round, 0x54);
30
0
  *quant = _mm256_castsi128_si256(_mm_load_si128((const __m128i *)quant_ptr));
31
0
  *quant = _mm256_permute4x64_epi64(*quant, 0x54);
32
0
  *dequant =
33
0
      _mm256_castsi128_si256(_mm_load_si128((const __m128i *)dequant_ptr));
34
0
  *dequant = _mm256_permute4x64_epi64(*dequant, 0x54);
35
0
  *shift = _mm256_castsi128_si256(_mm_load_si128((const __m128i *)shift_ptr));
36
0
  *shift = _mm256_permute4x64_epi64(*shift, 0x54);
37
0
}
38
39
0
static inline __m256i load_coefficients_avx2(const tran_low_t *coeff_ptr) {
40
0
  const __m256i coeff1 = _mm256_load_si256((__m256i *)(coeff_ptr));
41
0
  const __m256i coeff2 = _mm256_load_si256((__m256i *)(coeff_ptr + 8));
42
0
  return _mm256_packs_epi32(coeff1, coeff2);
43
0
}
44
45
static inline void update_mask1_avx2(__m256i *cmp_mask,
46
                                     const int16_t *iscan_ptr, int *is_found,
47
0
                                     __m256i *mask) {
48
0
  __m256i temp_mask = _mm256_setzero_si256();
49
0
  if (_mm256_movemask_epi8(*cmp_mask)) {
50
0
    __m256i iscan = _mm256_loadu_si256((const __m256i *)(iscan_ptr));
51
0
    temp_mask = _mm256_and_si256(*cmp_mask, iscan);
52
0
    *is_found = 1;
53
0
  }
54
0
  *mask = _mm256_max_epi16(temp_mask, *mask);
55
0
}
56
57
static inline void update_mask0_avx2(__m256i *qcoeff, __m256i *threshold,
58
                                     const int16_t *iscan_ptr, int *is_found,
59
0
                                     __m256i *mask) {
60
0
  __m256i zero = _mm256_setzero_si256();
61
0
  __m256i coeff[2], cmp_mask0, cmp_mask1;
62
0
  coeff[0] = _mm256_unpacklo_epi16(*qcoeff, zero);
63
0
  coeff[1] = _mm256_unpackhi_epi16(*qcoeff, zero);
64
0
  coeff[0] = _mm256_slli_epi32(coeff[0], AOM_QM_BITS);
65
0
  cmp_mask0 = _mm256_cmpgt_epi32(coeff[0], threshold[0]);
66
0
  coeff[1] = _mm256_slli_epi32(coeff[1], AOM_QM_BITS);
67
0
  cmp_mask1 = _mm256_cmpgt_epi32(coeff[1], threshold[1]);
68
0
  cmp_mask0 =
69
0
      _mm256_permute4x64_epi64(_mm256_packs_epi32(cmp_mask0, cmp_mask1), 0xd8);
70
0
  update_mask1_avx2(&cmp_mask0, iscan_ptr, is_found, mask);
71
0
}
72
73
static inline void calculate_qcoeff_avx2(__m256i *coeff, const __m256i *round,
74
                                         const __m256i *quant,
75
0
                                         const __m256i *shift) {
76
0
  __m256i tmp, qcoeff;
77
0
  qcoeff = _mm256_adds_epi16(*coeff, *round);
78
0
  tmp = _mm256_mulhi_epi16(qcoeff, *quant);
79
0
  qcoeff = _mm256_add_epi16(tmp, qcoeff);
80
0
  *coeff = _mm256_mulhi_epi16(qcoeff, *shift);
81
0
}
82
83
0
static inline __m256i calculate_dqcoeff_avx2(__m256i qcoeff, __m256i dequant) {
84
0
  return _mm256_mullo_epi16(qcoeff, dequant);
85
0
}
86
87
static inline void store_coefficients_avx2(__m256i coeff_vals,
88
0
                                           tran_low_t *coeff_ptr) {
89
0
  __m256i coeff_sign = _mm256_srai_epi16(coeff_vals, 15);
90
0
  __m256i coeff_vals_lo = _mm256_unpacklo_epi16(coeff_vals, coeff_sign);
91
0
  __m256i coeff_vals_hi = _mm256_unpackhi_epi16(coeff_vals, coeff_sign);
92
0
  _mm256_store_si256((__m256i *)(coeff_ptr), coeff_vals_lo);
93
0
  _mm256_store_si256((__m256i *)(coeff_ptr + 8), coeff_vals_hi);
94
0
}
95
96
void aom_quantize_b_adaptive_avx2(
97
    const tran_low_t *coeff_ptr, intptr_t n_coeffs, const int16_t *zbin_ptr,
98
    const int16_t *round_ptr, const int16_t *quant_ptr,
99
    const int16_t *quant_shift_ptr, tran_low_t *qcoeff_ptr,
100
    tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr,
101
0
    const int16_t *scan, const int16_t *iscan) {
102
0
  int index = 16;
103
0
  int non_zero_count = 0;
104
0
  int non_zero_count_prescan_add_zero = 0;
105
0
  int is_found0 = 0, is_found1 = 0;
106
0
  int eob = -1;
107
0
  const __m256i zero = _mm256_setzero_si256();
108
0
  __m256i zbin, round, quant, dequant, shift;
109
0
  __m256i coeff, qcoeff;
110
0
  __m256i cmp_mask, mask0 = zero, mask1 = zero;
111
0
  __m128i temp_mask0, temp_mask1;
112
0
  int prescan_add[2];
113
0
  int thresh[2];
114
0
  const qm_val_t wt = (1 << AOM_QM_BITS);
115
0
  for (int i = 0; i < 2; ++i) {
116
0
    prescan_add[i] = ROUND_POWER_OF_TWO(dequant_ptr[i] * EOB_FACTOR, 7);
117
0
    thresh[i] = (zbin_ptr[i] * wt + prescan_add[i]) - 1;
118
0
  }
119
0
  __m256i threshold[2];
120
0
  threshold[0] = _mm256_set1_epi32(thresh[0]);
121
0
  threshold[1] = _mm256_set1_epi32(thresh[1]);
122
0
  threshold[0] = _mm256_blend_epi32(threshold[0], threshold[1], 0xfe);
123
124
0
#if SKIP_EOB_FACTOR_ADJUST
125
0
  int first = -1;
126
0
#endif
127
128
  // Setup global values.
129
0
  load_b_values_avx2(zbin_ptr, &zbin, round_ptr, &round, quant_ptr, &quant,
130
0
                     dequant_ptr, &dequant, quant_shift_ptr, &shift);
131
132
  // Do DC and first 15 AC.
133
0
  coeff = load_coefficients_avx2(coeff_ptr);
134
0
  qcoeff = _mm256_abs_epi16(coeff);
135
0
  update_mask0_avx2(&qcoeff, threshold, iscan, &is_found0, &mask0);
136
0
  __m256i temp0 = _mm256_cmpgt_epi16(qcoeff, zbin);
137
0
  zbin = _mm256_unpackhi_epi64(zbin, zbin);
138
0
  cmp_mask = _mm256_permute4x64_epi64(temp0, 0xd8);
139
0
  update_mask1_avx2(&cmp_mask, iscan, &is_found1, &mask1);
140
0
  threshold[0] = threshold[1];
141
0
  if (_mm256_movemask_epi8(cmp_mask) == 0) {
142
0
    _mm256_store_si256((__m256i *)(qcoeff_ptr), zero);
143
0
    _mm256_store_si256((__m256i *)(qcoeff_ptr + 8), zero);
144
0
    _mm256_store_si256((__m256i *)(dqcoeff_ptr), zero);
145
0
    _mm256_store_si256((__m256i *)(dqcoeff_ptr + 8), zero);
146
0
    round = _mm256_unpackhi_epi64(round, round);
147
0
    quant = _mm256_unpackhi_epi64(quant, quant);
148
0
    shift = _mm256_unpackhi_epi64(shift, shift);
149
0
    dequant = _mm256_unpackhi_epi64(dequant, dequant);
150
0
  } else {
151
0
    calculate_qcoeff_avx2(&qcoeff, &round, &quant, &shift);
152
0
    round = _mm256_unpackhi_epi64(round, round);
153
0
    quant = _mm256_unpackhi_epi64(quant, quant);
154
0
    shift = _mm256_unpackhi_epi64(shift, shift);
155
    // Reinsert signs
156
0
    qcoeff = _mm256_sign_epi16(qcoeff, coeff);
157
    // Mask out zbin threshold coeffs
158
0
    qcoeff = _mm256_and_si256(qcoeff, temp0);
159
0
    store_coefficients_avx2(qcoeff, qcoeff_ptr);
160
0
    coeff = calculate_dqcoeff_avx2(qcoeff, dequant);
161
0
    dequant = _mm256_unpackhi_epi64(dequant, dequant);
162
0
    store_coefficients_avx2(coeff, dqcoeff_ptr);
163
0
  }
164
165
  // AC only loop.
166
0
  while (index < n_coeffs) {
167
0
    coeff = load_coefficients_avx2(coeff_ptr + index);
168
0
    qcoeff = _mm256_abs_epi16(coeff);
169
0
    update_mask0_avx2(&qcoeff, threshold, iscan + index, &is_found0, &mask0);
170
0
    temp0 = _mm256_cmpgt_epi16(qcoeff, zbin);
171
0
    cmp_mask = _mm256_permute4x64_epi64(temp0, 0xd8);
172
0
    update_mask1_avx2(&cmp_mask, iscan + index, &is_found1, &mask1);
173
0
    if (_mm256_movemask_epi8(cmp_mask) == 0) {
174
0
      _mm256_store_si256((__m256i *)(qcoeff_ptr + index), zero);
175
0
      _mm256_store_si256((__m256i *)(qcoeff_ptr + index + 8), zero);
176
0
      _mm256_store_si256((__m256i *)(dqcoeff_ptr + index), zero);
177
0
      _mm256_store_si256((__m256i *)(dqcoeff_ptr + index + 8), zero);
178
0
      index += 16;
179
0
      continue;
180
0
    }
181
0
    calculate_qcoeff_avx2(&qcoeff, &round, &quant, &shift);
182
0
    qcoeff = _mm256_sign_epi16(qcoeff, coeff);
183
0
    qcoeff = _mm256_and_si256(qcoeff, temp0);
184
0
    store_coefficients_avx2(qcoeff, qcoeff_ptr + index);
185
0
    coeff = calculate_dqcoeff_avx2(qcoeff, dequant);
186
0
    store_coefficients_avx2(coeff, dqcoeff_ptr + index);
187
0
    index += 16;
188
0
  }
189
0
  if (is_found0) {
190
0
    temp_mask0 = _mm_max_epi16(_mm256_castsi256_si128(mask0),
191
0
                               _mm256_extracti128_si256(mask0, 1));
192
0
    non_zero_count = calculate_non_zero_count(temp_mask0);
193
0
  }
194
0
  if (is_found1) {
195
0
    temp_mask1 = _mm_max_epi16(_mm256_castsi256_si128(mask1),
196
0
                               _mm256_extracti128_si256(mask1, 1));
197
0
    non_zero_count_prescan_add_zero = calculate_non_zero_count(temp_mask1);
198
0
  }
199
200
0
  for (int i = non_zero_count_prescan_add_zero - 1; i >= non_zero_count; i--) {
201
0
    const int rc = scan[i];
202
0
    qcoeff_ptr[rc] = 0;
203
0
    dqcoeff_ptr[rc] = 0;
204
0
  }
205
206
0
  for (int i = non_zero_count - 1; i >= 0; i--) {
207
0
    const int rc = scan[i];
208
0
    if (qcoeff_ptr[rc]) {
209
0
      eob = i;
210
0
      break;
211
0
    }
212
0
  }
213
214
0
  *eob_ptr = eob + 1;
215
0
#if SKIP_EOB_FACTOR_ADJUST
216
  // TODO(Aniket): Experiment the following loop with intrinsic by combining
217
  // with the quantization loop above
218
0
  for (int i = 0; i < non_zero_count; i++) {
219
0
    const int rc = scan[i];
220
0
    const int qcoeff0 = qcoeff_ptr[rc];
221
0
    if (qcoeff0) {
222
0
      first = i;
223
0
      break;
224
0
    }
225
0
  }
226
0
  if ((*eob_ptr - 1) >= 0 && first == (*eob_ptr - 1)) {
227
0
    const int rc = scan[(*eob_ptr - 1)];
228
0
    if (qcoeff_ptr[rc] == 1 || qcoeff_ptr[rc] == -1) {
229
0
      const int coeff0 = coeff_ptr[rc] * wt;
230
0
      const int coeff_sign = AOMSIGN(coeff0);
231
0
      const int abs_coeff = (coeff0 ^ coeff_sign) - coeff_sign;
232
0
      const int factor = EOB_FACTOR + SKIP_EOB_FACTOR_ADJUST;
233
0
      const int prescan_add_val =
234
0
          ROUND_POWER_OF_TWO(dequant_ptr[rc != 0] * factor, 7);
235
0
      if (abs_coeff <
236
0
          (zbin_ptr[rc != 0] * (1 << AOM_QM_BITS) + prescan_add_val)) {
237
0
        qcoeff_ptr[rc] = 0;
238
0
        dqcoeff_ptr[rc] = 0;
239
0
        *eob_ptr = 0;
240
0
      }
241
0
    }
242
0
  }
243
0
#endif
244
0
}