2 * Copyright (c) 2012 The WebM project authors. All Rights Reserved.
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.
11 #include "./vpx_config.h"
12 #include "./vpx_dsp_rtcd.h"
13 #include "test/acm_random.h"
14 #include "test/buffer.h"
15 #include "test/clear_system_state.h"
16 #include "test/register_state_check.h"
17 #include "third_party/googletest/src/include/gtest/gtest.h"
18 #include "vpx/vpx_integer.h"
19 #include "vpx_mem/vpx_mem.h"
21 using libvpx_test::ACMRandom;
22 using libvpx_test::Buffer;
24 typedef void (*VpxPostProcDownAndAcrossMbRowFunc)(
25 unsigned char *src_ptr, unsigned char *dst_ptr, int src_pixels_per_line,
26 int dst_pixels_per_line, int cols, unsigned char *flimit, int size);
28 typedef void (*VpxMbPostProcAcrossIpFunc)(unsigned char *src, int pitch,
29 int rows, int cols, int flimit);
31 typedef void (*VpxMbPostProcDownFunc)(unsigned char *dst, int pitch, int rows,
32 int cols, int flimit);
36 // Compute the filter level used in post proc from the loop filter strength
40 x = 50 + (x - 50) * 10 / 8;
44 class VpxPostProcDownAndAcrossMbRowTest
45 : public ::testing::TestWithParam<VpxPostProcDownAndAcrossMbRowFunc> {
47 virtual void TearDown() { libvpx_test::ClearSystemState(); }
50 // Test routine for the VPx post-processing function
51 // vpx_post_proc_down_and_across_mb_row_c.
53 TEST_P(VpxPostProcDownAndAcrossMbRowTest, CheckFilterOutput) {
54 // Size of the underlying data block that will be filtered.
55 const int block_width = 16;
56 const int block_height = 16;
58 // 5-tap filter needs 2 padding rows above and below the block in the input.
59 Buffer<uint8_t> src_image = Buffer<uint8_t>(block_width, block_height, 2);
61 // Filter extends output block by 8 samples at left and right edges.
62 // Though the left padding is only 8 bytes, the assembly code tries to
63 // read 16 bytes before the pointer.
64 Buffer<uint8_t> dst_image =
65 Buffer<uint8_t>(block_width, block_height, 8, 16, 8, 8);
67 uint8_t *const flimits =
68 reinterpret_cast<uint8_t *>(vpx_memalign(16, block_width));
69 (void)memset(flimits, 255, block_width);
71 // Initialize pixels in the input:
72 // block pixels to value 1,
73 // border pixels to value 10.
74 src_image.SetPadding(10);
77 // Initialize pixels in the output to 99.
80 ASM_REGISTER_STATE_CHECK(GetParam()(
81 src_image.TopLeftPixel(), dst_image.TopLeftPixel(), src_image.stride(),
82 dst_image.stride(), block_width, flimits, 16));
84 static const uint8_t kExpectedOutput[block_height] = {
85 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4
88 uint8_t *pixel_ptr = dst_image.TopLeftPixel();
89 for (int i = 0; i < block_height; ++i) {
90 for (int j = 0; j < block_width; ++j) {
91 ASSERT_EQ(kExpectedOutput[i], pixel_ptr[j]) << "at (" << i << ", " << j
94 pixel_ptr += dst_image.stride();
100 TEST_P(VpxPostProcDownAndAcrossMbRowTest, CheckCvsAssembly) {
101 // Size of the underlying data block that will be filtered.
102 // Y blocks are always a multiple of 16 wide and exactly 16 high. U and V
103 // blocks are always a multiple of 8 wide and exactly 8 high.
104 const int block_width = 136;
105 const int block_height = 16;
107 // 5-tap filter needs 2 padding rows above and below the block in the input.
108 // SSE2 reads in blocks of 16. Pad an extra 8 in case the width is not %16.
109 Buffer<uint8_t> src_image =
110 Buffer<uint8_t>(block_width, block_height, 2, 2, 10, 2);
112 // Filter extends output block by 8 samples at left and right edges.
113 // Though the left padding is only 8 bytes, there is 'above' padding as well
114 // so when the assembly code tries to read 16 bytes before the pointer it is
116 // SSE2 reads in blocks of 16. Pad an extra 8 in case the width is not %16.
117 Buffer<uint8_t> dst_image =
118 Buffer<uint8_t>(block_width, block_height, 8, 8, 16, 8);
119 Buffer<uint8_t> dst_image_ref = Buffer<uint8_t>(block_width, block_height, 8);
121 // Filter values are set in blocks of 16 for Y and 8 for U/V. Each macroblock
122 // can have a different filter. SSE2 assembly reads flimits in blocks of 16 so
123 // it must be padded out.
124 const int flimits_width = block_width % 16 ? block_width + 8 : block_width;
125 uint8_t *const flimits =
126 reinterpret_cast<uint8_t *>(vpx_memalign(16, flimits_width));
129 rnd.Reset(ACMRandom::DeterministicSeed());
130 // Initialize pixels in the input:
131 // block pixels to random values.
132 // border pixels to value 10.
133 src_image.SetPadding(10);
134 src_image.Set(&rnd, &ACMRandom::Rand8);
136 for (int blocks = 0; blocks < block_width; blocks += 8) {
137 (void)memset(flimits, 0, sizeof(*flimits) * flimits_width);
139 for (int f = 0; f < 255; f++) {
140 (void)memset(flimits + blocks, f, sizeof(*flimits) * 8);
143 dst_image_ref.Set(0);
145 vpx_post_proc_down_and_across_mb_row_c(
146 src_image.TopLeftPixel(), dst_image_ref.TopLeftPixel(),
147 src_image.stride(), dst_image_ref.stride(), block_width, flimits,
149 ASM_REGISTER_STATE_CHECK(
150 GetParam()(src_image.TopLeftPixel(), dst_image.TopLeftPixel(),
151 src_image.stride(), dst_image.stride(), block_width,
152 flimits, block_height));
154 ASSERT_TRUE(dst_image.CheckValues(dst_image_ref));
161 class VpxMbPostProcAcrossIpTest
162 : public ::testing::TestWithParam<VpxMbPostProcAcrossIpFunc> {
164 virtual void TearDown() { libvpx_test::ClearSystemState(); }
167 void SetCols(unsigned char *s, int rows, int cols, int src_width) {
168 for (int r = 0; r < rows; r++) {
169 for (int c = 0; c < cols; c++) {
176 void RunComparison(const unsigned char *expected_output, unsigned char *src_c,
177 int rows, int cols, int src_pitch) {
178 for (int r = 0; r < rows; r++) {
179 for (int c = 0; c < cols; c++) {
180 ASSERT_EQ(expected_output[c], src_c[c]) << "at (" << r << ", " << c
187 void RunFilterLevel(unsigned char *s, int rows, int cols, int src_width,
188 int filter_level, const unsigned char *expected_output) {
189 ASM_REGISTER_STATE_CHECK(
190 GetParam()(s, src_width, rows, cols, filter_level));
191 RunComparison(expected_output, s, rows, cols, src_width);
195 TEST_P(VpxMbPostProcAcrossIpTest, CheckLowFilterOutput) {
199 Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
201 SetCols(src.TopLeftPixel(), rows, cols, src.stride());
203 Buffer<uint8_t> expected_output = Buffer<uint8_t>(cols, rows, 0);
204 SetCols(expected_output.TopLeftPixel(), rows, cols, expected_output.stride());
206 RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(0),
207 expected_output.TopLeftPixel());
210 TEST_P(VpxMbPostProcAcrossIpTest, CheckMediumFilterOutput) {
214 Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
216 SetCols(src.TopLeftPixel(), rows, cols, src.stride());
218 static const unsigned char kExpectedOutput[cols] = {
219 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 13
222 RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(70),
226 TEST_P(VpxMbPostProcAcrossIpTest, CheckHighFilterOutput) {
230 Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
232 SetCols(src.TopLeftPixel(), rows, cols, src.stride());
234 static const unsigned char kExpectedOutput[cols] = {
235 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 13
238 RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), INT_MAX,
241 SetCols(src.TopLeftPixel(), rows, cols, src.stride());
243 RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(100),
247 TEST_P(VpxMbPostProcAcrossIpTest, CheckCvsAssembly) {
251 Buffer<uint8_t> c_mem = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
252 Buffer<uint8_t> asm_mem = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
254 // When level >= 100, the filter behaves the same as the level = INT_MAX
255 // When level < 20, it behaves the same as the level = 0
256 for (int level = 0; level < 100; level++) {
257 c_mem.SetPadding(10);
258 asm_mem.SetPadding(10);
259 SetCols(c_mem.TopLeftPixel(), rows, cols, c_mem.stride());
260 SetCols(asm_mem.TopLeftPixel(), rows, cols, asm_mem.stride());
262 vpx_mbpost_proc_across_ip_c(c_mem.TopLeftPixel(), c_mem.stride(), rows,
264 ASM_REGISTER_STATE_CHECK(GetParam()(
265 asm_mem.TopLeftPixel(), asm_mem.stride(), rows, cols, q2mbl(level)));
267 ASSERT_TRUE(asm_mem.CheckValues(c_mem));
271 class VpxMbPostProcDownTest
272 : public ::testing::TestWithParam<VpxMbPostProcDownFunc> {
274 virtual void TearDown() { libvpx_test::ClearSystemState(); }
277 void SetRows(unsigned char *src_c, int rows, int cols, int src_width) {
278 for (int r = 0; r < rows; r++) {
279 memset(src_c, r, cols);
284 void RunComparison(const unsigned char *expected_output, unsigned char *src_c,
285 int rows, int cols, int src_pitch) {
286 for (int r = 0; r < rows; r++) {
287 for (int c = 0; c < cols; c++) {
288 ASSERT_EQ(expected_output[r * rows + c], src_c[c]) << "at (" << r
295 void RunFilterLevel(unsigned char *s, int rows, int cols, int src_width,
296 int filter_level, const unsigned char *expected_output) {
297 ASM_REGISTER_STATE_CHECK(
298 GetParam()(s, src_width, rows, cols, filter_level));
299 RunComparison(expected_output, s, rows, cols, src_width);
303 TEST_P(VpxMbPostProcDownTest, CheckHighFilterOutput) {
307 Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
308 src_c.SetPadding(10);
310 SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
312 static const unsigned char kExpectedOutput[rows * cols] = {
313 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2,
314 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3,
315 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 3,
316 4, 4, 3, 4, 4, 3, 3, 4, 5, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4,
317 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
318 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
319 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
320 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 9, 9, 8, 8, 8, 9,
321 9, 8, 9, 9, 8, 8, 8, 9, 9, 10, 10, 9, 9, 9, 10, 10, 9, 10, 10,
322 9, 9, 9, 10, 10, 10, 11, 10, 10, 10, 11, 10, 11, 10, 11, 10, 10, 10, 11,
323 10, 11, 11, 11, 11, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 12, 11, 12,
324 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 12,
325 13, 12, 13, 12, 12, 12, 13, 12, 13, 12, 13, 12, 13, 13, 13, 14, 13, 13, 13,
326 13, 13, 13, 13, 14, 13, 13, 13, 13
329 RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), INT_MAX,
332 src_c.SetPadding(10);
333 SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
334 RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(100),
338 TEST_P(VpxMbPostProcDownTest, CheckMediumFilterOutput) {
342 Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
343 src_c.SetPadding(10);
345 SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
347 static const unsigned char kExpectedOutput[rows * cols] = {
348 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2,
349 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2,
350 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3,
351 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
352 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
353 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
354 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
355 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
356 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
357 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
358 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13,
359 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 13, 12,
360 13, 12, 13, 12, 12, 12, 13, 12, 13, 12, 13, 12, 13, 13, 13, 14, 13, 13, 13,
361 13, 13, 13, 13, 14, 13, 13, 13, 13
364 RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(70),
368 TEST_P(VpxMbPostProcDownTest, CheckLowFilterOutput) {
372 Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
373 src_c.SetPadding(10);
375 SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
377 unsigned char *expected_output = new unsigned char[rows * cols];
378 ASSERT_TRUE(expected_output != NULL);
379 SetRows(expected_output, rows, cols, cols);
381 RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(0),
384 delete[] expected_output;
387 TEST_P(VpxMbPostProcDownTest, CheckCvsAssembly) {
392 rnd.Reset(ACMRandom::DeterministicSeed());
394 Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
395 Buffer<uint8_t> src_asm = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
397 for (int level = 0; level < 100; level++) {
398 src_c.SetPadding(10);
399 src_asm.SetPadding(10);
400 src_c.Set(&rnd, &ACMRandom::Rand8);
401 src_asm.CopyFrom(src_c);
403 vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols,
405 ASM_REGISTER_STATE_CHECK(GetParam()(
406 src_asm.TopLeftPixel(), src_asm.stride(), rows, cols, q2mbl(level)));
407 ASSERT_TRUE(src_asm.CheckValues(src_c));
409 src_c.SetPadding(10);
410 src_asm.SetPadding(10);
411 src_c.Set(&rnd, &ACMRandom::Rand8Extremes);
412 src_asm.CopyFrom(src_c);
414 vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols,
416 ASM_REGISTER_STATE_CHECK(GetParam()(
417 src_asm.TopLeftPixel(), src_asm.stride(), rows, cols, q2mbl(level)));
418 ASSERT_TRUE(src_asm.CheckValues(src_c));
422 INSTANTIATE_TEST_CASE_P(
423 C, VpxPostProcDownAndAcrossMbRowTest,
424 ::testing::Values(vpx_post_proc_down_and_across_mb_row_c));
426 INSTANTIATE_TEST_CASE_P(C, VpxMbPostProcAcrossIpTest,
427 ::testing::Values(vpx_mbpost_proc_across_ip_c));
429 INSTANTIATE_TEST_CASE_P(C, VpxMbPostProcDownTest,
430 ::testing::Values(vpx_mbpost_proc_down_c));
433 INSTANTIATE_TEST_CASE_P(
434 SSE2, VpxPostProcDownAndAcrossMbRowTest,
435 ::testing::Values(vpx_post_proc_down_and_across_mb_row_sse2));
437 INSTANTIATE_TEST_CASE_P(SSE2, VpxMbPostProcAcrossIpTest,
438 ::testing::Values(vpx_mbpost_proc_across_ip_sse2));
440 INSTANTIATE_TEST_CASE_P(SSE2, VpxMbPostProcDownTest,
441 ::testing::Values(vpx_mbpost_proc_down_sse2));
445 INSTANTIATE_TEST_CASE_P(
446 NEON, VpxPostProcDownAndAcrossMbRowTest,
447 ::testing::Values(vpx_post_proc_down_and_across_mb_row_neon));
449 INSTANTIATE_TEST_CASE_P(NEON, VpxMbPostProcAcrossIpTest,
450 ::testing::Values(vpx_mbpost_proc_across_ip_neon));
452 INSTANTIATE_TEST_CASE_P(NEON, VpxMbPostProcDownTest,
453 ::testing::Values(vpx_mbpost_proc_down_neon));
457 INSTANTIATE_TEST_CASE_P(
458 MSA, VpxPostProcDownAndAcrossMbRowTest,
459 ::testing::Values(vpx_post_proc_down_and_across_mb_row_msa));
461 INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcAcrossIpTest,
462 ::testing::Values(vpx_mbpost_proc_across_ip_msa));
464 INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcDownTest,
465 ::testing::Values(vpx_mbpost_proc_down_msa));