xref: /aosp_15_r20/external/libgav1/src/dsp/super_res_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/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