• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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