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