Merge "Fix to avoid abrupt relaxation of max qindex in recode path"
[platform/upstream/libvpx.git] / test / pp_filter_test.cc
1 /*
2  *  Copyright (c) 2012 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 #include <limits.h>
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"
20
21 using libvpx_test::ACMRandom;
22 using libvpx_test::Buffer;
23
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);
27
28 typedef void (*VpxMbPostProcAcrossIpFunc)(unsigned char *src, int pitch,
29                                           int rows, int cols, int flimit);
30
31 typedef void (*VpxMbPostProcDownFunc)(unsigned char *dst, int pitch, int rows,
32                                       int cols, int flimit);
33
34 namespace {
35
36 // Compute the filter level used in post proc from the loop filter strength
37 int q2mbl(int x) {
38   if (x < 20) x = 20;
39
40   x = 50 + (x - 50) * 10 / 8;
41   return x * x / 3;
42 }
43
44 class VpxPostProcDownAndAcrossMbRowTest
45     : public ::testing::TestWithParam<VpxPostProcDownAndAcrossMbRowFunc> {
46  public:
47   virtual void TearDown() { libvpx_test::ClearSystemState(); }
48 };
49
50 // Test routine for the VPx post-processing function
51 // vpx_post_proc_down_and_across_mb_row_c.
52
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;
57
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);
60
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);
66
67   uint8_t *const flimits =
68       reinterpret_cast<uint8_t *>(vpx_memalign(16, block_width));
69   (void)memset(flimits, 255, block_width);
70
71   // Initialize pixels in the input:
72   //   block pixels to value 1,
73   //   border pixels to value 10.
74   src_image.SetPadding(10);
75   src_image.Set(1);
76
77   // Initialize pixels in the output to 99.
78   dst_image.Set(99);
79
80   ASM_REGISTER_STATE_CHECK(GetParam()(
81       src_image.TopLeftPixel(), dst_image.TopLeftPixel(), src_image.stride(),
82       dst_image.stride(), block_width, flimits, 16));
83
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
86   };
87
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
92                                                   << ")";
93     }
94     pixel_ptr += dst_image.stride();
95   }
96
97   vpx_free(flimits);
98 };
99
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;
106
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);
111
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
115   // not a problem.
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);
120
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));
127
128   ACMRandom rnd;
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);
135
136   for (int blocks = 0; blocks < block_width; blocks += 8) {
137     (void)memset(flimits, 0, sizeof(*flimits) * flimits_width);
138
139     for (int f = 0; f < 255; f++) {
140       (void)memset(flimits + blocks, f, sizeof(*flimits) * 8);
141
142       dst_image.Set(0);
143       dst_image_ref.Set(0);
144
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,
148           block_height);
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));
153
154       ASSERT_TRUE(dst_image.CheckValues(dst_image_ref));
155     }
156   }
157
158   vpx_free(flimits);
159 }
160
161 class VpxMbPostProcAcrossIpTest
162     : public ::testing::TestWithParam<VpxMbPostProcAcrossIpFunc> {
163  public:
164   virtual void TearDown() { libvpx_test::ClearSystemState(); }
165
166  protected:
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++) {
170         s[c] = c;
171       }
172       s += src_width;
173     }
174   }
175
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
181                                                 << ")";
182       }
183       src_c += src_pitch;
184     }
185   }
186
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);
192   }
193 };
194
195 TEST_P(VpxMbPostProcAcrossIpTest, CheckLowFilterOutput) {
196   const int rows = 16;
197   const int cols = 16;
198
199   Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
200   src.SetPadding(10);
201   SetCols(src.TopLeftPixel(), rows, cols, src.stride());
202
203   Buffer<uint8_t> expected_output = Buffer<uint8_t>(cols, rows, 0);
204   SetCols(expected_output.TopLeftPixel(), rows, cols, expected_output.stride());
205
206   RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(0),
207                  expected_output.TopLeftPixel());
208 }
209
210 TEST_P(VpxMbPostProcAcrossIpTest, CheckMediumFilterOutput) {
211   const int rows = 16;
212   const int cols = 16;
213
214   Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
215   src.SetPadding(10);
216   SetCols(src.TopLeftPixel(), rows, cols, src.stride());
217
218   static const unsigned char kExpectedOutput[cols] = {
219     2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 13
220   };
221
222   RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(70),
223                  kExpectedOutput);
224 }
225
226 TEST_P(VpxMbPostProcAcrossIpTest, CheckHighFilterOutput) {
227   const int rows = 16;
228   const int cols = 16;
229
230   Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8);
231   src.SetPadding(10);
232   SetCols(src.TopLeftPixel(), rows, cols, src.stride());
233
234   static const unsigned char kExpectedOutput[cols] = {
235     2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 13
236   };
237
238   RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), INT_MAX,
239                  kExpectedOutput);
240
241   SetCols(src.TopLeftPixel(), rows, cols, src.stride());
242
243   RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(100),
244                  kExpectedOutput);
245 }
246
247 TEST_P(VpxMbPostProcAcrossIpTest, CheckCvsAssembly) {
248   const int rows = 16;
249   const int cols = 16;
250
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);
253
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());
261
262     vpx_mbpost_proc_across_ip_c(c_mem.TopLeftPixel(), c_mem.stride(), rows,
263                                 cols, q2mbl(level));
264     ASM_REGISTER_STATE_CHECK(GetParam()(
265         asm_mem.TopLeftPixel(), asm_mem.stride(), rows, cols, q2mbl(level)));
266
267     ASSERT_TRUE(asm_mem.CheckValues(c_mem));
268   }
269 }
270
271 class VpxMbPostProcDownTest
272     : public ::testing::TestWithParam<VpxMbPostProcDownFunc> {
273  public:
274   virtual void TearDown() { libvpx_test::ClearSystemState(); }
275
276  protected:
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);
280       src_c += src_width;
281     }
282   }
283
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
289                                                            << ", " << c << ")";
290       }
291       src_c += src_pitch;
292     }
293   }
294
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);
300   }
301 };
302
303 TEST_P(VpxMbPostProcDownTest, CheckHighFilterOutput) {
304   const int rows = 16;
305   const int cols = 16;
306
307   Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
308   src_c.SetPadding(10);
309
310   SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
311
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
327   };
328
329   RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), INT_MAX,
330                  kExpectedOutput);
331
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),
335                  kExpectedOutput);
336 }
337
338 TEST_P(VpxMbPostProcDownTest, CheckMediumFilterOutput) {
339   const int rows = 16;
340   const int cols = 16;
341
342   Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
343   src_c.SetPadding(10);
344
345   SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
346
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
362   };
363
364   RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(70),
365                  kExpectedOutput);
366 }
367
368 TEST_P(VpxMbPostProcDownTest, CheckLowFilterOutput) {
369   const int rows = 16;
370   const int cols = 16;
371
372   Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17);
373   src_c.SetPadding(10);
374
375   SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride());
376
377   unsigned char *expected_output = new unsigned char[rows * cols];
378   ASSERT_TRUE(expected_output != NULL);
379   SetRows(expected_output, rows, cols, cols);
380
381   RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(0),
382                  expected_output);
383
384   delete[] expected_output;
385 }
386
387 TEST_P(VpxMbPostProcDownTest, CheckCvsAssembly) {
388   const int rows = 16;
389   const int cols = 16;
390
391   ACMRandom rnd;
392   rnd.Reset(ACMRandom::DeterministicSeed());
393
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);
396
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);
402
403     vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols,
404                            q2mbl(level));
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));
408
409     src_c.SetPadding(10);
410     src_asm.SetPadding(10);
411     src_c.Set(&rnd, &ACMRandom::Rand8Extremes);
412     src_asm.CopyFrom(src_c);
413
414     vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols,
415                            q2mbl(level));
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));
419   }
420 }
421
422 INSTANTIATE_TEST_CASE_P(
423     C, VpxPostProcDownAndAcrossMbRowTest,
424     ::testing::Values(vpx_post_proc_down_and_across_mb_row_c));
425
426 INSTANTIATE_TEST_CASE_P(C, VpxMbPostProcAcrossIpTest,
427                         ::testing::Values(vpx_mbpost_proc_across_ip_c));
428
429 INSTANTIATE_TEST_CASE_P(C, VpxMbPostProcDownTest,
430                         ::testing::Values(vpx_mbpost_proc_down_c));
431
432 #if HAVE_SSE2
433 INSTANTIATE_TEST_CASE_P(
434     SSE2, VpxPostProcDownAndAcrossMbRowTest,
435     ::testing::Values(vpx_post_proc_down_and_across_mb_row_sse2));
436
437 INSTANTIATE_TEST_CASE_P(SSE2, VpxMbPostProcAcrossIpTest,
438                         ::testing::Values(vpx_mbpost_proc_across_ip_sse2));
439
440 INSTANTIATE_TEST_CASE_P(SSE2, VpxMbPostProcDownTest,
441                         ::testing::Values(vpx_mbpost_proc_down_sse2));
442 #endif  // HAVE_SSE2
443
444 #if HAVE_NEON
445 INSTANTIATE_TEST_CASE_P(
446     NEON, VpxPostProcDownAndAcrossMbRowTest,
447     ::testing::Values(vpx_post_proc_down_and_across_mb_row_neon));
448
449 INSTANTIATE_TEST_CASE_P(NEON, VpxMbPostProcAcrossIpTest,
450                         ::testing::Values(vpx_mbpost_proc_across_ip_neon));
451
452 INSTANTIATE_TEST_CASE_P(NEON, VpxMbPostProcDownTest,
453                         ::testing::Values(vpx_mbpost_proc_down_neon));
454 #endif  // HAVE_NEON
455
456 #if HAVE_MSA
457 INSTANTIATE_TEST_CASE_P(
458     MSA, VpxPostProcDownAndAcrossMbRowTest,
459     ::testing::Values(vpx_post_proc_down_and_across_mb_row_msa));
460
461 INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcAcrossIpTest,
462                         ::testing::Values(vpx_mbpost_proc_across_ip_msa));
463
464 INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcDownTest,
465                         ::testing::Values(vpx_mbpost_proc_down_msa));
466 #endif  // HAVE_MSA
467
468 }  // namespace