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/dsp/super_res.h"
16
17 #include <cstdint>
18 #include <cstdio>
19 #include <cstring>
20 #include <string>
21 #include <vector>
22
23 #include "absl/strings/match.h"
24 #include "absl/strings/numbers.h"
25 #include "absl/strings/str_format.h"
26 #include "absl/strings/str_split.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/time/clock.h"
29 #include "absl/time/time.h"
30 #include "gtest/gtest.h"
31 #include "src/dsp/dsp.h"
32 #include "src/utils/common.h"
33 #include "src/utils/constants.h"
34 #include "src/utils/cpu.h"
35 #include "src/utils/memory.h"
36 #include "tests/third_party/libvpx/acm_random.h"
37 #include "tests/utils.h"
38
39 namespace libgav1 {
40 namespace dsp {
41 namespace {
42
43 constexpr int kNumSpeedTests = 5e5;
44
GetDigest8bpp(int id)45 const char* GetDigest8bpp(int id) {
46 static const char* const kDigestSuperRes[] = {
47 "52eb4eac1df0c51599d57696405b69d0", "ccb07cc8295fd1440ff2e3b9199ec4f9",
48 "baef34cca795b95f3d1fd81d609da679", "03f1579c2773c8ba9c867316a22b94a3"};
49 return kDigestSuperRes[id];
50 }
51
52 #if LIBGAV1_MAX_BITDEPTH >= 10
GetDigest10bpp(int id)53 const char* GetDigest10bpp(int id) {
54 static const char* const kDigestSuperRes[] = {
55 "8fd78e05d944aeb11fac278b47ee60ba", "948eaecb70fa5614ce1c1c95e9942dc3",
56 "126cd7727e787e0625ec3f5ce97f8fa0", "85c806c41d40b841764bcb54f6d3a712"};
57 return kDigestSuperRes[id];
58 }
59 #endif // LIBGAV1_MAX_BITDEPTH >= 10
60
61 #if LIBGAV1_MAX_BITDEPTH == 12
GetDigest12bpp(int id)62 const char* GetDigest12bpp(int id) {
63 static const char* const kDigestSuperRes[] = {
64 "9a08983d82df4983700976f18919201b", "6e5edbafcb6c38db37258bf79c00ea32",
65 "f5c57e6d3b518f9585f768ed19b91568", "b5de9b93c8a1a50580e7c7c9456fb615"};
66 return kDigestSuperRes[id];
67 }
68 #endif // LIBGAV1_MAX_BITDEPTH == 12
69
70 struct SuperResTestParam {
SuperResTestParamlibgav1::dsp::__anonf7ce25b40111::SuperResTestParam71 SuperResTestParam(int downscaled_width, int upscaled_width)
72 : downscaled_width(downscaled_width), upscaled_width(upscaled_width) {}
73 int downscaled_width;
74 int upscaled_width;
75 };
76
77 template <int bitdepth, typename Pixel, typename Coefficient>
78 class SuperResTest : public testing::TestWithParam<SuperResTestParam>,
79 public test_utils::MaxAlignedAllocable {
80 public:
81 static_assert(bitdepth >= kBitdepth8 && bitdepth <= LIBGAV1_MAX_BITDEPTH, "");
82 SuperResTest() = default;
SetUp()83 void SetUp() override {
84 test_utils::ResetDspTable(bitdepth);
85 SuperResInit_C();
86 const dsp::Dsp* const dsp = dsp::GetDspTable(bitdepth);
87 ASSERT_NE(dsp, nullptr);
88
89 const testing::TestInfo* const test_info =
90 testing::UnitTest::GetInstance()->current_test_info();
91 const std::vector<std::string> split_test_name =
92 absl::StrSplit(test_info->name(), '/');
93 ASSERT_TRUE(absl::SimpleAtoi(split_test_name[1], &test_id_));
94 const absl::string_view test_case = test_info->test_suite_name();
95 if (absl::StartsWith(test_case, "C/")) {
96 } else if (absl::StartsWith(test_case, "NEON/")) {
97 SuperResInit_NEON();
98 } else if (absl::StartsWith(test_case, "SSE41/")) {
99 if ((GetCpuInfo() & kSSE4_1) == 0) GTEST_SKIP() << "No SSE4.1 support!";
100 SuperResInit_SSE4_1();
101 } else {
102 FAIL() << "Unrecognized architecture prefix in test case name: "
103 << test_case;
104 }
105 super_res_coefficients_ = dsp->super_res_coefficients;
106 func_ = dsp->super_res;
107 }
108
109 void TestComputeSuperRes(int fixed_value, int num_runs);
110
111 private:
112 static constexpr int kHeight = 127;
113 // The maximum width that must be allocated.
114 static constexpr int kUpscaledBufferWidth = 192;
115 // Allow room for the filter taps.
116 static constexpr int kStride =
117 ((kUpscaledBufferWidth + 2 * kSuperResHorizontalBorder + 15) & ~15);
118 const int kDownscaledWidth = GetParam().downscaled_width;
119 const int kUpscaledWidth = GetParam().upscaled_width;
120 int test_id_;
121 SuperResCoefficientsFunc super_res_coefficients_;
122 SuperResFunc func_;
123 Pixel source_buffer_[kHeight][kStride];
124 alignas(kMaxAlignment) Pixel dest_buffer_[kHeight][kStride];
125 alignas(kMaxAlignment) Coefficient
126 superres_coefficients_[kSuperResFilterTaps * kUpscaledBufferWidth];
127 };
128
129 template <int bitdepth, typename Pixel, typename Coefficient>
TestComputeSuperRes(int fixed_value,int num_runs)130 void SuperResTest<bitdepth, Pixel, Coefficient>::TestComputeSuperRes(
131 int fixed_value, int num_runs) {
132 if (func_ == nullptr) return;
133 const int superres_width = kDownscaledWidth << kSuperResScaleBits;
134 const int step = (superres_width + kUpscaledWidth / 2) / kUpscaledWidth;
135 const int error = step * kUpscaledWidth - superres_width;
136 const int initial_subpixel_x =
137 ((-((kUpscaledWidth - kDownscaledWidth) << (kSuperResScaleBits - 1)) +
138 DivideBy2(kUpscaledWidth)) /
139 kUpscaledWidth +
140 (1 << (kSuperResExtraBits - 1)) - error / 2) &
141 kSuperResScaleMask;
142 if (super_res_coefficients_ != nullptr) {
143 super_res_coefficients_(kUpscaledWidth, initial_subpixel_x, step,
144 superres_coefficients_);
145 }
146 memset(dest_buffer_, 0, sizeof(dest_buffer_));
147 if (fixed_value != 0) {
148 SetBlock<Pixel>(kHeight, kStride, fixed_value, source_buffer_[0], kStride);
149 } else {
150 // Random values.
151 libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
152 const int bitdepth_mask = (1 << bitdepth) - 1;
153 for (int y = 0; y < kHeight; ++y) {
154 for (int x = 0; x < kStride; ++x) {
155 source_buffer_[y][x] = rnd.Rand16() & bitdepth_mask;
156 }
157 }
158 }
159 // Offset starting point in the buffer to accommodate line extension.
160 Pixel* src_ptr = source_buffer_[0] + kSuperResHorizontalBorder;
161
162 const absl::Time start = absl::Now();
163 for (int i = 0; i < num_runs; ++i) {
164 func_(superres_coefficients_, src_ptr, kStride, kHeight, kDownscaledWidth,
165 kUpscaledWidth, initial_subpixel_x, step, dest_buffer_, kStride);
166 }
167 const absl::Duration elapsed_time = absl::Now() - start;
168
169 if (fixed_value != 0) {
170 for (int y = 0; y < kHeight; ++y) {
171 for (int x = 0; x < kUpscaledWidth; ++x) {
172 EXPECT_TRUE(dest_buffer_[y][x] == fixed_value)
173 << "At location [" << y << ", " << x
174 << "]\nexpected: " << fixed_value
175 << "\nactual: " << dest_buffer_[y][x];
176 }
177 }
178 } else if (num_runs == 1) {
179 // Random values.
180 if ((kUpscaledWidth & 15) != 0) {
181 // The SIMD functions overwrite up to 15 pixels in each row. Reset them.
182 for (int y = 0; y < kHeight; ++y) {
183 for (int x = kUpscaledWidth; x < Align(kUpscaledWidth, 16); ++x) {
184 dest_buffer_[y][x] = 0;
185 }
186 }
187 }
188 const char* expected_digest = nullptr;
189 switch (bitdepth) {
190 case 8:
191 expected_digest = GetDigest8bpp(test_id_);
192 break;
193 #if LIBGAV1_MAX_BITDEPTH >= 10
194 case 10:
195 expected_digest = GetDigest10bpp(test_id_);
196 break;
197 #endif
198 #if LIBGAV1_MAX_BITDEPTH == 12
199 case 12:
200 expected_digest = GetDigest12bpp(test_id_);
201 break;
202 #endif
203 }
204 ASSERT_NE(expected_digest, nullptr);
205 test_utils::CheckMd5Digest(
206 "SuperRes",
207 absl::StrFormat("width %d, step %d, start %d", kUpscaledWidth, step,
208 initial_subpixel_x)
209 .c_str(),
210 expected_digest, dest_buffer_, sizeof(dest_buffer_), elapsed_time);
211 } else {
212 // Speed test.
213 printf("Mode SuperRes [width %d, step %d, start %d]: %d us\n",
214 kUpscaledWidth, step, initial_subpixel_x,
215 static_cast<int>(absl::ToInt64Microseconds(elapsed_time)));
216 }
217 }
218
219 using SuperResTest8bpp = SuperResTest<8, uint8_t, int8_t>;
220
TEST_P(SuperResTest8bpp,FixedValues)221 TEST_P(SuperResTest8bpp, FixedValues) {
222 TestComputeSuperRes(100, 1);
223 TestComputeSuperRes(255, 1);
224 TestComputeSuperRes(1, 1);
225 }
226
TEST_P(SuperResTest8bpp,RandomValues)227 TEST_P(SuperResTest8bpp, RandomValues) { TestComputeSuperRes(0, 1); }
228
TEST_P(SuperResTest8bpp,DISABLED_Speed)229 TEST_P(SuperResTest8bpp, DISABLED_Speed) {
230 TestComputeSuperRes(0, kNumSpeedTests);
231 }
232
233 const SuperResTestParam kSuperResTestParams[] = {
234 SuperResTestParam(96, 192),
235 SuperResTestParam(171, 192),
236 SuperResTestParam(102, 128),
237 SuperResTestParam(61, 121),
238 };
239
240 INSTANTIATE_TEST_SUITE_P(C, SuperResTest8bpp,
241 testing::ValuesIn(kSuperResTestParams));
242
243 #if LIBGAV1_ENABLE_NEON
244 INSTANTIATE_TEST_SUITE_P(NEON, SuperResTest8bpp,
245 testing::ValuesIn(kSuperResTestParams));
246 #endif
247
248 #if LIBGAV1_ENABLE_SSE4_1
249 INSTANTIATE_TEST_SUITE_P(SSE41, SuperResTest8bpp,
250 testing::ValuesIn(kSuperResTestParams));
251 #endif
252
253 #if LIBGAV1_MAX_BITDEPTH >= 10
254 using SuperResTest10bpp = SuperResTest<10, uint16_t, int16_t>;
255
TEST_P(SuperResTest10bpp,FixedValues)256 TEST_P(SuperResTest10bpp, FixedValues) {
257 TestComputeSuperRes(100, 1);
258 TestComputeSuperRes(511, 1);
259 TestComputeSuperRes(1, 1);
260 }
261
TEST_P(SuperResTest10bpp,RandomValues)262 TEST_P(SuperResTest10bpp, RandomValues) { TestComputeSuperRes(0, 1); }
263
TEST_P(SuperResTest10bpp,DISABLED_Speed)264 TEST_P(SuperResTest10bpp, DISABLED_Speed) {
265 TestComputeSuperRes(0, kNumSpeedTests);
266 }
267
268 INSTANTIATE_TEST_SUITE_P(C, SuperResTest10bpp,
269 testing::ValuesIn(kSuperResTestParams));
270
271 #if LIBGAV1_ENABLE_SSE4_1
272 INSTANTIATE_TEST_SUITE_P(SSE41, SuperResTest10bpp,
273 testing::ValuesIn(kSuperResTestParams));
274 #endif
275
276 #if LIBGAV1_ENABLE_NEON
277 INSTANTIATE_TEST_SUITE_P(NEON, SuperResTest10bpp,
278 testing::ValuesIn(kSuperResTestParams));
279 #endif
280 #endif // LIBGAV1_MAX_BITDEPTH >= 10
281
282 #if LIBGAV1_MAX_BITDEPTH == 12
283 using SuperResTest12bpp = SuperResTest<12, uint16_t, int16_t>;
284
TEST_P(SuperResTest12bpp,FixedValues)285 TEST_P(SuperResTest12bpp, FixedValues) {
286 TestComputeSuperRes(100, 1);
287 TestComputeSuperRes(2047, 1);
288 TestComputeSuperRes(1, 1);
289 }
290
TEST_P(SuperResTest12bpp,RandomValues)291 TEST_P(SuperResTest12bpp, RandomValues) { TestComputeSuperRes(0, 1); }
292
TEST_P(SuperResTest12bpp,DISABLED_Speed)293 TEST_P(SuperResTest12bpp, DISABLED_Speed) {
294 TestComputeSuperRes(0, kNumSpeedTests);
295 }
296
297 INSTANTIATE_TEST_SUITE_P(C, SuperResTest12bpp,
298 testing::ValuesIn(kSuperResTestParams));
299 #endif // LIBGAV1_MAX_BITDEPTH == 12
300
301 } // namespace
302 } // namespace dsp
303 } // namespace libgav1
304