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