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