xref: /aosp_15_r20/external/libaom/test/error_block_test.cc (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
1 /*
2  * Copyright (c) 2016, 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 <cmath>
13 #include <cstdlib>
14 #include <string>
15 #include <tuple>
16 
17 #include "gtest/gtest.h"
18 
19 #include "config/aom_config.h"
20 #include "config/av1_rtcd.h"
21 
22 #include "test/acm_random.h"
23 #include "test/register_state_check.h"
24 #include "test/util.h"
25 #include "av1/common/entropy.h"
26 #include "aom/aom_codec.h"
27 #include "aom/aom_integer.h"
28 
29 using libaom_test::ACMRandom;
30 
31 namespace {
32 const int kNumIterations = 1000;
33 
34 using ErrorBlockFunc = int64_t (*)(const tran_low_t *coeff,
35                                    const tran_low_t *dqcoeff,
36                                    intptr_t block_size, int64_t *ssz, int bps);
37 
38 using ErrorBlockFunc8Bits = int64_t (*)(const tran_low_t *coeff,
39                                         const tran_low_t *dqcoeff,
40                                         intptr_t block_size, int64_t *ssz);
41 
42 using ErrorBlockLpFunc = int64_t (*)(const int16_t *coeff,
43                                      const int16_t *dqcoeff,
44                                      intptr_t block_size);
45 
46 using ErrorBlockParam =
47     std::tuple<ErrorBlockFunc, ErrorBlockFunc, aom_bit_depth_t>;
48 
49 template <ErrorBlockFunc8Bits fn>
BlockError8BitWrapper(const tran_low_t * coeff,const tran_low_t * dqcoeff,intptr_t block_size,int64_t * ssz,int bps)50 int64_t BlockError8BitWrapper(const tran_low_t *coeff,
51                               const tran_low_t *dqcoeff, intptr_t block_size,
52                               int64_t *ssz, int bps) {
53   EXPECT_EQ(bps, 8);
54   return fn(coeff, dqcoeff, block_size, ssz);
55 }
56 
57 template <ErrorBlockLpFunc fn>
BlockErrorLpWrapper(const tran_low_t * coeff,const tran_low_t * dqcoeff,intptr_t block_size,int64_t * ssz,int bps)58 int64_t BlockErrorLpWrapper(const tran_low_t *coeff, const tran_low_t *dqcoeff,
59                             intptr_t block_size, int64_t *ssz, int bps) {
60   EXPECT_EQ(bps, 8);
61   *ssz = -1;
62   return fn(reinterpret_cast<const int16_t *>(coeff),
63             reinterpret_cast<const int16_t *>(dqcoeff), block_size);
64 }
65 
66 class ErrorBlockTest : public ::testing::TestWithParam<ErrorBlockParam> {
67  public:
68   ~ErrorBlockTest() override = default;
SetUp()69   void SetUp() override {
70     error_block_op_ = GET_PARAM(0);
71     ref_error_block_op_ = GET_PARAM(1);
72     bit_depth_ = GET_PARAM(2);
73   }
74 
75  protected:
76   aom_bit_depth_t bit_depth_;
77   ErrorBlockFunc error_block_op_;
78   ErrorBlockFunc ref_error_block_op_;
79 };
80 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ErrorBlockTest);
81 
TEST_P(ErrorBlockTest,OperationCheck)82 TEST_P(ErrorBlockTest, OperationCheck) {
83   ACMRandom rnd(ACMRandom::DeterministicSeed());
84   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
85   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
86   int err_count_total = 0;
87   int first_failure = -1;
88   intptr_t block_size;
89   int64_t ssz;
90   int64_t ret;
91   int64_t ref_ssz;
92   int64_t ref_ret;
93   const int msb = bit_depth_ + 8 - 1;
94   for (int i = 0; i < kNumIterations; ++i) {
95     int err_count = 0;
96     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
97     for (int j = 0; j < block_size; j++) {
98       // coeff and dqcoeff will always have at least the same sign, and this
99       // can be used for optimization, so generate test input precisely.
100       if (rnd(2)) {
101         // Positive number
102         coeff[j] = rnd(1 << msb);
103         dqcoeff[j] = rnd(1 << msb);
104       } else {
105         // Negative number
106         coeff[j] = -rnd(1 << msb);
107         dqcoeff[j] = -rnd(1 << msb);
108       }
109     }
110     ref_ret =
111         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
112     API_REGISTER_STATE_CHECK(
113         ret = error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_));
114     err_count += (ref_ret != ret) | (ref_ssz != ssz);
115     if (err_count && !err_count_total) {
116       first_failure = i;
117     }
118     err_count_total += err_count;
119   }
120   EXPECT_EQ(0, err_count_total)
121       << "Error: Error Block Test, C output doesn't match optimized output. "
122       << "First failed at test case " << first_failure;
123 }
124 
TEST_P(ErrorBlockTest,ExtremeValues)125 TEST_P(ErrorBlockTest, ExtremeValues) {
126   ACMRandom rnd(ACMRandom::DeterministicSeed());
127   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
128   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
129   int err_count_total = 0;
130   int first_failure = -1;
131   intptr_t block_size;
132   int64_t ssz;
133   int64_t ret;
134   int64_t ref_ssz;
135   int64_t ref_ret;
136   const int msb = bit_depth_ + 8 - 1;
137   int max_val = ((1 << msb) - 1);
138   for (int i = 0; i < kNumIterations; ++i) {
139     int err_count = 0;
140     int k = (i / 9) % 9;
141 
142     // Change the maximum coeff value, to test different bit boundaries
143     if (k == 8 && (i % 9) == 0) {
144       max_val >>= 1;
145     }
146     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
147     for (int j = 0; j < block_size; j++) {
148       if (k < 4) {
149         // Test at positive maximum values
150         coeff[j] = k % 2 ? max_val : 0;
151         dqcoeff[j] = (k >> 1) % 2 ? max_val : 0;
152       } else if (k < 8) {
153         // Test at negative maximum values
154         coeff[j] = k % 2 ? -max_val : 0;
155         dqcoeff[j] = (k >> 1) % 2 ? -max_val : 0;
156       } else {
157         if (rnd(2)) {
158           // Positive number
159           coeff[j] = rnd(1 << 14);
160           dqcoeff[j] = rnd(1 << 14);
161         } else {
162           // Negative number
163           coeff[j] = -rnd(1 << 14);
164           dqcoeff[j] = -rnd(1 << 14);
165         }
166       }
167     }
168     ref_ret =
169         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
170     API_REGISTER_STATE_CHECK(
171         ret = error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_));
172     err_count += (ref_ret != ret) | (ref_ssz != ssz);
173     if (err_count && !err_count_total) {
174       first_failure = i;
175     }
176     err_count_total += err_count;
177   }
178   EXPECT_EQ(0, err_count_total)
179       << "Error: Error Block Test, C output doesn't match optimized output. "
180       << "First failed at test case " << first_failure;
181 }
182 
TEST_P(ErrorBlockTest,DISABLED_Speed)183 TEST_P(ErrorBlockTest, DISABLED_Speed) {
184   ACMRandom rnd(ACMRandom::DeterministicSeed());
185   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
186   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
187   intptr_t block_size;
188   int64_t ssz;
189   int num_iters = 100000;
190   int64_t ref_ssz;
191   const int msb = bit_depth_ + 8 - 1;
192   for (int i = 0; i < 9; ++i) {
193     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
194     for (int k = 0; k < 9; k++) {
195       for (int j = 0; j < block_size; j++) {
196         if (k < 5) {
197           if (rnd(2)) {
198             // Positive number
199             coeff[j] = rnd(1 << msb);
200             dqcoeff[j] = rnd(1 << msb);
201           } else {
202             // Negative number
203             coeff[j] = -rnd(1 << msb);
204             dqcoeff[j] = -rnd(1 << msb);
205           }
206         } else {
207           if (rnd(2)) {
208             // Positive number
209             coeff[j] = rnd(1 << 14);
210             dqcoeff[j] = rnd(1 << 14);
211           } else {
212             // Negative number
213             coeff[j] = -rnd(1 << 14);
214             dqcoeff[j] = -rnd(1 << 14);
215           }
216         }
217       }
218       aom_usec_timer ref_timer, test_timer;
219 
220       aom_usec_timer_start(&ref_timer);
221       for (int iter = 0; iter < num_iters; ++iter) {
222         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
223       }
224       aom_usec_timer_mark(&ref_timer);
225       const int elapsed_time_c =
226           static_cast<int>(aom_usec_timer_elapsed(&ref_timer));
227 
228       aom_usec_timer_start(&test_timer);
229       for (int iter = 0; iter < num_iters; ++iter) {
230         error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_);
231       }
232       aom_usec_timer_mark(&test_timer);
233 
234       const int elapsed_time_simd =
235           static_cast<int>(aom_usec_timer_elapsed(&test_timer));
236 
237       printf(
238           " c_time=%d \t simd_time=%d \t "
239           "gain=%d \n",
240           elapsed_time_c, elapsed_time_simd,
241           (elapsed_time_c / elapsed_time_simd));
242     }
243   }
244 }
245 
246 using std::make_tuple;
247 
248 #if HAVE_SSE2
249 const ErrorBlockParam kErrorBlockTestParamsSse2[] = {
250 #if CONFIG_AV1_HIGHBITDEPTH
251   make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c,
252              AOM_BITS_10),
253   make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c,
254              AOM_BITS_12),
255   make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c,
256              AOM_BITS_8),
257 #endif
258   make_tuple(&BlockError8BitWrapper<av1_block_error_sse2>,
259              &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8),
260   make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_sse2>,
261              &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8)
262 };
263 
264 INSTANTIATE_TEST_SUITE_P(SSE2, ErrorBlockTest,
265                          ::testing::ValuesIn(kErrorBlockTestParamsSse2));
266 #endif  // HAVE_SSE2
267 
268 #if HAVE_AVX2
269 const ErrorBlockParam kErrorBlockTestParamsAvx2[] = {
270 #if CONFIG_AV1_HIGHBITDEPTH
271   make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c,
272              AOM_BITS_10),
273   make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c,
274              AOM_BITS_12),
275   make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c,
276              AOM_BITS_8),
277 #endif
278   make_tuple(&BlockError8BitWrapper<av1_block_error_avx2>,
279              &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8),
280   make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_avx2>,
281              &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8)
282 };
283 
284 INSTANTIATE_TEST_SUITE_P(AVX2, ErrorBlockTest,
285                          ::testing::ValuesIn(kErrorBlockTestParamsAvx2));
286 #endif  // HAVE_AVX2
287 
288 #if HAVE_NEON
289 const ErrorBlockParam kErrorBlockTestParamsNeon[] = {
290 #if CONFIG_AV1_HIGHBITDEPTH
291   make_tuple(&av1_highbd_block_error_neon, &av1_highbd_block_error_c,
292              AOM_BITS_10),
293   make_tuple(&av1_highbd_block_error_neon, &av1_highbd_block_error_c,
294              AOM_BITS_12),
295   make_tuple(&av1_highbd_block_error_neon, &av1_highbd_block_error_c,
296              AOM_BITS_8),
297 #endif
298   make_tuple(&BlockError8BitWrapper<av1_block_error_neon>,
299              &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8),
300   make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_neon>,
301              &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8)
302 };
303 
304 INSTANTIATE_TEST_SUITE_P(NEON, ErrorBlockTest,
305                          ::testing::ValuesIn(kErrorBlockTestParamsNeon));
306 #endif  // HAVE_NEON
307 
308 #if HAVE_SVE
309 const ErrorBlockParam kErrorBlockTestParamsSVE[] = {
310   make_tuple(&BlockError8BitWrapper<av1_block_error_sve>,
311              &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8),
312   make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_sve>,
313              &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8)
314 };
315 
316 INSTANTIATE_TEST_SUITE_P(SVE, ErrorBlockTest,
317                          ::testing::ValuesIn(kErrorBlockTestParamsSVE));
318 #endif  // HAVE_SVE
319 }  // namespace
320