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