• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 2 Clause License and
5  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6  * was not distributed with this source code in the LICENSE file, you can
7  * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8  * Media Patent License 1.0 was not distributed with this source code in the
9  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10  */
11 
12 #include <cmath>
13 #include <cstdlib>
14 #include <string>
15 
16 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
17 
18 #include "config/aom_config.h"
19 #include "config/av1_rtcd.h"
20 
21 #include "test/acm_random.h"
22 #include "test/clear_system_state.h"
23 #include "test/register_state_check.h"
24 #include "test/util.h"
25 #include "av1/common/entropy.h"
26 #include "aom/aom_codec.h"
27 #include "aom/aom_integer.h"
28 
29 using libaom_test::ACMRandom;
30 
31 namespace {
32 const int kNumIterations = 1000;
33 
34 typedef int64_t (*ErrorBlockFunc)(const tran_low_t *coeff,
35                                   const tran_low_t *dqcoeff,
36                                   intptr_t block_size, int64_t *ssz, int bps);
37 
38 typedef ::testing::tuple<ErrorBlockFunc, ErrorBlockFunc, aom_bit_depth_t>
39     ErrorBlockParam;
40 
41 class ErrorBlockTest : public ::testing::TestWithParam<ErrorBlockParam> {
42  public:
~ErrorBlockTest()43   virtual ~ErrorBlockTest() {}
SetUp()44   virtual void SetUp() {
45     error_block_op_ = GET_PARAM(0);
46     ref_error_block_op_ = GET_PARAM(1);
47     bit_depth_ = GET_PARAM(2);
48   }
49 
TearDown()50   virtual void TearDown() { libaom_test::ClearSystemState(); }
51 
52  protected:
53   aom_bit_depth_t bit_depth_;
54   ErrorBlockFunc error_block_op_;
55   ErrorBlockFunc ref_error_block_op_;
56 };
57 
TEST_P(ErrorBlockTest,OperationCheck)58 TEST_P(ErrorBlockTest, OperationCheck) {
59   ACMRandom rnd(ACMRandom::DeterministicSeed());
60   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
61   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
62   int err_count_total = 0;
63   int first_failure = -1;
64   intptr_t block_size;
65   int64_t ssz;
66   int64_t ret;
67   int64_t ref_ssz;
68   int64_t ref_ret;
69   const int msb = bit_depth_ + 8 - 1;
70   for (int i = 0; i < kNumIterations; ++i) {
71     int err_count = 0;
72     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
73     for (int j = 0; j < block_size; j++) {
74       // coeff and dqcoeff will always have at least the same sign, and this
75       // can be used for optimization, so generate test input precisely.
76       if (rnd(2)) {
77         // Positive number
78         coeff[j] = rnd(1 << msb);
79         dqcoeff[j] = rnd(1 << msb);
80       } else {
81         // Negative number
82         coeff[j] = -rnd(1 << msb);
83         dqcoeff[j] = -rnd(1 << msb);
84       }
85     }
86     ref_ret =
87         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
88     ASM_REGISTER_STATE_CHECK(
89         ret = error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_));
90     err_count += (ref_ret != ret) | (ref_ssz != ssz);
91     if (err_count && !err_count_total) {
92       first_failure = i;
93     }
94     err_count_total += err_count;
95   }
96   EXPECT_EQ(0, err_count_total)
97       << "Error: Error Block Test, C output doesn't match optimized output. "
98       << "First failed at test case " << first_failure;
99 }
100 
TEST_P(ErrorBlockTest,ExtremeValues)101 TEST_P(ErrorBlockTest, ExtremeValues) {
102   ACMRandom rnd(ACMRandom::DeterministicSeed());
103   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
104   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
105   int err_count_total = 0;
106   int first_failure = -1;
107   intptr_t block_size;
108   int64_t ssz;
109   int64_t ret;
110   int64_t ref_ssz;
111   int64_t ref_ret;
112   const int msb = bit_depth_ + 8 - 1;
113   int max_val = ((1 << msb) - 1);
114   for (int i = 0; i < kNumIterations; ++i) {
115     int err_count = 0;
116     int k = (i / 9) % 9;
117 
118     // Change the maximum coeff value, to test different bit boundaries
119     if (k == 8 && (i % 9) == 0) {
120       max_val >>= 1;
121     }
122     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
123     for (int j = 0; j < block_size; j++) {
124       if (k < 4) {
125         // Test at positive maximum values
126         coeff[j] = k % 2 ? max_val : 0;
127         dqcoeff[j] = (k >> 1) % 2 ? max_val : 0;
128       } else if (k < 8) {
129         // Test at negative maximum values
130         coeff[j] = k % 2 ? -max_val : 0;
131         dqcoeff[j] = (k >> 1) % 2 ? -max_val : 0;
132       } else {
133         if (rnd(2)) {
134           // Positive number
135           coeff[j] = rnd(1 << 14);
136           dqcoeff[j] = rnd(1 << 14);
137         } else {
138           // Negative number
139           coeff[j] = -rnd(1 << 14);
140           dqcoeff[j] = -rnd(1 << 14);
141         }
142       }
143     }
144     ref_ret =
145         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
146     ASM_REGISTER_STATE_CHECK(
147         ret = error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_));
148     err_count += (ref_ret != ret) | (ref_ssz != ssz);
149     if (err_count && !err_count_total) {
150       first_failure = i;
151     }
152     err_count_total += err_count;
153   }
154   EXPECT_EQ(0, err_count_total)
155       << "Error: Error Block Test, C output doesn't match optimized output. "
156       << "First failed at test case " << first_failure;
157 }
158 
TEST_P(ErrorBlockTest,DISABLED_Speed)159 TEST_P(ErrorBlockTest, DISABLED_Speed) {
160   ACMRandom rnd(ACMRandom::DeterministicSeed());
161   DECLARE_ALIGNED(16, tran_low_t, coeff[4096]);
162   DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]);
163   intptr_t block_size;
164   int64_t ssz;
165   int num_iters = 100000;
166   int64_t ref_ssz;
167   int k;
168   const int msb = bit_depth_ + 8 - 1;
169   for (int i = 0; i < 9; ++i) {
170     block_size = 16 << (i % 9);  // All block sizes from 4x4, 8x4 ..64x64
171     for (k = 0; k < 9; k++) {
172       for (int j = 0; j < block_size; j++) {
173         if (k < 5) {
174           if (rnd(2)) {
175             // Positive number
176             coeff[j] = rnd(1 << msb);
177             dqcoeff[j] = rnd(1 << msb);
178           } else {
179             // Negative number
180             coeff[j] = -rnd(1 << msb);
181             dqcoeff[j] = -rnd(1 << msb);
182           }
183         } else {
184           if (rnd(2)) {
185             // Positive number
186             coeff[j] = rnd(1 << 14);
187             dqcoeff[j] = rnd(1 << 14);
188           } else {
189             // Negative number
190             coeff[j] = -rnd(1 << 14);
191             dqcoeff[j] = -rnd(1 << 14);
192           }
193         }
194       }
195       aom_usec_timer ref_timer, test_timer;
196 
197       aom_usec_timer_start(&ref_timer);
198       for (int i = 0; i < num_iters; ++i) {
199         ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_);
200       }
201       aom_usec_timer_mark(&ref_timer);
202       const int elapsed_time_c =
203           static_cast<int>(aom_usec_timer_elapsed(&ref_timer));
204 
205       aom_usec_timer_start(&test_timer);
206       for (int i = 0; i < num_iters; ++i) {
207         error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_);
208       }
209       aom_usec_timer_mark(&test_timer);
210 
211       const int elapsed_time_simd =
212           static_cast<int>(aom_usec_timer_elapsed(&test_timer));
213 
214       printf(
215           " c_time=%d \t simd_time=%d \t "
216           "gain=%d \n",
217           elapsed_time_c, elapsed_time_simd,
218           (elapsed_time_c / elapsed_time_simd));
219     }
220   }
221 }
222 
223 #if (HAVE_SSE2 || HAVE_AVX)
224 using ::testing::make_tuple;
225 
226 INSTANTIATE_TEST_CASE_P(
227     SSE2, ErrorBlockTest,
228     ::testing::Values(make_tuple(&av1_highbd_block_error_sse2,
229                                  &av1_highbd_block_error_c, AOM_BITS_10),
230                       make_tuple(&av1_highbd_block_error_sse2,
231                                  &av1_highbd_block_error_c, AOM_BITS_12),
232                       make_tuple(&av1_highbd_block_error_sse2,
233                                  &av1_highbd_block_error_c, AOM_BITS_8)));
234 #endif  // HAVE_SSE2
235 
236 #if (HAVE_AVX2)
237 using ::testing::make_tuple;
238 
239 INSTANTIATE_TEST_CASE_P(
240     AVX2, ErrorBlockTest,
241     ::testing::Values(make_tuple(&av1_highbd_block_error_avx2,
242                                  &av1_highbd_block_error_c, AOM_BITS_10),
243                       make_tuple(&av1_highbd_block_error_avx2,
244                                  &av1_highbd_block_error_c, AOM_BITS_12),
245                       make_tuple(&av1_highbd_block_error_avx2,
246                                  &av1_highbd_block_error_c, AOM_BITS_8)));
247 #endif  // HAVE_AVX2
248 }  // namespace
249