1/*
2 *  Copyright (c) 2014 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 <cmath>
12#include <cstdlib>
13#include <string>
14
15#include "third_party/googletest/src/include/gtest/gtest.h"
16
17#include "./vpx_config.h"
18#include "./vp9_rtcd.h"
19#include "test/acm_random.h"
20#include "test/clear_system_state.h"
21#include "test/register_state_check.h"
22#include "test/util.h"
23#include "vp9/common/vp9_entropy.h"
24#include "vpx/vpx_codec.h"
25#include "vpx/vpx_integer.h"
26
27using libvpx_test::ACMRandom;
28
29namespace {
30#if CONFIG_VP9_HIGHBITDEPTH
31const int kNumIterations = 1000;
32
33typedef int64_t (*ErrorBlockFunc)(const tran_low_t *coeff,
34                                  const tran_low_t *dqcoeff,
35                                  intptr_t block_size,
36                                  int64_t *ssz, int bps);
37
38typedef std::tr1::tuple<ErrorBlockFunc, ErrorBlockFunc, vpx_bit_depth_t>
39                        ErrorBlockParam;
40
41class ErrorBlockTest
42  : public ::testing::TestWithParam<ErrorBlockParam> {
43 public:
44  virtual ~ErrorBlockTest() {}
45  virtual void SetUp() {
46    error_block_op_     = GET_PARAM(0);
47    ref_error_block_op_ = GET_PARAM(1);
48    bit_depth_  = GET_PARAM(2);
49  }
50
51  virtual void TearDown() { libvpx_test::ClearSystemState(); }
52
53 protected:
54  vpx_bit_depth_t bit_depth_;
55  ErrorBlockFunc error_block_op_;
56  ErrorBlockFunc ref_error_block_op_;
57};
58
59TEST_P(ErrorBlockTest, OperationCheck) {
60  ACMRandom rnd(ACMRandom::DeterministicSeed());
61  DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
62  DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
63  int err_count_total = 0;
64  int first_failure = -1;
65  intptr_t block_size;
66  int64_t ssz;
67  int64_t ret;
68  int64_t ref_ssz;
69  int64_t ref_ret;
70  const int msb = bit_depth_ + 8 - 1;
71  for (int i = 0; i < kNumIterations; ++i) {
72    int err_count = 0;
73    block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
74    for (int j = 0; j < block_size; j++) {
75      // coeff and dqcoeff will always have at least the same sign, and this
76      // can be used for optimization, so generate test input precisely.
77      if (rnd(2)) {
78        // Positive number
79        coeff[j]   = rnd(1 << msb);
80        dqcoeff[j] = rnd(1 << msb);
81      } else {
82        // Negative number
83        coeff[j]   = -rnd(1 << msb);
84        dqcoeff[j] = -rnd(1 << msb);
85      }
86    }
87    ref_ret = ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz,
88                                  bit_depth_);
89    ASM_REGISTER_STATE_CHECK(ret = error_block_op_(coeff, dqcoeff, block_size,
90                                                   &ssz, bit_depth_));
91    err_count += (ref_ret != ret) | (ref_ssz != ssz);
92    if (err_count && !err_count_total) {
93      first_failure = i;
94    }
95    err_count_total += err_count;
96  }
97  EXPECT_EQ(0, err_count_total)
98      << "Error: Error Block Test, C output doesn't match optimized output. "
99      << "First failed at test case " << first_failure;
100}
101
102TEST_P(ErrorBlockTest, ExtremeValues) {
103  ACMRandom rnd(ACMRandom::DeterministicSeed());
104  DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
105  DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
106  int err_count_total = 0;
107  int first_failure = -1;
108  intptr_t block_size;
109  int64_t ssz;
110  int64_t ret;
111  int64_t ref_ssz;
112  int64_t ref_ret;
113  const int msb = bit_depth_ + 8 - 1;
114  int max_val = ((1 << msb) - 1);
115  for (int i = 0; i < kNumIterations; ++i) {
116    int err_count = 0;
117    int k = (i / 9) % 9;
118
119    // Change the maximum coeff value, to test different bit boundaries
120    if ( k == 8 && (i % 9) == 0 ) {
121      max_val >>= 1;
122    }
123    block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
124    for (int j = 0; j < block_size; j++) {
125      if (k < 4) {
126        // Test at positive maximum values
127        coeff[j]   = k % 2 ? max_val : 0;
128        dqcoeff[j] = (k >> 1) % 2 ? max_val : 0;
129      } else if (k < 8) {
130        // Test at negative maximum values
131        coeff[j]   = k % 2 ? -max_val : 0;
132        dqcoeff[j] = (k >> 1) % 2 ? -max_val : 0;
133      } else {
134        if (rnd(2)) {
135          // Positive number
136          coeff[j]   = rnd(1 << 14);
137          dqcoeff[j] = rnd(1 << 14);
138        } else {
139          // Negative number
140          coeff[j]   = -rnd(1 << 14);
141          dqcoeff[j] = -rnd(1 << 14);
142        }
143      }
144    }
145    ref_ret = ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz,
146                                  bit_depth_);
147    ASM_REGISTER_STATE_CHECK(ret = error_block_op_(coeff, dqcoeff, block_size,
148                                                   &ssz, bit_depth_));
149    err_count += (ref_ret != ret) | (ref_ssz != ssz);
150    if (err_count && !err_count_total) {
151      first_failure = i;
152    }
153    err_count_total += err_count;
154  }
155  EXPECT_EQ(0, err_count_total)
156      << "Error: Error Block Test, C output doesn't match optimized output. "
157      << "First failed at test case " << first_failure;
158}
159
160using std::tr1::make_tuple;
161
162#if CONFIG_USE_X86INC
163int64_t wrap_vp9_highbd_block_error_8bit_c(const tran_low_t *coeff,
164                                           const tran_low_t *dqcoeff,
165                                           intptr_t block_size,
166                                           int64_t *ssz, int bps) {
167  assert(bps == 8);
168  return vp9_highbd_block_error_8bit_c(coeff, dqcoeff, block_size, ssz);
169}
170
171#if HAVE_SSE2
172int64_t wrap_vp9_highbd_block_error_8bit_sse2(const tran_low_t *coeff,
173                                              const tran_low_t *dqcoeff,
174                                              intptr_t block_size,
175                                              int64_t *ssz, int bps) {
176  assert(bps == 8);
177  return vp9_highbd_block_error_8bit_sse2(coeff, dqcoeff, block_size, ssz);
178}
179
180INSTANTIATE_TEST_CASE_P(
181    SSE2, ErrorBlockTest,
182    ::testing::Values(
183        make_tuple(&vp9_highbd_block_error_sse2,
184                   &vp9_highbd_block_error_c, VPX_BITS_10),
185        make_tuple(&vp9_highbd_block_error_sse2,
186                   &vp9_highbd_block_error_c, VPX_BITS_12),
187        make_tuple(&vp9_highbd_block_error_sse2,
188                   &vp9_highbd_block_error_c, VPX_BITS_8),
189        make_tuple(&wrap_vp9_highbd_block_error_8bit_sse2,
190                   &wrap_vp9_highbd_block_error_8bit_c, VPX_BITS_8)));
191#endif  // HAVE_SSE2
192
193#if HAVE_AVX
194int64_t wrap_vp9_highbd_block_error_8bit_avx(const tran_low_t *coeff,
195                                              const tran_low_t *dqcoeff,
196                                              intptr_t block_size,
197                                              int64_t *ssz, int bps) {
198  assert(bps == 8);
199  return vp9_highbd_block_error_8bit_avx(coeff, dqcoeff, block_size, ssz);
200}
201
202INSTANTIATE_TEST_CASE_P(
203    AVX, ErrorBlockTest,
204    ::testing::Values(
205        make_tuple(&wrap_vp9_highbd_block_error_8bit_avx,
206                   &wrap_vp9_highbd_block_error_8bit_c, VPX_BITS_8)));
207#endif  // HAVE_AVX
208
209#endif  // CONFIG_USE_X86INC
210#endif  // CONFIG_VP9_HIGHBITDEPTH
211}  // namespace
212