2 * Copyright (c) 2018, 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.
14 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
16 #include "config/av1_rtcd.h"
18 #include "aom_ports/aom_timer.h"
19 #include "av1/common/convolve.h"
20 #include "av1/common/resize.h"
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"
27 const int kTestIters
= 10;
28 const int kPerfIters
= 1000;
33 using ::testing::make_tuple
;
34 using ::testing::tuple
;
35 using libaom_test::ACMRandom
;
37 template <typename Pixel
>
40 TestImage(int w_src
, int h
, int superres_denom
, int x0
, int bd
)
41 : w_src_(w_src
), h_(h
), superres_denom_(superres_denom
), x0_(x0
),
44 assert(bd
<= 8 * static_cast<int>(sizeof(Pixel
)));
45 assert(9 <= superres_denom
&& superres_denom
<= 16);
46 assert(SCALE_NUMERATOR
== 8);
47 assert(0 <= x0_
&& x0_
<= RS_SCALE_SUBPEL_MASK
);
50 av1_calculate_unscaled_superres_size(&w_dst_
, NULL
, superres_denom
);
52 src_stride_
= ALIGN_POWER_OF_TWO(w_src_
+ 2 * kHPad
, 4);
53 dst_stride_
= ALIGN_POWER_OF_TWO(w_dst_
+ 2 * kHPad
, 4);
55 // Allocate image data
56 src_data_
.resize(2 * src_block_size());
57 dst_data_
.resize(2 * dst_block_size());
60 void Initialize(ACMRandom
*rnd
);
63 int src_stride() const { return src_stride_
; }
64 int dst_stride() const { return dst_stride_
; }
66 int src_block_size() const { return (h_
+ 2 * kVPad
) * src_stride(); }
67 int dst_block_size() const { return (h_
+ 2 * kVPad
) * dst_stride(); }
69 int src_width() const { return w_src_
; }
70 int dst_width() const { return w_dst_
; }
71 int height() const { return h_
; }
72 int x0() const { return x0_
; }
74 const Pixel
*GetSrcData(bool ref
, bool borders
) const {
75 const Pixel
*block
= &src_data_
[ref
? 0 : src_block_size()];
76 return borders
? block
: block
+ kHPad
+ src_stride_
* kVPad
;
79 Pixel
*GetDstData(bool ref
, bool borders
) {
80 Pixel
*block
= &dst_data_
[ref
? 0 : dst_block_size()];
81 return borders
? block
: block
+ kHPad
+ dst_stride_
* kVPad
;
85 int w_src_
, w_dst_
, h_
, superres_denom_
, x0_
, bd_
;
86 int src_stride_
, dst_stride_
;
88 std::vector
<Pixel
> src_data_
;
89 std::vector
<Pixel
> dst_data_
;
92 template <typename Pixel
>
93 void FillEdge(ACMRandom
*rnd
, int num_pixels
, int bd
, bool trash
, Pixel
*data
) {
95 memset(data
, 0, sizeof(*data
) * num_pixels
);
98 const Pixel mask
= (1 << bd
) - 1;
99 for (int i
= 0; i
< num_pixels
; ++i
) data
[i
] = rnd
->Rand16() & mask
;
102 template <typename Pixel
>
103 void PrepBuffers(ACMRandom
*rnd
, int w
, int h
, int stride
, int bd
,
104 bool trash_edges
, Pixel
*data
) {
106 const Pixel mask
= (1 << bd
) - 1;
108 // Fill in the first buffer with random data
110 FillEdge(rnd
, stride
* kVPad
, bd
, trash_edges
, data
);
111 for (int r
= 0; r
< h
; ++r
) {
112 Pixel
*row_data
= data
+ (kVPad
+ r
) * stride
;
113 // Left border, contents, right border
114 FillEdge(rnd
, kHPad
, bd
, trash_edges
, row_data
);
115 for (int c
= 0; c
< w
; ++c
) row_data
[kHPad
+ c
] = rnd
->Rand16() & mask
;
116 FillEdge(rnd
, kHPad
, bd
, trash_edges
, row_data
+ kHPad
+ w
);
119 FillEdge(rnd
, stride
* kVPad
, bd
, trash_edges
, data
+ stride
* (kVPad
+ h
));
121 const int bpp
= sizeof(*data
);
122 const int block_elts
= stride
* (h
+ 2 * kVPad
);
123 const int block_size
= bpp
* block_elts
;
125 // Now copy that to the second buffer
126 memcpy(data
+ block_elts
, data
, block_size
);
129 template <typename Pixel
>
130 void TestImage
<Pixel
>::Initialize(ACMRandom
*rnd
) {
131 PrepBuffers(rnd
, w_src_
, h_
, src_stride_
, bd_
, false, &src_data_
[0]);
132 PrepBuffers(rnd
, w_dst_
, h_
, dst_stride_
, bd_
, true, &dst_data_
[0]);
135 template <typename Pixel
>
136 void TestImage
<Pixel
>::Check() const {
137 const int num_pixels
= dst_block_size();
138 const Pixel
*ref_dst
= &dst_data_
[0];
139 const Pixel
*tst_dst
= &dst_data_
[num_pixels
];
141 // If memcmp returns 0, there's nothing to do.
142 if (0 == memcmp(ref_dst
, tst_dst
, sizeof(*ref_dst
) * num_pixels
)) return;
144 // Otherwise, iterate through the buffer looking for differences, *ignoring
146 const int stride
= dst_stride_
;
147 for (int r
= kVPad
; r
< h_
+ kVPad
; ++r
) {
148 for (int c
= kVPad
; c
< w_dst_
+ kHPad
; ++c
) {
149 const int32_t ref_value
= ref_dst
[r
* stride
+ c
];
150 const int32_t tst_value
= tst_dst
[r
* stride
+ c
];
152 EXPECT_EQ(tst_value
, ref_value
)
153 << "Error at row: " << (r
- kVPad
) << ", col: " << (c
- kHPad
)
154 << ", superres_denom: " << superres_denom_
<< ", height: " << h_
155 << ", src_width: " << w_src_
<< ", dst_width: " << w_dst_
161 template <typename Pixel
>
162 class ConvolveHorizRSTestBase
: public ::testing::Test
{
164 ConvolveHorizRSTestBase() : image_(NULL
) {}
165 virtual ~ConvolveHorizRSTestBase() {}
166 virtual void TearDown() { libaom_test::ClearSystemState(); }
168 // Implemented by subclasses (SetUp depends on the parameters passed
169 // in and RunOne depends on the function to be tested. These can't
170 // be templated for low/high bit depths because they have different
171 // numbers of parameters)
172 virtual void SetUp() = 0;
173 virtual void RunOne(bool ref
) = 0;
176 void SetBitDepth(int bd
) { bd_
= bd
; }
178 void CorrectnessTest() {
179 ACMRandom
rnd(ACMRandom::DeterministicSeed());
180 for (int i
= 0; i
< kTestIters
; ++i
) {
181 for (int superres_denom
= 9; superres_denom
<= 16; superres_denom
++) {
182 // Get a random height between 512 and 767
183 int height
= rnd
.Rand8() + 512;
185 // Get a random src width between 128 and 383
186 int width_src
= rnd
.Rand8() + 128;
188 // x0 is normally calculated by get_upscale_convolve_x0 in
189 // av1/common/resize.c. However, this test should work for
190 // any value of x0 between 0 and RS_SCALE_SUBPEL_MASK
191 // (inclusive), so we choose one at random.
192 int x0
= rnd
.Rand16() % (RS_SCALE_SUBPEL_MASK
+ 1);
195 new TestImage
<Pixel
>(width_src
, height
, superres_denom
, x0
, bd_
);
208 // Pick some specific parameters to test
211 int superres_denom
= 13;
212 int x0
= RS_SCALE_SUBPEL_MASK
>> 1;
214 image_
= new TestImage
<Pixel
>(width_src
, height
, superres_denom
, x0
, bd_
);
216 ACMRandom
rnd(ACMRandom::DeterministicSeed());
219 aom_usec_timer ref_timer
;
220 aom_usec_timer_start(&ref_timer
);
221 for (int i
= 0; i
< kPerfIters
; ++i
) RunOne(true);
222 aom_usec_timer_mark(&ref_timer
);
223 const int64_t ref_time
= aom_usec_timer_elapsed(&ref_timer
);
225 aom_usec_timer tst_timer
;
226 aom_usec_timer_start(&tst_timer
);
227 for (int i
= 0; i
< kPerfIters
; ++i
) RunOne(false);
228 aom_usec_timer_mark(&tst_timer
);
229 const int64_t tst_time
= aom_usec_timer_elapsed(&tst_timer
);
231 std::cout
<< "[ ] C time = " << ref_time
/ 1000
232 << " ms, SIMD time = " << tst_time
/ 1000 << " ms\n";
234 EXPECT_GT(ref_time
, tst_time
)
235 << "Error: ConvolveHorizRSTest (Speed Test), SIMD slower than C.\n"
236 << "C time: " << ref_time
<< " us\n"
237 << "SIMD time: " << tst_time
<< " us\n";
240 void Prep(ACMRandom
*rnd
) {
242 image_
->Initialize(rnd
);
246 TestImage
<Pixel
> *image_
;
249 typedef void (*LowBDConvolveHorizRsFunc
)(const uint8_t *src
, int src_stride
,
250 uint8_t *dst
, int dst_stride
, int w
,
251 int h
, const int16_t *x_filters
,
252 const int x0_qn
, const int x_step_qn
);
254 // Test parameter list:
256 typedef tuple
<LowBDConvolveHorizRsFunc
> LowBDParams
;
258 class LowBDConvolveHorizRSTest
259 : public ConvolveHorizRSTestBase
<uint8_t>,
260 public ::testing::WithParamInterface
<LowBDParams
> {
262 virtual ~LowBDConvolveHorizRSTest() {}
265 tst_fun_
= GET_PARAM(0);
270 void RunOne(bool ref
) {
271 const uint8_t *src
= image_
->GetSrcData(ref
, false);
272 uint8_t *dst
= image_
->GetDstData(ref
, false);
273 const int src_stride
= image_
->src_stride();
274 const int dst_stride
= image_
->dst_stride();
275 const int width_src
= image_
->src_width();
276 const int width_dst
= image_
->dst_width();
277 const int height
= image_
->height();
278 const int x0_qn
= image_
->x0();
280 const int32_t x_step_qn
=
281 av1_get_upscale_convolve_step(width_src
, width_dst
);
284 av1_convolve_horiz_rs_c(src
, src_stride
, dst
, dst_stride
, width_dst
,
285 height
, &av1_resize_filter_normative
[0][0], x0_qn
,
288 tst_fun_(src
, src_stride
, dst
, dst_stride
, width_dst
, height
,
289 &av1_resize_filter_normative
[0][0], x0_qn
, x_step_qn
);
294 LowBDConvolveHorizRsFunc tst_fun_
;
297 TEST_P(LowBDConvolveHorizRSTest
, Correctness
) { CorrectnessTest(); }
298 TEST_P(LowBDConvolveHorizRSTest
, DISABLED_Speed
) { SpeedTest(); }
300 INSTANTIATE_TEST_CASE_P(SSE4_1
, LowBDConvolveHorizRSTest
,
301 ::testing::Values(av1_convolve_horiz_rs_sse4_1
));
303 typedef void (*HighBDConvolveHorizRsFunc
)(const uint16_t *src
, int src_stride
,
304 uint16_t *dst
, int dst_stride
, int w
,
305 int h
, const int16_t *x_filters
,
306 const int x0_qn
, const int x_step_qn
,
309 // Test parameter list:
311 typedef tuple
<HighBDConvolveHorizRsFunc
, int> HighBDParams
;
313 class HighBDConvolveHorizRSTest
314 : public ConvolveHorizRSTestBase
<uint16_t>,
315 public ::testing::WithParamInterface
<HighBDParams
> {
317 virtual ~HighBDConvolveHorizRSTest() {}
320 tst_fun_
= GET_PARAM(0);
321 const int bd
= GET_PARAM(1);
325 void RunOne(bool ref
) {
326 const uint16_t *src
= image_
->GetSrcData(ref
, false);
327 uint16_t *dst
= image_
->GetDstData(ref
, false);
328 const int src_stride
= image_
->src_stride();
329 const int dst_stride
= image_
->dst_stride();
330 const int width_src
= image_
->src_width();
331 const int width_dst
= image_
->dst_width();
332 const int height
= image_
->height();
333 const int x0_qn
= image_
->x0();
335 const int32_t x_step_qn
=
336 av1_get_upscale_convolve_step(width_src
, width_dst
);
339 av1_highbd_convolve_horiz_rs_c(
340 src
, src_stride
, dst
, dst_stride
, width_dst
, height
,
341 &av1_resize_filter_normative
[0][0], x0_qn
, x_step_qn
, bd_
);
343 tst_fun_(src
, src_stride
, dst
, dst_stride
, width_dst
, height
,
344 &av1_resize_filter_normative
[0][0], x0_qn
, x_step_qn
, bd_
);
349 HighBDConvolveHorizRsFunc tst_fun_
;
352 const int kBDs
[] = { 8, 10, 12 };
354 TEST_P(HighBDConvolveHorizRSTest
, Correctness
) { CorrectnessTest(); }
355 TEST_P(HighBDConvolveHorizRSTest
, DISABLED_Speed
) { SpeedTest(); }
357 INSTANTIATE_TEST_CASE_P(
358 SSE4_1
, HighBDConvolveHorizRSTest
,
359 ::testing::Combine(::testing::Values(av1_highbd_convolve_horiz_rs_sse4_1
),
360 ::testing::ValuesIn(kBDs
)));