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.
11 #include "aom_ports/aom_timer.h"
12 #include "test/warp_filter_test_util.h"
14 using ::testing::make_tuple
;
15 using ::testing::tuple
;
17 namespace libaom_test
{
19 int32_t random_warped_param(libaom_test::ACMRandom
*rnd
, int bits
) {
20 // 1 in 8 chance of generating zero (arbitrarily chosen)
21 if (((rnd
->Rand8()) & 7) == 0) return 0;
22 // Otherwise, enerate uniform values in the range
23 // [-(1 << bits), 1] U [1, 1<<bits]
24 int32_t v
= 1 + (rnd
->Rand16() & ((1 << bits
) - 1));
25 if ((rnd
->Rand8()) & 1) return -v
;
29 void generate_warped_model(libaom_test::ACMRandom
*rnd
, int32_t *mat
,
30 int16_t *alpha
, int16_t *beta
, int16_t *gamma
,
31 int16_t *delta
, const int is_alpha_zero
,
32 const int is_beta_zero
, const int is_gamma_zero
,
33 const int is_delta_zero
) {
35 int rnd8
= rnd
->Rand8() & 3;
36 mat
[0] = random_warped_param(rnd
, WARPEDMODEL_PREC_BITS
+ 6);
37 mat
[1] = random_warped_param(rnd
, WARPEDMODEL_PREC_BITS
+ 6);
38 mat
[2] = (random_warped_param(rnd
, WARPEDMODEL_PREC_BITS
- 3)) +
39 (1 << WARPEDMODEL_PREC_BITS
);
40 mat
[3] = random_warped_param(rnd
, WARPEDMODEL_PREC_BITS
- 3);
44 mat
[4] = random_warped_param(rnd
, WARPEDMODEL_PREC_BITS
- 3);
45 mat
[5] = (random_warped_param(rnd
, WARPEDMODEL_PREC_BITS
- 3)) +
46 (1 << WARPEDMODEL_PREC_BITS
);
47 } else if (rnd8
== 2) {
51 mat
[4] = random_warped_param(rnd
, WARPEDMODEL_PREC_BITS
- 3);
52 mat
[5] = (random_warped_param(rnd
, WARPEDMODEL_PREC_BITS
- 3)) +
53 (1 << WARPEDMODEL_PREC_BITS
);
54 if (is_alpha_zero
== 1) mat
[2] = 1 << WARPEDMODEL_PREC_BITS
;
55 if (is_beta_zero
== 1) mat
[3] = 0;
56 if (is_gamma_zero
== 1) mat
[4] = 0;
57 if (is_delta_zero
== 1)
58 mat
[5] = (((int64_t)mat
[3] * mat
[4] + (mat
[2] / 2)) / mat
[2]) +
59 (1 << WARPEDMODEL_PREC_BITS
);
62 // Calculate the derived parameters and check that they are suitable
63 // for the warp filter.
66 *alpha
= clamp(mat
[2] - (1 << WARPEDMODEL_PREC_BITS
), INT16_MIN
, INT16_MAX
);
67 *beta
= clamp(mat
[3], INT16_MIN
, INT16_MAX
);
68 *gamma
= clamp(((int64_t)mat
[4] * (1 << WARPEDMODEL_PREC_BITS
)) / mat
[2],
69 INT16_MIN
, INT16_MAX
);
71 clamp(mat
[5] - (((int64_t)mat
[3] * mat
[4] + (mat
[2] / 2)) / mat
[2]) -
72 (1 << WARPEDMODEL_PREC_BITS
),
73 INT16_MIN
, INT16_MAX
);
75 if ((4 * abs(*alpha
) + 7 * abs(*beta
) >= (1 << WARPEDMODEL_PREC_BITS
)) ||
76 (4 * abs(*gamma
) + 4 * abs(*delta
) >= (1 << WARPEDMODEL_PREC_BITS
)))
79 *alpha
= ROUND_POWER_OF_TWO_SIGNED(*alpha
, WARP_PARAM_REDUCE_BITS
) *
80 (1 << WARP_PARAM_REDUCE_BITS
);
81 *beta
= ROUND_POWER_OF_TWO_SIGNED(*beta
, WARP_PARAM_REDUCE_BITS
) *
82 (1 << WARP_PARAM_REDUCE_BITS
);
83 *gamma
= ROUND_POWER_OF_TWO_SIGNED(*gamma
, WARP_PARAM_REDUCE_BITS
) *
84 (1 << WARP_PARAM_REDUCE_BITS
);
85 *delta
= ROUND_POWER_OF_TWO_SIGNED(*delta
, WARP_PARAM_REDUCE_BITS
) *
86 (1 << WARP_PARAM_REDUCE_BITS
);
88 // We have a valid model, so finish
93 namespace AV1WarpFilter
{
94 ::testing::internal::ParamGenerator
<WarpTestParams
> BuildParams(
95 warp_affine_func filter
) {
96 WarpTestParam params
[] = {
97 make_tuple(4, 4, 50000, filter
), make_tuple(8, 8, 50000, filter
),
98 make_tuple(64, 64, 1000, filter
), make_tuple(4, 16, 20000, filter
),
99 make_tuple(32, 8, 10000, filter
),
101 return ::testing::Combine(::testing::ValuesIn(params
),
102 ::testing::Values(0, 1), ::testing::Values(0, 1),
103 ::testing::Values(0, 1), ::testing::Values(0, 1));
106 AV1WarpFilterTest::~AV1WarpFilterTest() {}
107 void AV1WarpFilterTest::SetUp() { rnd_
.Reset(ACMRandom::DeterministicSeed()); }
109 void AV1WarpFilterTest::TearDown() { libaom_test::ClearSystemState(); }
111 void AV1WarpFilterTest::RunSpeedTest(warp_affine_func test_impl
) {
112 const int w
= 128, h
= 128;
113 const int border
= 16;
114 const int stride
= w
+ 2 * border
;
115 WarpTestParam params
= GET_PARAM(0);
116 const int out_w
= ::testing::get
<0>(params
),
117 out_h
= ::testing::get
<1>(params
);
118 const int is_alpha_zero
= GET_PARAM(1);
119 const int is_beta_zero
= GET_PARAM(2);
120 const int is_gamma_zero
= GET_PARAM(3);
121 const int is_delta_zero
= GET_PARAM(4);
125 uint8_t *input_
= new uint8_t[h
* stride
];
126 uint8_t *input
= input_
+ border
;
128 // The warp functions always write rows with widths that are multiples of 8.
129 // So to avoid a buffer overflow, we may need to pad rows to a multiple of 8.
130 int output_n
= ((out_w
+ 7) & ~7) * out_h
;
131 uint8_t *output
= new uint8_t[output_n
];
133 int16_t alpha
, beta
, gamma
, delta
;
134 ConvolveParams conv_params
= get_conv_params(0, 0, bd
);
135 CONV_BUF_TYPE
*dsta
= new CONV_BUF_TYPE
[output_n
];
136 generate_warped_model(&rnd_
, mat
, &alpha
, &beta
, &gamma
, &delta
,
137 is_alpha_zero
, is_beta_zero
, is_gamma_zero
,
140 for (int r
= 0; r
< h
; ++r
)
141 for (int c
= 0; c
< w
; ++c
) input
[r
* stride
+ c
] = rnd_
.Rand8();
142 for (int r
= 0; r
< h
; ++r
) {
143 memset(input
+ r
* stride
- border
, input
[r
* stride
], border
);
144 memset(input
+ r
* stride
+ w
, input
[r
* stride
+ (w
- 1)], border
);
151 conv_params
= get_conv_params_no_round(do_average
, 0, dsta
, out_w
, 1, bd
);
152 conv_params
.use_jnt_comp_avg
= 0;
154 const int num_loops
= 1000000000 / (out_w
+ out_h
);
155 aom_usec_timer timer
;
156 aom_usec_timer_start(&timer
);
157 for (int i
= 0; i
< num_loops
; ++i
)
158 test_impl(mat
, input
, w
, h
, stride
, output
, 32, 32, out_w
, out_h
, out_w
,
159 sub_x
, sub_y
, &conv_params
, alpha
, beta
, gamma
, delta
);
161 aom_usec_timer_mark(&timer
);
162 const int elapsed_time
= static_cast<int>(aom_usec_timer_elapsed(&timer
));
163 printf("warp %3dx%-3d: %7.2f ns\n", out_w
, out_h
,
164 1000.0 * elapsed_time
/ num_loops
);
171 void AV1WarpFilterTest::RunCheckOutput(warp_affine_func test_impl
) {
172 const int w
= 128, h
= 128;
173 const int border
= 16;
174 const int stride
= w
+ 2 * border
;
175 WarpTestParam params
= GET_PARAM(0);
176 const int is_alpha_zero
= GET_PARAM(1);
177 const int is_beta_zero
= GET_PARAM(2);
178 const int is_gamma_zero
= GET_PARAM(3);
179 const int is_delta_zero
= GET_PARAM(4);
180 const int out_w
= ::testing::get
<0>(params
),
181 out_h
= ::testing::get
<1>(params
);
182 const int num_iters
= ::testing::get
<2>(params
);
183 int i
, j
, sub_x
, sub_y
;
186 // The warp functions always write rows with widths that are multiples of 8.
187 // So to avoid a buffer overflow, we may need to pad rows to a multiple of 8.
188 int output_n
= ((out_w
+ 7) & ~7) * out_h
;
189 uint8_t *input_
= new uint8_t[h
* stride
];
190 uint8_t *input
= input_
+ border
;
191 uint8_t *output
= new uint8_t[output_n
];
192 uint8_t *output2
= new uint8_t[output_n
];
194 int16_t alpha
, beta
, gamma
, delta
;
195 ConvolveParams conv_params
= get_conv_params(0, 0, bd
);
196 CONV_BUF_TYPE
*dsta
= new CONV_BUF_TYPE
[output_n
];
197 CONV_BUF_TYPE
*dstb
= new CONV_BUF_TYPE
[output_n
];
198 for (int i
= 0; i
< output_n
; ++i
) output
[i
] = output2
[i
] = rnd_
.Rand8();
200 for (i
= 0; i
< num_iters
; ++i
) {
201 // Generate an input block and extend its borders horizontally
202 for (int r
= 0; r
< h
; ++r
)
203 for (int c
= 0; c
< w
; ++c
) input
[r
* stride
+ c
] = rnd_
.Rand8();
204 for (int r
= 0; r
< h
; ++r
) {
205 memset(input
+ r
* stride
- border
, input
[r
* stride
], border
);
206 memset(input
+ r
* stride
+ w
, input
[r
* stride
+ (w
- 1)], border
);
208 const int use_no_round
= rnd_
.Rand8() & 1;
209 for (sub_x
= 0; sub_x
< 2; ++sub_x
)
210 for (sub_y
= 0; sub_y
< 2; ++sub_y
) {
211 generate_warped_model(&rnd_
, mat
, &alpha
, &beta
, &gamma
, &delta
,
212 is_alpha_zero
, is_beta_zero
, is_gamma_zero
,
215 for (int ii
= 0; ii
< 2; ++ii
) {
216 for (int jj
= 0; jj
< 5; ++jj
) {
217 for (int do_average
= 0; do_average
<= 1; ++do_average
) {
220 get_conv_params_no_round(do_average
, 0, dsta
, out_w
, 1, bd
);
222 conv_params
= get_conv_params(0, 0, bd
);
225 conv_params
.use_jnt_comp_avg
= 0;
227 conv_params
.use_jnt_comp_avg
= 1;
228 conv_params
.fwd_offset
= quant_dist_lookup_table
[ii
][jj
][0];
229 conv_params
.bck_offset
= quant_dist_lookup_table
[ii
][jj
][1];
231 av1_warp_affine_c(mat
, input
, w
, h
, stride
, output
, 32, 32, out_w
,
232 out_h
, out_w
, sub_x
, sub_y
, &conv_params
, alpha
,
236 get_conv_params_no_round(do_average
, 0, dstb
, out_w
, 1, bd
);
239 conv_params
.use_jnt_comp_avg
= 0;
241 conv_params
.use_jnt_comp_avg
= 1;
242 conv_params
.fwd_offset
= quant_dist_lookup_table
[ii
][jj
][0];
243 conv_params
.bck_offset
= quant_dist_lookup_table
[ii
][jj
][1];
245 test_impl(mat
, input
, w
, h
, stride
, output2
, 32, 32, out_w
, out_h
,
246 out_w
, sub_x
, sub_y
, &conv_params
, alpha
, beta
, gamma
,
249 for (j
= 0; j
< out_w
* out_h
; ++j
)
250 ASSERT_EQ(dsta
[j
], dstb
[j
])
251 << "Pixel mismatch at index " << j
<< " = ("
252 << (j
% out_w
) << ", " << (j
/ out_w
) << ") on iteration "
254 for (j
= 0; j
< out_w
* out_h
; ++j
)
255 ASSERT_EQ(output
[j
], output2
[j
])
256 << "Pixel mismatch at index " << j
<< " = ("
257 << (j
% out_w
) << ", " << (j
/ out_w
) << ") on iteration "
260 for (j
= 0; j
< out_w
* out_h
; ++j
)
261 ASSERT_EQ(output
[j
], output2
[j
])
262 << "Pixel mismatch at index " << j
<< " = ("
263 << (j
% out_w
) << ", " << (j
/ out_w
) << ") on iteration "
277 } // namespace AV1WarpFilter
279 namespace AV1HighbdWarpFilter
{
280 ::testing::internal::ParamGenerator
<HighbdWarpTestParams
> BuildParams(
281 highbd_warp_affine_func filter
) {
282 const HighbdWarpTestParam params
[] = {
283 make_tuple(4, 4, 100, 8, filter
), make_tuple(8, 8, 100, 8, filter
),
284 make_tuple(64, 64, 100, 8, filter
), make_tuple(4, 16, 100, 8, filter
),
285 make_tuple(32, 8, 100, 8, filter
), make_tuple(4, 4, 100, 10, filter
),
286 make_tuple(8, 8, 100, 10, filter
), make_tuple(64, 64, 100, 10, filter
),
287 make_tuple(4, 16, 100, 10, filter
), make_tuple(32, 8, 100, 10, filter
),
288 make_tuple(4, 4, 100, 12, filter
), make_tuple(8, 8, 100, 12, filter
),
289 make_tuple(64, 64, 100, 12, filter
), make_tuple(4, 16, 100, 12, filter
),
290 make_tuple(32, 8, 100, 12, filter
),
292 return ::testing::Combine(::testing::ValuesIn(params
),
293 ::testing::Values(0, 1), ::testing::Values(0, 1),
294 ::testing::Values(0, 1), ::testing::Values(0, 1));
297 AV1HighbdWarpFilterTest::~AV1HighbdWarpFilterTest() {}
298 void AV1HighbdWarpFilterTest::SetUp() {
299 rnd_
.Reset(ACMRandom::DeterministicSeed());
302 void AV1HighbdWarpFilterTest::TearDown() { libaom_test::ClearSystemState(); }
304 void AV1HighbdWarpFilterTest::RunSpeedTest(highbd_warp_affine_func test_impl
) {
305 const int w
= 128, h
= 128;
306 const int border
= 16;
307 const int stride
= w
+ 2 * border
;
308 HighbdWarpTestParam param
= GET_PARAM(0);
309 const int is_alpha_zero
= GET_PARAM(1);
310 const int is_beta_zero
= GET_PARAM(2);
311 const int is_gamma_zero
= GET_PARAM(3);
312 const int is_delta_zero
= GET_PARAM(4);
313 const int out_w
= ::testing::get
<0>(param
), out_h
= ::testing::get
<1>(param
);
314 const int bd
= ::testing::get
<3>(param
);
315 const int mask
= (1 << bd
) - 1;
318 // The warp functions always write rows with widths that are multiples of 8.
319 // So to avoid a buffer overflow, we may need to pad rows to a multiple of 8.
320 int output_n
= ((out_w
+ 7) & ~7) * out_h
;
321 uint16_t *input_
= new uint16_t[h
* stride
];
322 uint16_t *input
= input_
+ border
;
323 uint16_t *output
= new uint16_t[output_n
];
325 int16_t alpha
, beta
, gamma
, delta
;
326 ConvolveParams conv_params
= get_conv_params(0, 0, bd
);
327 CONV_BUF_TYPE
*dsta
= new CONV_BUF_TYPE
[output_n
];
329 generate_warped_model(&rnd_
, mat
, &alpha
, &beta
, &gamma
, &delta
,
330 is_alpha_zero
, is_beta_zero
, is_gamma_zero
,
332 // Generate an input block and extend its borders horizontally
333 for (int r
= 0; r
< h
; ++r
)
334 for (int c
= 0; c
< w
; ++c
) input
[r
* stride
+ c
] = rnd_
.Rand16() & mask
;
335 for (int r
= 0; r
< h
; ++r
) {
336 for (int c
= 0; c
< border
; ++c
) {
337 input
[r
* stride
- border
+ c
] = input
[r
* stride
];
338 input
[r
* stride
+ w
+ c
] = input
[r
* stride
+ (w
- 1)];
345 conv_params
.use_jnt_comp_avg
= 0;
346 conv_params
= get_conv_params_no_round(do_average
, 0, dsta
, out_w
, 1, bd
);
348 const int num_loops
= 1000000000 / (out_w
+ out_h
);
349 aom_usec_timer timer
;
350 aom_usec_timer_start(&timer
);
352 for (int i
= 0; i
< num_loops
; ++i
)
353 test_impl(mat
, input
, w
, h
, stride
, output
, 32, 32, out_w
, out_h
, out_w
,
354 sub_x
, sub_y
, bd
, &conv_params
, alpha
, beta
, gamma
, delta
);
356 aom_usec_timer_mark(&timer
);
357 const int elapsed_time
= static_cast<int>(aom_usec_timer_elapsed(&timer
));
358 printf("highbd warp %3dx%-3d: %7.2f ns\n", out_w
, out_h
,
359 1000.0 * elapsed_time
/ num_loops
);
366 void AV1HighbdWarpFilterTest::RunCheckOutput(
367 highbd_warp_affine_func test_impl
) {
368 const int w
= 128, h
= 128;
369 const int border
= 16;
370 const int stride
= w
+ 2 * border
;
371 HighbdWarpTestParam param
= GET_PARAM(0);
372 const int is_alpha_zero
= GET_PARAM(1);
373 const int is_beta_zero
= GET_PARAM(2);
374 const int is_gamma_zero
= GET_PARAM(3);
375 const int is_delta_zero
= GET_PARAM(4);
376 const int out_w
= ::testing::get
<0>(param
), out_h
= ::testing::get
<1>(param
);
377 const int bd
= ::testing::get
<3>(param
);
378 const int num_iters
= ::testing::get
<2>(param
);
379 const int mask
= (1 << bd
) - 1;
380 int i
, j
, sub_x
, sub_y
;
382 // The warp functions always write rows with widths that are multiples of 8.
383 // So to avoid a buffer overflow, we may need to pad rows to a multiple of 8.
384 int output_n
= ((out_w
+ 7) & ~7) * out_h
;
385 uint16_t *input_
= new uint16_t[h
* stride
];
386 uint16_t *input
= input_
+ border
;
387 uint16_t *output
= new uint16_t[output_n
];
388 uint16_t *output2
= new uint16_t[output_n
];
390 int16_t alpha
, beta
, gamma
, delta
;
391 ConvolveParams conv_params
= get_conv_params(0, 0, bd
);
392 CONV_BUF_TYPE
*dsta
= new CONV_BUF_TYPE
[output_n
];
393 CONV_BUF_TYPE
*dstb
= new CONV_BUF_TYPE
[output_n
];
394 for (int i
= 0; i
< output_n
; ++i
) output
[i
] = output2
[i
] = rnd_
.Rand16();
396 for (i
= 0; i
< num_iters
; ++i
) {
397 // Generate an input block and extend its borders horizontally
398 for (int r
= 0; r
< h
; ++r
)
399 for (int c
= 0; c
< w
; ++c
) input
[r
* stride
+ c
] = rnd_
.Rand16() & mask
;
400 for (int r
= 0; r
< h
; ++r
) {
401 for (int c
= 0; c
< border
; ++c
) {
402 input
[r
* stride
- border
+ c
] = input
[r
* stride
];
403 input
[r
* stride
+ w
+ c
] = input
[r
* stride
+ (w
- 1)];
406 const int use_no_round
= rnd_
.Rand8() & 1;
407 for (sub_x
= 0; sub_x
< 2; ++sub_x
)
408 for (sub_y
= 0; sub_y
< 2; ++sub_y
) {
409 generate_warped_model(&rnd_
, mat
, &alpha
, &beta
, &gamma
, &delta
,
410 is_alpha_zero
, is_beta_zero
, is_gamma_zero
,
412 for (int ii
= 0; ii
< 2; ++ii
) {
413 for (int jj
= 0; jj
< 5; ++jj
) {
414 for (int do_average
= 0; do_average
<= 1; ++do_average
) {
417 get_conv_params_no_round(do_average
, 0, dsta
, out_w
, 1, bd
);
419 conv_params
= get_conv_params(0, 0, bd
);
422 conv_params
.use_jnt_comp_avg
= 0;
424 conv_params
.use_jnt_comp_avg
= 1;
425 conv_params
.fwd_offset
= quant_dist_lookup_table
[ii
][jj
][0];
426 conv_params
.bck_offset
= quant_dist_lookup_table
[ii
][jj
][1];
429 av1_highbd_warp_affine_c(mat
, input
, w
, h
, stride
, output
, 32, 32,
430 out_w
, out_h
, out_w
, sub_x
, sub_y
, bd
,
431 &conv_params
, alpha
, beta
, gamma
, delta
);
433 // TODO(angiebird): Change this to test_impl once we have SIMD
436 get_conv_params_no_round(do_average
, 0, dstb
, out_w
, 1, bd
);
439 conv_params
.use_jnt_comp_avg
= 0;
441 conv_params
.use_jnt_comp_avg
= 1;
442 conv_params
.fwd_offset
= quant_dist_lookup_table
[ii
][jj
][0];
443 conv_params
.bck_offset
= quant_dist_lookup_table
[ii
][jj
][1];
445 test_impl(mat
, input
, w
, h
, stride
, output2
, 32, 32, out_w
, out_h
,
446 out_w
, sub_x
, sub_y
, bd
, &conv_params
, alpha
, beta
,
450 for (j
= 0; j
< out_w
* out_h
; ++j
)
451 ASSERT_EQ(dsta
[j
], dstb
[j
])
452 << "Pixel mismatch at index " << j
<< " = ("
453 << (j
% out_w
) << ", " << (j
/ out_w
) << ") on iteration "
455 for (j
= 0; j
< out_w
* out_h
; ++j
)
456 ASSERT_EQ(output
[j
], output2
[j
])
457 << "Pixel mismatch at index " << j
<< " = ("
458 << (j
% out_w
) << ", " << (j
/ out_w
) << ") on iteration "
461 for (j
= 0; j
< out_w
* out_h
; ++j
)
462 ASSERT_EQ(output
[j
], output2
[j
])
463 << "Pixel mismatch at index " << j
<< " = ("
464 << (j
% out_w
) << ", " << (j
/ out_w
) << ") on iteration "
479 } // namespace AV1HighbdWarpFilter
480 } // namespace libaom_test