• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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