1 /*
2 * Copyright (c) 2012 The WebM project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <math.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "third_party/googletest/src/include/gtest/gtest.h"
16 #include "test/acm_random.h"
17 #include "test/clear_system_state.h"
18 #include "test/register_state_check.h"
19 #include "test/util.h"
20
21 #include "./vp9_rtcd.h"
22 #include "vp9/common/vp9_entropy.h"
23 #include "vpx/vpx_integer.h"
24
25 extern "C" {
26 void vp9_idct8x8_64_add_c(const int16_t *input, uint8_t *output, int pitch);
27 }
28
29 using libvpx_test::ACMRandom;
30
31 namespace {
32 typedef void (*fdct_t)(const int16_t *in, int16_t *out, int stride);
33 typedef void (*idct_t)(const int16_t *in, uint8_t *out, int stride);
34 typedef void (*fht_t) (const int16_t *in, int16_t *out, int stride,
35 int tx_type);
36 typedef void (*iht_t) (const int16_t *in, uint8_t *out, int stride,
37 int tx_type);
38
39 typedef std::tr1::tuple<fdct_t, idct_t, int> dct_8x8_param_t;
40 typedef std::tr1::tuple<fht_t, iht_t, int> ht_8x8_param_t;
41
fdct8x8_ref(const int16_t * in,int16_t * out,int stride,int tx_type)42 void fdct8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) {
43 vp9_fdct8x8_c(in, out, stride);
44 }
45
fht8x8_ref(const int16_t * in,int16_t * out,int stride,int tx_type)46 void fht8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) {
47 vp9_fht8x8_c(in, out, stride, tx_type);
48 }
49
50 class FwdTrans8x8TestBase {
51 public:
~FwdTrans8x8TestBase()52 virtual ~FwdTrans8x8TestBase() {}
53
54 protected:
55 virtual void RunFwdTxfm(int16_t *in, int16_t *out, int stride) = 0;
56 virtual void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) = 0;
57
RunSignBiasCheck()58 void RunSignBiasCheck() {
59 ACMRandom rnd(ACMRandom::DeterministicSeed());
60 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
61 DECLARE_ALIGNED_ARRAY(16, int16_t, test_output_block, 64);
62 int count_sign_block[64][2];
63 const int count_test_block = 100000;
64
65 memset(count_sign_block, 0, sizeof(count_sign_block));
66
67 for (int i = 0; i < count_test_block; ++i) {
68 // Initialize a test block with input range [-255, 255].
69 for (int j = 0; j < 64; ++j)
70 test_input_block[j] = rnd.Rand8() - rnd.Rand8();
71 REGISTER_STATE_CHECK(
72 RunFwdTxfm(test_input_block, test_output_block, pitch_));
73
74 for (int j = 0; j < 64; ++j) {
75 if (test_output_block[j] < 0)
76 ++count_sign_block[j][0];
77 else if (test_output_block[j] > 0)
78 ++count_sign_block[j][1];
79 }
80 }
81
82 for (int j = 0; j < 64; ++j) {
83 const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
84 const int max_diff = 1125;
85 EXPECT_LT(diff, max_diff)
86 << "Error: 8x8 FDCT/FHT has a sign bias > "
87 << 1. * max_diff / count_test_block * 100 << "%"
88 << " for input range [-255, 255] at index " << j
89 << " count0: " << count_sign_block[j][0]
90 << " count1: " << count_sign_block[j][1]
91 << " diff: " << diff;
92 }
93
94 memset(count_sign_block, 0, sizeof(count_sign_block));
95
96 for (int i = 0; i < count_test_block; ++i) {
97 // Initialize a test block with input range [-15, 15].
98 for (int j = 0; j < 64; ++j)
99 test_input_block[j] = (rnd.Rand8() >> 4) - (rnd.Rand8() >> 4);
100 REGISTER_STATE_CHECK(
101 RunFwdTxfm(test_input_block, test_output_block, pitch_));
102
103 for (int j = 0; j < 64; ++j) {
104 if (test_output_block[j] < 0)
105 ++count_sign_block[j][0];
106 else if (test_output_block[j] > 0)
107 ++count_sign_block[j][1];
108 }
109 }
110
111 for (int j = 0; j < 64; ++j) {
112 const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
113 const int max_diff = 10000;
114 EXPECT_LT(diff, max_diff)
115 << "Error: 4x4 FDCT/FHT has a sign bias > "
116 << 1. * max_diff / count_test_block * 100 << "%"
117 << " for input range [-15, 15] at index " << j
118 << " count0: " << count_sign_block[j][0]
119 << " count1: " << count_sign_block[j][1]
120 << " diff: " << diff;
121 }
122 }
123
RunRoundTripErrorCheck()124 void RunRoundTripErrorCheck() {
125 ACMRandom rnd(ACMRandom::DeterministicSeed());
126 int max_error = 0;
127 int total_error = 0;
128 const int count_test_block = 100000;
129 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
130 DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 64);
131 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
132 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
133
134 for (int i = 0; i < count_test_block; ++i) {
135 // Initialize a test block with input range [-255, 255].
136 for (int j = 0; j < 64; ++j) {
137 src[j] = rnd.Rand8();
138 dst[j] = rnd.Rand8();
139 test_input_block[j] = src[j] - dst[j];
140 }
141
142 REGISTER_STATE_CHECK(
143 RunFwdTxfm(test_input_block, test_temp_block, pitch_));
144 for (int j = 0; j < 64; ++j) {
145 if (test_temp_block[j] > 0) {
146 test_temp_block[j] += 2;
147 test_temp_block[j] /= 4;
148 test_temp_block[j] *= 4;
149 } else {
150 test_temp_block[j] -= 2;
151 test_temp_block[j] /= 4;
152 test_temp_block[j] *= 4;
153 }
154 }
155 REGISTER_STATE_CHECK(
156 RunInvTxfm(test_temp_block, dst, pitch_));
157
158 for (int j = 0; j < 64; ++j) {
159 const int diff = dst[j] - src[j];
160 const int error = diff * diff;
161 if (max_error < error)
162 max_error = error;
163 total_error += error;
164 }
165 }
166
167 EXPECT_GE(1, max_error)
168 << "Error: 8x8 FDCT/IDCT or FHT/IHT has an individual"
169 << " roundtrip error > 1";
170
171 EXPECT_GE(count_test_block/5, total_error)
172 << "Error: 8x8 FDCT/IDCT or FHT/IHT has average roundtrip "
173 << "error > 1/5 per block";
174 }
175
RunExtremalCheck()176 void RunExtremalCheck() {
177 ACMRandom rnd(ACMRandom::DeterministicSeed());
178 int max_error = 0;
179 int total_error = 0;
180 const int count_test_block = 100000;
181 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
182 DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 64);
183 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
184 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
185
186 for (int i = 0; i < count_test_block; ++i) {
187 // Initialize a test block with input range [-255, 255].
188 for (int j = 0; j < 64; ++j) {
189 src[j] = rnd.Rand8() % 2 ? 255 : 0;
190 dst[j] = src[j] > 0 ? 0 : 255;
191 test_input_block[j] = src[j] - dst[j];
192 }
193
194 REGISTER_STATE_CHECK(
195 RunFwdTxfm(test_input_block, test_temp_block, pitch_));
196 REGISTER_STATE_CHECK(
197 RunInvTxfm(test_temp_block, dst, pitch_));
198
199 for (int j = 0; j < 64; ++j) {
200 const int diff = dst[j] - src[j];
201 const int error = diff * diff;
202 if (max_error < error)
203 max_error = error;
204 total_error += error;
205 }
206
207 EXPECT_GE(1, max_error)
208 << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has"
209 << "an individual roundtrip error > 1";
210
211 EXPECT_GE(count_test_block/5, total_error)
212 << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has average"
213 << " roundtrip error > 1/5 per block";
214 }
215 }
216
217 int pitch_;
218 int tx_type_;
219 fht_t fwd_txfm_ref;
220 };
221
222 class FwdTrans8x8DCT
223 : public FwdTrans8x8TestBase,
224 public ::testing::TestWithParam<dct_8x8_param_t> {
225 public:
~FwdTrans8x8DCT()226 virtual ~FwdTrans8x8DCT() {}
227
SetUp()228 virtual void SetUp() {
229 fwd_txfm_ = GET_PARAM(0);
230 inv_txfm_ = GET_PARAM(1);
231 tx_type_ = GET_PARAM(2);
232 pitch_ = 8;
233 fwd_txfm_ref = fdct8x8_ref;
234 }
235
TearDown()236 virtual void TearDown() { libvpx_test::ClearSystemState(); }
237
238 protected:
RunFwdTxfm(int16_t * in,int16_t * out,int stride)239 void RunFwdTxfm(int16_t *in, int16_t *out, int stride) {
240 fwd_txfm_(in, out, stride);
241 }
RunInvTxfm(int16_t * out,uint8_t * dst,int stride)242 void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) {
243 inv_txfm_(out, dst, stride);
244 }
245
246 fdct_t fwd_txfm_;
247 idct_t inv_txfm_;
248 };
249
TEST_P(FwdTrans8x8DCT,SignBiasCheck)250 TEST_P(FwdTrans8x8DCT, SignBiasCheck) {
251 RunSignBiasCheck();
252 }
253
TEST_P(FwdTrans8x8DCT,RoundTripErrorCheck)254 TEST_P(FwdTrans8x8DCT, RoundTripErrorCheck) {
255 RunRoundTripErrorCheck();
256 }
257
TEST_P(FwdTrans8x8DCT,ExtremalCheck)258 TEST_P(FwdTrans8x8DCT, ExtremalCheck) {
259 RunExtremalCheck();
260 }
261
262 class FwdTrans8x8HT
263 : public FwdTrans8x8TestBase,
264 public ::testing::TestWithParam<ht_8x8_param_t> {
265 public:
~FwdTrans8x8HT()266 virtual ~FwdTrans8x8HT() {}
267
SetUp()268 virtual void SetUp() {
269 fwd_txfm_ = GET_PARAM(0);
270 inv_txfm_ = GET_PARAM(1);
271 tx_type_ = GET_PARAM(2);
272 pitch_ = 8;
273 fwd_txfm_ref = fht8x8_ref;
274 }
275
TearDown()276 virtual void TearDown() { libvpx_test::ClearSystemState(); }
277
278 protected:
RunFwdTxfm(int16_t * in,int16_t * out,int stride)279 void RunFwdTxfm(int16_t *in, int16_t *out, int stride) {
280 fwd_txfm_(in, out, stride, tx_type_);
281 }
RunInvTxfm(int16_t * out,uint8_t * dst,int stride)282 void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) {
283 inv_txfm_(out, dst, stride, tx_type_);
284 }
285
286 fht_t fwd_txfm_;
287 iht_t inv_txfm_;
288 };
289
TEST_P(FwdTrans8x8HT,SignBiasCheck)290 TEST_P(FwdTrans8x8HT, SignBiasCheck) {
291 RunSignBiasCheck();
292 }
293
TEST_P(FwdTrans8x8HT,RoundTripErrorCheck)294 TEST_P(FwdTrans8x8HT, RoundTripErrorCheck) {
295 RunRoundTripErrorCheck();
296 }
297
TEST_P(FwdTrans8x8HT,ExtremalCheck)298 TEST_P(FwdTrans8x8HT, ExtremalCheck) {
299 RunExtremalCheck();
300 }
301
302 using std::tr1::make_tuple;
303
304 INSTANTIATE_TEST_CASE_P(
305 C, FwdTrans8x8DCT,
306 ::testing::Values(
307 make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_c, 0)));
308 INSTANTIATE_TEST_CASE_P(
309 C, FwdTrans8x8HT,
310 ::testing::Values(
311 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0),
312 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 1),
313 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 2),
314 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 3)));
315
316 #if HAVE_NEON
317 INSTANTIATE_TEST_CASE_P(
318 NEON, FwdTrans8x8DCT,
319 ::testing::Values(
320 make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_neon, 0)));
321 INSTANTIATE_TEST_CASE_P(
322 DISABLED_NEON, FwdTrans8x8HT,
323 ::testing::Values(
324 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 0),
325 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 1),
326 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 2),
327 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 3)));
328 #endif
329
330 #if HAVE_SSE2
331 INSTANTIATE_TEST_CASE_P(
332 SSE2, FwdTrans8x8DCT,
333 ::testing::Values(
334 make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_sse2, 0)));
335 INSTANTIATE_TEST_CASE_P(
336 SSE2, FwdTrans8x8HT,
337 ::testing::Values(
338 make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 0),
339 make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 1),
340 make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 2),
341 make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 3)));
342 #endif
343 } // namespace
344