convolve_test.cc 50.3 KB
Newer Older
John Koleszar's avatar
John Koleszar committed
1 2 3 4 5 6 7 8 9 10
/*
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

11
#include <string.h>
12

James Zern's avatar
James Zern committed
13
#include "third_party/googletest/src/include/gtest/gtest.h"
John Koleszar's avatar
John Koleszar committed
14 15

#include "./vpx_config.h"
Yaowu Xu's avatar
Yaowu Xu committed
16
#include "./av1_rtcd.h"
Zoe Liu's avatar
Zoe Liu committed
17
#include "./vpx_dsp_rtcd.h"
18 19 20 21
#include "test/acm_random.h"
#include "test/clear_system_state.h"
#include "test/register_state_check.h"
#include "test/util.h"
Yaowu Xu's avatar
Yaowu Xu committed
22 23
#include "av1/common/common.h"
#include "av1/common/filter.h"
Yaowu Xu's avatar
Yaowu Xu committed
24 25 26 27
#include "aom_dsp/vpx_dsp_common.h"
#include "aom_dsp/vpx_filter.h"
#include "aom_mem/vpx_mem.h"
#include "aom_ports/mem.h"
John Koleszar's avatar
John Koleszar committed
28 29

namespace {
30

hkuang's avatar
hkuang committed
31
static const unsigned int kMaxDimension = 64;
32

33 34 35 36 37
typedef void (*ConvolveFunc)(const uint8_t *src, ptrdiff_t src_stride,
                             uint8_t *dst, ptrdiff_t dst_stride,
                             const int16_t *filter_x, int filter_x_stride,
                             const int16_t *filter_y, int filter_y_stride,
                             int w, int h);
John Koleszar's avatar
John Koleszar committed
38 39

struct ConvolveFunctions {
clang-format's avatar
clang-format committed
40 41 42 43 44 45
  ConvolveFunctions(ConvolveFunc copy, ConvolveFunc avg, ConvolveFunc h8,
                    ConvolveFunc h8_avg, ConvolveFunc v8, ConvolveFunc v8_avg,
                    ConvolveFunc hv8, ConvolveFunc hv8_avg, ConvolveFunc sh8,
                    ConvolveFunc sh8_avg, ConvolveFunc sv8,
                    ConvolveFunc sv8_avg, ConvolveFunc shv8,
                    ConvolveFunc shv8_avg, int bd)
46
      : copy_(copy), avg_(avg), h8_(h8), v8_(v8), hv8_(hv8), h8_avg_(h8_avg),
47 48 49
        v8_avg_(v8_avg), hv8_avg_(hv8_avg), sh8_(sh8), sv8_(sv8), shv8_(shv8),
        sh8_avg_(sh8_avg), sv8_avg_(sv8_avg), shv8_avg_(shv8_avg),
        use_highbd_(bd) {}
John Koleszar's avatar
John Koleszar committed
50

51 52
  ConvolveFunc copy_;
  ConvolveFunc avg_;
53 54 55 56 57 58
  ConvolveFunc h8_;
  ConvolveFunc v8_;
  ConvolveFunc hv8_;
  ConvolveFunc h8_avg_;
  ConvolveFunc v8_avg_;
  ConvolveFunc hv8_avg_;
clang-format's avatar
clang-format committed
59 60 61 62 63 64
  ConvolveFunc sh8_;       // scaled horiz
  ConvolveFunc sv8_;       // scaled vert
  ConvolveFunc shv8_;      // scaled horiz/vert
  ConvolveFunc sh8_avg_;   // scaled avg horiz
  ConvolveFunc sv8_avg_;   // scaled avg vert
  ConvolveFunc shv8_avg_;  // scaled avg horiz/vert
65
  int use_highbd_;  // 0 if high bitdepth not used, else the actual bit depth.
John Koleszar's avatar
John Koleszar committed
66 67
};

68
typedef std::tr1::tuple<int, int, const ConvolveFunctions *> ConvolveParam;
69

John Koleszar's avatar
John Koleszar committed
70 71 72
// Reference 8-tap subpixel filter, slightly modified to fit into this test.
#define VP9_FILTER_WEIGHT 128
#define VP9_FILTER_SHIFT 7
clang-format's avatar
clang-format committed
73
uint8_t clip_pixel(int x) { return x < 0 ? 0 : x > 255 ? 255 : x; }
John Koleszar's avatar
John Koleszar committed
74

clang-format's avatar
clang-format committed
75 76 77 78
void 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) {
John Koleszar's avatar
John Koleszar committed
79 80 81 82 83
  // Between passes, we use an intermediate buffer whose height is extended to
  // have enough horizontally filtered values as input for the vertical pass.
  // This buffer is allocated to be big enough for the largest block type we
  // support.
  const int kInterp_Extend = 4;
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
  const unsigned int intermediate_height =
      (kInterp_Extend - 1) + output_height + kInterp_Extend;
  unsigned int i, j;

  // Size of intermediate_buffer is max_intermediate_height * filter_max_width,
  // where max_intermediate_height = (kInterp_Extend - 1) + filter_max_height
  //                                 + kInterp_Extend
  //                               = 3 + 16 + 4
  //                               = 23
  // and filter_max_width          = 16
  //
  uint8_t intermediate_buffer[71 * kMaxDimension];
  const int intermediate_next_stride = 1 - intermediate_height * output_width;

  // Horizontal pass (src -> transposed intermediate).
  uint8_t *output_ptr = intermediate_buffer;
  const int src_next_row_stride = src_stride - output_width;
  src_ptr -= (kInterp_Extend - 1) * src_stride + (kInterp_Extend - 1);
  for (i = 0; i < intermediate_height; ++i) {
    for (j = 0; j < output_width; ++j) {
      // Apply filter...
clang-format's avatar
clang-format committed
105 106 107 108 109
      const int temp = (src_ptr[0] * HFilter[0]) + (src_ptr[1] * HFilter[1]) +
                       (src_ptr[2] * HFilter[2]) + (src_ptr[3] * HFilter[3]) +
                       (src_ptr[4] * HFilter[4]) + (src_ptr[5] * HFilter[5]) +
                       (src_ptr[6] * HFilter[6]) + (src_ptr[7] * HFilter[7]) +
                       (VP9_FILTER_WEIGHT >> 1);  // Rounding
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125

      // Normalize back to 0-255...
      *output_ptr = clip_pixel(temp >> VP9_FILTER_SHIFT);
      ++src_ptr;
      output_ptr += intermediate_height;
    }
    src_ptr += src_next_row_stride;
    output_ptr += intermediate_next_stride;
  }

  // Vertical pass (transposed intermediate -> dst).
  src_ptr = intermediate_buffer;
  const int dst_next_row_stride = dst_stride - output_width;
  for (i = 0; i < output_height; ++i) {
    for (j = 0; j < output_width; ++j) {
      // Apply filter...
clang-format's avatar
clang-format committed
126 127 128 129 130
      const int temp = (src_ptr[0] * VFilter[0]) + (src_ptr[1] * VFilter[1]) +
                       (src_ptr[2] * VFilter[2]) + (src_ptr[3] * VFilter[3]) +
                       (src_ptr[4] * VFilter[4]) + (src_ptr[5] * VFilter[5]) +
                       (src_ptr[6] * VFilter[6]) + (src_ptr[7] * VFilter[7]) +
                       (VP9_FILTER_WEIGHT >> 1);  // Rounding
131 132 133 134 135 136 137 138 139 140

      // Normalize back to 0-255...
      *dst_ptr++ = clip_pixel(temp >> VP9_FILTER_SHIFT);
      src_ptr += intermediate_height;
    }
    src_ptr += intermediate_next_stride;
    dst_ptr += dst_next_row_stride;
  }
}

clang-format's avatar
clang-format committed
141 142 143
void 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) {
144 145 146 147 148 149 150 151 152 153 154
  unsigned int i, j;
  for (i = 0; i < output_height; ++i) {
    for (j = 0; j < output_width; ++j) {
      output_ptr[j] = (output_ptr[j] + src[i * src_stride + j] + 1) >> 1;
    }
    output_ptr += output_stride;
  }
}

void filter_average_block2d_8_c(const uint8_t *src_ptr,
                                const unsigned int src_stride,
clang-format's avatar
clang-format committed
155 156
                                const int16_t *HFilter, const int16_t *VFilter,
                                uint8_t *dst_ptr, unsigned int dst_stride,
157 158 159 160 161 162 163 164
                                unsigned int output_width,
                                unsigned int output_height) {
  uint8_t tmp[kMaxDimension * kMaxDimension];

  assert(output_width <= kMaxDimension);
  assert(output_height <= kMaxDimension);
  filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, tmp, 64,
                     output_width, output_height);
clang-format's avatar
clang-format committed
165
  block2d_average_c(tmp, 64, dst_ptr, dst_stride, output_width, output_height);
166 167
}

168
#if CONFIG_VPX_HIGHBITDEPTH
169 170
void highbd_filter_block2d_8_c(const uint16_t *src_ptr,
                               const unsigned int src_stride,
clang-format's avatar
clang-format committed
171 172
                               const int16_t *HFilter, const int16_t *VFilter,
                               uint16_t *dst_ptr, unsigned int dst_stride,
173
                               unsigned int output_width,
clang-format's avatar
clang-format committed
174
                               unsigned int output_height, int bd) {
175 176 177 178 179
  // Between passes, we use an intermediate buffer whose height is extended to
  // have enough horizontally filtered values as input for the vertical pass.
  // This buffer is allocated to be big enough for the largest block type we
  // support.
  const int kInterp_Extend = 4;
John Koleszar's avatar
John Koleszar committed
180
  const unsigned int intermediate_height =
James Zern's avatar
James Zern committed
181
      (kInterp_Extend - 1) + output_height + kInterp_Extend;
John Koleszar's avatar
John Koleszar committed
182 183 184 185 186 187 188 189

  /* Size of intermediate_buffer is max_intermediate_height * filter_max_width,
   * where max_intermediate_height = (kInterp_Extend - 1) + filter_max_height
   *                                 + kInterp_Extend
   *                               = 3 + 16 + 4
   *                               = 23
   * and filter_max_width = 16
   */
190
  uint16_t intermediate_buffer[71 * kMaxDimension];
John Koleszar's avatar
John Koleszar committed
191 192 193 194
  const int intermediate_next_stride = 1 - intermediate_height * output_width;

  // Horizontal pass (src -> transposed intermediate).
  {
195
    uint16_t *output_ptr = intermediate_buffer;
John Koleszar's avatar
John Koleszar committed
196 197 198 199 200 201
    const int src_next_row_stride = src_stride - output_width;
    unsigned int i, j;
    src_ptr -= (kInterp_Extend - 1) * src_stride + (kInterp_Extend - 1);
    for (i = 0; i < intermediate_height; ++i) {
      for (j = 0; j < output_width; ++j) {
        // Apply filter...
clang-format's avatar
clang-format committed
202 203 204 205
        const int temp = (src_ptr[0] * HFilter[0]) + (src_ptr[1] * HFilter[1]) +
                         (src_ptr[2] * HFilter[2]) + (src_ptr[3] * HFilter[3]) +
                         (src_ptr[4] * HFilter[4]) + (src_ptr[5] * HFilter[5]) +
                         (src_ptr[6] * HFilter[6]) + (src_ptr[7] * HFilter[7]) +
James Zern's avatar
James Zern committed
206
                         (VP9_FILTER_WEIGHT >> 1);  // Rounding
John Koleszar's avatar
John Koleszar committed
207 208

        // Normalize back to 0-255...
209
        *output_ptr = clip_pixel_highbd(temp >> VP9_FILTER_SHIFT, bd);
John Koleszar's avatar
John Koleszar committed
210 211 212 213 214 215 216 217 218 219
        ++src_ptr;
        output_ptr += intermediate_height;
      }
      src_ptr += src_next_row_stride;
      output_ptr += intermediate_next_stride;
    }
  }

  // Vertical pass (transposed intermediate -> dst).
  {
220
    uint16_t *src_ptr = intermediate_buffer;
John Koleszar's avatar
John Koleszar committed
221 222 223 224 225
    const int dst_next_row_stride = dst_stride - output_width;
    unsigned int i, j;
    for (i = 0; i < output_height; ++i) {
      for (j = 0; j < output_width; ++j) {
        // Apply filter...
clang-format's avatar
clang-format committed
226 227 228 229
        const int temp = (src_ptr[0] * VFilter[0]) + (src_ptr[1] * VFilter[1]) +
                         (src_ptr[2] * VFilter[2]) + (src_ptr[3] * VFilter[3]) +
                         (src_ptr[4] * VFilter[4]) + (src_ptr[5] * VFilter[5]) +
                         (src_ptr[6] * VFilter[6]) + (src_ptr[7] * VFilter[7]) +
James Zern's avatar
James Zern committed
230
                         (VP9_FILTER_WEIGHT >> 1);  // Rounding
John Koleszar's avatar
John Koleszar committed
231 232

        // Normalize back to 0-255...
233
        *dst_ptr++ = clip_pixel_highbd(temp >> VP9_FILTER_SHIFT, bd);
John Koleszar's avatar
John Koleszar committed
234 235 236 237 238 239 240 241
        src_ptr += intermediate_height;
      }
      src_ptr += intermediate_next_stride;
      dst_ptr += dst_next_row_stride;
    }
  }
}

clang-format's avatar
clang-format committed
242 243
void highbd_block2d_average_c(uint16_t *src, unsigned int src_stride,
                              uint16_t *output_ptr, unsigned int output_stride,
244
                              unsigned int output_width,
clang-format's avatar
clang-format committed
245
                              unsigned int output_height, int bd) {
John Koleszar's avatar
John Koleszar committed
246 247 248 249 250 251 252 253 254
  unsigned int i, j;
  for (i = 0; i < output_height; ++i) {
    for (j = 0; j < output_width; ++j) {
      output_ptr[j] = (output_ptr[j] + src[i * src_stride + j] + 1) >> 1;
    }
    output_ptr += output_stride;
  }
}

clang-format's avatar
clang-format committed
255 256 257 258 259
void highbd_filter_average_block2d_8_c(
    const uint16_t *src_ptr, const unsigned int src_stride,
    const int16_t *HFilter, const int16_t *VFilter, uint16_t *dst_ptr,
    unsigned int dst_stride, unsigned int output_width,
    unsigned int output_height, int bd) {
260 261 262 263
  uint16_t tmp[kMaxDimension * kMaxDimension];

  assert(output_width <= kMaxDimension);
  assert(output_height <= kMaxDimension);
264 265
  highbd_filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, tmp, 64,
                            output_width, output_height, bd);
clang-format's avatar
clang-format committed
266 267
  highbd_block2d_average_c(tmp, 64, dst_ptr, dst_stride, output_width,
                           output_height, bd);
John Koleszar's avatar
John Koleszar committed
268
}
269
#endif  // CONFIG_VPX_HIGHBITDEPTH
John Koleszar's avatar
John Koleszar committed
270

271
class ConvolveTest : public ::testing::TestWithParam<ConvolveParam> {
272 273 274
 public:
  static void SetUpTestCase() {
    // Force input_ to be unaligned, output to be 16 byte aligned.
clang-format's avatar
clang-format committed
275 276 277 278
    input_ = reinterpret_cast<uint8_t *>(
                 vpx_memalign(kDataAlignment, kInputBufferSize + 1)) +
             1;
    output_ = reinterpret_cast<uint8_t *>(
279
        vpx_memalign(kDataAlignment, kOutputBufferSize));
clang-format's avatar
clang-format committed
280
    output_ref_ = reinterpret_cast<uint8_t *>(
281
        vpx_memalign(kDataAlignment, kOutputBufferSize));
282
#if CONFIG_VPX_HIGHBITDEPTH
clang-format's avatar
clang-format committed
283 284 285 286
    input16_ = reinterpret_cast<uint16_t *>(vpx_memalign(
                   kDataAlignment, (kInputBufferSize + 1) * sizeof(uint16_t))) +
               1;
    output16_ = reinterpret_cast<uint16_t *>(
287
        vpx_memalign(kDataAlignment, (kOutputBufferSize) * sizeof(uint16_t)));
clang-format's avatar
clang-format committed
288
    output16_ref_ = reinterpret_cast<uint16_t *>(
289
        vpx_memalign(kDataAlignment, (kOutputBufferSize) * sizeof(uint16_t)));
290
#endif
291 292
  }

Yaowu Xu's avatar
Yaowu Xu committed
293
  virtual void TearDown() { libaom_test::ClearSystemState(); }
294

295 296 297 298 299
  static void TearDownTestCase() {
    vpx_free(input_ - 1);
    input_ = NULL;
    vpx_free(output_);
    output_ = NULL;
300 301
    vpx_free(output_ref_);
    output_ref_ = NULL;
302
#if CONFIG_VPX_HIGHBITDEPTH
303 304 305 306
    vpx_free(input16_ - 1);
    input16_ = NULL;
    vpx_free(output16_);
    output16_ = NULL;
307 308
    vpx_free(output16_ref_);
    output16_ref_ = NULL;
309
#endif
310 311
  }

James Zern's avatar
James Zern committed
312 313
 protected:
  static const int kDataAlignment = 16;
314
  static const int kOuterBlockSize = 256;
James Zern's avatar
James Zern committed
315 316 317 318 319 320 321 322 323 324 325 326
  static const int kInputStride = kOuterBlockSize;
  static const int kOutputStride = kOuterBlockSize;
  static const int kInputBufferSize = kOuterBlockSize * kOuterBlockSize;
  static const int kOutputBufferSize = kOuterBlockSize * kOuterBlockSize;

  int Width() const { return GET_PARAM(0); }
  int Height() const { return GET_PARAM(1); }
  int BorderLeft() const {
    const int center = (kOuterBlockSize - Width()) / 2;
    return (center + (kDataAlignment - 1)) & ~(kDataAlignment - 1);
  }
  int BorderTop() const { return (kOuterBlockSize - Height()) / 2; }
John Koleszar's avatar
John Koleszar committed
327

James Zern's avatar
James Zern committed
328 329 330 331 332 333
  bool IsIndexInBorder(int i) {
    return (i < BorderTop() * kOuterBlockSize ||
            i >= (BorderTop() + Height()) * kOuterBlockSize ||
            i % kOuterBlockSize < BorderLeft() ||
            i % kOuterBlockSize >= (BorderLeft() + Width()));
  }
John Koleszar's avatar
John Koleszar committed
334

James Zern's avatar
James Zern committed
335 336
  virtual void SetUp() {
    UUT_ = GET_PARAM(2);
337
#if CONFIG_VPX_HIGHBITDEPTH
338 339
    if (UUT_->use_highbd_ != 0)
      mask_ = (1 << UUT_->use_highbd_) - 1;
340 341 342
    else
      mask_ = 255;
#endif
Johann's avatar
Johann committed
343
    /* Set up guard blocks for an inner block centered in the outer block */
James Zern's avatar
James Zern committed
344 345 346 347 348
    for (int i = 0; i < kOutputBufferSize; ++i) {
      if (IsIndexInBorder(i))
        output_[i] = 255;
      else
        output_[i] = 0;
John Koleszar's avatar
John Koleszar committed
349 350
    }

Yaowu Xu's avatar
Yaowu Xu committed
351
    ::libaom_test::ACMRandom prng;
352
    for (int i = 0; i < kInputBufferSize; ++i) {
353
      if (i & 1) {
354
        input_[i] = 255;
355
#if CONFIG_VPX_HIGHBITDEPTH
356 357 358
        input16_[i] = mask_;
#endif
      } else {
359
        input_[i] = prng.Rand8Extremes();
360
#if CONFIG_VPX_HIGHBITDEPTH
361 362 363
        input16_[i] = prng.Rand16() & mask_;
#endif
      }
364
    }
James Zern's avatar
James Zern committed
365
  }
John Koleszar's avatar
John Koleszar committed
366

367 368
  void SetConstantInput(int value) {
    memset(input_, value, kInputBufferSize);
369
#if CONFIG_VPX_HIGHBITDEPTH
370 371
    vpx_memset16(input16_, value, kInputBufferSize);
#endif
372 373
  }

374
  void CopyOutputToRef() {
James Zern's avatar
James Zern committed
375
    memcpy(output_ref_, output_, kOutputBufferSize);
376
#if CONFIG_VPX_HIGHBITDEPTH
James Zern's avatar
James Zern committed
377
    memcpy(output16_ref_, output16_, kOutputBufferSize);
378 379 380
#endif
  }

James Zern's avatar
James Zern committed
381 382
  void CheckGuardBlocks() {
    for (int i = 0; i < kOutputBufferSize; ++i) {
clang-format's avatar
clang-format committed
383
      if (IsIndexInBorder(i)) EXPECT_EQ(255, output_[i]);
John Koleszar's avatar
John Koleszar committed
384
    }
James Zern's avatar
James Zern committed
385
  }
John Koleszar's avatar
John Koleszar committed
386

387
  uint8_t *input() const {
388
#if CONFIG_VPX_HIGHBITDEPTH
389
    if (UUT_->use_highbd_ == 0) {
390 391 392 393 394 395
      return input_ + BorderTop() * kOuterBlockSize + BorderLeft();
    } else {
      return CONVERT_TO_BYTEPTR(input16_ + BorderTop() * kOuterBlockSize +
                                BorderLeft());
    }
#else
James Zern's avatar
James Zern committed
396
    return input_ + BorderTop() * kOuterBlockSize + BorderLeft();
397
#endif
James Zern's avatar
James Zern committed
398
  }
John Koleszar's avatar
John Koleszar committed
399

400
  uint8_t *output() const {
401
#if CONFIG_VPX_HIGHBITDEPTH
402
    if (UUT_->use_highbd_ == 0) {
403 404 405 406 407 408
      return output_ + BorderTop() * kOuterBlockSize + BorderLeft();
    } else {
      return CONVERT_TO_BYTEPTR(output16_ + BorderTop() * kOuterBlockSize +
                                BorderLeft());
    }
#else
James Zern's avatar
James Zern committed
409
    return output_ + BorderTop() * kOuterBlockSize + BorderLeft();
410 411 412
#endif
  }

413
  uint8_t *output_ref() const {
414
#if CONFIG_VPX_HIGHBITDEPTH
415 416 417 418 419 420 421 422 423 424 425
    if (UUT_->use_highbd_ == 0) {
      return output_ref_ + BorderTop() * kOuterBlockSize + BorderLeft();
    } else {
      return CONVERT_TO_BYTEPTR(output16_ref_ + BorderTop() * kOuterBlockSize +
                                BorderLeft());
    }
#else
    return output_ref_ + BorderTop() * kOuterBlockSize + BorderLeft();
#endif
  }

426
  uint16_t lookup(uint8_t *list, int index) const {
427
#if CONFIG_VPX_HIGHBITDEPTH
428
    if (UUT_->use_highbd_ == 0) {
429 430 431 432 433 434 435 436 437 438
      return list[index];
    } else {
      return CONVERT_TO_SHORTPTR(list)[index];
    }
#else
    return list[index];
#endif
  }

  void assign_val(uint8_t *list, int index, uint16_t val) const {
439
#if CONFIG_VPX_HIGHBITDEPTH
440
    if (UUT_->use_highbd_ == 0) {
clang-format's avatar
clang-format committed
441
      list[index] = (uint8_t)val;
442 443 444 445
    } else {
      CONVERT_TO_SHORTPTR(list)[index] = val;
    }
#else
clang-format's avatar
clang-format committed
446
    list[index] = (uint8_t)val;
447 448 449
#endif
  }

clang-format's avatar
clang-format committed
450 451 452 453 454
  void wrapper_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) {
455
#if CONFIG_VPX_HIGHBITDEPTH
456
    if (UUT_->use_highbd_ == 0) {
clang-format's avatar
clang-format committed
457 458
      filter_average_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, dst_ptr,
                                 dst_stride, output_width, output_height);
459
    } else {
clang-format's avatar
clang-format committed
460 461 462 463
      highbd_filter_average_block2d_8_c(
          CONVERT_TO_SHORTPTR(src_ptr), src_stride, HFilter, VFilter,
          CONVERT_TO_SHORTPTR(dst_ptr), dst_stride, output_width, output_height,
          UUT_->use_highbd_);
464 465
    }
#else
clang-format's avatar
clang-format committed
466 467
    filter_average_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, dst_ptr,
                               dst_stride, output_width, output_height);
468 469 470 471 472 473
#endif
  }

  void wrapper_filter_block2d_8_c(const uint8_t *src_ptr,
                                  const unsigned int src_stride,
                                  const int16_t *HFilter,
clang-format's avatar
clang-format committed
474
                                  const int16_t *VFilter, uint8_t *dst_ptr,
475 476 477
                                  unsigned int dst_stride,
                                  unsigned int output_width,
                                  unsigned int output_height) {
478
#if CONFIG_VPX_HIGHBITDEPTH
479
    if (UUT_->use_highbd_ == 0) {
clang-format's avatar
clang-format committed
480 481
      filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, dst_ptr,
                         dst_stride, output_width, output_height);
482
    } else {
483
      highbd_filter_block2d_8_c(CONVERT_TO_SHORTPTR(src_ptr), src_stride,
clang-format's avatar
clang-format committed
484 485 486
                                HFilter, VFilter, CONVERT_TO_SHORTPTR(dst_ptr),
                                dst_stride, output_width, output_height,
                                UUT_->use_highbd_);
487 488
    }
#else
clang-format's avatar
clang-format committed
489 490
    filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, dst_ptr,
                       dst_stride, output_width, output_height);
491
#endif
James Zern's avatar
James Zern committed
492 493
  }

clang-format's avatar
clang-format committed
494 495 496 497
  const ConvolveFunctions *UUT_;
  static uint8_t *input_;
  static uint8_t *output_;
  static uint8_t *output_ref_;
498
#if CONFIG_VPX_HIGHBITDEPTH
clang-format's avatar
clang-format committed
499 500 501
  static uint16_t *input16_;
  static uint16_t *output16_;
  static uint16_t *output16_ref_;
502 503
  int mask_;
#endif
John Koleszar's avatar
John Koleszar committed
504
};
505

clang-format's avatar
clang-format committed
506 507 508
uint8_t *ConvolveTest::input_ = NULL;
uint8_t *ConvolveTest::output_ = NULL;
uint8_t *ConvolveTest::output_ref_ = NULL;
509
#if CONFIG_VPX_HIGHBITDEPTH
clang-format's avatar
clang-format committed
510 511 512
uint16_t *ConvolveTest::input16_ = NULL;
uint16_t *ConvolveTest::output16_ = NULL;
uint16_t *ConvolveTest::output16_ref_ = NULL;
513
#endif
John Koleszar's avatar
John Koleszar committed
514

clang-format's avatar
clang-format committed
515
TEST_P(ConvolveTest, GuardBlocks) { CheckGuardBlocks(); }
John Koleszar's avatar
John Koleszar committed
516

517
TEST_P(ConvolveTest, Copy) {
clang-format's avatar
clang-format committed
518 519
  uint8_t *const in = input();
  uint8_t *const out = output();
520

clang-format's avatar
clang-format committed
521 522
  ASM_REGISTER_STATE_CHECK(UUT_->copy_(in, kInputStride, out, kOutputStride,
                                       NULL, 0, NULL, 0, Width(), Height()));
523 524 525 526 527 528 529 530 531 532 533

  CheckGuardBlocks();

  for (int y = 0; y < Height(); ++y)
    for (int x = 0; x < Width(); ++x)
      ASSERT_EQ(lookup(out, y * kOutputStride + x),
                lookup(in, y * kInputStride + x))
          << "(" << x << "," << y << ")";
}

TEST_P(ConvolveTest, Avg) {
clang-format's avatar
clang-format committed
534 535 536
  uint8_t *const in = input();
  uint8_t *const out = output();
  uint8_t *const out_ref = output_ref();
537 538
  CopyOutputToRef();

clang-format's avatar
clang-format committed
539 540
  ASM_REGISTER_STATE_CHECK(UUT_->avg_(in, kInputStride, out, kOutputStride,
                                      NULL, 0, NULL, 0, Width(), Height()));
541 542 543 544 545 546 547

  CheckGuardBlocks();

  for (int y = 0; y < Height(); ++y)
    for (int x = 0; x < Width(); ++x)
      ASSERT_EQ(lookup(out, y * kOutputStride + x),
                ROUND_POWER_OF_TWO(lookup(in, y * kInputStride + x) +
clang-format's avatar
clang-format committed
548 549
                                       lookup(out_ref, y * kOutputStride + x),
                                   1))
550 551 552
          << "(" << x << "," << y << ")";
}

John Koleszar's avatar
John Koleszar committed
553
TEST_P(ConvolveTest, CopyHoriz) {
clang-format's avatar
clang-format committed
554 555 556 557
  uint8_t *const in = input();
  uint8_t *const out = output();
  DECLARE_ALIGNED(256, const int16_t,
                  filter8[8]) = { 0, 0, 0, 128, 0, 0, 0, 0 };
John Koleszar's avatar
John Koleszar committed
558

clang-format's avatar
clang-format committed
559 560 561
  ASM_REGISTER_STATE_CHECK(UUT_->sh8_(in, kInputStride, out, kOutputStride,
                                      filter8, 16, filter8, 16, Width(),
                                      Height()));
John Koleszar's avatar
John Koleszar committed
562 563 564 565 566

  CheckGuardBlocks();

  for (int y = 0; y < Height(); ++y)
    for (int x = 0; x < Width(); ++x)
567 568
      ASSERT_EQ(lookup(out, y * kOutputStride + x),
                lookup(in, y * kInputStride + x))
John Koleszar's avatar
John Koleszar committed
569 570 571 572
          << "(" << x << "," << y << ")";
}

TEST_P(ConvolveTest, CopyVert) {
clang-format's avatar
clang-format committed
573 574 575 576
  uint8_t *const in = input();
  uint8_t *const out = output();
  DECLARE_ALIGNED(256, const int16_t,
                  filter8[8]) = { 0, 0, 0, 128, 0, 0, 0, 0 };
John Koleszar's avatar
John Koleszar committed
577

clang-format's avatar
clang-format committed
578 579 580
  ASM_REGISTER_STATE_CHECK(UUT_->sv8_(in, kInputStride, out, kOutputStride,
                                      filter8, 16, filter8, 16, Width(),
                                      Height()));
John Koleszar's avatar
John Koleszar committed
581 582 583 584 585

  CheckGuardBlocks();

  for (int y = 0; y < Height(); ++y)
    for (int x = 0; x < Width(); ++x)
586 587
      ASSERT_EQ(lookup(out, y * kOutputStride + x),
                lookup(in, y * kInputStride + x))
John Koleszar's avatar
John Koleszar committed
588 589 590 591
          << "(" << x << "," << y << ")";
}

TEST_P(ConvolveTest, Copy2D) {
clang-format's avatar
clang-format committed
592 593 594 595
  uint8_t *const in = input();
  uint8_t *const out = output();
  DECLARE_ALIGNED(256, const int16_t,
                  filter8[8]) = { 0, 0, 0, 128, 0, 0, 0, 0 };
John Koleszar's avatar
John Koleszar committed
596

clang-format's avatar
clang-format committed
597 598 599
  ASM_REGISTER_STATE_CHECK(UUT_->shv8_(in, kInputStride, out, kOutputStride,
                                       filter8, 16, filter8, 16, Width(),
                                       Height()));
John Koleszar's avatar
John Koleszar committed
600 601 602 603 604

  CheckGuardBlocks();

  for (int y = 0; y < Height(); ++y)
    for (int x = 0; x < Width(); ++x)
605 606
      ASSERT_EQ(lookup(out, y * kOutputStride + x),
                lookup(in, y * kInputStride + x))
John Koleszar's avatar
John Koleszar committed
607 608 609
          << "(" << x << "," << y << ")";
}

610
const int kNumFilterBanks = 4;
611 612 613 614
const int kNumFilters = 16;

TEST(ConvolveTest, FiltersWontSaturateWhenAddedPairwise) {
  for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
615
    const InterpKernel *filters =
616
        vp10_filter_kernels[static_cast<INTERP_FILTER>(filter_bank)];
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
    for (int i = 0; i < kNumFilters; i++) {
      const int p0 = filters[i][0] + filters[i][1];
      const int p1 = filters[i][2] + filters[i][3];
      const int p2 = filters[i][4] + filters[i][5];
      const int p3 = filters[i][6] + filters[i][7];
      EXPECT_LE(p0, 128);
      EXPECT_LE(p1, 128);
      EXPECT_LE(p2, 128);
      EXPECT_LE(p3, 128);
      EXPECT_LE(p0 + p3, 128);
      EXPECT_LE(p0 + p3 + p1, 128);
      EXPECT_LE(p0 + p3 + p1 + p2, 128);
      EXPECT_EQ(p0 + p1 + p2 + p3, 128);
    }
  }
}
633

634 635
const int16_t kInvalidFilter[8] = { 0 };

John Koleszar's avatar
John Koleszar committed
636
TEST_P(ConvolveTest, MatchesReferenceSubpixelFilter) {
clang-format's avatar
clang-format committed
637 638
  uint8_t *const in = input();
  uint8_t *const out = output();
639
#if CONFIG_VPX_HIGHBITDEPTH
640 641
  uint8_t ref8[kOutputStride * kMaxDimension];
  uint16_t ref16[kOutputStride * kMaxDimension];
clang-format's avatar
clang-format committed
642
  uint8_t *ref;
643
  if (UUT_->use_highbd_ == 0) {
644 645 646 647 648
    ref = ref8;
  } else {
    ref = CONVERT_TO_BYTEPTR(ref16);
  }
#else
John Koleszar's avatar
John Koleszar committed
649
  uint8_t ref[kOutputStride * kMaxDimension];
650
#endif
651 652

  for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
653
    const InterpKernel *filters =
654
        vp10_filter_kernels[static_cast<INTERP_FILTER>(filter_bank)];
655

656 657
    for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
      for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
clang-format's avatar
clang-format committed
658 659
        wrapper_filter_block2d_8_c(in, kInputStride, filters[filter_x],
                                   filters[filter_y], ref, kOutputStride,
660
                                   Width(), Height());
661

662
        if (filter_x && filter_y)
clang-format's avatar
clang-format committed
663 664 665
          ASM_REGISTER_STATE_CHECK(UUT_->hv8_(
              in, kInputStride, out, kOutputStride, filters[filter_x], 16,
              filters[filter_y], 16, Width(), Height()));
666
        else if (filter_y)
667
          ASM_REGISTER_STATE_CHECK(
clang-format's avatar
clang-format committed
668 669
              UUT_->v8_(in, kInputStride, out, kOutputStride, kInvalidFilter,
                        16, filters[filter_y], 16, Width(), Height()));
670
        else if (filter_x)
671
          ASM_REGISTER_STATE_CHECK(
clang-format's avatar
clang-format committed
672 673
              UUT_->h8_(in, kInputStride, out, kOutputStride, filters[filter_x],
                        16, kInvalidFilter, 16, Width(), Height()));
674 675
        else
          ASM_REGISTER_STATE_CHECK(
clang-format's avatar
clang-format committed
676 677
              UUT_->copy_(in, kInputStride, out, kOutputStride, kInvalidFilter,
                          0, kInvalidFilter, 0, Width(), Height()));
678 679 680 681 682

        CheckGuardBlocks();

        for (int y = 0; y < Height(); ++y)
          for (int x = 0; x < Width(); ++x)
683 684
            ASSERT_EQ(lookup(ref, y * kOutputStride + x),
                      lookup(out, y * kOutputStride + x))
685
                << "mismatch at (" << x << "," << y << "), "
clang-format's avatar
clang-format committed
686 687
                << "filters (" << filter_bank << "," << filter_x << ","
                << filter_y << ")";
688
      }
John Koleszar's avatar
John Koleszar committed
689 690 691 692 693
    }
  }
}

TEST_P(ConvolveTest, MatchesReferenceAveragingSubpixelFilter) {
clang-format's avatar
clang-format committed
694 695
  uint8_t *const in = input();
  uint8_t *const out = output();
696
#if CONFIG_VPX_HIGHBITDEPTH
697 698
  uint8_t ref8[kOutputStride * kMaxDimension];
  uint16_t ref16[kOutputStride * kMaxDimension];
clang-format's avatar
clang-format committed
699
  uint8_t *ref;
700
  if (UUT_->use_highbd_ == 0) {
701 702 703 704 705
    ref = ref8;
  } else {
    ref = CONVERT_TO_BYTEPTR(ref16);
  }
#else
John Koleszar's avatar
John Koleszar committed
706
  uint8_t ref[kOutputStride * kMaxDimension];
707
#endif
John Koleszar's avatar
John Koleszar committed
708 709

  // Populate ref and out with some random data
Yaowu Xu's avatar
Yaowu Xu committed
710
  ::libaom_test::ACMRandom prng;
John Koleszar's avatar
John Koleszar committed
711 712
  for (int y = 0; y < Height(); ++y) {
    for (int x = 0; x < Width(); ++x) {
713
      uint16_t r;
714
#if CONFIG_VPX_HIGHBITDEPTH
715
      if (UUT_->use_highbd_ == 0 || UUT_->use_highbd_ == 8) {
716 717 718 719 720 721 722
        r = prng.Rand8Extremes();
      } else {
        r = prng.Rand16() & mask_;
      }
#else
      r = prng.Rand8Extremes();
#endif
John Koleszar's avatar
John Koleszar committed
723

724 725
      assign_val(out, y * kOutputStride + x, r);
      assign_val(ref, y * kOutputStride + x, r);
John Koleszar's avatar
John Koleszar committed
726 727 728
    }
  }

729
  for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
730
    const InterpKernel *filters =
731
        vp10_filter_kernels[static_cast<INTERP_FILTER>(filter_bank)];
732 733 734

    for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
      for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
clang-format's avatar
clang-format committed
735 736 737
        wrapper_filter_average_block2d_8_c(in, kInputStride, filters[filter_x],
                                           filters[filter_y], ref,
                                           kOutputStride, Width(), Height());
738

739
        if (filter_x && filter_y)
clang-format's avatar
clang-format committed
740 741 742
          ASM_REGISTER_STATE_CHECK(UUT_->hv8_avg_(
              in, kInputStride, out, kOutputStride, filters[filter_x], 16,
              filters[filter_y], 16, Width(), Height()));
743
        else if (filter_y)
clang-format's avatar
clang-format committed
744 745 746
          ASM_REGISTER_STATE_CHECK(UUT_->v8_avg_(
              in, kInputStride, out, kOutputStride, kInvalidFilter, 16,
              filters[filter_y], 16, Width(), Height()));
747
        else if (filter_x)
clang-format's avatar
clang-format committed
748 749 750
          ASM_REGISTER_STATE_CHECK(UUT_->h8_avg_(
              in, kInputStride, out, kOutputStride, filters[filter_x], 16,
              kInvalidFilter, 16, Width(), Height()));
751 752
        else
          ASM_REGISTER_STATE_CHECK(
clang-format's avatar
clang-format committed
753 754
              UUT_->avg_(in, kInputStride, out, kOutputStride, kInvalidFilter,
                         0, kInvalidFilter, 0, Width(), Height()));
755 756 757 758 759

        CheckGuardBlocks();

        for (int y = 0; y < Height(); ++y)
          for (int x = 0; x < Width(); ++x)
760 761
            ASSERT_EQ(lookup(ref, y * kOutputStride + x),
                      lookup(out, y * kOutputStride + x))
762
                << "mismatch at (" << x << "," << y << "), "
clang-format's avatar
clang-format committed
763 764
                << "filters (" << filter_bank << "," << filter_x << ","
                << filter_y << ")";
765
      }
John Koleszar's avatar
John Koleszar committed
766 767 768 769
    }
  }
}

770 771 772
TEST_P(ConvolveTest, FilterExtremes) {
  uint8_t *const in = input();
  uint8_t *const out = output();
773
#if CONFIG_VPX_HIGHBITDEPTH
774 775 776
  uint8_t ref8[kOutputStride * kMaxDimension];
  uint16_t ref16[kOutputStride * kMaxDimension];
  uint8_t *ref;
777
  if (UUT_->use_highbd_ == 0) {
778 779 780 781 782 783 784 785 786
    ref = ref8;
  } else {
    ref = CONVERT_TO_BYTEPTR(ref16);
  }
#else
  uint8_t ref[kOutputStride * kMaxDimension];
#endif

  // Populate ref and out with some random data
Yaowu Xu's avatar
Yaowu Xu committed
787
  ::libaom_test::ACMRandom prng;
788 789 790
  for (int y = 0; y < Height(); ++y) {
    for (int x = 0; x < Width(); ++x) {
      uint16_t r;
791
#if CONFIG_VPX_HIGHBITDEPTH
792
      if (UUT_->use_highbd_ == 0 || UUT_->use_highbd_ == 8) {
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
        r = prng.Rand8Extremes();
      } else {
        r = prng.Rand16() & mask_;
      }
#else
      r = prng.Rand8Extremes();
#endif
      assign_val(out, y * kOutputStride + x, r);
      assign_val(ref, y * kOutputStride + x, r);
    }
  }

  for (int axis = 0; axis < 2; axis++) {
    int seed_val = 0;
    while (seed_val < 256) {
      for (int y = 0; y < 8; ++y) {
        for (int x = 0; x < 8; ++x) {
810
#if CONFIG_VPX_HIGHBITDEPTH
clang-format's avatar
clang-format committed
811 812
          assign_val(in, y * kOutputStride + x - SUBPEL_TAPS / 2 + 1,
                     ((seed_val >> (axis ? y : x)) & 1) * mask_);
813
#else
clang-format's avatar
clang-format committed
814 815
          assign_val(in, y * kOutputStride + x - SUBPEL_TAPS / 2 + 1,
                     ((seed_val >> (axis ? y : x)) & 1) * 255);
816 817 818 819
#endif
          if (axis) seed_val++;
        }
        if (axis)
clang-format's avatar
clang-format committed
820
          seed_val -= 8;
821 822 823 824 825 826 827
        else
          seed_val++;
      }
      if (axis) seed_val += 8;

      for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
        const InterpKernel *filters =
828
            vp10_filter_kernels[static_cast<INTERP_FILTER>(filter_bank)];
829 830
        for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
          for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
clang-format's avatar
clang-format committed
831 832
            wrapper_filter_block2d_8_c(in, kInputStride, filters[filter_x],
                                       filters[filter_y], ref, kOutputStride,
833
                                       Width(), Height());
834
            if (filter_x && filter_y)
clang-format's avatar
clang-format committed
835 836 837
              ASM_REGISTER_STATE_CHECK(UUT_->hv8_(
                  in, kInputStride, out, kOutputStride, filters[filter_x], 16,
                  filters[filter_y], 16, Width(), Height()));
838
            else if (filter_y)
clang-format's avatar
clang-format committed
839 840 841
              ASM_REGISTER_STATE_CHECK(UUT_->v8_(
                  in, kInputStride, out, kOutputStride, kInvalidFilter, 16,
                  filters[filter_y], 16, Width(), Height()));
842
            else if (filter_x)
clang-format's avatar
clang-format committed
843 844 845
              ASM_REGISTER_STATE_CHECK(UUT_->h8_(
                  in, kInputStride, out, kOutputStride, filters[filter_x], 16,
                  kInvalidFilter, 16, Width(), Height()));
846
            else
clang-format's avatar
clang-format committed
847 848 849
              ASM_REGISTER_STATE_CHECK(UUT_->copy_(
                  in, kInputStride, out, kOutputStride, kInvalidFilter, 0,
                  kInvalidFilter, 0, Width(), Height()));
850 851 852 853 854 855

            for (int y = 0; y < Height(); ++y)
              for (int x = 0; x < Width(); ++x)
                ASSERT_EQ(lookup(ref, y * kOutputStride + x),
                          lookup(out, y * kOutputStride + x))
                    << "mismatch at (" << x << "," << y << "), "
clang-format's avatar
clang-format committed
856 857
                    << "filters (" << filter_bank << "," << filter_x << ","
                    << filter_y << ")";
858 859 860 861 862 863 864
          }
        }
      }
    }
  }
}

865 866 867
/* This test exercises that enough rows and columns are filtered with every
   possible initial fractional positions and scaling steps. */
TEST_P(ConvolveTest, CheckScalingFiltering) {
clang-format's avatar
clang-format committed
868 869
  uint8_t *const in = input();
  uint8_t *const out = output();
870
  const InterpKernel *const eighttap = vp10_filter_kernels[EIGHTTAP];
871 872 873 874 875 876

  SetConstantInput(127);

  for (int frac = 0; frac < 16; ++frac) {
    for (int step = 1; step <= 32; ++step) {
      /* Test the horizontal and vertical filters in combination. */
877
      ASM_REGISTER_STATE_CHECK(UUT_->shv8_(in, kInputStride, out, kOutputStride,
clang-format's avatar
clang-format committed
878 879
                                           eighttap[frac], step, eighttap[frac],
                                           step, Width(), Height()));
880 881 882 883 884

      CheckGuardBlocks();

      for (int y = 0; y < Height(); ++y) {
        for (int x = 0; x < Width(); ++x) {
885 886
          ASSERT_EQ(lookup(in, y * kInputStride + x),
                    lookup(out, y * kOutputStride + x))
clang-format's avatar
clang-format committed
887 888
              << "x == " << x << ", y == " << y << ", frac == " << frac
              << ", step == " << step;
889 890 891 892 893 894
        }
      }
    }
  }
}

John Koleszar's avatar
John Koleszar committed
895 896
using std::tr1::make_tuple;

897
#if CONFIG_VPX_HIGHBITDEPTH
clang-format's avatar
clang-format committed
898 899 900 901 902