1 /*
2 * Copyright (c) 2010 The WebM project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <string.h>
12 #include "test/acm_random.h"
13 #include "test/register_state_check.h"
14 #include "test/util.h"
15 #include "third_party/googletest/src/include/gtest/gtest.h"
16
17 #include "./vpx_config.h"
18 #include "./vp9_rtcd.h"
19 #include "vp9/common/vp9_filter.h"
20 #include "vpx_mem/vpx_mem.h"
21 #include "vpx_ports/mem.h"
22
23 namespace {
24 typedef void (*convolve_fn_t)(const uint8_t *src, ptrdiff_t src_stride,
25 uint8_t *dst, ptrdiff_t dst_stride,
26 const int16_t *filter_x, int filter_x_stride,
27 const int16_t *filter_y, int filter_y_stride,
28 int w, int h);
29
30 struct ConvolveFunctions {
ConvolveFunctions__anon29eb83500111::ConvolveFunctions31 ConvolveFunctions(convolve_fn_t h8, convolve_fn_t h8_avg,
32 convolve_fn_t v8, convolve_fn_t v8_avg,
33 convolve_fn_t hv8, convolve_fn_t hv8_avg)
34 : h8_(h8), v8_(v8), hv8_(hv8), h8_avg_(h8_avg), v8_avg_(v8_avg),
35 hv8_avg_(hv8_avg) {}
36
37 convolve_fn_t h8_;
38 convolve_fn_t v8_;
39 convolve_fn_t hv8_;
40 convolve_fn_t h8_avg_;
41 convolve_fn_t v8_avg_;
42 convolve_fn_t hv8_avg_;
43 };
44
45 typedef std::tr1::tuple<int, int, const ConvolveFunctions*> convolve_param_t;
46
47 // Reference 8-tap subpixel filter, slightly modified to fit into this test.
48 #define VP9_FILTER_WEIGHT 128
49 #define VP9_FILTER_SHIFT 7
clip_pixel(int x)50 uint8_t clip_pixel(int x) {
51 return x < 0 ? 0 :
52 x > 255 ? 255 :
53 x;
54 }
55
filter_block2d_8_c(const uint8_t * src_ptr,const unsigned int src_stride,const int16_t * HFilter,const int16_t * VFilter,uint8_t * dst_ptr,unsigned int dst_stride,unsigned int output_width,unsigned int output_height)56 void filter_block2d_8_c(const uint8_t *src_ptr,
57 const unsigned int src_stride,
58 const int16_t *HFilter,
59 const int16_t *VFilter,
60 uint8_t *dst_ptr,
61 unsigned int dst_stride,
62 unsigned int output_width,
63 unsigned int output_height) {
64 // Between passes, we use an intermediate buffer whose height is extended to
65 // have enough horizontally filtered values as input for the vertical pass.
66 // This buffer is allocated to be big enough for the largest block type we
67 // support.
68 const int kInterp_Extend = 4;
69 const unsigned int intermediate_height =
70 (kInterp_Extend - 1) + output_height + kInterp_Extend;
71
72 /* Size of intermediate_buffer is max_intermediate_height * filter_max_width,
73 * where max_intermediate_height = (kInterp_Extend - 1) + filter_max_height
74 * + kInterp_Extend
75 * = 3 + 16 + 4
76 * = 23
77 * and filter_max_width = 16
78 */
79 uint8_t intermediate_buffer[71 * 64];
80 const int intermediate_next_stride = 1 - intermediate_height * output_width;
81
82 // Horizontal pass (src -> transposed intermediate).
83 {
84 uint8_t *output_ptr = intermediate_buffer;
85 const int src_next_row_stride = src_stride - output_width;
86 unsigned int i, j;
87 src_ptr -= (kInterp_Extend - 1) * src_stride + (kInterp_Extend - 1);
88 for (i = 0; i < intermediate_height; ++i) {
89 for (j = 0; j < output_width; ++j) {
90 // Apply filter...
91 const int temp = (src_ptr[0] * HFilter[0]) +
92 (src_ptr[1] * HFilter[1]) +
93 (src_ptr[2] * HFilter[2]) +
94 (src_ptr[3] * HFilter[3]) +
95 (src_ptr[4] * HFilter[4]) +
96 (src_ptr[5] * HFilter[5]) +
97 (src_ptr[6] * HFilter[6]) +
98 (src_ptr[7] * HFilter[7]) +
99 (VP9_FILTER_WEIGHT >> 1); // Rounding
100
101 // Normalize back to 0-255...
102 *output_ptr = clip_pixel(temp >> VP9_FILTER_SHIFT);
103 ++src_ptr;
104 output_ptr += intermediate_height;
105 }
106 src_ptr += src_next_row_stride;
107 output_ptr += intermediate_next_stride;
108 }
109 }
110
111 // Vertical pass (transposed intermediate -> dst).
112 {
113 uint8_t *src_ptr = intermediate_buffer;
114 const int dst_next_row_stride = dst_stride - output_width;
115 unsigned int i, j;
116 for (i = 0; i < output_height; ++i) {
117 for (j = 0; j < output_width; ++j) {
118 // Apply filter...
119 const int temp = (src_ptr[0] * VFilter[0]) +
120 (src_ptr[1] * VFilter[1]) +
121 (src_ptr[2] * VFilter[2]) +
122 (src_ptr[3] * VFilter[3]) +
123 (src_ptr[4] * VFilter[4]) +
124 (src_ptr[5] * VFilter[5]) +
125 (src_ptr[6] * VFilter[6]) +
126 (src_ptr[7] * VFilter[7]) +
127 (VP9_FILTER_WEIGHT >> 1); // Rounding
128
129 // Normalize back to 0-255...
130 *dst_ptr++ = clip_pixel(temp >> VP9_FILTER_SHIFT);
131 src_ptr += intermediate_height;
132 }
133 src_ptr += intermediate_next_stride;
134 dst_ptr += dst_next_row_stride;
135 }
136 }
137 }
138
block2d_average_c(uint8_t * src,unsigned int src_stride,uint8_t * output_ptr,unsigned int output_stride,unsigned int output_width,unsigned int output_height)139 void block2d_average_c(uint8_t *src,
140 unsigned int src_stride,
141 uint8_t *output_ptr,
142 unsigned int output_stride,
143 unsigned int output_width,
144 unsigned int output_height) {
145 unsigned int i, j;
146 for (i = 0; i < output_height; ++i) {
147 for (j = 0; j < output_width; ++j) {
148 output_ptr[j] = (output_ptr[j] + src[i * src_stride + j] + 1) >> 1;
149 }
150 output_ptr += output_stride;
151 }
152 }
153
filter_average_block2d_8_c(const uint8_t * src_ptr,const unsigned int src_stride,const int16_t * HFilter,const int16_t * VFilter,uint8_t * dst_ptr,unsigned int dst_stride,unsigned int output_width,unsigned int output_height)154 void filter_average_block2d_8_c(const uint8_t *src_ptr,
155 const unsigned int src_stride,
156 const int16_t *HFilter,
157 const int16_t *VFilter,
158 uint8_t *dst_ptr,
159 unsigned int dst_stride,
160 unsigned int output_width,
161 unsigned int output_height) {
162 uint8_t tmp[64 * 64];
163
164 assert(output_width <= 64);
165 assert(output_height <= 64);
166 filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, tmp, 64,
167 output_width, output_height);
168 block2d_average_c(tmp, 64, dst_ptr, dst_stride,
169 output_width, output_height);
170 }
171
172 class ConvolveTest : public ::testing::TestWithParam<convolve_param_t> {
173 public:
SetUpTestCase()174 static void SetUpTestCase() {
175 // Force input_ to be unaligned, output to be 16 byte aligned.
176 input_ = reinterpret_cast<uint8_t*>(
177 vpx_memalign(kDataAlignment, kInputBufferSize + 1)) + 1;
178 output_ = reinterpret_cast<uint8_t*>(
179 vpx_memalign(kDataAlignment, kOutputBufferSize));
180 }
181
TearDownTestCase()182 static void TearDownTestCase() {
183 vpx_free(input_ - 1);
184 input_ = NULL;
185 vpx_free(output_);
186 output_ = NULL;
187 }
188
189 protected:
190 static const int kDataAlignment = 16;
191 static const int kOuterBlockSize = 256;
192 static const int kInputStride = kOuterBlockSize;
193 static const int kOutputStride = kOuterBlockSize;
194 static const int kMaxDimension = 64;
195 static const int kInputBufferSize = kOuterBlockSize * kOuterBlockSize;
196 static const int kOutputBufferSize = kOuterBlockSize * kOuterBlockSize;
197
Width() const198 int Width() const { return GET_PARAM(0); }
Height() const199 int Height() const { return GET_PARAM(1); }
BorderLeft() const200 int BorderLeft() const {
201 const int center = (kOuterBlockSize - Width()) / 2;
202 return (center + (kDataAlignment - 1)) & ~(kDataAlignment - 1);
203 }
BorderTop() const204 int BorderTop() const { return (kOuterBlockSize - Height()) / 2; }
205
IsIndexInBorder(int i)206 bool IsIndexInBorder(int i) {
207 return (i < BorderTop() * kOuterBlockSize ||
208 i >= (BorderTop() + Height()) * kOuterBlockSize ||
209 i % kOuterBlockSize < BorderLeft() ||
210 i % kOuterBlockSize >= (BorderLeft() + Width()));
211 }
212
SetUp()213 virtual void SetUp() {
214 UUT_ = GET_PARAM(2);
215 /* Set up guard blocks for an inner block centered in the outer block */
216 for (int i = 0; i < kOutputBufferSize; ++i) {
217 if (IsIndexInBorder(i))
218 output_[i] = 255;
219 else
220 output_[i] = 0;
221 }
222
223 ::libvpx_test::ACMRandom prng;
224 for (int i = 0; i < kInputBufferSize; ++i)
225 input_[i] = prng.Rand8Extremes();
226 }
227
SetConstantInput(int value)228 void SetConstantInput(int value) {
229 memset(input_, value, kInputBufferSize);
230 }
231
CheckGuardBlocks()232 void CheckGuardBlocks() {
233 for (int i = 0; i < kOutputBufferSize; ++i) {
234 if (IsIndexInBorder(i))
235 EXPECT_EQ(255, output_[i]);
236 }
237 }
238
input() const239 uint8_t* input() const {
240 return input_ + BorderTop() * kOuterBlockSize + BorderLeft();
241 }
242
output() const243 uint8_t* output() const {
244 return output_ + BorderTop() * kOuterBlockSize + BorderLeft();
245 }
246
247 const ConvolveFunctions* UUT_;
248 static uint8_t* input_;
249 static uint8_t* output_;
250 };
251 uint8_t* ConvolveTest::input_ = NULL;
252 uint8_t* ConvolveTest::output_ = NULL;
253
TEST_P(ConvolveTest,GuardBlocks)254 TEST_P(ConvolveTest, GuardBlocks) {
255 CheckGuardBlocks();
256 }
257
TEST_P(ConvolveTest,CopyHoriz)258 TEST_P(ConvolveTest, CopyHoriz) {
259 uint8_t* const in = input();
260 uint8_t* const out = output();
261 DECLARE_ALIGNED(256, const int16_t, filter8[8]) = {0, 0, 0, 128, 0, 0, 0, 0};
262
263 REGISTER_STATE_CHECK(
264 UUT_->h8_(in, kInputStride, out, kOutputStride, filter8, 16, filter8, 16,
265 Width(), Height()));
266
267 CheckGuardBlocks();
268
269 for (int y = 0; y < Height(); ++y)
270 for (int x = 0; x < Width(); ++x)
271 ASSERT_EQ(out[y * kOutputStride + x], in[y * kInputStride + x])
272 << "(" << x << "," << y << ")";
273 }
274
TEST_P(ConvolveTest,CopyVert)275 TEST_P(ConvolveTest, CopyVert) {
276 uint8_t* const in = input();
277 uint8_t* const out = output();
278 DECLARE_ALIGNED(256, const int16_t, filter8[8]) = {0, 0, 0, 128, 0, 0, 0, 0};
279
280 REGISTER_STATE_CHECK(
281 UUT_->v8_(in, kInputStride, out, kOutputStride, filter8, 16, filter8, 16,
282 Width(), Height()));
283
284 CheckGuardBlocks();
285
286 for (int y = 0; y < Height(); ++y)
287 for (int x = 0; x < Width(); ++x)
288 ASSERT_EQ(out[y * kOutputStride + x], in[y * kInputStride + x])
289 << "(" << x << "," << y << ")";
290 }
291
TEST_P(ConvolveTest,Copy2D)292 TEST_P(ConvolveTest, Copy2D) {
293 uint8_t* const in = input();
294 uint8_t* const out = output();
295 DECLARE_ALIGNED(256, const int16_t, filter8[8]) = {0, 0, 0, 128, 0, 0, 0, 0};
296
297 REGISTER_STATE_CHECK(
298 UUT_->hv8_(in, kInputStride, out, kOutputStride, filter8, 16, filter8, 16,
299 Width(), Height()));
300
301 CheckGuardBlocks();
302
303 for (int y = 0; y < Height(); ++y)
304 for (int x = 0; x < Width(); ++x)
305 ASSERT_EQ(out[y * kOutputStride + x], in[y * kInputStride + x])
306 << "(" << x << "," << y << ")";
307 }
308
309 const int16_t (*kTestFilterList[])[8] = {
310 vp9_bilinear_filters,
311 vp9_sub_pel_filters_8,
312 vp9_sub_pel_filters_8s,
313 vp9_sub_pel_filters_8lp
314 };
315 const int kNumFilterBanks = sizeof(kTestFilterList) /
316 sizeof(kTestFilterList[0]);
317 const int kNumFilters = 16;
318
TEST(ConvolveTest,FiltersWontSaturateWhenAddedPairwise)319 TEST(ConvolveTest, FiltersWontSaturateWhenAddedPairwise) {
320 for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
321 const int16_t (*filters)[8] = kTestFilterList[filter_bank];
322 for (int i = 0; i < kNumFilters; i++) {
323 const int p0 = filters[i][0] + filters[i][1];
324 const int p1 = filters[i][2] + filters[i][3];
325 const int p2 = filters[i][4] + filters[i][5];
326 const int p3 = filters[i][6] + filters[i][7];
327 EXPECT_LE(p0, 128);
328 EXPECT_LE(p1, 128);
329 EXPECT_LE(p2, 128);
330 EXPECT_LE(p3, 128);
331 EXPECT_LE(p0 + p3, 128);
332 EXPECT_LE(p0 + p3 + p1, 128);
333 EXPECT_LE(p0 + p3 + p1 + p2, 128);
334 EXPECT_EQ(p0 + p1 + p2 + p3, 128);
335 }
336 }
337 }
338
339 const int16_t kInvalidFilter[8] = { 0 };
340
TEST_P(ConvolveTest,MatchesReferenceSubpixelFilter)341 TEST_P(ConvolveTest, MatchesReferenceSubpixelFilter) {
342 uint8_t* const in = input();
343 uint8_t* const out = output();
344 uint8_t ref[kOutputStride * kMaxDimension];
345
346
347 for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
348 const int16_t (*filters)[8] = kTestFilterList[filter_bank];
349
350 for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
351 for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
352 filter_block2d_8_c(in, kInputStride,
353 filters[filter_x], filters[filter_y],
354 ref, kOutputStride,
355 Width(), Height());
356
357 if (filters == vp9_sub_pel_filters_8lp || (filter_x && filter_y))
358 REGISTER_STATE_CHECK(
359 UUT_->hv8_(in, kInputStride, out, kOutputStride,
360 filters[filter_x], 16, filters[filter_y], 16,
361 Width(), Height()));
362 else if (filter_y)
363 REGISTER_STATE_CHECK(
364 UUT_->v8_(in, kInputStride, out, kOutputStride,
365 kInvalidFilter, 16, filters[filter_y], 16,
366 Width(), Height()));
367 else
368 REGISTER_STATE_CHECK(
369 UUT_->h8_(in, kInputStride, out, kOutputStride,
370 filters[filter_x], 16, kInvalidFilter, 16,
371 Width(), Height()));
372
373 CheckGuardBlocks();
374
375 for (int y = 0; y < Height(); ++y)
376 for (int x = 0; x < Width(); ++x)
377 ASSERT_EQ(ref[y * kOutputStride + x], out[y * kOutputStride + x])
378 << "mismatch at (" << x << "," << y << "), "
379 << "filters (" << filter_bank << ","
380 << filter_x << "," << filter_y << ")";
381 }
382 }
383 }
384 }
385
TEST_P(ConvolveTest,MatchesReferenceAveragingSubpixelFilter)386 TEST_P(ConvolveTest, MatchesReferenceAveragingSubpixelFilter) {
387 uint8_t* const in = input();
388 uint8_t* const out = output();
389 uint8_t ref[kOutputStride * kMaxDimension];
390
391 // Populate ref and out with some random data
392 ::libvpx_test::ACMRandom prng;
393 for (int y = 0; y < Height(); ++y) {
394 for (int x = 0; x < Width(); ++x) {
395 const uint8_t r = prng.Rand8Extremes();
396
397 out[y * kOutputStride + x] = r;
398 ref[y * kOutputStride + x] = r;
399 }
400 }
401
402 const int kNumFilterBanks = sizeof(kTestFilterList) /
403 sizeof(kTestFilterList[0]);
404
405 for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
406 const int16_t (*filters)[8] = kTestFilterList[filter_bank];
407 const int kNumFilters = 16;
408
409 for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
410 for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
411 filter_average_block2d_8_c(in, kInputStride,
412 filters[filter_x], filters[filter_y],
413 ref, kOutputStride,
414 Width(), Height());
415
416 if (filters == vp9_sub_pel_filters_8lp || (filter_x && filter_y))
417 REGISTER_STATE_CHECK(
418 UUT_->hv8_avg_(in, kInputStride, out, kOutputStride,
419 filters[filter_x], 16, filters[filter_y], 16,
420 Width(), Height()));
421 else if (filter_y)
422 REGISTER_STATE_CHECK(
423 UUT_->v8_avg_(in, kInputStride, out, kOutputStride,
424 filters[filter_x], 16, filters[filter_y], 16,
425 Width(), Height()));
426 else
427 REGISTER_STATE_CHECK(
428 UUT_->h8_avg_(in, kInputStride, out, kOutputStride,
429 filters[filter_x], 16, filters[filter_y], 16,
430 Width(), Height()));
431
432 CheckGuardBlocks();
433
434 for (int y = 0; y < Height(); ++y)
435 for (int x = 0; x < Width(); ++x)
436 ASSERT_EQ(ref[y * kOutputStride + x], out[y * kOutputStride + x])
437 << "mismatch at (" << x << "," << y << "), "
438 << "filters (" << filter_bank << ","
439 << filter_x << "," << filter_y << ")";
440 }
441 }
442 }
443 }
444
445 DECLARE_ALIGNED(256, const int16_t, kChangeFilters[16][8]) = {
446 { 0, 0, 0, 0, 0, 0, 0, 128},
447 { 0, 0, 0, 0, 0, 0, 128},
448 { 0, 0, 0, 0, 0, 128},
449 { 0, 0, 0, 0, 128},
450 { 0, 0, 0, 128},
451 { 0, 0, 128},
452 { 0, 128},
453 { 128},
454 { 0, 0, 0, 0, 0, 0, 0, 128},
455 { 0, 0, 0, 0, 0, 0, 128},
456 { 0, 0, 0, 0, 0, 128},
457 { 0, 0, 0, 0, 128},
458 { 0, 0, 0, 128},
459 { 0, 0, 128},
460 { 0, 128},
461 { 128}
462 };
463
464 /* This test exercises the horizontal and vertical filter functions. */
TEST_P(ConvolveTest,ChangeFilterWorks)465 TEST_P(ConvolveTest, ChangeFilterWorks) {
466 uint8_t* const in = input();
467 uint8_t* const out = output();
468
469 /* Assume that the first input sample is at the 8/16th position. */
470 const int kInitialSubPelOffset = 8;
471
472 /* Filters are 8-tap, so the first filter tap will be applied to the pixel
473 * at position -3 with respect to the current filtering position. Since
474 * kInitialSubPelOffset is set to 8, we first select sub-pixel filter 8,
475 * which is non-zero only in the last tap. So, applying the filter at the
476 * current input position will result in an output equal to the pixel at
477 * offset +4 (-3 + 7) with respect to the current filtering position.
478 */
479 const int kPixelSelected = 4;
480
481 /* Assume that each output pixel requires us to step on by 17/16th pixels in
482 * the input.
483 */
484 const int kInputPixelStep = 17;
485
486 /* The filters are setup in such a way that the expected output produces
487 * sets of 8 identical output samples. As the filter position moves to the
488 * next 1/16th pixel position the only active (=128) filter tap moves one
489 * position to the left, resulting in the same input pixel being replicated
490 * in to the output for 8 consecutive samples. After each set of 8 positions
491 * the filters select a different input pixel. kFilterPeriodAdjust below
492 * computes which input pixel is written to the output for a specified
493 * x or y position.
494 */
495
496 /* Test the horizontal filter. */
497 REGISTER_STATE_CHECK(UUT_->h8_(in, kInputStride, out, kOutputStride,
498 kChangeFilters[kInitialSubPelOffset],
499 kInputPixelStep, NULL, 0, Width(), Height()));
500
501 for (int x = 0; x < Width(); ++x) {
502 const int kFilterPeriodAdjust = (x >> 3) << 3;
503 const int ref_x =
504 kPixelSelected + ((kInitialSubPelOffset
505 + kFilterPeriodAdjust * kInputPixelStep)
506 >> SUBPEL_BITS);
507 ASSERT_EQ(in[ref_x], out[x]) << "x == " << x << "width = " << Width();
508 }
509
510 /* Test the vertical filter. */
511 REGISTER_STATE_CHECK(UUT_->v8_(in, kInputStride, out, kOutputStride,
512 NULL, 0, kChangeFilters[kInitialSubPelOffset],
513 kInputPixelStep, Width(), Height()));
514
515 for (int y = 0; y < Height(); ++y) {
516 const int kFilterPeriodAdjust = (y >> 3) << 3;
517 const int ref_y =
518 kPixelSelected + ((kInitialSubPelOffset
519 + kFilterPeriodAdjust * kInputPixelStep)
520 >> SUBPEL_BITS);
521 ASSERT_EQ(in[ref_y * kInputStride], out[y * kInputStride]) << "y == " << y;
522 }
523
524 /* Test the horizontal and vertical filters in combination. */
525 REGISTER_STATE_CHECK(UUT_->hv8_(in, kInputStride, out, kOutputStride,
526 kChangeFilters[kInitialSubPelOffset],
527 kInputPixelStep,
528 kChangeFilters[kInitialSubPelOffset],
529 kInputPixelStep,
530 Width(), Height()));
531
532 for (int y = 0; y < Height(); ++y) {
533 const int kFilterPeriodAdjustY = (y >> 3) << 3;
534 const int ref_y =
535 kPixelSelected + ((kInitialSubPelOffset
536 + kFilterPeriodAdjustY * kInputPixelStep)
537 >> SUBPEL_BITS);
538 for (int x = 0; x < Width(); ++x) {
539 const int kFilterPeriodAdjustX = (x >> 3) << 3;
540 const int ref_x =
541 kPixelSelected + ((kInitialSubPelOffset
542 + kFilterPeriodAdjustX * kInputPixelStep)
543 >> SUBPEL_BITS);
544
545 ASSERT_EQ(in[ref_y * kInputStride + ref_x], out[y * kOutputStride + x])
546 << "x == " << x << ", y == " << y;
547 }
548 }
549 }
550
551 /* This test exercises that enough rows and columns are filtered with every
552 possible initial fractional positions and scaling steps. */
TEST_P(ConvolveTest,CheckScalingFiltering)553 TEST_P(ConvolveTest, CheckScalingFiltering) {
554 uint8_t* const in = input();
555 uint8_t* const out = output();
556
557 SetConstantInput(127);
558
559 for (int frac = 0; frac < 16; ++frac) {
560 for (int step = 1; step <= 32; ++step) {
561 /* Test the horizontal and vertical filters in combination. */
562 REGISTER_STATE_CHECK(UUT_->hv8_(in, kInputStride, out, kOutputStride,
563 vp9_sub_pel_filters_8[frac], step,
564 vp9_sub_pel_filters_8[frac], step,
565 Width(), Height()));
566
567 CheckGuardBlocks();
568
569 for (int y = 0; y < Height(); ++y) {
570 for (int x = 0; x < Width(); ++x) {
571 ASSERT_EQ(in[y * kInputStride + x], out[y * kOutputStride + x])
572 << "x == " << x << ", y == " << y
573 << ", frac == " << frac << ", step == " << step;
574 }
575 }
576 }
577 }
578 }
579
580 using std::tr1::make_tuple;
581
582 const ConvolveFunctions convolve8_c(
583 vp9_convolve8_horiz_c, vp9_convolve8_avg_horiz_c,
584 vp9_convolve8_vert_c, vp9_convolve8_avg_vert_c,
585 vp9_convolve8_c, vp9_convolve8_avg_c);
586
587 INSTANTIATE_TEST_CASE_P(C, ConvolveTest, ::testing::Values(
588 make_tuple(4, 4, &convolve8_c),
589 make_tuple(8, 4, &convolve8_c),
590 make_tuple(4, 8, &convolve8_c),
591 make_tuple(8, 8, &convolve8_c),
592 make_tuple(16, 8, &convolve8_c),
593 make_tuple(8, 16, &convolve8_c),
594 make_tuple(16, 16, &convolve8_c),
595 make_tuple(32, 16, &convolve8_c),
596 make_tuple(16, 32, &convolve8_c),
597 make_tuple(32, 32, &convolve8_c),
598 make_tuple(64, 32, &convolve8_c),
599 make_tuple(32, 64, &convolve8_c),
600 make_tuple(64, 64, &convolve8_c)));
601
602 #if HAVE_SSE2
603 const ConvolveFunctions convolve8_sse2(
604 vp9_convolve8_horiz_sse2, vp9_convolve8_avg_horiz_sse2,
605 vp9_convolve8_vert_sse2, vp9_convolve8_avg_vert_sse2,
606 vp9_convolve8_sse2, vp9_convolve8_avg_sse2);
607
608 INSTANTIATE_TEST_CASE_P(SSE2, ConvolveTest, ::testing::Values(
609 make_tuple(4, 4, &convolve8_sse2),
610 make_tuple(8, 4, &convolve8_sse2),
611 make_tuple(4, 8, &convolve8_sse2),
612 make_tuple(8, 8, &convolve8_sse2),
613 make_tuple(16, 8, &convolve8_sse2),
614 make_tuple(8, 16, &convolve8_sse2),
615 make_tuple(16, 16, &convolve8_sse2),
616 make_tuple(32, 16, &convolve8_sse2),
617 make_tuple(16, 32, &convolve8_sse2),
618 make_tuple(32, 32, &convolve8_sse2),
619 make_tuple(64, 32, &convolve8_sse2),
620 make_tuple(32, 64, &convolve8_sse2),
621 make_tuple(64, 64, &convolve8_sse2)));
622 #endif
623
624 #if HAVE_SSSE3
625 const ConvolveFunctions convolve8_ssse3(
626 vp9_convolve8_horiz_ssse3, vp9_convolve8_avg_horiz_ssse3,
627 vp9_convolve8_vert_ssse3, vp9_convolve8_avg_vert_ssse3,
628 vp9_convolve8_ssse3, vp9_convolve8_avg_ssse3);
629
630 INSTANTIATE_TEST_CASE_P(SSSE3, ConvolveTest, ::testing::Values(
631 make_tuple(4, 4, &convolve8_ssse3),
632 make_tuple(8, 4, &convolve8_ssse3),
633 make_tuple(4, 8, &convolve8_ssse3),
634 make_tuple(8, 8, &convolve8_ssse3),
635 make_tuple(16, 8, &convolve8_ssse3),
636 make_tuple(8, 16, &convolve8_ssse3),
637 make_tuple(16, 16, &convolve8_ssse3),
638 make_tuple(32, 16, &convolve8_ssse3),
639 make_tuple(16, 32, &convolve8_ssse3),
640 make_tuple(32, 32, &convolve8_ssse3),
641 make_tuple(64, 32, &convolve8_ssse3),
642 make_tuple(32, 64, &convolve8_ssse3),
643 make_tuple(64, 64, &convolve8_ssse3)));
644 #endif
645
646 #if HAVE_NEON
647 const ConvolveFunctions convolve8_neon(
648 vp9_convolve8_horiz_neon, vp9_convolve8_avg_horiz_neon,
649 vp9_convolve8_vert_neon, vp9_convolve8_avg_vert_neon,
650 vp9_convolve8_neon, vp9_convolve8_avg_neon);
651
652 INSTANTIATE_TEST_CASE_P(NEON, ConvolveTest, ::testing::Values(
653 make_tuple(4, 4, &convolve8_neon),
654 make_tuple(8, 4, &convolve8_neon),
655 make_tuple(4, 8, &convolve8_neon),
656 make_tuple(8, 8, &convolve8_neon),
657 make_tuple(16, 8, &convolve8_neon),
658 make_tuple(8, 16, &convolve8_neon),
659 make_tuple(16, 16, &convolve8_neon),
660 make_tuple(32, 16, &convolve8_neon),
661 make_tuple(16, 32, &convolve8_neon),
662 make_tuple(32, 32, &convolve8_neon),
663 make_tuple(64, 32, &convolve8_neon),
664 make_tuple(32, 64, &convolve8_neon),
665 make_tuple(64, 64, &convolve8_neon)));
666 #endif
667
668 #if HAVE_DSPR2
669 const ConvolveFunctions convolve8_dspr2(
670 vp9_convolve8_horiz_dspr2, vp9_convolve8_avg_horiz_dspr2,
671 vp9_convolve8_vert_dspr2, vp9_convolve8_avg_vert_dspr2,
672 vp9_convolve8_dspr2, vp9_convolve8_avg_dspr2);
673
674 INSTANTIATE_TEST_CASE_P(DSPR2, ConvolveTest, ::testing::Values(
675 make_tuple(4, 4, &convolve8_dspr2),
676 make_tuple(8, 4, &convolve8_dspr2),
677 make_tuple(4, 8, &convolve8_dspr2),
678 make_tuple(8, 8, &convolve8_dspr2),
679 make_tuple(16, 8, &convolve8_dspr2),
680 make_tuple(8, 16, &convolve8_dspr2),
681 make_tuple(16, 16, &convolve8_dspr2),
682 make_tuple(32, 16, &convolve8_dspr2),
683 make_tuple(16, 32, &convolve8_dspr2),
684 make_tuple(32, 32, &convolve8_dspr2),
685 make_tuple(64, 32, &convolve8_dspr2),
686 make_tuple(32, 64, &convolve8_dspr2),
687 make_tuple(64, 64, &convolve8_dspr2)));
688 #endif
689 } // namespace
690