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
25extern "C" {
26void vp9_idct8x8_64_add_c(const int16_t *input, uint8_t *output, int pitch);
27}
28
29using libvpx_test::ACMRandom;
30
31namespace {
32typedef void (*fdct_t)(const int16_t *in, int16_t *out, int stride);
33typedef void (*idct_t)(const int16_t *in, uint8_t *out, int stride);
34typedef void (*fht_t) (const int16_t *in, int16_t *out, int stride,
35                       int tx_type);
36typedef void (*iht_t) (const int16_t *in, uint8_t *out, int stride,
37                       int tx_type);
38
39typedef std::tr1::tuple<fdct_t, idct_t, int> dct_8x8_param_t;
40typedef std::tr1::tuple<fht_t, iht_t, int> ht_8x8_param_t;
41
42void fdct8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) {
43  vp9_fdct8x8_c(in, out, stride);
44}
45
46void 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
50class FwdTrans8x8TestBase {
51 public:
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
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
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
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
222class FwdTrans8x8DCT
223    : public FwdTrans8x8TestBase,
224      public ::testing::TestWithParam<dct_8x8_param_t> {
225 public:
226  virtual ~FwdTrans8x8DCT() {}
227
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
236  virtual void TearDown() { libvpx_test::ClearSystemState(); }
237
238 protected:
239  void RunFwdTxfm(int16_t *in, int16_t *out, int stride) {
240    fwd_txfm_(in, out, stride);
241  }
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
250TEST_P(FwdTrans8x8DCT, SignBiasCheck) {
251  RunSignBiasCheck();
252}
253
254TEST_P(FwdTrans8x8DCT, RoundTripErrorCheck) {
255  RunRoundTripErrorCheck();
256}
257
258TEST_P(FwdTrans8x8DCT, ExtremalCheck) {
259  RunExtremalCheck();
260}
261
262class FwdTrans8x8HT
263    : public FwdTrans8x8TestBase,
264      public ::testing::TestWithParam<ht_8x8_param_t> {
265 public:
266  virtual ~FwdTrans8x8HT() {}
267
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
276  virtual void TearDown() { libvpx_test::ClearSystemState(); }
277
278 protected:
279  void RunFwdTxfm(int16_t *in, int16_t *out, int stride) {
280    fwd_txfm_(in, out, stride, tx_type_);
281  }
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
290TEST_P(FwdTrans8x8HT, SignBiasCheck) {
291  RunSignBiasCheck();
292}
293
294TEST_P(FwdTrans8x8HT, RoundTripErrorCheck) {
295  RunRoundTripErrorCheck();
296}
297
298TEST_P(FwdTrans8x8HT, ExtremalCheck) {
299  RunExtremalCheck();
300}
301
302using std::tr1::make_tuple;
303
304INSTANTIATE_TEST_CASE_P(
305    C, FwdTrans8x8DCT,
306    ::testing::Values(
307        make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_c, 0)));
308INSTANTIATE_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
317INSTANTIATE_TEST_CASE_P(
318    NEON, FwdTrans8x8DCT,
319    ::testing::Values(
320        make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_neon, 0)));
321INSTANTIATE_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
331INSTANTIATE_TEST_CASE_P(
332    SSE2, FwdTrans8x8DCT,
333    ::testing::Values(
334        make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_sse2, 0)));
335INSTANTIATE_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