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