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