xref: /aosp_15_r20/external/libgav1/src/post_filter_test.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
1 // Copyright 2021 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/post_filter.h"
16 
17 #include <algorithm>
18 #include <array>
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstdio>
22 #include <cstring>
23 #include <ostream>
24 #include <string>
25 #include <vector>
26 
27 #include "absl/time/clock.h"
28 #include "absl/time/time.h"
29 #include "gtest/gtest.h"
30 #include "src/dsp/cdef.h"
31 #include "src/dsp/dsp.h"
32 #include "src/dsp/super_res.h"
33 #include "src/frame_scratch_buffer.h"
34 #include "src/obu_parser.h"
35 #include "src/threading_strategy.h"
36 #include "src/utils/array_2d.h"
37 #include "src/utils/common.h"
38 #include "src/utils/constants.h"
39 #include "src/utils/memory.h"
40 #include "src/utils/types.h"
41 #include "src/yuv_buffer.h"
42 #include "tests/block_utils.h"
43 #include "tests/third_party/libvpx/acm_random.h"
44 #include "tests/utils.h"
45 
46 namespace libgav1 {
47 namespace {
48 
49 constexpr char kCdef[] = "Cdef";
50 constexpr char kApplyCdefName[] = "ApplyCdef";
51 constexpr int kMaxBlockWidth4x4 = 32;
52 constexpr int kMaxBlockHeight4x4 = 32;
53 constexpr int kMaxTestFrameSize = 1920 * 1080;
54 
GetIdFromInputParam(int subsampling_x,int subsampling_y,int height)55 int GetIdFromInputParam(int subsampling_x, int subsampling_y, int height) {
56   int id = subsampling_x * 8 + subsampling_y * 4;
57   if (height == 288) {
58     id += 0;
59   } else if (height == 480) {
60     id += 1;
61   } else if (height == 1080) {
62     id += 2;
63   } else {
64     id += 3;
65   }
66   return id;
67 }
68 
GetSuperResDigest8bpp(int id,int plane)69 const char* GetSuperResDigest8bpp(int id, int plane) {
70   static const char* const kDigestSuperRes[][kMaxPlanes] = {
71       {
72           // all input is 0.
73           "ff5f7a63d3b1f9176e216eb01a0387ad",  // kPlaneY.
74           "38b6551d7ac3e86c8af407d5a1aa36dc",  // kPlaneU.
75           "38b6551d7ac3e86c8af407d5a1aa36dc",  // kPlaneV.
76       },
77       {
78           // all input is 1.
79           "819f21dcce0e779180bbd613a9e3543c",  // kPlaneY.
80           "e784bfa8f517d83b014c3dcd45b780a5",  // kPlaneU.
81           "e784bfa8f517d83b014c3dcd45b780a5",  // kPlaneV.
82       },
83       {
84           // all input is 128.
85           "2d6ea5b39f9168d56c2e2b8846d208ec",  // kPlaneY.
86           "8030b6e70f1544efbc37b902d3f88bd3",  // kPlaneU.
87           "8030b6e70f1544efbc37b902d3f88bd3",  // kPlaneV.
88       },
89       {
90           // all input is 255.
91           "5c0b4bc50e0980dc6ba7c042d3b50a5e",  // kPlaneY.
92           "3c566ef847c45be09ddac297123a3bad",  // kPlaneU.
93           "3c566ef847c45be09ddac297123a3bad",  // kPlaneV.
94       },
95       {
96           // random input.
97           "50514467dd6a5c3a8268eddaa542c41f",  // kPlaneY.
98           "3ce720c2b5b44928e1477b11040e5c00",  // kPlaneU.
99           "3ce720c2b5b44928e1477b11040e5c00",  // kPlaneV.
100       },
101   };
102   return kDigestSuperRes[id][plane];
103 }
104 
105 #if LIBGAV1_MAX_BITDEPTH >= 10
GetSuperResDigest10bpp(int id,int plane)106 const char* GetSuperResDigest10bpp(int id, int plane) {
107   // Digests are in Y/U/V order.
108   static const char* const kDigestSuperRes[][kMaxPlanes] = {
109       {
110           // all input is 0.
111           "fccb1f57b252b1a86d335aea929d1d58",
112           "2f244a56091c9705794e92e6bcc38058",
113           "2f244a56091c9705794e92e6bcc38058",
114       },
115       {
116           // all input is 1.
117           "de8556204999d6e4bf74cfdde61a095b",
118           "e7d0f4ce6df81c46de95da7790a67384",
119           "e7d0f4ce6df81c46de95da7790a67384",
120       },
121       {
122           // all input is 512.
123           "d3b6980363eb9b808885537b3485af87",
124           "bcffddb26210da6861e7b31414e58b77",
125           "bcffddb26210da6861e7b31414e58b77",
126       },
127       {
128           // all input is 1023.
129           "ce0762aeee1cdef1db101e4ca39bcbd6",
130           "33aeaa7f5d7c032e3dfda43925c3dcb2",
131           "33aeaa7f5d7c032e3dfda43925c3dcb2",
132       },
133       {
134           // random input.
135           "63c701bceb187ffa535be15ae58f8171",
136           "f570e30e9ea8d2a1e6d99202cd2f8994",
137           "f570e30e9ea8d2a1e6d99202cd2f8994",
138       },
139   };
140   return kDigestSuperRes[id][plane];
141 }
142 #endif  // LIBGAV1_MAX_BITDEPTH >= 10
143 
144 #if LIBGAV1_MAX_BITDEPTH == 12
GetSuperResDigest12bpp(int id,int plane)145 const char* GetSuperResDigest12bpp(int id, int plane) {
146   // Digests are in Y/U/V order.
147   static const char* const kDigestSuperRes[][kMaxPlanes] = {
148       {
149           // all input is 0.
150           "fccb1f57b252b1a86d335aea929d1d58",
151           "2f244a56091c9705794e92e6bcc38058",
152           "2f244a56091c9705794e92e6bcc38058",
153       },
154       {
155           // all input is 1.
156           "de8556204999d6e4bf74cfdde61a095b",
157           "e7d0f4ce6df81c46de95da7790a67384",
158           "e7d0f4ce6df81c46de95da7790a67384",
159       },
160       {
161           // all input is 2048.
162           "83d600a7b3dc9bc3f710668ee2244e6b",
163           "468eec1453edc1befeb8a346f61950a7",
164           "468eec1453edc1befeb8a346f61950a7",
165       },
166       {
167           // all input is 4095.
168           "30bdb1dfee2b02b12b38e6b9f6287e27",
169           "34d673f075d2caa93a2f648ee3569e20",
170           "34d673f075d2caa93a2f648ee3569e20",
171       },
172       {
173           // random input.
174           "f10f21f5322231d991550fce7ef9787d",
175           "a2d8b6140bd5002e86644ef433b8eb42",
176           "a2d8b6140bd5002e86644ef433b8eb42",
177       },
178   };
179   return kDigestSuperRes[id][plane];
180 }
181 #endif  // LIBGAV1_MAX_BITDEPTH == 12
182 
183 }  // namespace
184 
185 // This type is used to parameterize the tests so is defined outside the
186 // anonymous namespace to avoid the GCC -Wsubobject-linkage warning.
187 struct FrameSizeParam {
FrameSizeParamlibgav1::FrameSizeParam188   FrameSizeParam(uint32_t width, uint32_t upscaled_width, uint32_t height,
189                  int8_t ss_x, int8_t ss_y)
190       : width(width),
191         upscaled_width(upscaled_width),
192         height(height),
193         subsampling_x(ss_x),
194         subsampling_y(ss_y) {}
195   uint32_t width;
196   uint32_t upscaled_width;
197   uint32_t height;
198   int8_t subsampling_x;
199   int8_t subsampling_y;
200 };
201 
202 // Print operators must be defined in the same namespace as the type for the
203 // lookup to work correctly.
operator <<(std::ostream & os,const FrameSizeParam & param)204 static std::ostream& operator<<(std::ostream& os, const FrameSizeParam& param) {
205   return os << param.width << "x" << param.height
206             << ", upscaled_width: " << param.upscaled_width
207             << ", subsampling(x/y): " << static_cast<int>(param.subsampling_x)
208             << "/" << static_cast<int>(param.subsampling_y);
209 }
210 
211 // Note the following test classes access private functions/members of
212 // PostFilter. To be declared friends of PostFilter they must not have internal
213 // linkage (they must be outside the anonymous namespace).
214 template <int bitdepth, typename Pixel>
215 class PostFilterTestBase : public testing::TestWithParam<FrameSizeParam> {
216  public:
217   static_assert(bitdepth >= kBitdepth8 && bitdepth <= LIBGAV1_MAX_BITDEPTH, "");
218   PostFilterTestBase() = default;
219   PostFilterTestBase(const PostFilterTestBase&) = delete;
220   PostFilterTestBase& operator=(const PostFilterTestBase&) = delete;
221   ~PostFilterTestBase() override = default;
222 
SetUp()223   void SetUp() override {
224     // Allocate buffer_ with a border size of kBorderPixels (which is
225     // subsampled for chroma planes). Some tests (for loop restoration) only use
226     // the nearest 2 or 3 pixels (for both luma and chroma planes) in the
227     // border.
228     ASSERT_TRUE(buffer_.Realloc(
229         bitdepth, /*is_monochrome=*/false, frame_size_.upscaled_width,
230         frame_size_.height, frame_size_.subsampling_x,
231         frame_size_.subsampling_y, kBorderPixels, kBorderPixels, kBorderPixels,
232         kBorderPixels, nullptr, nullptr, nullptr));
233 
234     ASSERT_TRUE(loop_restoration_border_.Realloc(
235         bitdepth, /*is_monochrome=*/false, frame_size_.upscaled_width,
236         frame_size_.height, frame_size_.subsampling_x,
237         frame_size_.subsampling_y, kBorderPixels, kBorderPixels, kBorderPixels,
238         kBorderPixels, nullptr, nullptr, nullptr));
239 
240     for (int plane = kPlaneY; plane < kMaxPlanes; ++plane) {
241       const int8_t subsampling_x =
242           (plane == kPlaneY) ? 0 : frame_size_.subsampling_x;
243       const int8_t subsampling_y =
244           (plane == kPlaneY) ? 0 : frame_size_.subsampling_y;
245       width_[plane] = frame_size_.width >> subsampling_x;
246       upscaled_width_[plane] = frame_size_.upscaled_width >> subsampling_x;
247       stride_[plane] =
248           (frame_size_.upscaled_width + 2 * kBorderPixels) >> subsampling_x;
249       height_[plane] =
250           (frame_size_.height + 2 * kBorderPixels) >> subsampling_y;
251 
252       reference_buffer_[plane].reserve(stride_[plane] * height_[plane]);
253       reference_buffer_[plane].resize(stride_[plane] * height_[plane]);
254       std::fill(reference_buffer_[plane].begin(),
255                 reference_buffer_[plane].end(), 0);
256     }
257   }
258 
259  protected:
260   YuvBuffer buffer_;
261   YuvBuffer cdef_border_;
262   YuvBuffer loop_restoration_border_;
263   uint32_t width_[kMaxPlanes];
264   uint32_t upscaled_width_[kMaxPlanes];
265   uint32_t stride_[kMaxPlanes];
266   uint32_t height_[kMaxPlanes];
267   std::vector<Pixel> reference_buffer_[kMaxPlanes];
268   const FrameSizeParam frame_size_ = GetParam();
269 };
270 
271 template <int bitdepth, typename Pixel>
272 class PostFilterHelperFuncTest : public PostFilterTestBase<bitdepth, Pixel> {
273  public:
274   static_assert(bitdepth >= kBitdepth8 && bitdepth <= LIBGAV1_MAX_BITDEPTH, "");
275   PostFilterHelperFuncTest() = default;
276   PostFilterHelperFuncTest(const PostFilterHelperFuncTest&) = delete;
277   PostFilterHelperFuncTest& operator=(const PostFilterHelperFuncTest&) = delete;
278   ~PostFilterHelperFuncTest() override = default;
279 
280  protected:
281   using PostFilterTestBase<bitdepth, Pixel>::buffer_;
282   using PostFilterTestBase<bitdepth, Pixel>::cdef_border_;
283   using PostFilterTestBase<bitdepth, Pixel>::loop_restoration_border_;
284   using PostFilterTestBase<bitdepth, Pixel>::width_;
285   using PostFilterTestBase<bitdepth, Pixel>::upscaled_width_;
286   using PostFilterTestBase<bitdepth, Pixel>::stride_;
287   using PostFilterTestBase<bitdepth, Pixel>::height_;
288   using PostFilterTestBase<bitdepth, Pixel>::reference_buffer_;
289   using PostFilterTestBase<bitdepth, Pixel>::frame_size_;
290 
SetUp()291   void SetUp() override {
292     PostFilterTestBase<bitdepth, Pixel>::SetUp();
293 
294     for (int plane = kPlaneY; plane < kMaxPlanes; ++plane) {
295       const int8_t subsampling_x =
296           (plane == kPlaneY) ? 0 : frame_size_.subsampling_x;
297       const int8_t subsampling_y =
298           (plane == kPlaneY) ? 0 : frame_size_.subsampling_y;
299       width_[plane] = frame_size_.width >> subsampling_x;
300       upscaled_width_[plane] = frame_size_.upscaled_width >> subsampling_x;
301       stride_[plane] = (frame_size_.upscaled_width >> subsampling_x) +
302                        2 * kRestorationHorizontalBorder;
303       height_[plane] = (frame_size_.height >> subsampling_y) +
304                        2 * kRestorationVerticalBorder;
305       reference_buffer_[plane].reserve(stride_[plane] * height_[plane]);
306       reference_buffer_[plane].resize(stride_[plane] * height_[plane]);
307       std::fill(reference_buffer_[plane].begin(),
308                 reference_buffer_[plane].end(), 0);
309       buffer_border_corner_[plane] =
310           reinterpret_cast<Pixel*>(buffer_.data(plane)) -
311           buffer_.stride(plane) / sizeof(Pixel) * kRestorationVerticalBorder -
312           kRestorationHorizontalBorder;
313       loop_restoration_border_corner_[plane] =
314           reinterpret_cast<Pixel*>(loop_restoration_border_.data(plane)) -
315           loop_restoration_border_.stride(plane) / sizeof(Pixel) *
316               kRestorationVerticalBorder -
317           kRestorationHorizontalBorder;
318     }
319   }
320 
321   void TestExtendFrame(bool use_fixed_values, Pixel value);
322   void TestAdjustFrameBufferPointer();
323   void TestPrepareLoopRestorationBlock();
324 
325   // Fill the frame buffer with either a fixed value, or random values.
326   // If fill in with random values, make special operations at buffer
327   // boundaries. Make the outer most 3 pixel wide borders the same value
328   // as their immediate inner neighbor. For example:
329   // 4 4 4   4 5 6   6 6 6
330   // 4 4 4   4 5 6   6 6 6
331   // 4 4 4   4 5 6   6 6 6
332   //       ---------
333   // 4 4 4 | 4 5 6 | 6 6 6
334   // 1 1 1 | 1 0 1 | 1 1 1
335   // 0 0 0 | 0 1 0 | 0 0 0
336   // 1 1 1 | 1 0 1 | 1 1 1
337   // 0 0 0 | 0 1 0 | 0 0 0
338   // 6 6 6 | 6 5 4 | 4 4 4
339   //        -------
340   // 6 6 6   6 5 4   4 4 4
341   // 6 6 6   6 5 4   4 4 4
342   // 6 6 6   6 5 4   4 4 4
343   // Pixels within box is the current block. Outside is extended area from it.
344   void FillBuffer(bool use_fixed_values, Pixel value);
345 
346   // Points to the upper left corner of the restoration border in buffer_.
347   Pixel* buffer_border_corner_[kMaxPlanes];
348   // Points to the upper left corner of the restoration border in
349   // loop_restoration_border_.
350   Pixel* loop_restoration_border_corner_[kMaxPlanes];
351 };
352 
353 template <int bitdepth, typename Pixel>
FillBuffer(bool use_fixed_values,Pixel value)354 void PostFilterHelperFuncTest<bitdepth, Pixel>::FillBuffer(
355     bool use_fixed_values, Pixel value) {
356   if (use_fixed_values) {
357     for (int plane = kPlaneY; plane < kMaxPlanes; ++plane) {
358       // Fill buffer with a fixed value.
359       std::fill(reference_buffer_[plane].begin(),
360                 reference_buffer_[plane].end(), value);
361       // Fill frame buffer. Note that the border is not filled.
362       auto* row = reinterpret_cast<Pixel*>(buffer_.data(plane));
363       for (int i = 0; i < buffer_.height(plane); ++i) {
364         std::fill(row, row + width_[plane], value);
365         row += buffer_.stride(plane) / sizeof(Pixel);
366       }
367     }
368   } else {  // Random value.
369     libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
370     const int mask = (1 << bitdepth) - 1;
371     for (int plane = kPlaneY; plane < kMaxPlanes; ++plane) {
372       // Fill buffer with random values.
373       std::vector<Pixel> line_buffer(stride_[plane]);
374       std::fill(line_buffer.begin(), line_buffer.end(), 0);
375       for (int i = kRestorationHorizontalBorder;
376            i < stride_[plane] - kRestorationHorizontalBorder; ++i) {
377         line_buffer[i] = rnd.Rand16() & mask;
378       }
379       // Copy boundary values to extended border.
380       for (int i = 0; i < kRestorationHorizontalBorder; ++i) {
381         line_buffer[i] = line_buffer[kRestorationHorizontalBorder];
382         line_buffer[stride_[plane] - i - 1] =
383             line_buffer[stride_[plane] - 1 - kRestorationHorizontalBorder];
384       }
385       // The first three rows are the same as the line_buffer.
386       for (int i = 0; i < kRestorationVerticalBorder + 1; ++i) {
387         std::copy(line_buffer.begin(), line_buffer.end(),
388                   reference_buffer_[plane].begin() + i * stride_[plane]);
389       }
390       for (int i = kRestorationVerticalBorder + 1;
391            i < height_[plane] - kRestorationVerticalBorder; ++i) {
392         for (int j = kRestorationHorizontalBorder;
393              j < stride_[plane] - kRestorationHorizontalBorder; ++j) {
394           line_buffer[j] = rnd.Rand16() & mask;
395         }
396         for (int j = 0; j < kRestorationHorizontalBorder; ++j) {
397           line_buffer[j] = line_buffer[kRestorationHorizontalBorder];
398           line_buffer[stride_[plane] - j - 1] =
399               line_buffer[stride_[plane] - 1 - kRestorationHorizontalBorder];
400         }
401         std::copy(line_buffer.begin(), line_buffer.end(),
402                   reference_buffer_[plane].begin() + i * stride_[plane]);
403       }
404       // The extended border are the same as the line_buffer.
405       for (int i = 0; i < kRestorationVerticalBorder; ++i) {
406         std::copy(line_buffer.begin(), line_buffer.end(),
407                   reference_buffer_[plane].begin() +
408                       (height_[plane] - kRestorationVerticalBorder + i) *
409                           stride_[plane]);
410       }
411 
412       // Fill frame buffer. Note that the border is not filled.
413       for (int i = 0; i < buffer_.height(plane); ++i) {
414         memcpy(buffer_.data(plane) + i * buffer_.stride(plane),
415                reference_buffer_[plane].data() + kRestorationHorizontalBorder +
416                    (i + kRestorationVerticalBorder) * stride_[plane],
417                sizeof(Pixel) * width_[plane]);
418       }
419     }
420   }
421 }
422 
423 template <int bitdepth, typename Pixel>
TestExtendFrame(bool use_fixed_values,Pixel value)424 void PostFilterHelperFuncTest<bitdepth, Pixel>::TestExtendFrame(
425     bool use_fixed_values, Pixel value) {
426   ObuFrameHeader frame_header = {};
427   frame_header.upscaled_width = frame_size_.upscaled_width;
428   frame_header.width = frame_size_.width;
429   frame_header.height = frame_size_.height;
430   ObuSequenceHeader sequence_header;
431   sequence_header.color_config.bitdepth = bitdepth;
432   sequence_header.color_config.is_monochrome = false;
433   sequence_header.color_config.subsampling_x = frame_size_.subsampling_x;
434   sequence_header.color_config.subsampling_y = frame_size_.subsampling_y;
435 
436   const dsp::Dsp* const dsp = dsp::GetDspTable(bitdepth);
437   ASSERT_NE(dsp, nullptr);
438   FrameScratchBuffer frame_scratch_buffer;
439 
440   PostFilter post_filter(frame_header, sequence_header, &frame_scratch_buffer,
441                          &buffer_, dsp,
442                          /*do_post_filter_mask=*/0x00);
443   FillBuffer(use_fixed_values, value);
444   for (int plane = kPlaneY; plane < kMaxPlanes; ++plane) {
445     const int plane_width =
446         plane == kPlaneY ? frame_header.upscaled_width
447                          : frame_header.upscaled_width >>
448                                sequence_header.color_config.subsampling_x;
449     const int plane_height =
450         plane == kPlaneY
451             ? frame_header.height
452             : frame_header.height >> sequence_header.color_config.subsampling_y;
453     PostFilter::ExtendFrame<Pixel>(
454         reinterpret_cast<Pixel*>(buffer_.data(plane)), plane_width,
455         plane_height, buffer_.stride(plane) / sizeof(Pixel),
456         kRestorationHorizontalBorder, kRestorationHorizontalBorder,
457         kRestorationVerticalBorder, kRestorationVerticalBorder);
458     const bool success = test_utils::CompareBlocks<Pixel>(
459         buffer_border_corner_[plane], reference_buffer_[plane].data(),
460         stride_[plane], height_[plane], buffer_.stride(plane) / sizeof(Pixel),
461         stride_[plane], /*check_padding=*/false, /*print_diff=*/false);
462     ASSERT_TRUE(success) << "Failure of extend frame at plane: " << plane;
463   }
464 }
465 
466 template <int bitdepth, typename Pixel>
467 class PostFilterSuperResTest : public PostFilterTestBase<bitdepth, Pixel> {
468  public:
469   static_assert(bitdepth >= kBitdepth8 && bitdepth <= LIBGAV1_MAX_BITDEPTH, "");
PostFilterSuperResTest()470   PostFilterSuperResTest() {
471     test_utils::ResetDspTable(bitdepth);
472     dsp::SuperResInit_C();
473     dsp::SuperResInit_SSE4_1();
474     dsp::SuperResInit_NEON();
475   }
476   PostFilterSuperResTest(const PostFilterSuperResTest&) = delete;
477   PostFilterSuperResTest& operator=(const PostFilterSuperResTest&) = delete;
478   ~PostFilterSuperResTest() override = default;
479 
480  protected:
481   using PostFilterTestBase<bitdepth, Pixel>::buffer_;
482   using PostFilterTestBase<bitdepth, Pixel>::width_;
483   using PostFilterTestBase<bitdepth, Pixel>::upscaled_width_;
484   using PostFilterTestBase<bitdepth, Pixel>::stride_;
485   using PostFilterTestBase<bitdepth, Pixel>::height_;
486   using PostFilterTestBase<bitdepth, Pixel>::reference_buffer_;
487   using PostFilterTestBase<bitdepth, Pixel>::frame_size_;
488 
489   void TestApplySuperRes(bool use_fixed_values, Pixel value, int id,
490                          bool multi_threaded);
491 };
492 
493 // This class must be in namespace libgav1 to access private member function
494 // of class PostFilter in src/post_filter.h.
495 template <int bitdepth, typename Pixel>
TestApplySuperRes(bool use_fixed_values,Pixel value,int id,bool multi_threaded)496 void PostFilterSuperResTest<bitdepth, Pixel>::TestApplySuperRes(
497     bool use_fixed_values, Pixel value, int id, bool multi_threaded) {
498   ObuFrameHeader frame_header = {};
499   frame_header.width = frame_size_.width;
500   frame_header.upscaled_width = frame_size_.upscaled_width;
501   frame_header.height = frame_size_.height;
502   frame_header.rows4x4 = DivideBy4(frame_size_.height);
503   frame_header.columns4x4 = DivideBy4(frame_size_.width);
504   frame_header.tile_info.tile_count = 1;
505   ObuSequenceHeader sequence_header;
506   sequence_header.color_config.bitdepth = bitdepth;
507   sequence_header.color_config.is_monochrome = false;
508   sequence_header.color_config.subsampling_x = frame_size_.subsampling_x;
509   sequence_header.color_config.subsampling_y = frame_size_.subsampling_y;
510 
511   // Apply SuperRes.
512   Array2D<int16_t> cdef_index;
513   Array2D<TransformSize> inter_transform_sizes;
514   const dsp::Dsp* const dsp = dsp::GetDspTable(bitdepth);
515   ASSERT_NE(dsp, nullptr);
516   constexpr int kNumThreads = 4;
517   FrameScratchBuffer frame_scratch_buffer;
518   if (multi_threaded) {
519     ASSERT_TRUE(frame_scratch_buffer.threading_strategy.Reset(frame_header,
520                                                               kNumThreads));
521   }
522   const int pixel_size = sequence_header.color_config.bitdepth == 8
523                              ? sizeof(uint8_t)
524                              : sizeof(uint16_t);
525   ASSERT_TRUE(frame_scratch_buffer.superres_coefficients[kPlaneTypeY].Resize(
526       kSuperResFilterTaps * Align(frame_header.upscaled_width, 16) *
527       pixel_size));
528   if (!sequence_header.color_config.is_monochrome &&
529       sequence_header.color_config.subsampling_x != 0) {
530     ASSERT_TRUE(frame_scratch_buffer.superres_coefficients[kPlaneTypeUV].Resize(
531         kSuperResFilterTaps *
532         Align(SubsampledValue(frame_header.upscaled_width, 1), 16) *
533         pixel_size));
534   }
535   ASSERT_TRUE(frame_scratch_buffer.superres_line_buffer.Realloc(
536       sequence_header.color_config.bitdepth,
537       sequence_header.color_config.is_monochrome,
538       MultiplyBy4(frame_header.columns4x4), (multi_threaded ? kNumThreads : 1),
539       sequence_header.color_config.subsampling_x,
540       /*subsampling_y=*/0, 2 * kSuperResHorizontalBorder,
541       2 * (kSuperResHorizontalBorder + kSuperResHorizontalPadding), 0, 0,
542       nullptr, nullptr, nullptr));
543   PostFilter post_filter(frame_header, sequence_header, &frame_scratch_buffer,
544                          &buffer_, dsp,
545                          /*do_post_filter_mask=*/0x04);
546 
547   const int num_planes = sequence_header.color_config.is_monochrome
548                              ? kMaxPlanesMonochrome
549                              : kMaxPlanes;
550   int width[kMaxPlanes];
551   int upscaled_width[kMaxPlanes];
552   int height[kMaxPlanes];
553 
554   for (int plane = kPlaneY; plane < num_planes; ++plane) {
555     const int8_t subsampling_x =
556         (plane == kPlaneY) ? 0 : frame_size_.subsampling_x;
557     const int8_t subsampling_y =
558         (plane == kPlaneY) ? 0 : frame_size_.subsampling_y;
559     width[plane] = frame_size_.width >> subsampling_x;
560     upscaled_width[plane] = frame_size_.upscaled_width >> subsampling_x;
561     height[plane] = frame_size_.height >> subsampling_y;
562     if (use_fixed_values) {
563       auto* src = reinterpret_cast<Pixel*>(post_filter.cdef_buffer_[plane]);
564       for (int y = 0; y < height[plane]; ++y) {
565         for (int x = 0; x < width[plane]; ++x) {
566           src[x] = value;
567         }
568         src += buffer_.stride(plane) / sizeof(Pixel);
569       }
570     } else {  // Random input.
571       const int mask = (1 << bitdepth) - 1;
572       libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
573       auto* src = reinterpret_cast<Pixel*>(post_filter.cdef_buffer_[plane]);
574       for (int y = 0; y < height[plane]; ++y) {
575         for (int x = 0; x < width[plane]; ++x) {
576           src[x] = rnd.Rand16() & mask;
577         }
578         src += buffer_.stride(plane) / sizeof(Pixel);
579       }
580     }
581   }
582 
583   if (multi_threaded) {
584     post_filter.ApplySuperResThreaded();
585   } else {
586     std::array<uint8_t*, kMaxPlanes> buffers = {
587         post_filter.cdef_buffer_[kPlaneY], post_filter.cdef_buffer_[kPlaneU],
588         post_filter.cdef_buffer_[kPlaneV]};
589     std::array<uint8_t*, kMaxPlanes> dst = {
590         post_filter.GetSuperResBuffer(static_cast<Plane>(kPlaneY), 0, 0),
591         post_filter.GetSuperResBuffer(static_cast<Plane>(kPlaneU), 0, 0),
592         post_filter.GetSuperResBuffer(static_cast<Plane>(kPlaneV), 0, 0)};
593     std::array<int, kMaxPlanes> rows = {
594         frame_header.rows4x4 * 4,
595         (frame_header.rows4x4 * 4) >> frame_size_.subsampling_y,
596         (frame_header.rows4x4 * 4) >> frame_size_.subsampling_y};
597     post_filter.ApplySuperRes(buffers, rows, /*line_buffer_row=*/-1, dst);
598   }
599 
600   // Check md5.
601   std::vector<Pixel> output;
602   for (int plane = kPlaneY; plane < num_planes; ++plane) {
603     output.reserve(upscaled_width[plane] * height[plane]);
604     output.resize(upscaled_width[plane] * height[plane]);
605     auto* dst = reinterpret_cast<Pixel*>(
606         post_filter.GetSuperResBuffer(static_cast<Plane>(plane), 0, 0));
607     for (int y = 0; y < height[plane]; ++y) {
608       for (int x = 0; x < upscaled_width[plane]; ++x) {
609         output[y * upscaled_width[plane] + x] = dst[x];
610       }
611       dst += buffer_.stride(plane) / sizeof(Pixel);
612     }
613     const std::string digest = test_utils::GetMd5Sum(
614         output.data(), upscaled_width[plane] * height[plane] * sizeof(Pixel));
615     printf("MD5: %s\n", digest.c_str());
616     const char* expected_digest = nullptr;
617     switch (bitdepth) {
618       case 8:
619         expected_digest = GetSuperResDigest8bpp(id, plane);
620         break;
621 #if LIBGAV1_MAX_BITDEPTH >= 10
622       case 10:
623         expected_digest = GetSuperResDigest10bpp(id, plane);
624         break;
625 #endif
626 #if LIBGAV1_MAX_BITDEPTH == 12
627       case 12:
628         expected_digest = GetSuperResDigest12bpp(id, plane);
629         break;
630 #endif
631     }
632     ASSERT_NE(expected_digest, nullptr);
633     EXPECT_STREQ(digest.c_str(), expected_digest);
634   }
635 }
636 
637 using PostFilterSuperResTest8bpp = PostFilterSuperResTest<8, uint8_t>;
638 
639 const FrameSizeParam kTestParamSuperRes[] = {
640     FrameSizeParam(176, 352, 288, 1, 1)};
641 
TEST_P(PostFilterSuperResTest8bpp,ApplySuperRes)642 TEST_P(PostFilterSuperResTest8bpp, ApplySuperRes) {
643   TestApplySuperRes(true, 0, 0, false);
644   TestApplySuperRes(true, 1, 1, false);
645   TestApplySuperRes(true, 128, 2, false);
646   TestApplySuperRes(true, 255, 3, false);
647   TestApplySuperRes(false, 0, 4, false);
648 }
649 
TEST_P(PostFilterSuperResTest8bpp,ApplySuperResThreaded)650 TEST_P(PostFilterSuperResTest8bpp, ApplySuperResThreaded) {
651   TestApplySuperRes(true, 0, 0, true);
652   TestApplySuperRes(true, 1, 1, true);
653   TestApplySuperRes(true, 128, 2, true);
654   TestApplySuperRes(true, 255, 3, true);
655   TestApplySuperRes(false, 0, 4, true);
656 }
657 
658 INSTANTIATE_TEST_SUITE_P(PostFilterSuperResTestInstance,
659                          PostFilterSuperResTest8bpp,
660                          testing::ValuesIn(kTestParamSuperRes));
661 
662 using PostFilterHelperFuncTest8bpp = PostFilterHelperFuncTest<8, uint8_t>;
663 
664 const FrameSizeParam kTestParamExtendFrame[] = {
665     FrameSizeParam(16, 16, 16, 1, 1),
666     FrameSizeParam(64, 64, 64, 1, 1),
667     FrameSizeParam(128, 128, 64, 1, 1),
668     FrameSizeParam(64, 64, 128, 1, 1),
669     FrameSizeParam(352, 352, 288, 1, 1),
670     FrameSizeParam(720, 720, 480, 1, 1),
671     FrameSizeParam(1080, 1080, 720, 1, 1),
672     FrameSizeParam(16, 16, 16, 0, 0),
673     FrameSizeParam(64, 64, 64, 0, 0),
674     FrameSizeParam(128, 128, 64, 0, 0),
675     FrameSizeParam(64, 64, 128, 0, 0),
676     FrameSizeParam(352, 352, 288, 0, 0),
677     FrameSizeParam(720, 720, 480, 0, 0),
678     FrameSizeParam(1080, 1080, 720, 0, 0)};
679 
TEST_P(PostFilterHelperFuncTest8bpp,ExtendFrame)680 TEST_P(PostFilterHelperFuncTest8bpp, ExtendFrame) {
681   TestExtendFrame(true, 0);
682   TestExtendFrame(true, 1);
683   TestExtendFrame(true, 128);
684   TestExtendFrame(true, 255);
685   TestExtendFrame(false, 0);
686 }
687 
688 INSTANTIATE_TEST_SUITE_P(PostFilterHelperFuncTestInstance,
689                          PostFilterHelperFuncTest8bpp,
690                          testing::ValuesIn(kTestParamExtendFrame));
691 
692 #if LIBGAV1_MAX_BITDEPTH >= 10
693 using PostFilterSuperResTest10bpp = PostFilterSuperResTest<10, uint16_t>;
694 
TEST_P(PostFilterSuperResTest10bpp,ApplySuperRes)695 TEST_P(PostFilterSuperResTest10bpp, ApplySuperRes) {
696   TestApplySuperRes(true, 0, 0, false);
697   TestApplySuperRes(true, 1, 1, false);
698   TestApplySuperRes(true, 1 << 9, 2, false);
699   TestApplySuperRes(true, (1 << 10) - 1, 3, false);
700   TestApplySuperRes(false, 0, 4, false);
701 }
702 
TEST_P(PostFilterSuperResTest10bpp,ApplySuperResThreaded)703 TEST_P(PostFilterSuperResTest10bpp, ApplySuperResThreaded) {
704   TestApplySuperRes(true, 0, 0, true);
705   TestApplySuperRes(true, 1, 1, true);
706   TestApplySuperRes(true, 1 << 9, 2, true);
707   TestApplySuperRes(true, (1 << 10) - 1, 3, true);
708   TestApplySuperRes(false, 0, 4, true);
709 }
710 
711 INSTANTIATE_TEST_SUITE_P(PostFilterSuperResTestInstance,
712                          PostFilterSuperResTest10bpp,
713                          testing::ValuesIn(kTestParamSuperRes));
714 
715 using PostFilterHelperFuncTest10bpp = PostFilterHelperFuncTest<10, uint16_t>;
716 
TEST_P(PostFilterHelperFuncTest10bpp,ExtendFrame)717 TEST_P(PostFilterHelperFuncTest10bpp, ExtendFrame) {
718   TestExtendFrame(true, 0);
719   TestExtendFrame(true, 1);
720   TestExtendFrame(true, 255);
721   TestExtendFrame(true, (1 << 10) - 1);
722   TestExtendFrame(false, 0);
723 }
724 
725 INSTANTIATE_TEST_SUITE_P(PostFilterHelperFuncTestInstance,
726                          PostFilterHelperFuncTest10bpp,
727                          testing::ValuesIn(kTestParamExtendFrame));
728 #endif  // LIBGAV1_MAX_BITDEPTH >= 10
729 
730 #if LIBGAV1_MAX_BITDEPTH == 12
731 using PostFilterSuperResTest12bpp = PostFilterSuperResTest<12, uint16_t>;
732 
TEST_P(PostFilterSuperResTest12bpp,ApplySuperRes)733 TEST_P(PostFilterSuperResTest12bpp, ApplySuperRes) {
734   TestApplySuperRes(true, 0, 0, false);
735   TestApplySuperRes(true, 1, 1, false);
736   TestApplySuperRes(true, 1 << 11, 2, false);
737   TestApplySuperRes(true, (1 << 12) - 1, 3, false);
738   TestApplySuperRes(false, 0, 4, false);
739 }
740 
TEST_P(PostFilterSuperResTest12bpp,ApplySuperResThreaded)741 TEST_P(PostFilterSuperResTest12bpp, ApplySuperResThreaded) {
742   TestApplySuperRes(true, 0, 0, true);
743   TestApplySuperRes(true, 1, 1, true);
744   TestApplySuperRes(true, 1 << 11, 2, true);
745   TestApplySuperRes(true, (1 << 12) - 1, 3, true);
746   TestApplySuperRes(false, 0, 4, true);
747 }
748 
749 INSTANTIATE_TEST_SUITE_P(PostFilterSuperResTestInstance,
750                          PostFilterSuperResTest12bpp,
751                          testing::ValuesIn(kTestParamSuperRes));
752 
753 using PostFilterHelperFuncTest12bpp = PostFilterHelperFuncTest<12, uint16_t>;
754 
TEST_P(PostFilterHelperFuncTest12bpp,ExtendFrame)755 TEST_P(PostFilterHelperFuncTest12bpp, ExtendFrame) {
756   TestExtendFrame(true, 0);
757   TestExtendFrame(true, 1);
758   TestExtendFrame(true, 255);
759   TestExtendFrame(true, (1 << 12) - 1);
760   TestExtendFrame(false, 0);
761 }
762 
763 INSTANTIATE_TEST_SUITE_P(PostFilterHelperFuncTestInstance,
764                          PostFilterHelperFuncTest12bpp,
765                          testing::ValuesIn(kTestParamExtendFrame));
766 #endif  // LIBGAV1_MAX_BITDEPTH == 12
767 
768 namespace {
769 
GetDigestApplyCdef8bpp(int id)770 const char* GetDigestApplyCdef8bpp(int id) {
771   static const char* const kDigest[] = {
772       "9593af24f9c6faecce53437f6e128edf", "ecb633cc2ecd6e7e0cf39d4439f4a6ea",
773       "9ec4cb4124f0a686a7bda72b447f5b8e", "7ebd859a23162bc864a69dbea60bc687",
774       "de7a15fc00664692a794aa68cf695980", "cf3fc8fe041f68d31ab4e34ad3643541",
775       "94c116b191b0268cf7ab4a0e6996e1ec", "1ad60c943a5a914aba7bc26706620a05",
776       "ce33c6f80e3608c4d18c49be2e393c20", "e140586ffc663798b74b8f6fb5b44736",
777       "b7379bba8bcb97f09a74655f4e0eee91", "02ce174061c98babd3987461b3984e47",
778       "64655dd1dfba8317e27d2fdcb211b7b4", "eeb6a61c70c5ee75a4c31dc5099b4dfb",
779       "ee944b31148fa2e30938084f7c046464", "db7b63497750fa4c51cf45c56a2da01c",
780   };
781   return kDigest[id];
782 }
783 
784 #if LIBGAV1_MAX_BITDEPTH >= 10
GetDigestApplyCdef10bpp(int id)785 const char* GetDigestApplyCdef10bpp(int id) {
786   static const char* const kDigest[] = {
787       "53f8d68ac7f3aea65151b2066f8501c9", "021e70d5406fa182dd9713380eb66d1d",
788       "bab1c84e7f06b87d81617d2d0a194b89", "58e302ff0522f64901909fb97535b270",
789       "5ff95a6a798eadc7207793c03d898ce4", "1483d28cc0f1bfffedd1128966719aa0",
790       "6af5a36890b465ae962c2878af874f70", "bd1ed4a2ff09d323ab98190d1805a010",
791       "5ff95a6a798eadc7207793c03d898ce4", "1483d28cc0f1bfffedd1128966719aa0",
792       "6af5a36890b465ae962c2878af874f70", "bd1ed4a2ff09d323ab98190d1805a010",
793       "6f0299645cd6f0655fd26044cd43a37c", "56d7febf5bbebdc82e8f157ab926a0bb",
794       "f54654f11006453f496be5883216a3bb", "9abc6e3230792ba78bcc65504a62075e",
795   };
796   return kDigest[id];
797 }
798 #endif  // LIBGAV1_MAX_BITDEPTH >= 10
799 
800 #if LIBGAV1_MAX_BITDEPTH == 12
GetDigestApplyCdef12bpp(int id)801 const char* GetDigestApplyCdef12bpp(int id) {
802   static const char* const kDigest[] = {
803       "06e2d09b6ce3924f3b5d4c00ab76eea5", "287240e4b13cb75e17932a3dd7ba3b3c",
804       "265da123e3347c4fb3e434f26a3949e7", "e032ce6eb76242df6894482ac6688406",
805       "f648328221f0f02a5b7fc3d55a66271a", "8f759aa84a110902025dacf8062d2f6a",
806       "592b49e4b993d6b4634d8eb1ee3bba54", "29a3e8e329ec70d06910e982ea763e6b",
807       "f648328221f0f02a5b7fc3d55a66271a", "8f759aa84a110902025dacf8062d2f6a",
808       "592b49e4b993d6b4634d8eb1ee3bba54", "29a3e8e329ec70d06910e982ea763e6b",
809       "155dd4283f8037f86cce34b6cfe67a7e", "0a022c70ead199517af9bad2002d70cd",
810       "a966dfea52a7a2084545f68b2c9e1735", "e098438a23a7c9f276e594b98b2db922",
811   };
812   return kDigest[id];
813 }
814 #endif  // LIBGAV1_MAX_BITDEPTH == 12
815 
816 }  // namespace
817 
818 template <int bitdepth, typename Pixel>
819 class PostFilterApplyCdefTest : public testing::TestWithParam<FrameSizeParam>,
820                                 public test_utils::MaxAlignedAllocable {
821  public:
822   static_assert(bitdepth >= kBitdepth8 && bitdepth <= LIBGAV1_MAX_BITDEPTH, "");
823   PostFilterApplyCdefTest() = default;
824   PostFilterApplyCdefTest(const PostFilterApplyCdefTest&) = delete;
825   PostFilterApplyCdefTest& operator=(const PostFilterApplyCdefTest&) = delete;
826   ~PostFilterApplyCdefTest() override = default;
827 
828  protected:
SetUp()829   void SetUp() override {
830     test_utils::ResetDspTable(bitdepth);
831     dsp::CdefInit_C();
832     dsp::CdefInit_SSE4_1();
833     dsp::CdefInit_NEON();
834 
835     dsp_ = dsp::GetDspTable(bitdepth);
836     ASSERT_NE(dsp_, nullptr);
837   }
838 
839   // Sets sequence_header_, frame_header_, cdef_index_ and cdef_skip_.
840   // Allocates yuv_buffer_ but does not set it.
841   void SetInput(libvpx_test::ACMRandom* rnd);
842   // Sets yuv_buffer_.
843   void SetInputBuffer(libvpx_test::ACMRandom* rnd, PostFilter* post_filter);
844   void CopyFilterOutputToDestBuffer();
845   void TestMultiThread(int num_threads);
846 
847   ObuSequenceHeader sequence_header_;
848   ObuFrameHeader frame_header_ = {};
849   FrameScratchBuffer frame_scratch_buffer_;
850   YuvBuffer yuv_buffer_;
851   const dsp::Dsp* dsp_;
852   FrameSizeParam param_ = GetParam();
853   Pixel dest_[kMaxTestFrameSize * kMaxPlanes];
854   const size_t y_size_ = param_.width * param_.height;
855   const size_t uv_size_ = y_size_ >>
856                           (param_.subsampling_x + param_.subsampling_y);
857   const size_t size_ = y_size_ + uv_size_ * 2;
858 };
859 
860 template <int bitdepth, typename Pixel>
SetInput(libvpx_test::ACMRandom * rnd)861 void PostFilterApplyCdefTest<bitdepth, Pixel>::SetInput(
862     libvpx_test::ACMRandom* rnd) {
863   sequence_header_.color_config.bitdepth = bitdepth;
864   sequence_header_.color_config.subsampling_x = param_.subsampling_x;
865   sequence_header_.color_config.subsampling_y = param_.subsampling_y;
866   sequence_header_.color_config.is_monochrome = false;
867   sequence_header_.use_128x128_superblock =
868       static_cast<bool>(rnd->Rand16() & 1);
869 
870   ASSERT_TRUE(param_.width <= param_.upscaled_width);
871   ASSERT_TRUE(param_.upscaled_width * param_.height <= kMaxTestFrameSize)
872       << "Please adjust the max frame size.";
873 
874   frame_header_.width = param_.width;
875   frame_header_.upscaled_width = param_.upscaled_width;
876   frame_header_.height = param_.height;
877   frame_header_.columns4x4 = DivideBy4(Align(frame_header_.width, 8));
878   frame_header_.rows4x4 = DivideBy4(Align(frame_header_.height, 8));
879   frame_header_.tile_info.tile_count = 1;
880   frame_header_.refresh_frame_flags = 0;
881   Cdef* const cdef = &frame_header_.cdef;
882   const int coeff_shift = bitdepth - 8;
883   do {
884     cdef->damping = (rnd->Rand16() & 3) + 3 + coeff_shift;
885     cdef->bits = rnd->Rand16() & 3;
886   } while (cdef->bits <= 0);
887   for (int i = 0; i < (1 << cdef->bits); ++i) {
888     cdef->y_primary_strength[i] = (rnd->Rand16() & 15) << coeff_shift;
889     cdef->y_secondary_strength[i] = rnd->Rand16() & 3;
890     if (cdef->y_secondary_strength[i] == 3) {
891       ++cdef->y_secondary_strength[i];
892     }
893     cdef->y_secondary_strength[i] <<= coeff_shift;
894     cdef->uv_primary_strength[i] = (rnd->Rand16() & 15) << coeff_shift;
895     cdef->uv_secondary_strength[i] = rnd->Rand16() & 3;
896     if (cdef->uv_secondary_strength[i] == 3) {
897       ++cdef->uv_secondary_strength[i];
898     }
899     cdef->uv_secondary_strength[i] <<= coeff_shift;
900   }
901 
902   const int rows64x64 = DivideBy16(frame_header_.rows4x4 + kMaxBlockHeight4x4);
903   const int columns64x64 =
904       DivideBy16(frame_header_.columns4x4 + kMaxBlockWidth4x4);
905   ASSERT_TRUE(frame_scratch_buffer_.cdef_index.Reset(rows64x64, columns64x64));
906   for (int row = 0; row < rows64x64; ++row) {
907     for (int column = 0; column < columns64x64; ++column) {
908       frame_scratch_buffer_.cdef_index[row][column] =
909           rnd->Rand16() & ((1 << cdef->bits) - 1);
910     }
911   }
912 
913   const int skip_rows = DivideBy2(frame_header_.rows4x4 + kMaxBlockHeight4x4);
914   const int skip_columns =
915       DivideBy16(frame_header_.columns4x4 + kMaxBlockWidth4x4);
916   ASSERT_TRUE(frame_scratch_buffer_.cdef_skip.Reset(skip_rows, skip_columns));
917   for (int row = 0; row < skip_rows; ++row) {
918     memset(frame_scratch_buffer_.cdef_skip[row], 0xFF, skip_columns);
919   }
920 
921   ASSERT_TRUE(yuv_buffer_.Realloc(
922       sequence_header_.color_config.bitdepth,
923       sequence_header_.color_config.is_monochrome, frame_header_.upscaled_width,
924       frame_header_.height, sequence_header_.color_config.subsampling_x,
925       sequence_header_.color_config.subsampling_y, kBorderPixels, kBorderPixels,
926       kBorderPixels, kBorderPixels, nullptr, nullptr, nullptr))
927       << "Failed to allocate source buffer.";
928 }
929 
930 template <int bitdepth, typename Pixel>
SetInputBuffer(libvpx_test::ACMRandom * rnd,PostFilter * post_filter)931 void PostFilterApplyCdefTest<bitdepth, Pixel>::SetInputBuffer(
932     libvpx_test::ACMRandom* rnd, PostFilter* post_filter) {
933   for (int plane = kPlaneY; plane < kMaxPlanes; ++plane) {
934     const int subsampling_x = (plane == 0) ? 0 : param_.subsampling_x;
935     const int subsampling_y = (plane == 0) ? 0 : param_.subsampling_y;
936     const int plane_width =
937         MultiplyBy4(frame_header_.columns4x4) >> subsampling_x;
938     const int plane_height =
939         MultiplyBy4(frame_header_.rows4x4) >> subsampling_y;
940     auto* src =
941         reinterpret_cast<Pixel*>(post_filter->GetUnfilteredBuffer(plane));
942     const int src_stride = yuv_buffer_.stride(plane) / sizeof(src[0]);
943     for (int y = 0; y < plane_height; ++y) {
944       for (int x = 0; x < plane_width; ++x) {
945         src[x] = rnd->Rand16() & ((1 << bitdepth) - 1);
946       }
947       src += src_stride;
948     }
949   }
950 }
951 
952 template <int bitdepth, typename Pixel>
CopyFilterOutputToDestBuffer()953 void PostFilterApplyCdefTest<bitdepth, Pixel>::CopyFilterOutputToDestBuffer() {
954   for (int plane = kPlaneY; plane < kMaxPlanes; ++plane) {
955     const int subsampling_x = (plane == 0) ? 0 : param_.subsampling_x;
956     const int subsampling_y = (plane == 0) ? 0 : param_.subsampling_y;
957     const int plane_width = SubsampledValue(param_.width, subsampling_x);
958     const int plane_height = SubsampledValue(param_.height, subsampling_y);
959     auto* src = reinterpret_cast<Pixel*>(yuv_buffer_.data(plane));
960     const int src_stride = yuv_buffer_.stride(plane) / sizeof(src[0]);
961     Pixel* dest_plane =
962         dest_ +
963         ((plane == 0) ? 0 : ((plane == 1) ? y_size_ : y_size_ + uv_size_));
964     for (int y = 0; y < plane_height; ++y) {
965       for (int x = 0; x < plane_width; ++x) {
966         dest_plane[y * plane_width + x] = src[x];
967       }
968       src += src_stride;
969     }
970   }
971 }
972 
973 template <int bitdepth, typename Pixel>
TestMultiThread(int num_threads)974 void PostFilterApplyCdefTest<bitdepth, Pixel>::TestMultiThread(
975     int num_threads) {
976   libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
977   SetInput(&rnd);
978 
979   ASSERT_TRUE(frame_scratch_buffer_.threading_strategy.Reset(frame_header_,
980                                                              num_threads));
981   if (num_threads > 1) {
982     const int num_units =
983         MultiplyBy4(RightShiftWithCeiling(frame_header_.rows4x4, 4));
984     ASSERT_TRUE(frame_scratch_buffer_.cdef_border.Realloc(
985         bitdepth, /*is_monochrome=*/false,
986         MultiplyBy4(frame_header_.columns4x4), num_units,
987         sequence_header_.color_config.subsampling_x,
988         /*subsampling_y=*/0, kBorderPixels, kBorderPixels, kBorderPixels,
989         kBorderPixels, nullptr, nullptr, nullptr));
990   }
991 
992   PostFilter post_filter(frame_header_, sequence_header_,
993                          &frame_scratch_buffer_, &yuv_buffer_, dsp_,
994                          /*do_post_filter_mask=*/0x02);
995   SetInputBuffer(&rnd, &post_filter);
996 
997   const int id = GetIdFromInputParam(param_.subsampling_x, param_.subsampling_y,
998                                      param_.height);
999   absl::Duration elapsed_time;
1000   const absl::Time start = absl::Now();
1001 
1002   // Only ApplyCdef() and frame copy inside ApplyFilteringThreaded() are
1003   // triggered, since we set the filter mask to 0x02.
1004   post_filter.ApplyFilteringThreaded();
1005   elapsed_time += absl::Now() - start;
1006 
1007   CopyFilterOutputToDestBuffer();
1008   const char* expected_digest = nullptr;
1009   switch (bitdepth) {
1010     case 8:
1011       expected_digest = GetDigestApplyCdef8bpp(id);
1012       break;
1013 #if LIBGAV1_MAX_BITDEPTH >= 10
1014     case 10:
1015       expected_digest = GetDigestApplyCdef10bpp(id);
1016       break;
1017 #endif
1018 #if LIBGAV1_MAX_BITDEPTH == 12
1019     case 12:
1020       expected_digest = GetDigestApplyCdef12bpp(id);
1021       break;
1022 #endif
1023   }
1024   ASSERT_NE(expected_digest, nullptr);
1025   test_utils::CheckMd5Digest(kCdef, kApplyCdefName, expected_digest, dest_,
1026                              size_, elapsed_time);
1027 }
1028 
1029 const FrameSizeParam kTestParamApplyCdef[] = {
1030     FrameSizeParam(352, 352, 288, 0, 0),    FrameSizeParam(720, 720, 480, 0, 0),
1031     FrameSizeParam(1920, 1920, 1080, 0, 0), FrameSizeParam(251, 251, 187, 0, 0),
1032     FrameSizeParam(352, 352, 288, 0, 1),    FrameSizeParam(720, 720, 480, 0, 1),
1033     FrameSizeParam(1920, 1920, 1080, 0, 1), FrameSizeParam(251, 251, 187, 0, 1),
1034     FrameSizeParam(352, 352, 288, 1, 0),    FrameSizeParam(720, 720, 480, 1, 0),
1035     FrameSizeParam(1920, 1920, 1080, 1, 0), FrameSizeParam(251, 251, 187, 1, 0),
1036     FrameSizeParam(352, 352, 288, 1, 1),    FrameSizeParam(720, 720, 480, 1, 1),
1037     FrameSizeParam(1920, 1920, 1080, 1, 1), FrameSizeParam(251, 251, 187, 1, 1),
1038 };
1039 
1040 using PostFilterApplyCdefTest8bpp = PostFilterApplyCdefTest<8, uint8_t>;
1041 
TEST_P(PostFilterApplyCdefTest8bpp,ApplyCdef)1042 TEST_P(PostFilterApplyCdefTest8bpp, ApplyCdef) {
1043   TestMultiThread(2);
1044   TestMultiThread(4);
1045   TestMultiThread(8);
1046 }
1047 
1048 INSTANTIATE_TEST_SUITE_P(PostFilterApplyCdefTestInstance,
1049                          PostFilterApplyCdefTest8bpp,
1050                          testing::ValuesIn(kTestParamApplyCdef));
1051 
1052 #if LIBGAV1_MAX_BITDEPTH >= 10
1053 using PostFilterApplyCdefTest10bpp = PostFilterApplyCdefTest<10, uint16_t>;
1054 
TEST_P(PostFilterApplyCdefTest10bpp,ApplyCdef)1055 TEST_P(PostFilterApplyCdefTest10bpp, ApplyCdef) {
1056   TestMultiThread(2);
1057   TestMultiThread(4);
1058   TestMultiThread(8);
1059 }
1060 
1061 INSTANTIATE_TEST_SUITE_P(PostFilterApplyCdefTestInstance,
1062                          PostFilterApplyCdefTest10bpp,
1063                          testing::ValuesIn(kTestParamApplyCdef));
1064 #endif  // LIBGAV1_MAX_BITDEPTH >= 10
1065 
1066 #if LIBGAV1_MAX_BITDEPTH == 12
1067 using PostFilterApplyCdefTest12bpp = PostFilterApplyCdefTest<12, uint16_t>;
1068 
TEST_P(PostFilterApplyCdefTest12bpp,ApplyCdef)1069 TEST_P(PostFilterApplyCdefTest12bpp, ApplyCdef) {
1070   TestMultiThread(2);
1071   TestMultiThread(4);
1072   TestMultiThread(8);
1073 }
1074 
1075 INSTANTIATE_TEST_SUITE_P(PostFilterApplyCdefTestInstance,
1076                          PostFilterApplyCdefTest12bpp,
1077                          testing::ValuesIn(kTestParamApplyCdef));
1078 #endif  // LIBGAV1_MAX_BITDEPTH == 12
1079 
1080 }  // namespace libgav1
1081