• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "third_party/googletest/src/googletest/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:
~ErrorBlockTest()68   virtual ~ErrorBlockTest() {}
SetUp()69   virtual void SetUp() {
70     error_block_op_ = GET_PARAM(0);
71     ref_error_block_op_ = GET_PARAM(1);
72     bit_depth_ = GET_PARAM(2);
73   }
74 
TearDown()75   virtual void TearDown() {}
76 
77  protected:
78   aom_bit_depth_t bit_depth_;
79   ErrorBlockFunc error_block_op_;
80   ErrorBlockFunc ref_error_block_op_;
81 };
82 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ErrorBlockTest);
83 
TEST_P(ErrorBlockTest,OperationCheck)84 TEST_P(ErrorBlockTest, OperationCheck) {
85   ACMRandom rnd(ACMRandom::DeterministicSeed());
86   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
87   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
88   int err_count_total = 0;
89   int first_failure = -1;
90   intptr_t block_size;
91   int64_t ssz;
92   int64_t ret;
93   int64_t ref_ssz;
94   int64_t ref_ret;
95   const int msb = bit_depth_ + 8 - 1;
96   for (int i = 0; i < kNumIterations; ++i) {
97     int err_count = 0;
98     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
99     for (int j = 0; j < block_size; j++) {
100       // coeff and dqcoeff will always have at least the same sign, and this
101       // can be used for optimization, so generate test input precisely.
102       if (rnd(2)) {
103         // Positive number
104         coeff[j] = rnd(1 << msb);
105         dqcoeff[j] = rnd(1 << msb);
106       } else {
107         // Negative number
108         coeff[j] = -rnd(1 << msb);
109         dqcoeff[j] = -rnd(1 << msb);
110       }
111     }
112     ref_ret =
113         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
114     API_REGISTER_STATE_CHECK(
115         ret = error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_));
116     err_count += (ref_ret != ret) | (ref_ssz != ssz);
117     if (err_count && !err_count_total) {
118       first_failure = i;
119     }
120     err_count_total += err_count;
121   }
122   EXPECT_EQ(0, err_count_total)
123       << "Error: Error Block Test, C output doesn't match optimized output. "
124       << "First failed at test case " << first_failure;
125 }
126 
TEST_P(ErrorBlockTest,ExtremeValues)127 TEST_P(ErrorBlockTest, ExtremeValues) {
128   ACMRandom rnd(ACMRandom::DeterministicSeed());
129   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
130   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
131   int err_count_total = 0;
132   int first_failure = -1;
133   intptr_t block_size;
134   int64_t ssz;
135   int64_t ret;
136   int64_t ref_ssz;
137   int64_t ref_ret;
138   const int msb = bit_depth_ + 8 - 1;
139   int max_val = ((1 << msb) - 1);
140   for (int i = 0; i < kNumIterations; ++i) {
141     int err_count = 0;
142     int k = (i / 9) % 9;
143 
144     // Change the maximum coeff value, to test different bit boundaries
145     if (k == 8 && (i % 9) == 0) {
146       max_val >>= 1;
147     }
148     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
149     for (int j = 0; j < block_size; j++) {
150       if (k < 4) {
151         // Test at positive maximum values
152         coeff[j] = k % 2 ? max_val : 0;
153         dqcoeff[j] = (k >> 1) % 2 ? max_val : 0;
154       } else if (k < 8) {
155         // Test at negative maximum values
156         coeff[j] = k % 2 ? -max_val : 0;
157         dqcoeff[j] = (k >> 1) % 2 ? -max_val : 0;
158       } else {
159         if (rnd(2)) {
160           // Positive number
161           coeff[j] = rnd(1 << 14);
162           dqcoeff[j] = rnd(1 << 14);
163         } else {
164           // Negative number
165           coeff[j] = -rnd(1 << 14);
166           dqcoeff[j] = -rnd(1 << 14);
167         }
168       }
169     }
170     ref_ret =
171         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
172     API_REGISTER_STATE_CHECK(
173         ret = error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_));
174     err_count += (ref_ret != ret) | (ref_ssz != ssz);
175     if (err_count && !err_count_total) {
176       first_failure = i;
177     }
178     err_count_total += err_count;
179   }
180   EXPECT_EQ(0, err_count_total)
181       << "Error: Error Block Test, C output doesn't match optimized output. "
182       << "First failed at test case " << first_failure;
183 }
184 
TEST_P(ErrorBlockTest,DISABLED_Speed)185 TEST_P(ErrorBlockTest, DISABLED_Speed) {
186   ACMRandom rnd(ACMRandom::DeterministicSeed());
187   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
188   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
189   intptr_t block_size;
190   int64_t ssz;
191   int num_iters = 100000;
192   int64_t ref_ssz;
193   int k;
194   const int msb = bit_depth_ + 8 - 1;
195   for (int i = 0; i < 9; ++i) {
196     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
197     for (k = 0; k < 9; k++) {
198       for (int j = 0; j < block_size; j++) {
199         if (k < 5) {
200           if (rnd(2)) {
201             // Positive number
202             coeff[j] = rnd(1 << msb);
203             dqcoeff[j] = rnd(1 << msb);
204           } else {
205             // Negative number
206             coeff[j] = -rnd(1 << msb);
207             dqcoeff[j] = -rnd(1 << msb);
208           }
209         } else {
210           if (rnd(2)) {
211             // Positive number
212             coeff[j] = rnd(1 << 14);
213             dqcoeff[j] = rnd(1 << 14);
214           } else {
215             // Negative number
216             coeff[j] = -rnd(1 << 14);
217             dqcoeff[j] = -rnd(1 << 14);
218           }
219         }
220       }
221       aom_usec_timer ref_timer, test_timer;
222 
223       aom_usec_timer_start(&ref_timer);
224       for (int i = 0; i < num_iters; ++i) {
225         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
226       }
227       aom_usec_timer_mark(&ref_timer);
228       const int elapsed_time_c =
229           static_cast<int>(aom_usec_timer_elapsed(&ref_timer));
230 
231       aom_usec_timer_start(&test_timer);
232       for (int i = 0; i < num_iters; ++i) {
233         error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_);
234       }
235       aom_usec_timer_mark(&test_timer);
236 
237       const int elapsed_time_simd =
238           static_cast<int>(aom_usec_timer_elapsed(&test_timer));
239 
240       printf(
241           " c_time=%d \t simd_time=%d \t "
242           "gain=%d \n",
243           elapsed_time_c, elapsed_time_simd,
244           (elapsed_time_c / elapsed_time_simd));
245     }
246   }
247 }
248 
249 using std::make_tuple;
250 
251 #if (HAVE_SSE2)
252 const ErrorBlockParam kErrorBlockTestParamsSse2[] = {
253 #if CONFIG_AV1_HIGHBITDEPTH
254   make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c,
255              AOM_BITS_10),
256   make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c,
257              AOM_BITS_12),
258   make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c,
259              AOM_BITS_8),
260 #endif
261   make_tuple(&BlockError8BitWrapper<av1_block_error_sse2>,
262              &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8),
263   make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_sse2>,
264              &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8)
265 };
266 
267 INSTANTIATE_TEST_SUITE_P(SSE2, ErrorBlockTest,
268                          ::testing::ValuesIn(kErrorBlockTestParamsSse2));
269 #endif  // HAVE_SSE2
270 
271 #if (HAVE_AVX2)
272 const ErrorBlockParam kErrorBlockTestParamsAvx2[] = {
273 #if CONFIG_AV1_HIGHBITDEPTH
274   make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c,
275              AOM_BITS_10),
276   make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c,
277              AOM_BITS_12),
278   make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c,
279              AOM_BITS_8),
280 #endif
281   make_tuple(&BlockError8BitWrapper<av1_block_error_avx2>,
282              &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8),
283   make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_avx2>,
284              &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8)
285 };
286 
287 INSTANTIATE_TEST_SUITE_P(AVX2, ErrorBlockTest,
288                          ::testing::ValuesIn(kErrorBlockTestParamsAvx2));
289 #endif  // HAVE_AVX2
290 
291 #if (HAVE_NEON)
292 const ErrorBlockParam kErrorBlockTestParamsNeon[] = {
293   make_tuple(&BlockError8BitWrapper<av1_block_error_neon>,
294              &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8),
295   make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_neon>,
296              &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8)
297 };
298 
299 INSTANTIATE_TEST_SUITE_P(NEON, ErrorBlockTest,
300                          ::testing::ValuesIn(kErrorBlockTestParamsNeon));
301 #endif  // HAVE_NEON
302 }  // namespace
303