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::__anon2ccbc7c30111::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 SuperResInit_SSE4_1();
100 } else {
101 FAIL() << "Unrecognized architecture prefix in test case name: "
102 << test_case;
103 }
104 super_res_coefficients_ = dsp->super_res_coefficients;
105 func_ = dsp->super_res;
106 }
107
108 void TestComputeSuperRes(int fixed_value, int num_runs);
109
110 private:
111 static constexpr int kHeight = 127;
112 // The maximum width that must be allocated.
113 static constexpr int kUpscaledBufferWidth = 192;
114 // Allow room for the filter taps.
115 static constexpr int kStride =
116 ((kUpscaledBufferWidth + 2 * kSuperResHorizontalBorder + 15) & ~15);
117 const int kDownscaledWidth = GetParam().downscaled_width;
118 const int kUpscaledWidth = GetParam().upscaled_width;
119 int test_id_;
120 SuperResCoefficientsFunc super_res_coefficients_;
121 SuperResFunc func_;
122 Pixel source_buffer_[kHeight][kStride];
123 alignas(kMaxAlignment) Pixel dest_buffer_[kHeight][kStride];
124 alignas(kMaxAlignment) Coefficient
125 superres_coefficients_[kSuperResFilterTaps * kUpscaledBufferWidth];
126 };
127
128 template <int bitdepth, typename Pixel, typename Coefficient>
TestComputeSuperRes(int fixed_value,int num_runs)129 void SuperResTest<bitdepth, Pixel, Coefficient>::TestComputeSuperRes(
130 int fixed_value, int num_runs) {
131 if (func_ == nullptr) return;
132 const int superres_width = kDownscaledWidth << kSuperResScaleBits;
133 const int step = (superres_width + kUpscaledWidth / 2) / kUpscaledWidth;
134 const int error = step * kUpscaledWidth - superres_width;
135 const int initial_subpixel_x =
136 ((-((kUpscaledWidth - kDownscaledWidth) << (kSuperResScaleBits - 1)) +
137 DivideBy2(kUpscaledWidth)) /
138 kUpscaledWidth +
139 (1 << (kSuperResExtraBits - 1)) - error / 2) &
140 kSuperResScaleMask;
141 if (super_res_coefficients_ != nullptr) {
142 super_res_coefficients_(kUpscaledWidth, initial_subpixel_x, step,
143 superres_coefficients_);
144 }
145 memset(dest_buffer_, 0, sizeof(dest_buffer_));
146 if (fixed_value != 0) {
147 SetBlock<Pixel>(kHeight, kStride, fixed_value, source_buffer_[0], kStride);
148 } else {
149 // Random values.
150 libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
151 const int bitdepth_mask = (1 << bitdepth) - 1;
152 for (int y = 0; y < kHeight; ++y) {
153 for (int x = 0; x < kStride; ++x) {
154 source_buffer_[y][x] = rnd.Rand16() & bitdepth_mask;
155 }
156 }
157 }
158 // Offset starting point in the buffer to accommodate line extension.
159 Pixel* src_ptr = source_buffer_[0] + kSuperResHorizontalBorder;
160
161 const absl::Time start = absl::Now();
162 for (int i = 0; i < num_runs; ++i) {
163 func_(superres_coefficients_, src_ptr, kStride, kHeight, kDownscaledWidth,
164 kUpscaledWidth, initial_subpixel_x, step, dest_buffer_, kStride);
165 }
166 const absl::Duration elapsed_time = absl::Now() - start;
167
168 if (fixed_value != 0) {
169 for (int y = 0; y < kHeight; ++y) {
170 for (int x = 0; x < kUpscaledWidth; ++x) {
171 EXPECT_TRUE(dest_buffer_[y][x] == fixed_value)
172 << "At location [" << y << ", " << x
173 << "]\nexpected: " << fixed_value
174 << "\nactual: " << dest_buffer_[y][x];
175 }
176 }
177 } else if (num_runs == 1) {
178 // Random values.
179 if ((kUpscaledWidth & 15) != 0) {
180 // The SIMD functions overwrite up to 15 pixels in each row. Reset them.
181 for (int y = 0; y < kHeight; ++y) {
182 for (int x = kUpscaledWidth; x < Align(kUpscaledWidth, 16); ++x) {
183 dest_buffer_[y][x] = 0;
184 }
185 }
186 }
187 const char* expected_digest = nullptr;
188 switch (bitdepth) {
189 case 8:
190 expected_digest = GetDigest8bpp(test_id_);
191 break;
192 #if LIBGAV1_MAX_BITDEPTH >= 10
193 case 10:
194 expected_digest = GetDigest10bpp(test_id_);
195 break;
196 #endif
197 #if LIBGAV1_MAX_BITDEPTH == 12
198 case 12:
199 expected_digest = GetDigest12bpp(test_id_);
200 break;
201 #endif
202 }
203 ASSERT_NE(expected_digest, nullptr);
204 test_utils::CheckMd5Digest(
205 "SuperRes",
206 absl::StrFormat("width %d, step %d, start %d", kUpscaledWidth, step,
207 initial_subpixel_x)
208 .c_str(),
209 expected_digest, dest_buffer_, sizeof(dest_buffer_), elapsed_time);
210 } else {
211 // Speed test.
212 printf("Mode SuperRes [width %d, step %d, start %d]: %d us\n",
213 kUpscaledWidth, step, initial_subpixel_x,
214 static_cast<int>(absl::ToInt64Microseconds(elapsed_time)));
215 }
216 }
217
218 using SuperResTest8bpp = SuperResTest<8, uint8_t, int8_t>;
219
TEST_P(SuperResTest8bpp,FixedValues)220 TEST_P(SuperResTest8bpp, FixedValues) {
221 TestComputeSuperRes(100, 1);
222 TestComputeSuperRes(255, 1);
223 TestComputeSuperRes(1, 1);
224 }
225
TEST_P(SuperResTest8bpp,RandomValues)226 TEST_P(SuperResTest8bpp, RandomValues) { TestComputeSuperRes(0, 1); }
227
TEST_P(SuperResTest8bpp,DISABLED_Speed)228 TEST_P(SuperResTest8bpp, DISABLED_Speed) {
229 TestComputeSuperRes(0, kNumSpeedTests);
230 }
231
232 const SuperResTestParam kSuperResTestParams[] = {
233 SuperResTestParam(96, 192),
234 SuperResTestParam(171, 192),
235 SuperResTestParam(102, 128),
236 SuperResTestParam(61, 121),
237 };
238
239 INSTANTIATE_TEST_SUITE_P(C, SuperResTest8bpp,
240 testing::ValuesIn(kSuperResTestParams));
241
242 #if LIBGAV1_ENABLE_NEON
243 INSTANTIATE_TEST_SUITE_P(NEON, SuperResTest8bpp,
244 testing::ValuesIn(kSuperResTestParams));
245 #endif
246
247 #if LIBGAV1_ENABLE_SSE4_1
248 INSTANTIATE_TEST_SUITE_P(SSE41, SuperResTest8bpp,
249 testing::ValuesIn(kSuperResTestParams));
250 #endif
251
252 #if LIBGAV1_MAX_BITDEPTH >= 10
253 using SuperResTest10bpp = SuperResTest<10, uint16_t, int16_t>;
254
TEST_P(SuperResTest10bpp,FixedValues)255 TEST_P(SuperResTest10bpp, FixedValues) {
256 TestComputeSuperRes(100, 1);
257 TestComputeSuperRes(511, 1);
258 TestComputeSuperRes(1, 1);
259 }
260
TEST_P(SuperResTest10bpp,RandomValues)261 TEST_P(SuperResTest10bpp, RandomValues) { TestComputeSuperRes(0, 1); }
262
TEST_P(SuperResTest10bpp,DISABLED_Speed)263 TEST_P(SuperResTest10bpp, DISABLED_Speed) {
264 TestComputeSuperRes(0, kNumSpeedTests);
265 }
266
267 INSTANTIATE_TEST_SUITE_P(C, SuperResTest10bpp,
268 testing::ValuesIn(kSuperResTestParams));
269
270 #if LIBGAV1_ENABLE_SSE4_1
271 INSTANTIATE_TEST_SUITE_P(SSE41, SuperResTest10bpp,
272 testing::ValuesIn(kSuperResTestParams));
273 #endif
274
275 #if LIBGAV1_ENABLE_NEON
276 INSTANTIATE_TEST_SUITE_P(NEON, SuperResTest10bpp,
277 testing::ValuesIn(kSuperResTestParams));
278 #endif
279 #endif // LIBGAV1_MAX_BITDEPTH >= 10
280
281 #if LIBGAV1_MAX_BITDEPTH == 12
282 using SuperResTest12bpp = SuperResTest<12, uint16_t, int16_t>;
283
TEST_P(SuperResTest12bpp,FixedValues)284 TEST_P(SuperResTest12bpp, FixedValues) {
285 TestComputeSuperRes(100, 1);
286 TestComputeSuperRes(2047, 1);
287 TestComputeSuperRes(1, 1);
288 }
289
TEST_P(SuperResTest12bpp,RandomValues)290 TEST_P(SuperResTest12bpp, RandomValues) { TestComputeSuperRes(0, 1); }
291
TEST_P(SuperResTest12bpp,DISABLED_Speed)292 TEST_P(SuperResTest12bpp, DISABLED_Speed) {
293 TestComputeSuperRes(0, kNumSpeedTests);
294 }
295
296 INSTANTIATE_TEST_SUITE_P(C, SuperResTest12bpp,
297 testing::ValuesIn(kSuperResTestParams));
298 #endif // LIBGAV1_MAX_BITDEPTH == 12
299
300 } // namespace
301 } // namespace dsp
302 } // namespace libgav1
303