xref: /aosp_15_r20/external/libaom/test/encodetxb_test.cc (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
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 #include <tuple>
16 
17 #include "gtest/gtest.h"
18 
19 #include "config/aom_config.h"
20 #include "config/av1_rtcd.h"
21 
22 #include "aom_ports/aom_timer.h"
23 #include "aom_ports/mem.h"
24 #include "av1/common/av1_common_int.h"
25 #include "av1/common/idct.h"
26 #include "av1/common/scan.h"
27 #include "av1/common/txb_common.h"
28 #include "test/acm_random.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 
45   ~EncodeTxbTest() override = default;
46 
SetUp()47   void SetUp() override {
48     coeff_contexts_ref_ = reinterpret_cast<int8_t *>(
49         aom_memalign(16, sizeof(*coeff_contexts_ref_) * MAX_TX_SQUARE));
50     ASSERT_NE(coeff_contexts_ref_, nullptr);
51     coeff_contexts_ = reinterpret_cast<int8_t *>(
52         aom_memalign(16, sizeof(*coeff_contexts_) * MAX_TX_SQUARE));
53     ASSERT_NE(coeff_contexts_, nullptr);
54   }
55 
TearDown()56   void TearDown() override {
57     aom_free(coeff_contexts_ref_);
58     aom_free(coeff_contexts_);
59   }
60 
GetNzMapContextsRun()61   void GetNzMapContextsRun() {
62     const int kNumTests = 10;
63     int result = 0;
64 
65     for (int is_inter = 0; is_inter < 2; ++is_inter) {
66       for (int tx_type = DCT_DCT; tx_type < TX_TYPES; ++tx_type) {
67         const TX_CLASS tx_class = tx_type_to_class[tx_type];
68         for (int tx_size = TX_4X4; tx_size < TX_SIZES_ALL; ++tx_size) {
69           const int bhl = get_txb_bhl((TX_SIZE)tx_size);
70           const int width = get_txb_wide((TX_SIZE)tx_size);
71           const int height = get_txb_high((TX_SIZE)tx_size);
72           const int real_width = tx_size_wide[tx_size];
73           const int real_height = tx_size_high[tx_size];
74           const int16_t *const scan = av1_scan_orders[tx_size][tx_type].scan;
75 
76           levels_ = set_levels(levels_buf_, height);
77           for (int i = 0; i < kNumTests && !result; ++i) {
78             for (int eob = 1; eob <= width * height && !result; ++eob) {
79               InitDataWithEob(scan, bhl, eob);
80 
81               av1_get_nz_map_contexts_c(levels_, scan, eob, (TX_SIZE)tx_size,
82                                         tx_class, coeff_contexts_ref_);
83               get_nz_map_contexts_func_(levels_, scan, eob, (TX_SIZE)tx_size,
84                                         tx_class, coeff_contexts_);
85 
86               result = Compare(scan, eob);
87 
88               EXPECT_EQ(result, 0)
89                   << " tx_class " << (int)tx_class << " width " << real_width
90                   << " height " << real_height << " eob " << eob;
91             }
92           }
93         }
94       }
95     }
96   }
97 
SpeedTestGetNzMapContextsRun()98   void SpeedTestGetNzMapContextsRun() {
99     const int kNumTests = 2000000000;
100     aom_usec_timer timer;
101     aom_usec_timer timer_ref;
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 bhl = get_txb_bhl((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_, height);
117       InitDataWithEob(scan, bhl, eob);
118 
119       aom_usec_timer_start(&timer_ref);
120       for (int i = 0; i < numTests; ++i) {
121         av1_get_nz_map_contexts_c(levels_, scan, eob, (TX_SIZE)tx_size,
122                                   tx_class, coeff_contexts_ref_);
123       }
124       aom_usec_timer_mark(&timer_ref);
125 
126       levels_ = set_levels(levels_buf_, height);
127       InitDataWithEob(scan, bhl, eob);
128 
129       aom_usec_timer_start(&timer);
130       for (int i = 0; i < numTests; ++i) {
131         get_nz_map_contexts_func_(levels_, scan, eob, (TX_SIZE)tx_size,
132                                   tx_class, coeff_contexts_);
133       }
134       aom_usec_timer_mark(&timer);
135 
136       const int elapsed_time_ref =
137           static_cast<int>(aom_usec_timer_elapsed(&timer_ref));
138       const int elapsed_time = static_cast<int>(aom_usec_timer_elapsed(&timer));
139 
140       printf("get_nz_map_contexts_%2dx%2d: %7.1f ms ref %7.1f ms gain %4.2f\n",
141              real_width, real_height, elapsed_time / 1000.0,
142              elapsed_time_ref / 1000.0,
143              (elapsed_time_ref * 1.0) / (elapsed_time * 1.0));
144     }
145   }
146 
147  private:
InitDataWithEob(const int16_t * const scan,const int bhl,const int eob)148   void InitDataWithEob(const int16_t *const scan, const int bhl,
149                        const int eob) {
150     memset(levels_buf_, 0, sizeof(levels_buf_));
151     memset(coeff_contexts_, 0, sizeof(*coeff_contexts_) * MAX_TX_SQUARE);
152 
153     for (int c = 0; c < eob; ++c) {
154       levels_[get_padded_idx(scan[c], bhl)] =
155           static_cast<uint8_t>(clamp(rnd_.Rand8(), 0, INT8_MAX));
156       coeff_contexts_[scan[c]] = static_cast<int8_t>(rnd_.Rand16() >> 1);
157     }
158 
159     memcpy(coeff_contexts_ref_, coeff_contexts_,
160            sizeof(*coeff_contexts_) * MAX_TX_SQUARE);
161   }
162 
Compare(const int16_t * const scan,const int eob) const163   bool Compare(const int16_t *const scan, const int eob) const {
164     bool result = false;
165     if (memcmp(coeff_contexts_, coeff_contexts_ref_,
166                sizeof(*coeff_contexts_ref_) * MAX_TX_SQUARE)) {
167       for (int i = 0; i < eob; i++) {
168         const int pos = scan[i];
169         if (coeff_contexts_ref_[pos] != coeff_contexts_[pos]) {
170           printf("coeff_contexts_[%d] diff:%6d (ref),%6d (opt)\n", pos,
171                  coeff_contexts_ref_[pos], coeff_contexts_[pos]);
172           result = true;
173           break;
174         }
175       }
176     }
177     return result;
178   }
179 
180   GetNzMapContextsFunc get_nz_map_contexts_func_;
181   ACMRandom rnd_;
182   uint8_t levels_buf_[TX_PAD_2D];
183   uint8_t *levels_;
184   int8_t *coeff_contexts_ref_;
185   int8_t *coeff_contexts_;
186 };
187 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EncodeTxbTest);
188 
TEST_P(EncodeTxbTest,GetNzMapContexts)189 TEST_P(EncodeTxbTest, GetNzMapContexts) { GetNzMapContextsRun(); }
190 
TEST_P(EncodeTxbTest,DISABLED_SpeedTestGetNzMapContexts)191 TEST_P(EncodeTxbTest, DISABLED_SpeedTestGetNzMapContexts) {
192   SpeedTestGetNzMapContextsRun();
193 }
194 
195 #if HAVE_SSE2
196 INSTANTIATE_TEST_SUITE_P(SSE2, EncodeTxbTest,
197                          ::testing::Values(av1_get_nz_map_contexts_sse2));
198 #endif
199 
200 #if HAVE_NEON
201 INSTANTIATE_TEST_SUITE_P(NEON, EncodeTxbTest,
202                          ::testing::Values(av1_get_nz_map_contexts_neon));
203 #endif
204 
205 typedef void (*av1_txb_init_levels_func)(const tran_low_t *const coeff,
206                                          const int width, const int height,
207                                          uint8_t *const levels);
208 
209 typedef std::tuple<av1_txb_init_levels_func, int> TxbInitLevelParam;
210 
211 class EncodeTxbInitLevelTest
212     : public ::testing::TestWithParam<TxbInitLevelParam> {
213  public:
214   ~EncodeTxbInitLevelTest() override = default;
215   void RunTest(av1_txb_init_levels_func test_func, int tx_size, int is_speed);
216 };
217 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EncodeTxbInitLevelTest);
218 
RunTest(av1_txb_init_levels_func test_func,int tx_size,int is_speed)219 void EncodeTxbInitLevelTest::RunTest(av1_txb_init_levels_func test_func,
220                                      int tx_size, int is_speed) {
221   const int width = get_txb_wide((TX_SIZE)tx_size);
222   const int height = get_txb_high((TX_SIZE)tx_size);
223   tran_low_t coeff[MAX_TX_SQUARE];
224 
225   uint8_t levels_buf[2][TX_PAD_2D];
226   uint8_t *const levels0 = set_levels(levels_buf[0], height);
227   uint8_t *const levels1 = set_levels(levels_buf[1], height);
228 
229   ACMRandom rnd(ACMRandom::DeterministicSeed());
230   for (int i = 0; i < width * height; i++) {
231     coeff[i] = rnd.Rand16Signed();
232   }
233   for (int i = 0; i < TX_PAD_2D; i++) {
234     levels_buf[0][i] = rnd.Rand8();
235     levels_buf[1][i] = rnd.Rand8();
236   }
237   const int run_times = is_speed ? (width * height) * 10000 : 1;
238   aom_usec_timer timer;
239   aom_usec_timer_start(&timer);
240   for (int i = 0; i < run_times; ++i) {
241     av1_txb_init_levels_c(coeff, width, height, levels0);
242   }
243   const double t1 = get_time_mark(&timer);
244   aom_usec_timer_start(&timer);
245   for (int i = 0; i < run_times; ++i) {
246     test_func(coeff, width, height, levels1);
247   }
248   const double t2 = get_time_mark(&timer);
249   if (is_speed) {
250     printf("init %3dx%-3d:%7.2f/%7.2fns", width, height, t1, t2);
251     printf("(%3.2f)\n", t1 / t2);
252   }
253   const int stride = width + TX_PAD_HOR;
254   for (int r = 0; r < height + TX_PAD_VER; ++r) {
255     for (int c = 0; c < stride; ++c) {
256       ASSERT_EQ(levels_buf[0][c + r * stride], levels_buf[1][c + r * stride])
257           << "[" << r << "," << c << "] " << run_times << width << "x"
258           << height;
259     }
260   }
261 }
262 
TEST_P(EncodeTxbInitLevelTest,match)263 TEST_P(EncodeTxbInitLevelTest, match) {
264   RunTest(GET_PARAM(0), GET_PARAM(1), 0);
265 }
266 
TEST_P(EncodeTxbInitLevelTest,DISABLED_Speed)267 TEST_P(EncodeTxbInitLevelTest, DISABLED_Speed) {
268   RunTest(GET_PARAM(0), GET_PARAM(1), 1);
269 }
270 
271 #if HAVE_SSE4_1
272 INSTANTIATE_TEST_SUITE_P(
273     SSE4_1, EncodeTxbInitLevelTest,
274     ::testing::Combine(::testing::Values(&av1_txb_init_levels_sse4_1),
275                        ::testing::Range(0, static_cast<int>(TX_SIZES_ALL), 1)));
276 #endif
277 #if HAVE_AVX2
278 INSTANTIATE_TEST_SUITE_P(
279     AVX2, EncodeTxbInitLevelTest,
280     ::testing::Combine(::testing::Values(&av1_txb_init_levels_avx2),
281                        ::testing::Range(0, static_cast<int>(TX_SIZES_ALL), 1)));
282 #endif
283 #if HAVE_NEON
284 INSTANTIATE_TEST_SUITE_P(
285     NEON, EncodeTxbInitLevelTest,
286     ::testing::Combine(::testing::Values(&av1_txb_init_levels_neon),
287                        ::testing::Range(0, static_cast<int>(TX_SIZES_ALL), 1)));
288 #endif
289 }  // namespace
290