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