2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
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.
17 #include "config/av1_rtcd.h"
19 #include "aom_ports/aom_timer.h"
20 #include "av1/common/av1_inv_txfm1d_cfg.h"
21 #include "av1/common/scan.h"
22 #include "test/acm_random.h"
23 #include "test/av1_txfm_test.h"
24 #include "test/util.h"
26 using libaom_test::ACMRandom
;
27 using libaom_test::InvTxfm2dFunc
;
28 using libaom_test::LbdInvTxfm2dFunc
;
29 using libaom_test::bd
;
30 using libaom_test::compute_avg_abs_error
;
31 using libaom_test::input_base
;
33 using ::testing::Combine
;
34 using ::testing::Range
;
35 using ::testing::Values
;
41 // AV1InvTxfm2dParam argument list:
42 // tx_type_, tx_size_, max_error_, max_avg_error_
43 typedef ::testing::tuple
<TX_TYPE
, TX_SIZE
, int, double> AV1InvTxfm2dParam
;
45 class AV1InvTxfm2d
: public ::testing::TestWithParam
<AV1InvTxfm2dParam
> {
47 virtual void SetUp() {
48 tx_type_
= GET_PARAM(0);
49 tx_size_
= GET_PARAM(1);
50 max_error_
= GET_PARAM(2);
51 max_avg_error_
= GET_PARAM(3);
54 void RunRoundtripCheck() {
55 int tx_w
= tx_size_wide
[tx_size_
];
56 int tx_h
= tx_size_high
[tx_size_
];
57 int txfm2d_size
= tx_w
* tx_h
;
58 const FwdTxfm2dFunc fwd_txfm_func
= libaom_test::fwd_txfm_func_ls
[tx_size_
];
59 const InvTxfm2dFunc inv_txfm_func
= libaom_test::inv_txfm_func_ls
[tx_size_
];
60 double avg_abs_error
= 0;
61 ACMRandom
rnd(ACMRandom::DeterministicSeed());
63 const int count
= 500;
65 for (int ci
= 0; ci
< count
; ci
++) {
66 DECLARE_ALIGNED(16, int16_t, input
[64 * 64]) = { 0 };
67 ASSERT_LE(txfm2d_size
, NELEMENTS(input
));
69 for (int ni
= 0; ni
< txfm2d_size
; ++ni
) {
71 int extreme_input
= input_base
- 1;
72 input
[ni
] = extreme_input
; // extreme case
74 input
[ni
] = rnd
.Rand16() % input_base
;
78 DECLARE_ALIGNED(16, uint16_t, expected
[64 * 64]) = { 0 };
79 ASSERT_LE(txfm2d_size
, NELEMENTS(expected
));
80 if (TxfmUsesApproximation()) {
81 // Compare reference forward HT + inverse HT vs forward HT + inverse HT.
82 double ref_input
[64 * 64];
83 ASSERT_LE(txfm2d_size
, NELEMENTS(ref_input
));
84 for (int ni
= 0; ni
< txfm2d_size
; ++ni
) {
85 ref_input
[ni
] = input
[ni
];
87 double ref_coeffs
[64 * 64] = { 0 };
88 ASSERT_LE(txfm2d_size
, NELEMENTS(ref_coeffs
));
89 ASSERT_EQ(tx_type_
, DCT_DCT
);
90 libaom_test::reference_hybrid_2d(ref_input
, ref_coeffs
, tx_type_
,
92 DECLARE_ALIGNED(16, int32_t, ref_coeffs_int
[64 * 64]) = { 0 };
93 ASSERT_LE(txfm2d_size
, NELEMENTS(ref_coeffs_int
));
94 for (int ni
= 0; ni
< txfm2d_size
; ++ni
) {
95 ref_coeffs_int
[ni
] = (int32_t)round(ref_coeffs
[ni
]);
97 inv_txfm_func(ref_coeffs_int
, expected
, tx_w
, tx_type_
, bd
);
99 // Compare original input vs forward HT + inverse HT.
100 for (int ni
= 0; ni
< txfm2d_size
; ++ni
) {
101 expected
[ni
] = input
[ni
];
105 DECLARE_ALIGNED(16, int32_t, coeffs
[64 * 64]) = { 0 };
106 ASSERT_LE(txfm2d_size
, NELEMENTS(coeffs
));
107 fwd_txfm_func(input
, coeffs
, tx_w
, tx_type_
, bd
);
109 DECLARE_ALIGNED(16, uint16_t, actual
[64 * 64]) = { 0 };
110 ASSERT_LE(txfm2d_size
, NELEMENTS(actual
));
111 inv_txfm_func(coeffs
, actual
, tx_w
, tx_type_
, bd
);
113 double actual_max_error
= 0;
114 for (int ni
= 0; ni
< txfm2d_size
; ++ni
) {
115 const double this_error
= abs(expected
[ni
] - actual
[ni
]);
116 actual_max_error
= AOMMAX(actual_max_error
, this_error
);
118 EXPECT_GE(max_error_
, actual_max_error
)
119 << " tx_w: " << tx_w
<< " tx_h " << tx_h
<< " tx_type: " << tx_type_
;
120 if (actual_max_error
> max_error_
) { // exit early.
123 avg_abs_error
+= compute_avg_abs_error
<uint16_t, uint16_t>(
124 expected
, actual
, txfm2d_size
);
127 avg_abs_error
/= count
;
128 EXPECT_GE(max_avg_error_
, avg_abs_error
)
129 << " tx_w: " << tx_w
<< " tx_h " << tx_h
<< " tx_type: " << tx_type_
;
133 bool TxfmUsesApproximation() {
134 if (tx_size_wide
[tx_size_
] == 64 || tx_size_high
[tx_size_
] == 64) {
141 double max_avg_error_
;
146 static int max_error_ls
[TX_SIZES_ALL
] = {
149 2, // 16x16 transform
150 4, // 32x32 transform
151 3, // 64x64 transform
156 3, // 16x32 transform
157 3, // 32x16 transform
158 5, // 32x64 transform
159 5, // 64x32 transform
164 3, // 16x64 transform
165 3, // 64x16 transform
168 static double avg_error_ls
[TX_SIZES_ALL
] = {
169 0.002, // 4x4 transform
170 0.05, // 8x8 transform
171 0.07, // 16x16 transform
172 0.4, // 32x32 transform
173 0.3, // 64x64 transform
174 0.02, // 4x8 transform
175 0.02, // 8x4 transform
176 0.04, // 8x16 transform
177 0.07, // 16x8 transform
178 0.4, // 16x32 transform
179 0.5, // 32x16 transform
180 0.38, // 32x64 transform
181 0.39, // 64x32 transform
182 0.2, // 4x16 transform
183 0.2, // 16x4 transform
184 0.2, // 8x32 transform
185 0.2, // 32x8 transform
186 0.38, // 16x64 transform
187 0.38, // 64x16 transform
190 vector
<AV1InvTxfm2dParam
> GetInvTxfm2dParamList() {
191 vector
<AV1InvTxfm2dParam
> param_list
;
192 for (int s
= 0; s
< TX_SIZES
; ++s
) {
193 const int max_error
= max_error_ls
[s
];
194 const double avg_error
= avg_error_ls
[s
];
195 for (int t
= 0; t
< TX_TYPES
; ++t
) {
196 const TX_TYPE tx_type
= static_cast<TX_TYPE
>(t
);
197 const TX_SIZE tx_size
= static_cast<TX_SIZE
>(s
);
198 if (libaom_test::IsTxSizeTypeValid(tx_size
, tx_type
)) {
199 param_list
.push_back(
200 AV1InvTxfm2dParam(tx_type
, tx_size
, max_error
, avg_error
));
207 INSTANTIATE_TEST_CASE_P(C
, AV1InvTxfm2d
,
208 ::testing::ValuesIn(GetInvTxfm2dParamList()));
210 TEST_P(AV1InvTxfm2d
, RunRoundtripCheck
) { RunRoundtripCheck(); }
212 TEST(AV1InvTxfm2d
, CfgTest
) {
213 for (int bd_idx
= 0; bd_idx
< BD_NUM
; ++bd_idx
) {
214 int bd
= libaom_test::bd_arr
[bd_idx
];
215 int8_t low_range
= libaom_test::low_range_arr
[bd_idx
];
216 int8_t high_range
= libaom_test::high_range_arr
[bd_idx
];
217 for (int tx_size
= 0; tx_size
< TX_SIZES_ALL
; ++tx_size
) {
218 for (int tx_type
= 0; tx_type
< TX_TYPES
; ++tx_type
) {
219 if (libaom_test::IsTxSizeTypeValid(static_cast<TX_SIZE
>(tx_size
),
220 static_cast<TX_TYPE
>(tx_type
)) ==
224 TXFM_2D_FLIP_CFG cfg
;
225 av1_get_inv_txfm_cfg(static_cast<TX_TYPE
>(tx_type
),
226 static_cast<TX_SIZE
>(tx_size
), &cfg
);
227 int8_t stage_range_col
[MAX_TXFM_STAGE_NUM
];
228 int8_t stage_range_row
[MAX_TXFM_STAGE_NUM
];
229 av1_gen_inv_stage_range(stage_range_col
, stage_range_row
, &cfg
,
230 (TX_SIZE
)tx_size
, bd
);
231 libaom_test::txfm_stage_range_check(stage_range_col
, cfg
.stage_num_col
,
232 cfg
.cos_bit_col
, low_range
,
234 libaom_test::txfm_stage_range_check(stage_range_row
, cfg
.stage_num_row
,
235 cfg
.cos_bit_row
, low_range
,
242 typedef ::testing::tuple
<const LbdInvTxfm2dFunc
> AV1LbdInvTxfm2dParam
;
243 class AV1LbdInvTxfm2d
: public ::testing::TestWithParam
<AV1LbdInvTxfm2dParam
> {
245 virtual void SetUp() { target_func_
= GET_PARAM(0); }
246 void RunAV1InvTxfm2dTest(TX_TYPE tx_type
, TX_SIZE tx_size
, int run_times
);
249 LbdInvTxfm2dFunc target_func_
;
252 void AV1LbdInvTxfm2d::RunAV1InvTxfm2dTest(TX_TYPE tx_type
, TX_SIZE tx_size
,
254 FwdTxfm2dFunc fwd_func_
= libaom_test::fwd_txfm_func_ls
[tx_size
];
255 InvTxfm2dFunc ref_func_
= libaom_test::inv_txfm_func_ls
[tx_size
];
256 if (fwd_func_
== NULL
|| ref_func_
== NULL
|| target_func_
== NULL
) {
260 const int BLK_WIDTH
= 64;
261 const int BLK_SIZE
= BLK_WIDTH
* BLK_WIDTH
;
262 DECLARE_ALIGNED(16, int16_t, input
[BLK_SIZE
]) = { 0 };
263 DECLARE_ALIGNED(32, int32_t, inv_input
[BLK_SIZE
]) = { 0 };
264 DECLARE_ALIGNED(16, uint8_t, output
[BLK_SIZE
]) = { 0 };
265 DECLARE_ALIGNED(16, uint16_t, ref_output
[BLK_SIZE
]) = { 0 };
266 int stride
= BLK_WIDTH
;
267 int rows
= tx_size_high
[tx_size
];
268 int cols
= tx_size_wide
[tx_size
];
269 const int rows_nonezero
= AOMMIN(32, rows
);
270 const int cols_nonezero
= AOMMIN(32, cols
);
271 run_times
/= (rows
* cols
);
272 run_times
= AOMMAX(1, run_times
);
273 const SCAN_ORDER
*scan_order
= get_default_scan(tx_size
, tx_type
);
274 const int16_t *scan
= scan_order
->scan
;
275 const int16_t eobmax
= rows_nonezero
* cols_nonezero
;
276 ACMRandom
rnd(ACMRandom::DeterministicSeed());
277 int randTimes
= run_times
== 1 ? (eobmax
+ 500) : 1;
278 for (int cnt
= 0; cnt
< randTimes
; ++cnt
) {
279 const int16_t max_in
= (1 << (bd
)) - 1;
280 for (int r
= 0; r
< BLK_WIDTH
; ++r
) {
281 for (int c
= 0; c
< BLK_WIDTH
; ++c
) {
282 input
[r
* cols
+ c
] = (cnt
== 0) ? max_in
: rnd
.Rand8Extremes();
283 output
[r
* stride
+ c
] = (cnt
== 0) ? 128 : rnd
.Rand8();
284 ref_output
[r
* stride
+ c
] = output
[r
* stride
+ c
];
287 fwd_func_(input
, inv_input
, stride
, tx_type
, bd
);
289 // produce eob input by setting high freq coeffs to zero
290 const int eob
= AOMMIN(cnt
+ 1, eobmax
);
291 for (int i
= eob
; i
< eobmax
; i
++) {
292 inv_input
[scan
[i
]] = 0;
295 aom_usec_timer timer
;
296 aom_usec_timer_start(&timer
);
297 for (int i
= 0; i
< run_times
; ++i
) {
298 ref_func_(inv_input
, ref_output
, stride
, tx_type
, bd
);
300 aom_usec_timer_mark(&timer
);
301 const double time1
= static_cast<double>(aom_usec_timer_elapsed(&timer
));
302 aom_usec_timer_start(&timer
);
303 for (int i
= 0; i
< run_times
; ++i
) {
304 target_func_(inv_input
, output
, stride
, tx_type
, tx_size
, eob
);
306 aom_usec_timer_mark(&timer
);
307 const double time2
= static_cast<double>(aom_usec_timer_elapsed(&timer
));
308 if (run_times
> 10) {
309 printf("txfm[%d] %3dx%-3d:%7.2f/%7.2fns", tx_type
, cols
, rows
, time1
,
311 printf("(%3.2f)\n", time1
/ time2
);
313 for (int r
= 0; r
< rows
; ++r
) {
314 for (int c
= 0; c
< cols
; ++c
) {
315 uint8_t ref_value
= static_cast<uint8_t>(ref_output
[r
* stride
+ c
]);
316 ASSERT_EQ(ref_value
, output
[r
* stride
+ c
])
317 << "[" << r
<< "," << c
<< "] " << cnt
318 << " tx_size: " << static_cast<int>(tx_size
)
319 << " tx_type: " << tx_type
<< " eob " << eob
;
325 TEST_P(AV1LbdInvTxfm2d
, match
) {
326 for (int j
= 0; j
< (int)(TX_SIZES_ALL
); ++j
) {
327 for (int i
= 0; i
< (int)TX_TYPES
; ++i
) {
328 if (libaom_test::IsTxSizeTypeValid(static_cast<TX_SIZE
>(j
),
329 static_cast<TX_TYPE
>(i
))) {
330 RunAV1InvTxfm2dTest(static_cast<TX_TYPE
>(i
), static_cast<TX_SIZE
>(j
),
337 TEST_P(AV1LbdInvTxfm2d
, DISABLED_Speed
) {
338 for (int j
= 0; j
< (int)(TX_SIZES_ALL
); ++j
) {
339 for (int i
= 0; i
< (int)TX_TYPES
; ++i
) {
340 if (libaom_test::IsTxSizeTypeValid(static_cast<TX_SIZE
>(j
),
341 static_cast<TX_TYPE
>(i
))) {
342 RunAV1InvTxfm2dTest(static_cast<TX_TYPE
>(i
), static_cast<TX_SIZE
>(j
),
350 #if defined(_MSC_VER) || defined(__SSSE3__)
351 #include "av1/common/x86/av1_inv_txfm_ssse3.h"
352 INSTANTIATE_TEST_CASE_P(SSSE3
, AV1LbdInvTxfm2d
,
353 ::testing::Values(av1_lowbd_inv_txfm2d_add_ssse3
));
354 #endif // _MSC_VER || __SSSE3__
358 extern "C" void av1_lowbd_inv_txfm2d_add_avx2(const int32_t *input
,
359 uint8_t *output
, int stride
,
360 TX_TYPE tx_type
, TX_SIZE tx_size
,
363 INSTANTIATE_TEST_CASE_P(AVX2
, AV1LbdInvTxfm2d
,
364 ::testing::Values(av1_lowbd_inv_txfm2d_add_avx2
));
369 extern "C" void av1_lowbd_inv_txfm2d_add_neon(const int32_t *input
,
370 uint8_t *output
, int stride
,
371 TX_TYPE tx_type
, TX_SIZE tx_size
,
374 INSTANTIATE_TEST_CASE_P(NEON
, AV1LbdInvTxfm2d
,
375 ::testing::Values(av1_lowbd_inv_txfm2d_add_neon
));