• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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