1 /*
2 * Copyright (c) 2021, 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 <tuple>
13
14 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
15
16 #include "config/aom_config.h"
17 #include "config/aom_dsp_rtcd.h"
18 #include "config/av1_rtcd.h"
19
20 #include "aom/aom_codec.h"
21 #include "aom_ports/aom_timer.h"
22 #include "av1/encoder/encoder.h"
23 #include "av1/common/scan.h"
24 #include "test/acm_random.h"
25 #include "test/register_state_check.h"
26 #include "test/util.h"
27
28 namespace {
29 using libaom_test::ACMRandom;
30
31 #define QUAN_LP_PARAM_LIST \
32 const int16_t *coeff_ptr, intptr_t n_coeffs, const int16_t *round_ptr, \
33 const int16_t *quant_ptr, int16_t *qcoeff_ptr, int16_t *dqcoeff_ptr, \
34 const int16_t *dequant_ptr, uint16_t *eob_ptr, const int16_t *scan, \
35 const int16_t *iscan
36
37 typedef void (*QuantizeFunc)(QUAN_LP_PARAM_LIST);
38
39 using std::tuple;
40 typedef tuple<QuantizeFunc, QuantizeFunc, TX_SIZE, aom_bit_depth_t>
41 QuantizeParam;
42
43 typedef struct {
44 QUANTS quant;
45 Dequants dequant;
46 } QuanTable;
47
48 const int kTestNum = 1000;
49
50 template <typename CoeffType>
51 class QuantizeTestBase : public ::testing::TestWithParam<QuantizeParam> {
52 protected:
QuantizeTestBase()53 QuantizeTestBase()
54 : quant_ref_(GET_PARAM(0)), quant_(GET_PARAM(1)), tx_size_(GET_PARAM(2)),
55 bd_(GET_PARAM(3)) {}
56
~QuantizeTestBase()57 virtual ~QuantizeTestBase() {}
58
SetUp()59 virtual void SetUp() {
60 qtab_ = reinterpret_cast<QuanTable *>(aom_memalign(32, sizeof(*qtab_)));
61 const int n_coeffs = coeff_num();
62 coeff_ = reinterpret_cast<CoeffType *>(
63 aom_memalign(32, 6 * n_coeffs * sizeof(CoeffType)));
64 InitQuantizer();
65 }
66
TearDown()67 virtual void TearDown() {
68 aom_free(qtab_);
69 qtab_ = NULL;
70 aom_free(coeff_);
71 coeff_ = NULL;
72 }
73
InitQuantizer()74 void InitQuantizer() {
75 av1_build_quantizer(bd_, 0, 0, 0, 0, 0, &qtab_->quant, &qtab_->dequant);
76 }
77
78 virtual void RunQuantizeFunc(const CoeffType *coeff_ptr, intptr_t n_coeffs,
79 const int16_t *round_ptr,
80 const int16_t *quant_ptr, CoeffType *qcoeff_ptr,
81 CoeffType *qcoeff_ref_ptr,
82 CoeffType *dqcoeff_ptr,
83 CoeffType *dqcoeff_ref_ptr,
84 const int16_t *dequant_ptr,
85 uint16_t *eob_ref_ptr, uint16_t *eob_ptr,
86 const int16_t *scan, const int16_t *iscan) = 0;
87
QuantizeRun(bool is_loop,int q=0,int test_num=1)88 void QuantizeRun(bool is_loop, int q = 0, int test_num = 1) {
89 CoeffType *coeff_ptr = coeff_;
90 const intptr_t n_coeffs = coeff_num();
91
92 CoeffType *qcoeff_ref = coeff_ptr + n_coeffs;
93 CoeffType *dqcoeff_ref = qcoeff_ref + n_coeffs;
94
95 CoeffType *qcoeff = dqcoeff_ref + n_coeffs;
96 CoeffType *dqcoeff = qcoeff + n_coeffs;
97 uint16_t *eob = (uint16_t *)(dqcoeff + n_coeffs);
98
99 // Testing uses 2-D DCT scan order table
100 const SCAN_ORDER *const sc = get_default_scan(tx_size_, DCT_DCT);
101
102 // Testing uses luminance quantization table
103 const int16_t *round = 0;
104 const int16_t *quant = 0;
105 round = qtab_->quant.y_round_fp[q];
106 quant = qtab_->quant.y_quant_fp[q];
107
108 const int16_t *dequant = qtab_->dequant.y_dequant_QTX[q];
109
110 for (int i = 0; i < test_num; ++i) {
111 if (is_loop) FillCoeffRandom();
112
113 memset(qcoeff_ref, 0, 5 * n_coeffs * sizeof(*qcoeff_ref));
114
115 RunQuantizeFunc(coeff_ptr, n_coeffs, round, quant, qcoeff, qcoeff_ref,
116 dqcoeff, dqcoeff_ref, dequant, &eob[0], &eob[1], sc->scan,
117 sc->iscan);
118
119 quant_ref_(coeff_ptr, n_coeffs, round, quant, qcoeff_ref, dqcoeff_ref,
120 dequant, &eob[0], sc->scan, sc->iscan);
121
122 API_REGISTER_STATE_CHECK(quant_(coeff_ptr, n_coeffs, round, quant, qcoeff,
123 dqcoeff, dequant, &eob[1], sc->scan,
124 sc->iscan));
125
126 for (int j = 0; j < n_coeffs; ++j) {
127 ASSERT_EQ(qcoeff_ref[j], qcoeff[j])
128 << "Q mismatch on test: " << i << " at position: " << j
129 << " Q: " << q << " coeff: " << coeff_ptr[j];
130 }
131
132 for (int j = 0; j < n_coeffs; ++j) {
133 ASSERT_EQ(dqcoeff_ref[j], dqcoeff[j])
134 << "Dq mismatch on test: " << i << " at position: " << j
135 << " Q: " << q << " coeff: " << coeff_ptr[j];
136 }
137
138 ASSERT_EQ(eob[0], eob[1])
139 << "eobs mismatch on test: " << i << " Q: " << q;
140 }
141 }
142
CompareResults(const CoeffType * buf_ref,const CoeffType * buf,int size,const char * text,int q,int number)143 void CompareResults(const CoeffType *buf_ref, const CoeffType *buf, int size,
144 const char *text, int q, int number) {
145 int i;
146 for (i = 0; i < size; ++i) {
147 ASSERT_EQ(buf_ref[i], buf[i]) << text << " mismatch on test: " << number
148 << " at position: " << i << " Q: " << q;
149 }
150 }
151
coeff_num() const152 int coeff_num() const { return av1_get_max_eob(tx_size_); }
153
FillCoeff(CoeffType c)154 void FillCoeff(CoeffType c) {
155 const int n_coeffs = coeff_num();
156 for (int i = 0; i < n_coeffs; ++i) {
157 coeff_[i] = c;
158 }
159 }
160
FillCoeffRandom()161 void FillCoeffRandom() {
162 const int n_coeffs = coeff_num();
163 FillCoeffZero();
164 int num = rnd_.Rand16() % n_coeffs;
165 for (int i = 0; i < num; ++i) {
166 coeff_[i] = GetRandomCoeff();
167 }
168 }
169
FillCoeffRandomRows(int num)170 void FillCoeffRandomRows(int num) {
171 FillCoeffZero();
172 for (int i = 0; i < num; ++i) {
173 coeff_[i] = GetRandomCoeff();
174 }
175 }
176
FillCoeffZero()177 void FillCoeffZero() { FillCoeff(0); }
178
FillCoeffConstant()179 void FillCoeffConstant() {
180 CoeffType c = GetRandomCoeff();
181 FillCoeff(c);
182 }
183
FillDcOnly()184 void FillDcOnly() {
185 FillCoeffZero();
186 coeff_[0] = GetRandomCoeff();
187 }
188
FillDcLargeNegative()189 void FillDcLargeNegative() {
190 FillCoeffZero();
191 // Generate a qcoeff which contains 512/-512 (0x0100/0xFE00) to catch issues
192 // like BUG=883 where the constant being compared was incorrectly
193 // initialized.
194 coeff_[0] = -8191;
195 }
196
GetRandomCoeff()197 CoeffType GetRandomCoeff() {
198 CoeffType coeff;
199 if (bd_ == AOM_BITS_8) {
200 coeff =
201 clamp(static_cast<int16_t>(rnd_.Rand16()), INT16_MIN + 1, INT16_MAX);
202 } else {
203 CoeffType min = -(1 << (7 + bd_));
204 CoeffType max = -min - 1;
205 coeff = clamp(static_cast<CoeffType>(rnd_.Rand31()), min, max);
206 }
207 return coeff;
208 }
209
210 ACMRandom rnd_;
211 QuanTable *qtab_;
212 CoeffType *coeff_;
213 QuantizeFunc quant_ref_;
214 QuantizeFunc quant_;
215 TX_SIZE tx_size_;
216 aom_bit_depth_t bd_;
217 };
218
219 class FullPrecisionQuantizeLpTest : public QuantizeTestBase<int16_t> {
RunQuantizeFunc(const int16_t * coeff_ptr,intptr_t n_coeffs,const int16_t * round_ptr,const int16_t * quant_ptr,int16_t * qcoeff_ptr,int16_t * qcoeff_ref_ptr,int16_t * dqcoeff_ptr,int16_t * dqcoeff_ref_ptr,const int16_t * dequant_ptr,uint16_t * eob_ref_ptr,uint16_t * eob_ptr,const int16_t * scan,const int16_t * iscan)220 void RunQuantizeFunc(const int16_t *coeff_ptr, intptr_t n_coeffs,
221 const int16_t *round_ptr, const int16_t *quant_ptr,
222 int16_t *qcoeff_ptr, int16_t *qcoeff_ref_ptr,
223 int16_t *dqcoeff_ptr, int16_t *dqcoeff_ref_ptr,
224 const int16_t *dequant_ptr, uint16_t *eob_ref_ptr,
225 uint16_t *eob_ptr, const int16_t *scan,
226 const int16_t *iscan) override {
227 quant_ref_(coeff_ptr, n_coeffs, round_ptr, quant_ptr, qcoeff_ref_ptr,
228 dqcoeff_ref_ptr, dequant_ptr, eob_ref_ptr, scan, iscan);
229
230 API_REGISTER_STATE_CHECK(quant_(coeff_ptr, n_coeffs, round_ptr, quant_ptr,
231 qcoeff_ptr, dqcoeff_ptr, dequant_ptr,
232 eob_ptr, scan, iscan));
233 }
234 };
235 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FullPrecisionQuantizeLpTest);
236
TEST_P(FullPrecisionQuantizeLpTest,ZeroInput)237 TEST_P(FullPrecisionQuantizeLpTest, ZeroInput) {
238 FillCoeffZero();
239 QuantizeRun(false);
240 }
241
TEST_P(FullPrecisionQuantizeLpTest,LargeNegativeInput)242 TEST_P(FullPrecisionQuantizeLpTest, LargeNegativeInput) {
243 FillDcLargeNegative();
244 QuantizeRun(false, 0, 1);
245 }
246
TEST_P(FullPrecisionQuantizeLpTest,DcOnlyInput)247 TEST_P(FullPrecisionQuantizeLpTest, DcOnlyInput) {
248 FillDcOnly();
249 QuantizeRun(false, 0, 1);
250 }
251
TEST_P(FullPrecisionQuantizeLpTest,RandomInput)252 TEST_P(FullPrecisionQuantizeLpTest, RandomInput) {
253 QuantizeRun(true, 0, kTestNum);
254 }
255
TEST_P(FullPrecisionQuantizeLpTest,MultipleQ)256 TEST_P(FullPrecisionQuantizeLpTest, MultipleQ) {
257 for (int q = 0; q < QINDEX_RANGE; ++q) {
258 QuantizeRun(true, q, kTestNum);
259 }
260 }
261
262 // Force the coeff to be half the value of the dequant. This exposes a
263 // mismatch found in av1_quantize_fp_sse2().
TEST_P(FullPrecisionQuantizeLpTest,CoeffHalfDequant)264 TEST_P(FullPrecisionQuantizeLpTest, CoeffHalfDequant) {
265 FillCoeff(16);
266 QuantizeRun(false, 25, 1);
267 }
268
TEST_P(FullPrecisionQuantizeLpTest,DISABLED_Speed)269 TEST_P(FullPrecisionQuantizeLpTest, DISABLED_Speed) {
270 int16_t *coeff_ptr = coeff_;
271 const intptr_t n_coeffs = coeff_num();
272
273 int16_t *qcoeff_ref = coeff_ptr + n_coeffs;
274 int16_t *dqcoeff_ref = qcoeff_ref + n_coeffs;
275
276 int16_t *qcoeff = dqcoeff_ref + n_coeffs;
277 int16_t *dqcoeff = qcoeff + n_coeffs;
278 uint16_t *eob = (uint16_t *)(dqcoeff + n_coeffs);
279
280 // Testing uses 2-D DCT scan order table
281 const SCAN_ORDER *const sc = get_default_scan(tx_size_, DCT_DCT);
282
283 // Testing uses luminance quantization table
284 const int q = 22;
285 const int16_t *round_fp = qtab_->quant.y_round_fp[q];
286 const int16_t *quant_fp = qtab_->quant.y_quant_fp[q];
287 const int16_t *dequant = qtab_->dequant.y_dequant_QTX[q];
288 const int kNumTests = 5000000;
289 aom_usec_timer timer, simd_timer;
290 int rows = tx_size_high[tx_size_];
291 int cols = tx_size_wide[tx_size_];
292 rows = AOMMIN(32, rows);
293 cols = AOMMIN(32, cols);
294 for (int cnt = 0; cnt <= rows; cnt++) {
295 FillCoeffRandomRows(cnt * cols);
296
297 aom_usec_timer_start(&timer);
298 for (int n = 0; n < kNumTests; ++n) {
299 quant_ref_(coeff_ptr, n_coeffs, round_fp, quant_fp, qcoeff, dqcoeff,
300 dequant, eob, sc->scan, sc->iscan);
301 }
302 aom_usec_timer_mark(&timer);
303
304 aom_usec_timer_start(&simd_timer);
305 for (int n = 0; n < kNumTests; ++n) {
306 quant_(coeff_ptr, n_coeffs, round_fp, quant_fp, qcoeff, dqcoeff, dequant,
307 eob, sc->scan, sc->iscan);
308 }
309 aom_usec_timer_mark(&simd_timer);
310
311 const int elapsed_time = static_cast<int>(aom_usec_timer_elapsed(&timer));
312 const int simd_elapsed_time =
313 static_cast<int>(aom_usec_timer_elapsed(&simd_timer));
314 printf("c_time = %d \t simd_time = %d \t Gain = %f \n", elapsed_time,
315 simd_elapsed_time, ((float)elapsed_time / simd_elapsed_time));
316 }
317 }
318
319 using std::make_tuple;
320
321 #if HAVE_AVX2
322 const QuantizeParam kQParamArrayAVX2[] = {
323 // av1_quantize_lp is only called in nonrd_pickmode.c, and is used for 16X16,
324 // 8X8, and 4X4.
325 make_tuple(&av1_quantize_lp_c, &av1_quantize_lp_avx2,
326 static_cast<TX_SIZE>(TX_16X16), AOM_BITS_8),
327 make_tuple(&av1_quantize_lp_c, &av1_quantize_lp_avx2,
328 static_cast<TX_SIZE>(TX_8X8), AOM_BITS_8),
329 make_tuple(&av1_quantize_lp_c, &av1_quantize_lp_avx2,
330 static_cast<TX_SIZE>(TX_4X4), AOM_BITS_8)
331 };
332
333 INSTANTIATE_TEST_SUITE_P(AVX2, FullPrecisionQuantizeLpTest,
334 ::testing::ValuesIn(kQParamArrayAVX2));
335 #endif
336
337 #if HAVE_NEON
338 const QuantizeParam kQParamArrayNEON[] = {
339 make_tuple(&av1_quantize_lp_c, &av1_quantize_lp_neon,
340 static_cast<TX_SIZE>(TX_16X16), AOM_BITS_8),
341 make_tuple(&av1_quantize_lp_c, &av1_quantize_lp_neon,
342 static_cast<TX_SIZE>(TX_8X8), AOM_BITS_8),
343 make_tuple(&av1_quantize_lp_c, &av1_quantize_lp_neon,
344 static_cast<TX_SIZE>(TX_4X4), AOM_BITS_8)
345 };
346
347 INSTANTIATE_TEST_SUITE_P(NEON, FullPrecisionQuantizeLpTest,
348 ::testing::ValuesIn(kQParamArrayNEON));
349 #endif
350
351 } // namespace
352