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