1 // Copyright 2020 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/dsp/weight_mask.h"
16
17 #include <algorithm>
18 #include <cstdint>
19 #include <ostream>
20 #include <string>
21 #include <type_traits>
22
23 #include "absl/strings/match.h"
24 #include "absl/strings/str_format.h"
25 #include "absl/time/clock.h"
26 #include "absl/time/time.h"
27 #include "gtest/gtest.h"
28 #include "src/dsp/dsp.h"
29 #include "src/utils/common.h"
30 #include "src/utils/constants.h"
31 #include "src/utils/cpu.h"
32 #include "src/utils/memory.h"
33 #include "tests/third_party/libvpx/acm_random.h"
34 #include "tests/utils.h"
35
36 namespace libgav1 {
37 namespace dsp {
38 namespace {
39
40 constexpr int kNumSpeedTests = 50000;
41 constexpr int kMaxPredictionSize = 128;
42 // weight_mask is only used with kCompoundPredictionTypeDiffWeighted with
43 // convolve producing the most extreme ranges.
44 // This includes kCompoundOffset in 10bpp and 12bpp.
45 // see: src/dsp/convolve.cc & src/dsp/warp.cc.
46 constexpr int kCompoundPredictionRange[3][2] = {
47 // 8bpp
48 {-5132, 9212},
49 // 10bpp
50 {3988, 61532},
51 // 12bpp
52 {3974, 61559},
53 };
54
GetDigest8bpp(int id)55 const char* GetDigest8bpp(int id) {
56 static const char* const kDigest[] = {
57 "035267cb2ac5a0f8ff50c2d30ad52226",
58 "3231f4972dd858b734e0cc48c4cd001e",
59 "7e163b69721a13ec9f75b5cd74ffee3f",
60 "" /*kBlock4x16*/,
61 "b75e90abc224acca8754c82039b3ba93",
62 "9f555f3a2c1a933a663d6103b8118dea",
63 "8539e54f34cd6668ff6e6606210be201",
64 "20f85c9db7c878c21fbf2052936f269e",
65 "620ec166de57b0639260b2d72eebfc3e",
66 "be666394b5a894d78f4097b6cca272fe",
67 "57a96816e84cdb381f596c23827b5922",
68 "f2e0d348f608f246b6d8d799b66c189e",
69 "161ac051f38372d9339d36728b9926ba",
70 "d5fad48aaf132a81cb62bba4f07bbebb",
71 "e10be2dca2f7dae38dae75150fc1612d",
72 "7f744481eb551bbc224b5236c82cbade",
73 "0d99bbf31ecddc1c2d5063a68c0e9375",
74 "5fb8ec5f582f0ebfe519ed55860f67c4",
75
76 // mask_is_inverse = true.
77 "a4250ca39daa700836138371d36d465f",
78 "abe9a9a1c3a5accda9bfefd4d6e81ccb",
79 "e95b08878d0bb5f2293c27c3a6fe0253",
80 "" /*kBlock4x16*/,
81 "e1c52be02ce9ab2800015bb08b866c31",
82 "eea1dc73811f73866edfeb4555865f20",
83 "3178e64085645bd819256a8ab43c7b0a",
84 "ee83884e4d5cd2c9ac04879116bab681",
85 "d107eff7d5ae9ba14d2c6b3b8d9fca49",
86 "400aeea7d299626fc336c46b1ad7a9d8",
87 "e9e26a400f67f3ad36350fe4171fc613",
88 "4c31ad714f470f34127febaf1bac714b",
89 "bbdcb1097c66d561dd4ea16b3fb73f97",
90 "3a21dfbf53e4c964e303a75a3308ce15",
91 "3416dab4512fd0dc61d788b433cd624e",
92 "68ace8f01fdd74aec3fee528c8167738",
93 "9fabe05a6523da81a45150e19f75acff",
94 "7c0643e4d02421d06d7ca71822a94e1d",
95 };
96 return kDigest[id];
97 }
98
99 #if LIBGAV1_MAX_BITDEPTH >= 10
GetDigest10bpp(int id)100 const char* GetDigest10bpp(int id) {
101 static const char* const kDigest[] = {
102 "1dc9bdd042e5228705b857b42798e364",
103 "c054c8644bd482ce78a139d8e063e013",
104 "bbe4ac48f013f34c84779da05b0bcbe0",
105 "" /*kBlock4x16*/,
106 "13d4759277637a607f25439182553708",
107 "f089667610561a47d50f9f930ad7c454",
108 "46715e6f7819f59725bdb083f4403255",
109 "3774541c339ae3af920ef2b1d6abf6a1",
110 "94913b01d226cb5eb273dfee84b51f65",
111 "be0c0847629dfff8e0e991ed67697a7d",
112 "716b5398b77d7459274d4ea9c91ebd8e",
113 "f5c1b0b461df4182529949472242b421",
114 "5e9576ea4cf107249ce4ae89a72b9c95",
115 "da021bcdf7936f7bd9a2399c69e4d37c",
116 "b3a310a39c1900e00f992839ff188656",
117 "9f3a15351af5945615f296242ec56a38",
118 "b6e0bd03c521c5f00e90530daa7d4432",
119 "3270d7f621d488aec5b76bcf121debd0",
120
121 // mask_is_inverse = true.
122 "33df96dd246683133eefe4caea6e3f7d",
123 "73e0ccc5d42806548a4b59f856256c1e",
124 "3561a0358cf831aee9477d07feafae2d",
125 "" /*kBlock4x16*/,
126 "c5a2e633c0cd6925e68f21f47f0e2d84",
127 "8755a2d3840dde5fd6a0cce6bd6642c5",
128 "85ec538b72cecd6ea1fddab5ce3b4e64",
129 "a53e0dec84c675c4c6b1f5792b0232ff",
130 "86180da325f9727670a98cf2dbf7410e",
131 "a5fdc95104948047e179b2bc3d47f51d",
132 "9b95b3858187838e4669180e2ddb295e",
133 "6e40ca55608f6bf2f8cd91c8dbf3ddbf",
134 "d3a092672e921b588279d57e50b31888",
135 "9883eb19b733ee9f1cb6a6b6a1a00bb5",
136 "dd34764e068b228b7820321b06864e63",
137 "6c743dc9c8c87c7044151d29993e5042",
138 "44925dab01011a98b8ab1f0308fa852a",
139 "6d984b2ccfa056278e2130771127a943",
140 };
141 return kDigest[id];
142 }
143 #endif // LIBGAV1_MAX_BITDEPTH >= 10
144
145 struct WeightMaskTestParam {
WeightMaskTestParamlibgav1::dsp::__anonbf17a3520111::WeightMaskTestParam146 WeightMaskTestParam(int width, int height, bool mask_is_inverse)
147 : width(width), height(height), mask_is_inverse(mask_is_inverse) {}
148 int width;
149 int height;
150 bool mask_is_inverse;
151 };
152
operator <<(std::ostream & os,const WeightMaskTestParam & param)153 std::ostream& operator<<(std::ostream& os, const WeightMaskTestParam& param) {
154 return os << param.width << "x" << param.height
155 << ", mask_is_inverse: " << param.mask_is_inverse;
156 }
157
158 template <int bitdepth>
159 class WeightMaskTest : public testing::TestWithParam<WeightMaskTestParam>,
160 public test_utils::MaxAlignedAllocable {
161 public:
162 WeightMaskTest() = default;
163 ~WeightMaskTest() override = default;
164
SetUp()165 void SetUp() override {
166 test_utils::ResetDspTable(bitdepth);
167 WeightMaskInit_C();
168 const dsp::Dsp* const dsp = dsp::GetDspTable(bitdepth);
169 ASSERT_NE(dsp, nullptr);
170 const int width_index = FloorLog2(width_) - 3;
171 const int height_index = FloorLog2(height_) - 3;
172 const testing::TestInfo* const test_info =
173 testing::UnitTest::GetInstance()->current_test_info();
174 const char* const test_case = test_info->test_suite_name();
175 if (absl::StartsWith(test_case, "C/")) {
176 } else if (absl::StartsWith(test_case, "NEON/")) {
177 WeightMaskInit_NEON();
178 } else if (absl::StartsWith(test_case, "SSE41/")) {
179 WeightMaskInit_SSE4_1();
180 }
181 func_ = dsp->weight_mask[width_index][height_index][mask_is_inverse_];
182 }
183
184 protected:
185 void SetInputData(bool use_fixed_values, int value_1, int value_2);
186 void Test(int num_runs, bool use_fixed_values, int value_1, int value_2);
187
188 private:
189 const int width_ = GetParam().width;
190 const int height_ = GetParam().height;
191 const bool mask_is_inverse_ = GetParam().mask_is_inverse;
192 using PredType =
193 typename std::conditional<bitdepth == 8, int16_t, uint16_t>::type;
194 alignas(
195 kMaxAlignment) PredType block_1_[kMaxPredictionSize * kMaxPredictionSize];
196 alignas(
197 kMaxAlignment) PredType block_2_[kMaxPredictionSize * kMaxPredictionSize];
198 uint8_t mask_[kMaxPredictionSize * kMaxPredictionSize] = {};
199 dsp::WeightMaskFunc func_;
200 };
201
202 template <int bitdepth>
SetInputData(const bool use_fixed_values,const int value_1,const int value_2)203 void WeightMaskTest<bitdepth>::SetInputData(const bool use_fixed_values,
204 const int value_1,
205 const int value_2) {
206 if (use_fixed_values) {
207 std::fill(block_1_, block_1_ + kMaxPredictionSize * kMaxPredictionSize,
208 value_1);
209 std::fill(block_2_, block_2_ + kMaxPredictionSize * kMaxPredictionSize,
210 value_2);
211 } else {
212 constexpr int bitdepth_index = (bitdepth - 8) >> 1;
213 libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
214 for (int y = 0; y < height_; ++y) {
215 for (int x = 0; x < width_; ++x) {
216 const int min_val = kCompoundPredictionRange[bitdepth_index][0];
217 const int max_val = kCompoundPredictionRange[bitdepth_index][1];
218 block_1_[y * width_ + x] =
219 static_cast<PredType>(rnd(max_val - min_val) + min_val);
220 block_2_[y * width_ + x] =
221 static_cast<PredType>(rnd(max_val - min_val) + min_val);
222 }
223 }
224 }
225 }
226
DimensionsToBlockSize(int width,int height)227 BlockSize DimensionsToBlockSize(int width, int height) {
228 if (width == 4) {
229 if (height == 4) return kBlock4x4;
230 if (height == 8) return kBlock4x8;
231 if (height == 16) return kBlock4x16;
232 return kBlockInvalid;
233 }
234 if (width == 8) {
235 if (height == 4) return kBlock8x4;
236 if (height == 8) return kBlock8x8;
237 if (height == 16) return kBlock8x16;
238 if (height == 32) return kBlock8x32;
239 return kBlockInvalid;
240 }
241 if (width == 16) {
242 if (height == 4) return kBlock16x4;
243 if (height == 8) return kBlock16x8;
244 if (height == 16) return kBlock16x16;
245 if (height == 32) return kBlock16x32;
246 if (height == 64) return kBlock16x64;
247 return kBlockInvalid;
248 }
249 if (width == 32) {
250 if (height == 8) return kBlock32x8;
251 if (height == 16) return kBlock32x16;
252 if (height == 32) return kBlock32x32;
253 if (height == 64) return kBlock32x64;
254 return kBlockInvalid;
255 }
256 if (width == 64) {
257 if (height == 16) return kBlock64x16;
258 if (height == 32) return kBlock64x32;
259 if (height == 64) return kBlock64x64;
260 if (height == 128) return kBlock64x128;
261 return kBlockInvalid;
262 }
263 if (width == 128) {
264 if (height == 64) return kBlock128x64;
265 if (height == 128) return kBlock128x128;
266 return kBlockInvalid;
267 }
268 return kBlockInvalid;
269 }
270
271 template <int bitdepth>
Test(const int num_runs,const bool use_fixed_values,const int value_1,const int value_2)272 void WeightMaskTest<bitdepth>::Test(const int num_runs,
273 const bool use_fixed_values,
274 const int value_1, const int value_2) {
275 if (func_ == nullptr) return;
276 SetInputData(use_fixed_values, value_1, value_2);
277 const absl::Time start = absl::Now();
278 for (int i = 0; i < num_runs; ++i) {
279 func_(block_1_, block_2_, mask_, kMaxPredictionSize);
280 }
281 const absl::Duration elapsed_time = absl::Now() - start;
282 if (use_fixed_values) {
283 int fixed_value = (value_1 - value_2 == 0) ? 38 : 64;
284 if (mask_is_inverse_) fixed_value = 64 - fixed_value;
285 for (int y = 0; y < height_; ++y) {
286 for (int x = 0; x < width_; ++x) {
287 ASSERT_EQ(static_cast<int>(mask_[y * kMaxPredictionSize + x]),
288 fixed_value)
289 << "x: " << x << " y: " << y;
290 }
291 }
292 } else {
293 const int id_offset = mask_is_inverse_ ? kMaxBlockSizes - 4 : 0;
294 const int id = id_offset +
295 static_cast<int>(DimensionsToBlockSize(width_, height_)) - 4;
296 if (bitdepth == 8) {
297 test_utils::CheckMd5Digest(
298 absl::StrFormat("BlockSize %dx%d", width_, height_).c_str(),
299 "WeightMask", GetDigest8bpp(id), mask_, sizeof(mask_), elapsed_time);
300 #if LIBGAV1_MAX_BITDEPTH >= 10
301 } else {
302 test_utils::CheckMd5Digest(
303 absl::StrFormat("BlockSize %dx%d", width_, height_).c_str(),
304 "WeightMask", GetDigest10bpp(id), mask_, sizeof(mask_), elapsed_time);
305 #endif
306 }
307 }
308 }
309
310 const WeightMaskTestParam weight_mask_test_param[] = {
311 WeightMaskTestParam(8, 8, false), WeightMaskTestParam(8, 16, false),
312 WeightMaskTestParam(8, 32, false), WeightMaskTestParam(16, 8, false),
313 WeightMaskTestParam(16, 16, false), WeightMaskTestParam(16, 32, false),
314 WeightMaskTestParam(16, 64, false), WeightMaskTestParam(32, 8, false),
315 WeightMaskTestParam(32, 16, false), WeightMaskTestParam(32, 32, false),
316 WeightMaskTestParam(32, 64, false), WeightMaskTestParam(64, 16, false),
317 WeightMaskTestParam(64, 32, false), WeightMaskTestParam(64, 64, false),
318 WeightMaskTestParam(64, 128, false), WeightMaskTestParam(128, 64, false),
319 WeightMaskTestParam(128, 128, false), WeightMaskTestParam(8, 8, true),
320 WeightMaskTestParam(8, 16, true), WeightMaskTestParam(8, 32, true),
321 WeightMaskTestParam(16, 8, true), WeightMaskTestParam(16, 16, true),
322 WeightMaskTestParam(16, 32, true), WeightMaskTestParam(16, 64, true),
323 WeightMaskTestParam(32, 8, true), WeightMaskTestParam(32, 16, true),
324 WeightMaskTestParam(32, 32, true), WeightMaskTestParam(32, 64, true),
325 WeightMaskTestParam(64, 16, true), WeightMaskTestParam(64, 32, true),
326 WeightMaskTestParam(64, 64, true), WeightMaskTestParam(64, 128, true),
327 WeightMaskTestParam(128, 64, true), WeightMaskTestParam(128, 128, true),
328 };
329
330 using WeightMaskTest8bpp = WeightMaskTest<8>;
331
TEST_P(WeightMaskTest8bpp,FixedValues)332 TEST_P(WeightMaskTest8bpp, FixedValues) {
333 const int min = kCompoundPredictionRange[0][0];
334 const int max = kCompoundPredictionRange[0][1];
335 Test(1, true, min, min);
336 Test(1, true, min, max);
337 Test(1, true, max, min);
338 Test(1, true, max, max);
339 }
340
TEST_P(WeightMaskTest8bpp,RandomValues)341 TEST_P(WeightMaskTest8bpp, RandomValues) { Test(1, false, -1, -1); }
342
TEST_P(WeightMaskTest8bpp,DISABLED_Speed)343 TEST_P(WeightMaskTest8bpp, DISABLED_Speed) {
344 Test(kNumSpeedTests, false, -1, -1);
345 }
346
347 INSTANTIATE_TEST_SUITE_P(C, WeightMaskTest8bpp,
348 testing::ValuesIn(weight_mask_test_param));
349 #if LIBGAV1_ENABLE_NEON
350 INSTANTIATE_TEST_SUITE_P(NEON, WeightMaskTest8bpp,
351 testing::ValuesIn(weight_mask_test_param));
352 #endif
353 #if LIBGAV1_ENABLE_SSE4_1
354 INSTANTIATE_TEST_SUITE_P(SSE41, WeightMaskTest8bpp,
355 testing::ValuesIn(weight_mask_test_param));
356 #endif
357
358 #if LIBGAV1_MAX_BITDEPTH >= 10
359 using WeightMaskTest10bpp = WeightMaskTest<10>;
360
TEST_P(WeightMaskTest10bpp,FixedValues)361 TEST_P(WeightMaskTest10bpp, FixedValues) {
362 const int min = kCompoundPredictionRange[1][0];
363 const int max = kCompoundPredictionRange[1][1];
364 Test(1, true, min, min);
365 Test(1, true, min, max);
366 Test(1, true, max, min);
367 Test(1, true, max, max);
368 }
369
TEST_P(WeightMaskTest10bpp,RandomValues)370 TEST_P(WeightMaskTest10bpp, RandomValues) { Test(1, false, -1, -1); }
371
TEST_P(WeightMaskTest10bpp,DISABLED_Speed)372 TEST_P(WeightMaskTest10bpp, DISABLED_Speed) {
373 Test(kNumSpeedTests, false, -1, -1);
374 }
375
376 INSTANTIATE_TEST_SUITE_P(C, WeightMaskTest10bpp,
377 testing::ValuesIn(weight_mask_test_param));
378 #if LIBGAV1_ENABLE_NEON
379 INSTANTIATE_TEST_SUITE_P(NEON, WeightMaskTest10bpp,
380 testing::ValuesIn(weight_mask_test_param));
381 #endif
382 #if LIBGAV1_ENABLE_SSE4_1
383 INSTANTIATE_TEST_SUITE_P(SSE41, WeightMaskTest10bpp,
384 testing::ValuesIn(weight_mask_test_param));
385 #endif
386 #endif // LIBGAV1_MAX_BITDEPTH >= 10
387
388 } // namespace
389 } // namespace dsp
390 } // namespace libgav1
391