1/*
2 *  Copyright (c) 2013 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_blockd.h"
23#include "vp9/common/vp9_scan.h"
24#include "vpx/vpx_integer.h"
25
26using libvpx_test::ACMRandom;
27
28namespace {
29typedef void (*FwdTxfmFunc)(const int16_t *in, tran_low_t *out, int stride);
30typedef void (*InvTxfmFunc)(const tran_low_t *in, uint8_t *out, int stride);
31typedef std::tr1::tuple<FwdTxfmFunc,
32                        InvTxfmFunc,
33                        InvTxfmFunc,
34                        TX_SIZE, int> PartialInvTxfmParam;
35const int kMaxNumCoeffs = 1024;
36class PartialIDctTest : public ::testing::TestWithParam<PartialInvTxfmParam> {
37 public:
38  virtual ~PartialIDctTest() {}
39  virtual void SetUp() {
40    ftxfm_ = GET_PARAM(0);
41    full_itxfm_ = GET_PARAM(1);
42    partial_itxfm_ = GET_PARAM(2);
43    tx_size_  = GET_PARAM(3);
44    last_nonzero_ = GET_PARAM(4);
45  }
46
47  virtual void TearDown() { libvpx_test::ClearSystemState(); }
48
49 protected:
50  int last_nonzero_;
51  TX_SIZE tx_size_;
52  FwdTxfmFunc ftxfm_;
53  InvTxfmFunc full_itxfm_;
54  InvTxfmFunc partial_itxfm_;
55};
56
57TEST_P(PartialIDctTest, RunQuantCheck) {
58  ACMRandom rnd(ACMRandom::DeterministicSeed());
59  int size;
60  switch (tx_size_) {
61    case TX_4X4:
62      size = 4;
63      break;
64    case TX_8X8:
65      size = 8;
66      break;
67    case TX_16X16:
68      size = 16;
69      break;
70    case TX_32X32:
71      size = 32;
72      break;
73    default:
74      FAIL() << "Wrong Size!";
75      break;
76  }
77  DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_coef_block1, kMaxNumCoeffs);
78  DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_coef_block2, kMaxNumCoeffs);
79  DECLARE_ALIGNED_ARRAY(16, uint8_t, dst1, kMaxNumCoeffs);
80  DECLARE_ALIGNED_ARRAY(16, uint8_t, dst2, kMaxNumCoeffs);
81
82  const int count_test_block = 1000;
83  const int block_size = size * size;
84
85  DECLARE_ALIGNED_ARRAY(16, int16_t, input_extreme_block, kMaxNumCoeffs);
86  DECLARE_ALIGNED_ARRAY(16, tran_low_t, output_ref_block, kMaxNumCoeffs);
87
88  int max_error = 0;
89  for (int i = 0; i < count_test_block; ++i) {
90    // clear out destination buffer
91    memset(dst1, 0, sizeof(*dst1) * block_size);
92    memset(dst2, 0, sizeof(*dst2) * block_size);
93    memset(test_coef_block1, 0, sizeof(*test_coef_block1) * block_size);
94    memset(test_coef_block2, 0, sizeof(*test_coef_block2) * block_size);
95
96    ACMRandom rnd(ACMRandom::DeterministicSeed());
97
98    for (int i = 0; i < count_test_block; ++i) {
99      // Initialize a test block with input range [-255, 255].
100      if (i == 0) {
101        for (int j = 0; j < block_size; ++j)
102          input_extreme_block[j] = 255;
103      } else if (i == 1) {
104        for (int j = 0; j < block_size; ++j)
105          input_extreme_block[j] = -255;
106      } else {
107        for (int j = 0; j < block_size; ++j) {
108          input_extreme_block[j] = rnd.Rand8() % 2 ? 255 : -255;
109        }
110      }
111
112      ftxfm_(input_extreme_block, output_ref_block, size);
113
114      // quantization with maximum allowed step sizes
115      test_coef_block1[0] = (output_ref_block[0] / 1336) * 1336;
116      for (int j = 1; j < last_nonzero_; ++j)
117        test_coef_block1[vp9_default_scan_orders[tx_size_].scan[j]]
118                         = (output_ref_block[j] / 1828) * 1828;
119    }
120
121    ASM_REGISTER_STATE_CHECK(full_itxfm_(test_coef_block1, dst1, size));
122    ASM_REGISTER_STATE_CHECK(partial_itxfm_(test_coef_block1, dst2, size));
123
124    for (int j = 0; j < block_size; ++j) {
125      const int diff = dst1[j] - dst2[j];
126      const int error = diff * diff;
127      if (max_error < error)
128        max_error = error;
129    }
130  }
131
132  EXPECT_EQ(0, max_error)
133      << "Error: partial inverse transform produces different results";
134}
135
136TEST_P(PartialIDctTest, ResultsMatch) {
137  ACMRandom rnd(ACMRandom::DeterministicSeed());
138  int size;
139  switch (tx_size_) {
140    case TX_4X4:
141      size = 4;
142      break;
143    case TX_8X8:
144      size = 8;
145      break;
146    case TX_16X16:
147      size = 16;
148      break;
149    case TX_32X32:
150      size = 32;
151      break;
152    default:
153      FAIL() << "Wrong Size!";
154      break;
155  }
156  DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_coef_block1, kMaxNumCoeffs);
157  DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_coef_block2, kMaxNumCoeffs);
158  DECLARE_ALIGNED_ARRAY(16, uint8_t, dst1, kMaxNumCoeffs);
159  DECLARE_ALIGNED_ARRAY(16, uint8_t, dst2, kMaxNumCoeffs);
160  const int count_test_block = 1000;
161  const int max_coeff = 32766 / 4;
162  const int block_size = size * size;
163  int max_error = 0;
164  for (int i = 0; i < count_test_block; ++i) {
165    // clear out destination buffer
166    memset(dst1, 0, sizeof(*dst1) * block_size);
167    memset(dst2, 0, sizeof(*dst2) * block_size);
168    memset(test_coef_block1, 0, sizeof(*test_coef_block1) * block_size);
169    memset(test_coef_block2, 0, sizeof(*test_coef_block2) * block_size);
170    int max_energy_leftover = max_coeff * max_coeff;
171    for (int j = 0; j < last_nonzero_; ++j) {
172      int16_t coef = static_cast<int16_t>(sqrt(1.0 * max_energy_leftover) *
173                                          (rnd.Rand16() - 32768) / 65536);
174      max_energy_leftover -= coef * coef;
175      if (max_energy_leftover < 0) {
176        max_energy_leftover = 0;
177        coef = 0;
178      }
179      test_coef_block1[vp9_default_scan_orders[tx_size_].scan[j]] = coef;
180    }
181
182    memcpy(test_coef_block2, test_coef_block1,
183           sizeof(*test_coef_block2) * block_size);
184
185    ASM_REGISTER_STATE_CHECK(full_itxfm_(test_coef_block1, dst1, size));
186    ASM_REGISTER_STATE_CHECK(partial_itxfm_(test_coef_block2, dst2, size));
187
188    for (int j = 0; j < block_size; ++j) {
189      const int diff = dst1[j] - dst2[j];
190      const int error = diff * diff;
191      if (max_error < error)
192        max_error = error;
193    }
194  }
195
196  EXPECT_EQ(0, max_error)
197      << "Error: partial inverse transform produces different results";
198}
199using std::tr1::make_tuple;
200
201INSTANTIATE_TEST_CASE_P(
202    C, PartialIDctTest,
203    ::testing::Values(
204        make_tuple(&vp9_fdct32x32_c,
205                   &vp9_idct32x32_1024_add_c,
206                   &vp9_idct32x32_34_add_c,
207                   TX_32X32, 34),
208        make_tuple(&vp9_fdct32x32_c,
209                   &vp9_idct32x32_1024_add_c,
210                   &vp9_idct32x32_1_add_c,
211                   TX_32X32, 1),
212        make_tuple(&vp9_fdct16x16_c,
213                   &vp9_idct16x16_256_add_c,
214                   &vp9_idct16x16_10_add_c,
215                   TX_16X16, 10),
216        make_tuple(&vp9_fdct16x16_c,
217                   &vp9_idct16x16_256_add_c,
218                   &vp9_idct16x16_1_add_c,
219                   TX_16X16, 1),
220        make_tuple(&vp9_fdct8x8_c,
221                   &vp9_idct8x8_64_add_c,
222                   &vp9_idct8x8_12_add_c,
223                   TX_8X8, 12),
224        make_tuple(&vp9_fdct8x8_c,
225                   &vp9_idct8x8_64_add_c,
226                   &vp9_idct8x8_1_add_c,
227                   TX_8X8, 1),
228        make_tuple(&vp9_fdct4x4_c,
229                   &vp9_idct4x4_16_add_c,
230                   &vp9_idct4x4_1_add_c,
231                   TX_4X4, 1)));
232
233#if HAVE_NEON_ASM
234INSTANTIATE_TEST_CASE_P(
235    NEON, PartialIDctTest,
236    ::testing::Values(
237        make_tuple(&vp9_fdct32x32_c,
238                   &vp9_idct32x32_1024_add_c,
239                   &vp9_idct32x32_1_add_neon,
240                   TX_32X32, 1),
241        make_tuple(&vp9_fdct16x16_c,
242                   &vp9_idct16x16_256_add_c,
243                   &vp9_idct16x16_10_add_neon,
244                   TX_16X16, 10),
245        make_tuple(&vp9_fdct16x16_c,
246                   &vp9_idct16x16_256_add_c,
247                   &vp9_idct16x16_1_add_neon,
248                   TX_16X16, 1),
249        make_tuple(&vp9_fdct8x8_c,
250                   &vp9_idct8x8_64_add_c,
251                   &vp9_idct8x8_12_add_neon,
252                   TX_8X8, 12),
253        make_tuple(&vp9_fdct8x8_c,
254                   &vp9_idct8x8_64_add_c,
255                   &vp9_idct8x8_1_add_neon,
256                   TX_8X8, 1),
257        make_tuple(&vp9_fdct4x4_c,
258                   &vp9_idct4x4_16_add_c,
259                   &vp9_idct4x4_1_add_neon,
260                   TX_4X4, 1)));
261#endif
262
263#if HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH
264INSTANTIATE_TEST_CASE_P(
265    SSE2, PartialIDctTest,
266    ::testing::Values(
267        make_tuple(&vp9_fdct32x32_c,
268                   &vp9_idct32x32_1024_add_c,
269                   &vp9_idct32x32_34_add_sse2,
270                   TX_32X32, 34),
271        make_tuple(&vp9_fdct32x32_c,
272                   &vp9_idct32x32_1024_add_c,
273                   &vp9_idct32x32_1_add_sse2,
274                   TX_32X32, 1),
275        make_tuple(&vp9_fdct16x16_c,
276                   &vp9_idct16x16_256_add_c,
277                   &vp9_idct16x16_10_add_sse2,
278                   TX_16X16, 10),
279        make_tuple(&vp9_fdct16x16_c,
280                   &vp9_idct16x16_256_add_c,
281                   &vp9_idct16x16_1_add_sse2,
282                   TX_16X16, 1),
283        make_tuple(&vp9_fdct8x8_c,
284                   &vp9_idct8x8_64_add_c,
285                   &vp9_idct8x8_12_add_sse2,
286                   TX_8X8, 12),
287        make_tuple(&vp9_fdct8x8_c,
288                   &vp9_idct8x8_64_add_c,
289                   &vp9_idct8x8_1_add_sse2,
290                   TX_8X8, 1),
291        make_tuple(&vp9_fdct4x4_c,
292                   &vp9_idct4x4_16_add_c,
293                   &vp9_idct4x4_1_add_sse2,
294                   TX_4X4, 1)));
295#endif
296
297#if HAVE_SSSE3 && ARCH_X86_64 && !CONFIG_VP9_HIGHBITDEPTH
298INSTANTIATE_TEST_CASE_P(
299    SSSE3_64, PartialIDctTest,
300    ::testing::Values(
301        make_tuple(&vp9_fdct8x8_c,
302                   &vp9_idct8x8_64_add_c,
303                   &vp9_idct8x8_12_add_ssse3,
304                   TX_8X8, 12)));
305#endif
306
307#if HAVE_SSSE3 && !CONFIG_VP9_HIGHBITDEPTH
308INSTANTIATE_TEST_CASE_P(
309    SSSE3, PartialIDctTest,
310    ::testing::Values(
311        make_tuple(&vp9_fdct16x16_c,
312                   &vp9_idct16x16_256_add_c,
313                   &vp9_idct16x16_10_add_ssse3,
314                   TX_16X16, 10)));
315#endif
316}  // namespace
317