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
23namespace {
24typedef 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
30struct 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
45typedef 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
50uint8_t clip_pixel(int x) {
51  return x < 0 ? 0 :
52         x > 255 ? 255 :
53         x;
54}
55
56void 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
139void 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
154void 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
172class 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};
251uint8_t* ConvolveTest::input_ = NULL;
252uint8_t* ConvolveTest::output_ = NULL;
253
254TEST_P(ConvolveTest, GuardBlocks) {
255  CheckGuardBlocks();
256}
257
258TEST_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
275TEST_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
292TEST_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
309const 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};
315const int kNumFilterBanks = sizeof(kTestFilterList) /
316                            sizeof(kTestFilterList[0]);
317const int kNumFilters = 16;
318
319TEST(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
339const int16_t kInvalidFilter[8] = { 0 };
340
341TEST_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
386TEST_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
445DECLARE_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. */
465TEST_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. */
553TEST_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
580using std::tr1::make_tuple;
581
582const 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
587INSTANTIATE_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
603const 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
608INSTANTIATE_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
625const 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
630INSTANTIATE_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
647const 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
652INSTANTIATE_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
669const 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
674INSTANTIATE_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