1 /*
2 * Copyright (c) 2017, 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 <stdint.h>
13 #include <stdio.h>
14 #include <string.h>
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 "aom_ports/aom_timer.h"
22 #include "aom_ports/mem.h"
23 #include "av1/common/idct.h"
24 #include "av1/common/onyxc_int.h"
25 #include "av1/common/scan.h"
26 #include "av1/common/txb_common.h"
27 #include "test/acm_random.h"
28 #include "test/clear_system_state.h"
29 #include "test/register_state_check.h"
30 #include "test/util.h"
31
32 namespace {
33 using libaom_test::ACMRandom;
34
35 typedef void (*GetNzMapContextsFunc)(const uint8_t *const levels,
36 const int16_t *const scan,
37 const uint16_t eob, const TX_SIZE tx_size,
38 const TX_CLASS tx_class,
39 int8_t *const coeff_contexts);
40
41 class EncodeTxbTest : public ::testing::TestWithParam<GetNzMapContextsFunc> {
42 public:
EncodeTxbTest()43 EncodeTxbTest() : get_nz_map_contexts_func_(GetParam()) {}
44
~EncodeTxbTest()45 virtual ~EncodeTxbTest() {}
46
SetUp()47 virtual void SetUp() {
48 coeff_contexts_ref_ = reinterpret_cast<int8_t *>(
49 aom_memalign(16, sizeof(*coeff_contexts_ref_) * MAX_TX_SQUARE));
50 ASSERT_TRUE(coeff_contexts_ref_ != NULL);
51 coeff_contexts_ = reinterpret_cast<int8_t *>(
52 aom_memalign(16, sizeof(*coeff_contexts_) * MAX_TX_SQUARE));
53 ASSERT_TRUE(coeff_contexts_ != NULL);
54 }
55
TearDown()56 virtual void TearDown() {
57 aom_free(coeff_contexts_ref_);
58 aom_free(coeff_contexts_);
59 libaom_test::ClearSystemState();
60 }
61
GetNzMapContextsRun()62 void GetNzMapContextsRun() {
63 const int kNumTests = 10;
64 int result = 0;
65
66 for (int is_inter = 0; is_inter < 2; ++is_inter) {
67 for (int tx_type = DCT_DCT; tx_type < TX_TYPES; ++tx_type) {
68 const TX_CLASS tx_class = tx_type_to_class[tx_type];
69 for (int tx_size = TX_4X4; tx_size < TX_SIZES_ALL; ++tx_size) {
70 const int bwl = get_txb_bwl((TX_SIZE)tx_size);
71 const int width = get_txb_wide((TX_SIZE)tx_size);
72 const int height = get_txb_high((TX_SIZE)tx_size);
73 const int real_width = tx_size_wide[tx_size];
74 const int real_height = tx_size_high[tx_size];
75 const int16_t *const scan = av1_scan_orders[tx_size][tx_type].scan;
76
77 levels_ = set_levels(levels_buf_, width);
78 for (int i = 0; i < kNumTests && !result; ++i) {
79 for (int eob = 1; eob <= width * height && !result; ++eob) {
80 InitDataWithEob(scan, bwl, eob);
81
82 av1_get_nz_map_contexts_c(levels_, scan, eob, (TX_SIZE)tx_size,
83 tx_class, coeff_contexts_ref_);
84 get_nz_map_contexts_func_(levels_, scan, eob, (TX_SIZE)tx_size,
85 tx_class, coeff_contexts_);
86
87 result = Compare(scan, eob);
88
89 EXPECT_EQ(result, 0)
90 << " tx_class " << tx_class << " width " << real_width
91 << " height " << real_height << " eob " << eob;
92 }
93 }
94 }
95 }
96 }
97 }
98
SpeedTestGetNzMapContextsRun()99 void SpeedTestGetNzMapContextsRun() {
100 const int kNumTests = 2000000000;
101 aom_usec_timer timer;
102
103 printf("Note: Only test the largest possible eob case!\n");
104 for (int tx_size = TX_4X4; tx_size < TX_SIZES_ALL; ++tx_size) {
105 const int bwl = get_txb_bwl((TX_SIZE)tx_size);
106 const int width = get_txb_wide((TX_SIZE)tx_size);
107 const int height = get_txb_high((TX_SIZE)tx_size);
108 const int real_width = tx_size_wide[tx_size];
109 const int real_height = tx_size_high[tx_size];
110 const TX_TYPE tx_type = DCT_DCT;
111 const TX_CLASS tx_class = tx_type_to_class[tx_type];
112 const int16_t *const scan = av1_scan_orders[tx_size][tx_type].scan;
113 const int eob = width * height;
114 const int numTests = kNumTests / (width * height);
115
116 levels_ = set_levels(levels_buf_, width);
117 InitDataWithEob(scan, bwl, eob);
118
119 aom_usec_timer_start(&timer);
120 for (int i = 0; i < numTests; ++i) {
121 get_nz_map_contexts_func_(levels_, scan, eob, (TX_SIZE)tx_size,
122 tx_class, coeff_contexts_);
123 }
124 aom_usec_timer_mark(&timer);
125
126 const int elapsed_time = static_cast<int>(aom_usec_timer_elapsed(&timer));
127 printf("get_nz_map_contexts_%2dx%2d: %7.1f ms\n", real_width, real_height,
128 elapsed_time / 1000.0);
129 }
130 }
131
132 private:
InitDataWithEob(const int16_t * const scan,const int bwl,const int eob)133 void InitDataWithEob(const int16_t *const scan, const int bwl,
134 const int eob) {
135 memset(levels_buf_, 0, sizeof(levels_buf_));
136 memset(coeff_contexts_, 0, sizeof(*coeff_contexts_) * MAX_TX_SQUARE);
137
138 for (int c = 0; c < eob; ++c) {
139 levels_[get_padded_idx(scan[c], bwl)] =
140 static_cast<uint8_t>(clamp(rnd_.Rand8(), 0, INT8_MAX));
141 coeff_contexts_[scan[c]] = rnd_.Rand16() >> 1;
142 }
143
144 memcpy(coeff_contexts_ref_, coeff_contexts_,
145 sizeof(*coeff_contexts_) * MAX_TX_SQUARE);
146 }
147
Compare(const int16_t * const scan,const int eob) const148 bool Compare(const int16_t *const scan, const int eob) const {
149 bool result = false;
150 if (memcmp(coeff_contexts_, coeff_contexts_ref_,
151 sizeof(*coeff_contexts_ref_) * MAX_TX_SQUARE)) {
152 for (int i = 0; i < eob; i++) {
153 const int pos = scan[i];
154 if (coeff_contexts_ref_[pos] != coeff_contexts_[pos]) {
155 printf("coeff_contexts_[%d] diff:%6d (ref),%6d (opt)\n", pos,
156 coeff_contexts_ref_[pos], coeff_contexts_[pos]);
157 result = true;
158 break;
159 }
160 }
161 }
162 return result;
163 }
164
165 GetNzMapContextsFunc get_nz_map_contexts_func_;
166 ACMRandom rnd_;
167 uint8_t levels_buf_[TX_PAD_2D];
168 uint8_t *levels_;
169 int8_t *coeff_contexts_ref_;
170 int8_t *coeff_contexts_;
171 };
172
TEST_P(EncodeTxbTest,GetNzMapContexts)173 TEST_P(EncodeTxbTest, GetNzMapContexts) { GetNzMapContextsRun(); }
174
TEST_P(EncodeTxbTest,DISABLED_SpeedTestGetNzMapContexts)175 TEST_P(EncodeTxbTest, DISABLED_SpeedTestGetNzMapContexts) {
176 SpeedTestGetNzMapContextsRun();
177 }
178
179 #if HAVE_SSE2
180 INSTANTIATE_TEST_CASE_P(SSE2, EncodeTxbTest,
181 ::testing::Values(av1_get_nz_map_contexts_sse2));
182 #endif
183
184 typedef void (*av1_txb_init_levels_func)(const tran_low_t *const coeff,
185 const int width, const int height,
186 uint8_t *const levels);
187
188 typedef ::testing::tuple<av1_txb_init_levels_func, int> TxbInitLevelParam;
189
190 class EncodeTxbInitLevelTest
191 : public ::testing::TestWithParam<TxbInitLevelParam> {
192 public:
~EncodeTxbInitLevelTest()193 virtual ~EncodeTxbInitLevelTest() {}
TearDown()194 virtual void TearDown() { libaom_test::ClearSystemState(); }
195 void RunTest(av1_txb_init_levels_func test_func, int tx_size, int is_speed);
196 };
197
RunTest(av1_txb_init_levels_func test_func,int tx_size,int is_speed)198 void EncodeTxbInitLevelTest::RunTest(av1_txb_init_levels_func test_func,
199 int tx_size, int is_speed) {
200 const int width = get_txb_wide((TX_SIZE)tx_size);
201 const int height = get_txb_high((TX_SIZE)tx_size);
202 tran_low_t coeff[MAX_TX_SQUARE];
203
204 uint8_t levels_buf[2][TX_PAD_2D];
205 uint8_t *const levels0 = set_levels(levels_buf[0], width);
206 uint8_t *const levels1 = set_levels(levels_buf[1], width);
207
208 ACMRandom rnd(ACMRandom::DeterministicSeed());
209 for (int i = 0; i < width * height; i++) {
210 coeff[i] = rnd.Rand15Signed() + rnd.Rand15Signed();
211 }
212 for (int i = 0; i < TX_PAD_2D; i++) {
213 levels_buf[0][i] = rnd.Rand8();
214 levels_buf[1][i] = rnd.Rand8();
215 }
216 const int run_times = is_speed ? (width * height) * 10000 : 1;
217 aom_usec_timer timer;
218 aom_usec_timer_start(&timer);
219 for (int i = 0; i < run_times; ++i) {
220 av1_txb_init_levels_c(coeff, width, height, levels0);
221 }
222 const double t1 = get_time_mark(&timer);
223 aom_usec_timer_start(&timer);
224 for (int i = 0; i < run_times; ++i) {
225 test_func(coeff, width, height, levels1);
226 }
227 const double t2 = get_time_mark(&timer);
228 if (is_speed) {
229 printf("init %3dx%-3d:%7.2f/%7.2fns", width, height, t1, t2);
230 printf("(%3.2f)\n", t1 / t2);
231 }
232 const int stride = width + TX_PAD_HOR;
233 for (int r = 0; r < height + TX_PAD_VER; ++r) {
234 for (int c = 0; c < stride; ++c) {
235 ASSERT_EQ(levels_buf[0][c + r * stride], levels_buf[1][c + r * stride])
236 << "[" << r << "," << c << "] " << run_times << width << "x"
237 << height;
238 }
239 }
240 }
241
TEST_P(EncodeTxbInitLevelTest,match)242 TEST_P(EncodeTxbInitLevelTest, match) {
243 RunTest(GET_PARAM(0), GET_PARAM(1), 0);
244 }
245
TEST_P(EncodeTxbInitLevelTest,DISABLED_Speed)246 TEST_P(EncodeTxbInitLevelTest, DISABLED_Speed) {
247 RunTest(GET_PARAM(0), GET_PARAM(1), 1);
248 }
249
250 #if HAVE_SSE4_1
251 INSTANTIATE_TEST_CASE_P(
252 SSE4_1, EncodeTxbInitLevelTest,
253 ::testing::Combine(::testing::Values(&av1_txb_init_levels_sse4_1),
254 ::testing::Range(0, static_cast<int>(TX_SIZES_ALL), 1)));
255 #endif
256 #if HAVE_AVX2
257 INSTANTIATE_TEST_CASE_P(
258 AVX2, EncodeTxbInitLevelTest,
259 ::testing::Combine(::testing::Values(&av1_txb_init_levels_avx2),
260 ::testing::Range(0, static_cast<int>(TX_SIZES_ALL), 1)));
261 #endif
262 } // namespace
263