xref: /aosp_15_r20/external/libgav1/src/dsp/motion_field_projection_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/motion_field_projection.h"
16 
17 #include <algorithm>
18 #include <array>
19 #include <cassert>
20 #include <cmath>
21 #include <cstdint>
22 #include <string>
23 
24 #include "absl/strings/match.h"
25 #include "absl/strings/str_format.h"
26 #include "absl/time/clock.h"
27 #include "absl/time/time.h"
28 #include "gtest/gtest.h"
29 #include "src/dsp/dsp.h"
30 #include "src/utils/array_2d.h"
31 #include "src/utils/common.h"
32 #include "src/utils/constants.h"
33 #include "src/utils/cpu.h"
34 #include "src/utils/reference_info.h"
35 #include "src/utils/types.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 kMotionFieldWidth = 160;
44 constexpr int kMotionFieldHight = 120;
45 
46 // The 'int' parameter is unused but required to allow for instantiations of C,
47 // NEON, etc.
48 class MotionFieldProjectionTest : public testing::TestWithParam<int> {
49  public:
50   MotionFieldProjectionTest() = default;
51   MotionFieldProjectionTest(const MotionFieldProjectionTest&) = delete;
52   MotionFieldProjectionTest& operator=(const MotionFieldProjectionTest&) =
53       delete;
54   ~MotionFieldProjectionTest() override = default;
55 
SetUp()56   void SetUp() override {
57     test_utils::ResetDspTable(8);
58     MotionFieldProjectionInit_C();
59     const testing::TestInfo* const test_info =
60         testing::UnitTest::GetInstance()->current_test_info();
61     const char* const test_case = test_info->test_suite_name();
62     if (absl::StartsWith(test_case, "C/")) {
63     } else if (absl::StartsWith(test_case, "NEON/")) {
64       MotionFieldProjectionInit_NEON();
65     } else if (absl::StartsWith(test_case, "SSE41/")) {
66       if ((GetCpuInfo() & kSSE4_1) == 0) GTEST_SKIP() << "No SSE4.1 support!";
67       MotionFieldProjectionInit_SSE4_1();
68     } else {
69       FAIL() << "Unrecognized architecture prefix in test case name: "
70              << test_case;
71     }
72     const Dsp* const dsp = GetDspTable(8);
73     ASSERT_NE(dsp, nullptr);
74     target_motion_field_projection_kernel_func_ =
75         dsp->motion_field_projection_kernel;
76   }
77 
78   void SetInputData(int motion_field_width, libvpx_test::ACMRandom* rnd);
79   void TestRandomValues(bool speed);
80 
81  private:
82   MotionFieldProjectionKernelFunc target_motion_field_projection_kernel_func_;
83   ReferenceInfo reference_info_;
84   TemporalMotionField motion_field_;
85 };
86 
SetInputData(const int motion_field_width,libvpx_test::ACMRandom * const rnd)87 void MotionFieldProjectionTest::SetInputData(
88     const int motion_field_width, libvpx_test::ACMRandom* const rnd) {
89   ASSERT_TRUE(reference_info_.Reset(kMotionFieldHight, motion_field_width));
90   ASSERT_TRUE(motion_field_.mv.Reset(kMotionFieldHight, motion_field_width,
91                                      /*zero_initialize=*/false));
92   ASSERT_TRUE(motion_field_.reference_offset.Reset(kMotionFieldHight,
93                                                    motion_field_width,
94                                                    /*zero_initialize=*/false));
95   constexpr int order_hint_bits = 6;
96   unsigned int order_hint_shift_bits = Mod32(32 - order_hint_bits);
97   const unsigned int current_frame_order_hint =
98       rnd->Rand8() & ((1 << order_hint_bits) - 1);  // [0, 63]
99   uint8_t reference_frame_order_hint = 0;
100   reference_info_.relative_distance_to[0] = 0;
101   reference_info_.skip_references[kReferenceFrameIntra] = true;
102   reference_info_.projection_divisions[kReferenceFrameIntra] = 0;
103   for (int i = kReferenceFrameLast; i < kNumReferenceFrameTypes; ++i) {
104     reference_frame_order_hint =
105         rnd->Rand8() & ((1 << order_hint_bits) - 1);  // [0, 63]
106     const int relative_distance_to =
107         GetRelativeDistance(current_frame_order_hint,
108                             reference_frame_order_hint, order_hint_shift_bits);
109     reference_info_.relative_distance_to[i] = relative_distance_to;
110     reference_info_.skip_references[i] =
111         relative_distance_to > kMaxFrameDistance || relative_distance_to <= 0;
112     reference_info_.projection_divisions[i] =
113         reference_info_.skip_references[i]
114             ? 0
115             : kProjectionMvDivisionLookup[relative_distance_to];
116   }
117   for (int y = 0; y < kMotionFieldHight; ++y) {
118     for (int x = 0; x < motion_field_width; ++x) {
119       reference_info_.motion_field_reference_frame[y][x] =
120           static_cast<ReferenceFrameType>(rnd->Rand16() &
121                                           kReferenceFrameAlternate);
122       reference_info_.motion_field_mv[y][x].mv[0] = rnd->Rand16Signed() / 512;
123       reference_info_.motion_field_mv[y][x].mv[1] = rnd->Rand16Signed() / 512;
124     }
125   }
126   MotionVector invalid_mv;
127   invalid_mv.mv[0] = kInvalidMvValue;
128   invalid_mv.mv[1] = kInvalidMvValue;
129   MotionVector* const motion_field_mv = &motion_field_.mv[0][0];
130   int8_t* const motion_field_reference_offset =
131       &motion_field_.reference_offset[0][0];
132   std::fill(motion_field_mv, motion_field_mv + motion_field_.mv.size(),
133             invalid_mv);
134   std::fill(
135       motion_field_reference_offset,
136       motion_field_reference_offset + motion_field_.reference_offset.size(),
137       -128);
138 }
139 
TestRandomValues(bool speed)140 void MotionFieldProjectionTest::TestRandomValues(bool speed) {
141   static const char* const kDigestMv[8] = {
142       "87c2a74538f5c015809492ac2e521075", "ba7b4a5d82c6083b13a5b02eb7655ab7",
143       "8c37d96bf1744d5553860bf44a4f60a3", "720aa644f85e48995db9785e87cd02e3",
144       "9289c0c66524bb77a605870d78285f35", "f0326509885c2b2c89feeac53698cd47",
145       "6b9ad1d672dec825cb1803063d35badc", "dfe06c57cc9c70d27246df7fd0afa0b2"};
146   static const char* const kDigestReferenceOffset[8] = {
147       "d8d1384268d7cf5c4514b39c329f94fb", "7f30e79ceb064befbad64a20d206a540",
148       "61e2eb5644edbd3a91b939403edc891e", "7a018f1bf88193e86934241af445dc36",
149       "2d6166bf8bbe1db77baf687ecf71d028", "95fee61f0219e06076d6f0e1073b1a4e",
150       "64d0a63751267bdc573cab761f1fe685", "906a99e0e791dbcb9183c9b68ecc4ea3"};
151   const int num_tests = speed ? 2000 : 1;
152   if (target_motion_field_projection_kernel_func_ == nullptr) return;
153   libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
154   for (int width_idx = 0; width_idx < 8; ++width_idx) {
155     const int motion_field_width = kMotionFieldWidth + width_idx;
156     SetInputData(motion_field_width, &rnd);
157     const int dst_sign = ((rnd.Rand16() & 1) != 0) ? 0 : -1;
158     const int reference_to_current_with_sign =
159         rnd.PseudoUniform(2 * kMaxFrameDistance + 1) - kMaxFrameDistance;
160     assert(std::abs(reference_to_current_with_sign) <= kMaxFrameDistance);
161     // Step of y8 and x8 is at least 16 except the last hop.
162     for (int step = 16; step <= 80; step += 16) {
163       const absl::Time start = absl::Now();
164       for (int k = 0; k < num_tests; ++k) {
165         for (int y8 = 0; y8 < kMotionFieldHight; y8 += step) {
166           const int y8_end = std::min(y8 + step, kMotionFieldHight);
167           for (int x8 = 0; x8 < motion_field_width; x8 += step) {
168             const int x8_end = std::min(x8 + step, motion_field_width);
169             target_motion_field_projection_kernel_func_(
170                 reference_info_, reference_to_current_with_sign, dst_sign, y8,
171                 y8_end, x8, x8_end, &motion_field_);
172           }
173         }
174       }
175       const absl::Duration elapsed_time = absl::Now() - start;
176       test_utils::CheckMd5Digest(
177           "MotionFieldProjectionKernel",
178           absl::StrFormat("(mv) width %d  step %d", motion_field_width, step)
179               .c_str(),
180           kDigestMv[width_idx], motion_field_.mv[0],
181           sizeof(motion_field_.mv[0][0]) * motion_field_.mv.size(),
182           elapsed_time);
183       test_utils::CheckMd5Digest(
184           "MotionFieldProjectionKernel",
185           absl::StrFormat("(ref offset) width %d  step %d", motion_field_width,
186                           step)
187               .c_str(),
188           kDigestReferenceOffset[width_idx], motion_field_.reference_offset[0],
189           sizeof(motion_field_.reference_offset[0][0]) *
190               motion_field_.reference_offset.size(),
191           elapsed_time);
192     }
193   }
194 }
195 
TEST_P(MotionFieldProjectionTest,Correctness)196 TEST_P(MotionFieldProjectionTest, Correctness) { TestRandomValues(false); }
197 
TEST_P(MotionFieldProjectionTest,DISABLED_Speed)198 TEST_P(MotionFieldProjectionTest, DISABLED_Speed) { TestRandomValues(true); }
199 
200 INSTANTIATE_TEST_SUITE_P(C, MotionFieldProjectionTest, testing::Values(0));
201 
202 #if LIBGAV1_ENABLE_NEON
203 INSTANTIATE_TEST_SUITE_P(NEON, MotionFieldProjectionTest, testing::Values(0));
204 #endif
205 
206 #if LIBGAV1_ENABLE_SSE4_1
207 INSTANTIATE_TEST_SUITE_P(SSE41, MotionFieldProjectionTest, testing::Values(0));
208 #endif
209 
210 }  // namespace
211 }  // namespace dsp
212 }  // namespace libgav1
213