Home | History | Annotate | Download | only in test
      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 {
     31   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
     50 uint8_t clip_pixel(int x) {
     51   return x < 0 ? 0 :
     52          x > 255 ? 255 :
     53          x;
     54 }
     55 
     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 
    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 
    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:
    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 
    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 
    198   int Width() const { return GET_PARAM(0); }
    199   int Height() const { return GET_PARAM(1); }
    200   int BorderLeft() const {
    201     const int center = (kOuterBlockSize - Width()) / 2;
    202     return (center + (kDataAlignment - 1)) & ~(kDataAlignment - 1);
    203   }
    204   int BorderTop() const { return (kOuterBlockSize - Height()) / 2; }
    205 
    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 
    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 
    228   void SetConstantInput(int value) {
    229     memset(input_, value, kInputBufferSize);
    230   }
    231 
    232   void CheckGuardBlocks() {
    233     for (int i = 0; i < kOutputBufferSize; ++i) {
    234       if (IsIndexInBorder(i))
    235         EXPECT_EQ(255, output_[i]);
    236     }
    237   }
    238 
    239   uint8_t* input() const {
    240     return input_ + BorderTop() * kOuterBlockSize + BorderLeft();
    241   }
    242 
    243   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 
    254 TEST_P(ConvolveTest, GuardBlocks) {
    255   CheckGuardBlocks();
    256 }
    257 
    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 
    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 
    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 
    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 
    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 
    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. */
    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. */
    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