blob: 6af2abb79f66d94498634035e1d1a66a14a9ae11 [file] [log] [blame]
John Koleszar5ca6a362013-01-25 09:47:09 -08001/*
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
Tero Rintaluomae326cec2013-08-22 11:29:19 +030011#include <string.h>
James Zern8fb48af2013-05-02 13:08:19 -070012#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"
John Koleszar5ca6a362013-01-25 09:47:09 -080016
John Koleszar5ca6a362013-01-25 09:47:09 -080017#include "./vpx_config.h"
18#include "./vp9_rtcd.h"
John Koleszar557a1b22013-02-20 16:13:01 -080019#include "vp9/common/vp9_filter.h"
John Koleszar29d47ac2013-02-07 17:00:37 -080020#include "vpx_mem/vpx_mem.h"
John Koleszar6fd7dd12013-02-20 15:59:20 -080021#include "vpx_ports/mem.h"
John Koleszar5ca6a362013-01-25 09:47:09 -080022
23namespace {
Ronald S. Bultjedecead72013-07-10 11:17:19 -070024typedef void (*convolve_fn_t)(const uint8_t *src, ptrdiff_t src_stride,
25 uint8_t *dst, ptrdiff_t dst_stride,
John Koleszar5ca6a362013-01-25 09:47:09 -080026 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
Joshua Litt51490e52013-11-18 17:07:55 -080045typedef std::tr1::tuple<int, int, const ConvolveFunctions*> convolve_param_t;
46
John Koleszar5ca6a362013-01-25 09:47:09 -080047// Reference 8-tap subpixel filter, slightly modified to fit into this test.
48#define VP9_FILTER_WEIGHT 128
49#define VP9_FILTER_SHIFT 7
James Zern8fb48af2013-05-02 13:08:19 -070050uint8_t clip_pixel(int x) {
John Koleszar5ca6a362013-01-25 09:47:09 -080051 return x < 0 ? 0 :
52 x > 255 ? 255 :
53 x;
54}
55
James Zern8fb48af2013-05-02 13:08:19 -070056void 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) {
John Koleszar5ca6a362013-01-25 09:47:09 -080064 // 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 =
James Zern8fb48af2013-05-02 13:08:19 -070070 (kInterp_Extend - 1) + output_height + kInterp_Extend;
John Koleszar5ca6a362013-01-25 09:47:09 -080071
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 */
John Koleszara9ebbcc2013-04-18 13:05:38 -070079 uint8_t intermediate_buffer[71 * 64];
John Koleszar5ca6a362013-01-25 09:47:09 -080080 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...
James Zern8fb48af2013-05-02 13:08:19 -070091 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
John Koleszar5ca6a362013-01-25 09:47:09 -0800100
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...
James Zern8fb48af2013-05-02 13:08:19 -0700119 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
John Koleszar5ca6a362013-01-25 09:47:09 -0800128
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
James Zern8fb48af2013-05-02 13:08:19 -0700139void 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) {
John Koleszar5ca6a362013-01-25 09:47:09 -0800145 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
James Zern8fb48af2013-05-02 13:08:19 -0700154void 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];
John Koleszar5ca6a362013-01-25 09:47:09 -0800163
John Koleszara9ebbcc2013-04-18 13:05:38 -0700164 assert(output_width <= 64);
165 assert(output_height <= 64);
166 filter_block2d_8_c(src_ptr, src_stride, HFilter, VFilter, tmp, 64,
John Koleszar5ca6a362013-01-25 09:47:09 -0800167 output_width, output_height);
John Koleszara9ebbcc2013-04-18 13:05:38 -0700168 block2d_average_c(tmp, 64, dst_ptr, dst_stride,
John Koleszar5ca6a362013-01-25 09:47:09 -0800169 output_width, output_height);
170}
171
Joshua Litt51490e52013-11-18 17:07:55 -0800172class ConvolveTest : public ::testing::TestWithParam<convolve_param_t> {
John Koleszar29d47ac2013-02-07 17:00:37 -0800173 public:
174 static void SetUpTestCase() {
175 // Force input_ to be unaligned, output to be 16 byte aligned.
176 input_ = reinterpret_cast<uint8_t*>(
James Zernb0e57752013-05-02 12:29:34 -0700177 vpx_memalign(kDataAlignment, kInputBufferSize + 1)) + 1;
John Koleszar29d47ac2013-02-07 17:00:37 -0800178 output_ = reinterpret_cast<uint8_t*>(
James Zernb0e57752013-05-02 12:29:34 -0700179 vpx_memalign(kDataAlignment, kOutputBufferSize));
John Koleszar29d47ac2013-02-07 17:00:37 -0800180 }
181
182 static void TearDownTestCase() {
183 vpx_free(input_ - 1);
184 input_ = NULL;
185 vpx_free(output_);
186 output_ = NULL;
187 }
188
James Zern8fb48af2013-05-02 13:08:19 -0700189 protected:
190 static const int kDataAlignment = 16;
Tero Rintaluomae326cec2013-08-22 11:29:19 +0300191 static const int kOuterBlockSize = 256;
James Zern8fb48af2013-05-02 13:08:19 -0700192 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;
John Koleszar5ca6a362013-01-25 09:47:09 -0800197
James Zern8fb48af2013-05-02 13:08:19 -0700198 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; }
John Koleszar5ca6a362013-01-25 09:47:09 -0800205
James Zern8fb48af2013-05-02 13:08:19 -0700206 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);
Johann158c80c2013-05-23 12:50:41 -0700215 /* Set up guard blocks for an inner block centered in the outer block */
James Zern8fb48af2013-05-02 13:08:19 -0700216 for (int i = 0; i < kOutputBufferSize; ++i) {
217 if (IsIndexInBorder(i))
218 output_[i] = 255;
219 else
220 output_[i] = 0;
John Koleszar5ca6a362013-01-25 09:47:09 -0800221 }
222
James Zern8fb48af2013-05-02 13:08:19 -0700223 ::libvpx_test::ACMRandom prng;
Yaowu Xu077144d2014-05-23 12:23:29 -0700224 for (int i = 0; i < kInputBufferSize; ++i) {
225 if (i & 1)
226 input_[i] = 255;
227 else
228 input_[i] = prng.Rand8Extremes();
229 }
James Zern8fb48af2013-05-02 13:08:19 -0700230 }
John Koleszar5ca6a362013-01-25 09:47:09 -0800231
Tero Rintaluomae326cec2013-08-22 11:29:19 +0300232 void SetConstantInput(int value) {
233 memset(input_, value, kInputBufferSize);
234 }
235
James Zern8fb48af2013-05-02 13:08:19 -0700236 void CheckGuardBlocks() {
237 for (int i = 0; i < kOutputBufferSize; ++i) {
238 if (IsIndexInBorder(i))
239 EXPECT_EQ(255, output_[i]);
John Koleszar5ca6a362013-01-25 09:47:09 -0800240 }
James Zern8fb48af2013-05-02 13:08:19 -0700241 }
John Koleszar5ca6a362013-01-25 09:47:09 -0800242
James Zern8fb48af2013-05-02 13:08:19 -0700243 uint8_t* input() const {
244 return input_ + BorderTop() * kOuterBlockSize + BorderLeft();
245 }
John Koleszar5ca6a362013-01-25 09:47:09 -0800246
James Zern8fb48af2013-05-02 13:08:19 -0700247 uint8_t* output() const {
248 return output_ + BorderTop() * kOuterBlockSize + BorderLeft();
249 }
John Koleszar5ca6a362013-01-25 09:47:09 -0800250
James Zern8fb48af2013-05-02 13:08:19 -0700251 const ConvolveFunctions* UUT_;
252 static uint8_t* input_;
253 static uint8_t* output_;
John Koleszar5ca6a362013-01-25 09:47:09 -0800254};
John Koleszar29d47ac2013-02-07 17:00:37 -0800255uint8_t* ConvolveTest::input_ = NULL;
256uint8_t* ConvolveTest::output_ = NULL;
John Koleszar5ca6a362013-01-25 09:47:09 -0800257
258TEST_P(ConvolveTest, GuardBlocks) {
259 CheckGuardBlocks();
260}
261
262TEST_P(ConvolveTest, CopyHoriz) {
263 uint8_t* const in = input();
264 uint8_t* const out = output();
James Zerne7b599f2013-06-17 23:11:01 -0700265 DECLARE_ALIGNED(256, const int16_t, filter8[8]) = {0, 0, 0, 128, 0, 0, 0, 0};
John Koleszar5ca6a362013-01-25 09:47:09 -0800266
267 REGISTER_STATE_CHECK(
268 UUT_->h8_(in, kInputStride, out, kOutputStride, filter8, 16, filter8, 16,
269 Width(), Height()));
270
271 CheckGuardBlocks();
272
273 for (int y = 0; y < Height(); ++y)
274 for (int x = 0; x < Width(); ++x)
275 ASSERT_EQ(out[y * kOutputStride + x], in[y * kInputStride + x])
276 << "(" << x << "," << y << ")";
277}
278
279TEST_P(ConvolveTest, CopyVert) {
280 uint8_t* const in = input();
281 uint8_t* const out = output();
James Zerne7b599f2013-06-17 23:11:01 -0700282 DECLARE_ALIGNED(256, const int16_t, filter8[8]) = {0, 0, 0, 128, 0, 0, 0, 0};
John Koleszar5ca6a362013-01-25 09:47:09 -0800283
284 REGISTER_STATE_CHECK(
285 UUT_->v8_(in, kInputStride, out, kOutputStride, filter8, 16, filter8, 16,
286 Width(), Height()));
287
288 CheckGuardBlocks();
289
290 for (int y = 0; y < Height(); ++y)
291 for (int x = 0; x < Width(); ++x)
292 ASSERT_EQ(out[y * kOutputStride + x], in[y * kInputStride + x])
293 << "(" << x << "," << y << ")";
294}
295
296TEST_P(ConvolveTest, Copy2D) {
297 uint8_t* const in = input();
298 uint8_t* const out = output();
James Zerne7b599f2013-06-17 23:11:01 -0700299 DECLARE_ALIGNED(256, const int16_t, filter8[8]) = {0, 0, 0, 128, 0, 0, 0, 0};
John Koleszar5ca6a362013-01-25 09:47:09 -0800300
301 REGISTER_STATE_CHECK(
302 UUT_->hv8_(in, kInputStride, out, kOutputStride, filter8, 16, filter8, 16,
303 Width(), Height()));
304
305 CheckGuardBlocks();
306
307 for (int y = 0; y < Height(); ++y)
308 for (int x = 0; x < Width(); ++x)
309 ASSERT_EQ(out[y * kOutputStride + x], in[y * kInputStride + x])
310 << "(" << x << "," << y << ")";
311}
312
Dmitry Kovalev3d4ed272014-04-21 14:15:35 -0700313const int kNumFilterBanks = 4;
John Koleszara9ebbcc2013-04-18 13:05:38 -0700314const int kNumFilters = 16;
315
316TEST(ConvolveTest, FiltersWontSaturateWhenAddedPairwise) {
317 for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
Dmitry Kovalev3d4ed272014-04-21 14:15:35 -0700318 const InterpKernel *filters =
319 vp9_get_interp_kernel(static_cast<INTERP_FILTER>(filter_bank));
John Koleszara9ebbcc2013-04-18 13:05:38 -0700320 for (int i = 0; i < kNumFilters; i++) {
321 const int p0 = filters[i][0] + filters[i][1];
322 const int p1 = filters[i][2] + filters[i][3];
323 const int p2 = filters[i][4] + filters[i][5];
324 const int p3 = filters[i][6] + filters[i][7];
325 EXPECT_LE(p0, 128);
326 EXPECT_LE(p1, 128);
327 EXPECT_LE(p2, 128);
328 EXPECT_LE(p3, 128);
329 EXPECT_LE(p0 + p3, 128);
330 EXPECT_LE(p0 + p3 + p1, 128);
331 EXPECT_LE(p0 + p3 + p1 + p2, 128);
332 EXPECT_EQ(p0 + p1 + p2 + p3, 128);
333 }
334 }
335}
John Koleszar557a1b22013-02-20 16:13:01 -0800336
John Koleszar04c24072013-02-20 16:32:02 -0800337const int16_t kInvalidFilter[8] = { 0 };
338
John Koleszar5ca6a362013-01-25 09:47:09 -0800339TEST_P(ConvolveTest, MatchesReferenceSubpixelFilter) {
340 uint8_t* const in = input();
341 uint8_t* const out = output();
342 uint8_t ref[kOutputStride * kMaxDimension];
343
John Koleszar5ca6a362013-01-25 09:47:09 -0800344
John Koleszar557a1b22013-02-20 16:13:01 -0800345 for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
Dmitry Kovalev3d4ed272014-04-21 14:15:35 -0700346 const InterpKernel *filters =
347 vp9_get_interp_kernel(static_cast<INTERP_FILTER>(filter_bank));
Dmitry Kovalev021eaab2014-05-14 16:21:41 -0700348 const InterpKernel *const eighttap_smooth =
349 vp9_get_interp_kernel(EIGHTTAP_SMOOTH);
350
John Koleszar557a1b22013-02-20 16:13:01 -0800351 for (int filter_x = 0; filter_x < kNumFilters; ++filter_x) {
352 for (int filter_y = 0; filter_y < kNumFilters; ++filter_y) {
353 filter_block2d_8_c(in, kInputStride,
354 filters[filter_x], filters[filter_y],
355 ref, kOutputStride,
356 Width(), Height());
John Koleszar5ca6a362013-01-25 09:47:09 -0800357
Dmitry Kovalev021eaab2014-05-14 16:21:41 -0700358 if (filters == eighttap_smooth || (filter_x && filter_y))
John Koleszar557a1b22013-02-20 16:13:01 -0800359 REGISTER_STATE_CHECK(
360 UUT_->hv8_(in, kInputStride, out, kOutputStride,
361 filters[filter_x], 16, filters[filter_y], 16,
362 Width(), Height()));
363 else if (filter_y)
364 REGISTER_STATE_CHECK(
365 UUT_->v8_(in, kInputStride, out, kOutputStride,
John Koleszar04c24072013-02-20 16:32:02 -0800366 kInvalidFilter, 16, filters[filter_y], 16,
John Koleszar557a1b22013-02-20 16:13:01 -0800367 Width(), Height()));
368 else
369 REGISTER_STATE_CHECK(
370 UUT_->h8_(in, kInputStride, out, kOutputStride,
John Koleszar04c24072013-02-20 16:32:02 -0800371 filters[filter_x], 16, kInvalidFilter, 16,
John Koleszar557a1b22013-02-20 16:13:01 -0800372 Width(), Height()));
John Koleszar5ca6a362013-01-25 09:47:09 -0800373
John Koleszar557a1b22013-02-20 16:13:01 -0800374 CheckGuardBlocks();
John Koleszar5ca6a362013-01-25 09:47:09 -0800375
John Koleszar557a1b22013-02-20 16:13:01 -0800376 for (int y = 0; y < Height(); ++y)
377 for (int x = 0; x < Width(); ++x)
378 ASSERT_EQ(ref[y * kOutputStride + x], out[y * kOutputStride + x])
379 << "mismatch at (" << x << "," << y << "), "
380 << "filters (" << filter_bank << ","
381 << filter_x << "," << filter_y << ")";
382 }
John Koleszar5ca6a362013-01-25 09:47:09 -0800383 }
384 }
385}
386
387TEST_P(ConvolveTest, MatchesReferenceAveragingSubpixelFilter) {
388 uint8_t* const in = input();
389 uint8_t* const out = output();
390 uint8_t ref[kOutputStride * kMaxDimension];
391
392 // Populate ref and out with some random data
393 ::libvpx_test::ACMRandom prng;
394 for (int y = 0; y < Height(); ++y) {
395 for (int x = 0; x < Width(); ++x) {
John Koleszara9ebbcc2013-04-18 13:05:38 -0700396 const uint8_t r = prng.Rand8Extremes();
John Koleszar5ca6a362013-01-25 09:47:09 -0800397
398 out[y * kOutputStride + x] = r;
399 ref[y * kOutputStride + x] = r;
400 }
401 }
402
John Koleszar557a1b22013-02-20 16:13:01 -0800403 for (int filter_bank = 0; filter_bank < kNumFilterBanks; ++filter_bank) {
Dmitry Kovalev3d4ed272014-04-21 14:15:35 -0700404 const InterpKernel *filters =
405 vp9_get_interp_kernel(static_cast<INTERP_FILTER>(filter_bank));
Dmitry Kovalev021eaab2014-05-14 16:21:41 -0700406 const InterpKernel *const eighttap_smooth =
407 vp9_get_interp_kernel(EIGHTTAP_SMOOTH);
John Koleszar5ca6a362013-01-25 09:47:09 -0800408
John Koleszar557a1b22013-02-20 16:13:01 -0800409 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());
John Koleszar5ca6a362013-01-25 09:47:09 -0800415
Dmitry Kovalev021eaab2014-05-14 16:21:41 -0700416 if (filters == eighttap_smooth || (filter_x && filter_y))
John Koleszar557a1b22013-02-20 16:13:01 -0800417 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()));
John Koleszar5ca6a362013-01-25 09:47:09 -0800431
John Koleszar557a1b22013-02-20 16:13:01 -0800432 CheckGuardBlocks();
John Koleszar5ca6a362013-01-25 09:47:09 -0800433
John Koleszar557a1b22013-02-20 16:13:01 -0800434 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 }
John Koleszar5ca6a362013-01-25 09:47:09 -0800441 }
442 }
443}
444
John Koleszar6fd7dd12013-02-20 15:59:20 -0800445DECLARE_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
Adrian Grange3f108312013-08-22 16:02:18 -0700464/* This test exercises the horizontal and vertical filter functions. */
John Koleszar5ca6a362013-01-25 09:47:09 -0800465TEST_P(ConvolveTest, ChangeFilterWorks) {
466 uint8_t* const in = input();
467 uint8_t* const out = output();
Adrian Grange3f108312013-08-22 16:02:18 -0700468
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 */
John Koleszara9ebbcc2013-04-18 13:05:38 -0700479 const int kPixelSelected = 4;
John Koleszar5ca6a362013-01-25 09:47:09 -0800480
Adrian Grange3f108312013-08-22 16:02:18 -0700481 /* 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. */
John Koleszar5ca6a362013-01-25 09:47:09 -0800497 REGISTER_STATE_CHECK(UUT_->h8_(in, kInputStride, out, kOutputStride,
Adrian Grange3f108312013-08-22 16:02:18 -0700498 kChangeFilters[kInitialSubPelOffset],
499 kInputPixelStep, NULL, 0, Width(), Height()));
John Koleszar5ca6a362013-01-25 09:47:09 -0800500
John Koleszar6fd7dd12013-02-20 15:59:20 -0800501 for (int x = 0; x < Width(); ++x) {
John Koleszara9ebbcc2013-04-18 13:05:38 -0700502 const int kFilterPeriodAdjust = (x >> 3) << 3;
Adrian Grange3f108312013-08-22 16:02:18 -0700503 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();
John Koleszar5ca6a362013-01-25 09:47:09 -0800508 }
509
Adrian Grange3f108312013-08-22 16:02:18 -0700510 /* Test the vertical filter. */
John Koleszar5ca6a362013-01-25 09:47:09 -0800511 REGISTER_STATE_CHECK(UUT_->v8_(in, kInputStride, out, kOutputStride,
Adrian Grange3f108312013-08-22 16:02:18 -0700512 NULL, 0, kChangeFilters[kInitialSubPelOffset],
513 kInputPixelStep, Width(), Height()));
John Koleszar5ca6a362013-01-25 09:47:09 -0800514
John Koleszar6fd7dd12013-02-20 15:59:20 -0800515 for (int y = 0; y < Height(); ++y) {
John Koleszara9ebbcc2013-04-18 13:05:38 -0700516 const int kFilterPeriodAdjust = (y >> 3) << 3;
Adrian Grange3f108312013-08-22 16:02:18 -0700517 const int ref_y =
518 kPixelSelected + ((kInitialSubPelOffset
519 + kFilterPeriodAdjust * kInputPixelStep)
520 >> SUBPEL_BITS);
John Koleszara9ebbcc2013-04-18 13:05:38 -0700521 ASSERT_EQ(in[ref_y * kInputStride], out[y * kInputStride]) << "y == " << y;
John Koleszar5ca6a362013-01-25 09:47:09 -0800522 }
523
Adrian Grange3f108312013-08-22 16:02:18 -0700524 /* Test the horizontal and vertical filters in combination. */
John Koleszar5ca6a362013-01-25 09:47:09 -0800525 REGISTER_STATE_CHECK(UUT_->hv8_(in, kInputStride, out, kOutputStride,
Adrian Grange3f108312013-08-22 16:02:18 -0700526 kChangeFilters[kInitialSubPelOffset],
527 kInputPixelStep,
528 kChangeFilters[kInitialSubPelOffset],
529 kInputPixelStep,
John Koleszar5ca6a362013-01-25 09:47:09 -0800530 Width(), Height()));
531
John Koleszar6fd7dd12013-02-20 15:59:20 -0800532 for (int y = 0; y < Height(); ++y) {
John Koleszara9ebbcc2013-04-18 13:05:38 -0700533 const int kFilterPeriodAdjustY = (y >> 3) << 3;
Adrian Grange3f108312013-08-22 16:02:18 -0700534 const int ref_y =
535 kPixelSelected + ((kInitialSubPelOffset
536 + kFilterPeriodAdjustY * kInputPixelStep)
537 >> SUBPEL_BITS);
John Koleszar6fd7dd12013-02-20 15:59:20 -0800538 for (int x = 0; x < Width(); ++x) {
John Koleszara9ebbcc2013-04-18 13:05:38 -0700539 const int kFilterPeriodAdjustX = (x >> 3) << 3;
Adrian Grange3f108312013-08-22 16:02:18 -0700540 const int ref_x =
541 kPixelSelected + ((kInitialSubPelOffset
542 + kFilterPeriodAdjustX * kInputPixelStep)
543 >> SUBPEL_BITS);
John Koleszar6fd7dd12013-02-20 15:59:20 -0800544
545 ASSERT_EQ(in[ref_y * kInputStride + ref_x], out[y * kOutputStride + x])
John Koleszar5ca6a362013-01-25 09:47:09 -0800546 << "x == " << x << ", y == " << y;
547 }
548 }
549}
550
Tero Rintaluomae326cec2013-08-22 11:29:19 +0300551/* 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();
Dmitry Kovalev021eaab2014-05-14 16:21:41 -0700556 const InterpKernel *const eighttap = vp9_get_interp_kernel(EIGHTTAP);
Tero Rintaluomae326cec2013-08-22 11:29:19 +0300557
558 SetConstantInput(127);
559
560 for (int frac = 0; frac < 16; ++frac) {
561 for (int step = 1; step <= 32; ++step) {
562 /* Test the horizontal and vertical filters in combination. */
563 REGISTER_STATE_CHECK(UUT_->hv8_(in, kInputStride, out, kOutputStride,
Dmitry Kovalev021eaab2014-05-14 16:21:41 -0700564 eighttap[frac], step,
565 eighttap[frac], step,
Tero Rintaluomae326cec2013-08-22 11:29:19 +0300566 Width(), Height()));
567
568 CheckGuardBlocks();
569
570 for (int y = 0; y < Height(); ++y) {
571 for (int x = 0; x < Width(); ++x) {
572 ASSERT_EQ(in[y * kInputStride + x], out[y * kOutputStride + x])
573 << "x == " << x << ", y == " << y
574 << ", frac == " << frac << ", step == " << step;
575 }
576 }
577 }
578 }
579}
580
John Koleszar5ca6a362013-01-25 09:47:09 -0800581using std::tr1::make_tuple;
582
John Koleszar5ca6a362013-01-25 09:47:09 -0800583const ConvolveFunctions convolve8_c(
584 vp9_convolve8_horiz_c, vp9_convolve8_avg_horiz_c,
585 vp9_convolve8_vert_c, vp9_convolve8_avg_vert_c,
586 vp9_convolve8_c, vp9_convolve8_avg_c);
587
588INSTANTIATE_TEST_CASE_P(C, ConvolveTest, ::testing::Values(
John Koleszar5ca6a362013-01-25 09:47:09 -0800589 make_tuple(4, 4, &convolve8_c),
590 make_tuple(8, 4, &convolve8_c),
John Koleszara9ebbcc2013-04-18 13:05:38 -0700591 make_tuple(4, 8, &convolve8_c),
John Koleszar5ca6a362013-01-25 09:47:09 -0800592 make_tuple(8, 8, &convolve8_c),
John Koleszar6a4f7082013-02-08 17:49:44 -0800593 make_tuple(16, 8, &convolve8_c),
John Koleszara9ebbcc2013-04-18 13:05:38 -0700594 make_tuple(8, 16, &convolve8_c),
595 make_tuple(16, 16, &convolve8_c),
596 make_tuple(32, 16, &convolve8_c),
597 make_tuple(16, 32, &convolve8_c),
598 make_tuple(32, 32, &convolve8_c),
599 make_tuple(64, 32, &convolve8_c),
600 make_tuple(32, 64, &convolve8_c),
601 make_tuple(64, 64, &convolve8_c)));
John Koleszar29d47ac2013-02-07 17:00:37 -0800602
Yunqing Wang3fb728c2013-10-10 13:51:35 -0700603#if HAVE_SSE2
604const ConvolveFunctions convolve8_sse2(
605 vp9_convolve8_horiz_sse2, vp9_convolve8_avg_horiz_sse2,
606 vp9_convolve8_vert_sse2, vp9_convolve8_avg_vert_sse2,
607 vp9_convolve8_sse2, vp9_convolve8_avg_sse2);
608
609INSTANTIATE_TEST_CASE_P(SSE2, ConvolveTest, ::testing::Values(
610 make_tuple(4, 4, &convolve8_sse2),
611 make_tuple(8, 4, &convolve8_sse2),
612 make_tuple(4, 8, &convolve8_sse2),
613 make_tuple(8, 8, &convolve8_sse2),
614 make_tuple(16, 8, &convolve8_sse2),
615 make_tuple(8, 16, &convolve8_sse2),
616 make_tuple(16, 16, &convolve8_sse2),
617 make_tuple(32, 16, &convolve8_sse2),
618 make_tuple(16, 32, &convolve8_sse2),
619 make_tuple(32, 32, &convolve8_sse2),
620 make_tuple(64, 32, &convolve8_sse2),
621 make_tuple(32, 64, &convolve8_sse2),
622 make_tuple(64, 64, &convolve8_sse2)));
623#endif
624
John Koleszar29d47ac2013-02-07 17:00:37 -0800625#if HAVE_SSSE3
626const ConvolveFunctions convolve8_ssse3(
Jim Bankoskic3809f32013-08-05 12:07:30 -0700627 vp9_convolve8_horiz_ssse3, vp9_convolve8_avg_horiz_ssse3,
628 vp9_convolve8_vert_ssse3, vp9_convolve8_avg_vert_ssse3,
629 vp9_convolve8_ssse3, vp9_convolve8_avg_ssse3);
John Koleszar29d47ac2013-02-07 17:00:37 -0800630
631INSTANTIATE_TEST_CASE_P(SSSE3, ConvolveTest, ::testing::Values(
632 make_tuple(4, 4, &convolve8_ssse3),
633 make_tuple(8, 4, &convolve8_ssse3),
John Koleszara9ebbcc2013-04-18 13:05:38 -0700634 make_tuple(4, 8, &convolve8_ssse3),
John Koleszar29d47ac2013-02-07 17:00:37 -0800635 make_tuple(8, 8, &convolve8_ssse3),
John Koleszar6a4f7082013-02-08 17:49:44 -0800636 make_tuple(16, 8, &convolve8_ssse3),
John Koleszara9ebbcc2013-04-18 13:05:38 -0700637 make_tuple(8, 16, &convolve8_ssse3),
638 make_tuple(16, 16, &convolve8_ssse3),
639 make_tuple(32, 16, &convolve8_ssse3),
640 make_tuple(16, 32, &convolve8_ssse3),
641 make_tuple(32, 32, &convolve8_ssse3),
642 make_tuple(64, 32, &convolve8_ssse3),
643 make_tuple(32, 64, &convolve8_ssse3),
644 make_tuple(64, 64, &convolve8_ssse3)));
John Koleszar29d47ac2013-02-07 17:00:37 -0800645#endif
Johann158c80c2013-05-23 12:50:41 -0700646
Yunqing Wang4f0943b2014-05-27 10:36:56 -0700647#if HAVE_AVX2
James Zern57045782014-06-09 18:36:58 -0700648// TODO(jzern): these prototypes can be removed after the avx2 versions are
649// reenabled in vp9_rtcd_defs.pl.
650extern "C" {
651void vp9_convolve8_vert_avx2(const uint8_t *src, ptrdiff_t src_stride,
652 uint8_t *dst, ptrdiff_t dst_stride,
653 const int16_t *filter_x, int x_step_q4,
654 const int16_t *filter_y, int y_step_q4,
655 int w, int h);
656void vp9_convolve8_horiz_avx2(const uint8_t *src, ptrdiff_t src_stride,
657 uint8_t *dst, ptrdiff_t dst_stride,
658 const int16_t *filter_x, int x_step_q4,
659 const int16_t *filter_y, int y_step_q4,
660 int w, int h);
661void vp9_convolve8_avx2(const uint8_t *src, ptrdiff_t src_stride,
662 uint8_t *dst, ptrdiff_t dst_stride,
663 const int16_t *filter_x, int x_step_q4,
664 const int16_t *filter_y, int y_step_q4,
665 int w, int h);
666}
667
Yunqing Wang4f0943b2014-05-27 10:36:56 -0700668const ConvolveFunctions convolve8_avx2(
669 vp9_convolve8_horiz_avx2, vp9_convolve8_avg_horiz_ssse3,
670 vp9_convolve8_vert_avx2, vp9_convolve8_avg_vert_ssse3,
671 vp9_convolve8_avx2, vp9_convolve8_avg_ssse3);
672
673INSTANTIATE_TEST_CASE_P(AVX2, ConvolveTest, ::testing::Values(
674 make_tuple(4, 4, &convolve8_avx2),
675 make_tuple(8, 4, &convolve8_avx2),
676 make_tuple(4, 8, &convolve8_avx2),
677 make_tuple(8, 8, &convolve8_avx2),
James Zern57045782014-06-09 18:36:58 -0700678 make_tuple(8, 16, &convolve8_avx2)));
679
680INSTANTIATE_TEST_CASE_P(DISABLED_AVX2, ConvolveTest, ::testing::Values(
Yunqing Wang4f0943b2014-05-27 10:36:56 -0700681 make_tuple(16, 8, &convolve8_avx2),
Yunqing Wang4f0943b2014-05-27 10:36:56 -0700682 make_tuple(16, 16, &convolve8_avx2),
683 make_tuple(32, 16, &convolve8_avx2),
684 make_tuple(16, 32, &convolve8_avx2),
685 make_tuple(32, 32, &convolve8_avx2),
686 make_tuple(64, 32, &convolve8_avx2),
687 make_tuple(32, 64, &convolve8_avx2),
688 make_tuple(64, 64, &convolve8_avx2)));
689#endif
690
Johannce239312014-05-07 11:01:31 -0700691#if HAVE_NEON_ASM
Johann158c80c2013-05-23 12:50:41 -0700692const ConvolveFunctions convolve8_neon(
Johanna15bebf2013-07-12 16:12:58 -0700693 vp9_convolve8_horiz_neon, vp9_convolve8_avg_horiz_neon,
694 vp9_convolve8_vert_neon, vp9_convolve8_avg_vert_neon,
Johann59dc4e92013-07-16 10:13:06 -0700695 vp9_convolve8_neon, vp9_convolve8_avg_neon);
Johann158c80c2013-05-23 12:50:41 -0700696
697INSTANTIATE_TEST_CASE_P(NEON, ConvolveTest, ::testing::Values(
698 make_tuple(4, 4, &convolve8_neon),
699 make_tuple(8, 4, &convolve8_neon),
700 make_tuple(4, 8, &convolve8_neon),
701 make_tuple(8, 8, &convolve8_neon),
702 make_tuple(16, 8, &convolve8_neon),
703 make_tuple(8, 16, &convolve8_neon),
704 make_tuple(16, 16, &convolve8_neon),
705 make_tuple(32, 16, &convolve8_neon),
706 make_tuple(16, 32, &convolve8_neon),
707 make_tuple(32, 32, &convolve8_neon),
708 make_tuple(64, 32, &convolve8_neon),
709 make_tuple(32, 64, &convolve8_neon),
710 make_tuple(64, 64, &convolve8_neon)));
711#endif
Parag Salasakar40edab52013-09-13 15:18:32 +0530712
713#if HAVE_DSPR2
714const ConvolveFunctions convolve8_dspr2(
715 vp9_convolve8_horiz_dspr2, vp9_convolve8_avg_horiz_dspr2,
716 vp9_convolve8_vert_dspr2, vp9_convolve8_avg_vert_dspr2,
717 vp9_convolve8_dspr2, vp9_convolve8_avg_dspr2);
718
719INSTANTIATE_TEST_CASE_P(DSPR2, ConvolveTest, ::testing::Values(
720 make_tuple(4, 4, &convolve8_dspr2),
721 make_tuple(8, 4, &convolve8_dspr2),
722 make_tuple(4, 8, &convolve8_dspr2),
723 make_tuple(8, 8, &convolve8_dspr2),
724 make_tuple(16, 8, &convolve8_dspr2),
725 make_tuple(8, 16, &convolve8_dspr2),
726 make_tuple(16, 16, &convolve8_dspr2),
727 make_tuple(32, 16, &convolve8_dspr2),
728 make_tuple(16, 32, &convolve8_dspr2),
729 make_tuple(32, 32, &convolve8_dspr2),
730 make_tuple(64, 32, &convolve8_dspr2),
731 make_tuple(32, 64, &convolve8_dspr2),
732 make_tuple(64, 64, &convolve8_dspr2)));
733#endif
James Zern8fb48af2013-05-02 13:08:19 -0700734} // namespace