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_vector_search.h"
16
17 #include <cstdint>
18 #include <string>
19
20 #include "absl/strings/match.h"
21 #include "absl/strings/str_format.h"
22 #include "absl/time/clock.h"
23 #include "absl/time/time.h"
24 #include "gtest/gtest.h"
25 #include "src/dsp/dsp.h"
26 #include "src/utils/common.h"
27 #include "src/utils/constants.h"
28 #include "src/utils/cpu.h"
29 #include "src/utils/memory.h"
30 #include "src/utils/types.h"
31 #include "tests/third_party/libvpx/acm_random.h"
32 #include "tests/utils.h"
33
34 namespace libgav1 {
35 namespace dsp {
36 namespace {
37
38 // The 'int' parameter is unused but required to allow for instantiations of C,
39 // NEON, etc.
40 class MotionVectorSearchTest : public testing::TestWithParam<int>,
41 public test_utils::MaxAlignedAllocable {
42 public:
43 MotionVectorSearchTest() = default;
44 MotionVectorSearchTest(const MotionVectorSearchTest&) = delete;
45 MotionVectorSearchTest& operator=(const MotionVectorSearchTest&) = delete;
46 ~MotionVectorSearchTest() override = default;
47
SetUp()48 void SetUp() override {
49 test_utils::ResetDspTable(8);
50 MotionVectorSearchInit_C();
51 const testing::TestInfo* const test_info =
52 testing::UnitTest::GetInstance()->current_test_info();
53 const char* const test_case = test_info->test_suite_name();
54 if (absl::StartsWith(test_case, "C/")) {
55 } else if (absl::StartsWith(test_case, "NEON/")) {
56 MotionVectorSearchInit_NEON();
57 } else if (absl::StartsWith(test_case, "SSE41/")) {
58 if ((GetCpuInfo() & kSSE4_1) == 0) GTEST_SKIP() << "No SSE4.1 support!";
59 MotionVectorSearchInit_SSE4_1();
60 } else {
61 FAIL() << "Unrecognized architecture prefix in test case name: "
62 << test_case;
63 }
64 const Dsp* const dsp = GetDspTable(8);
65 ASSERT_NE(dsp, nullptr);
66 mv_projection_compound_[0] = dsp->mv_projection_compound[0];
67 mv_projection_compound_[1] = dsp->mv_projection_compound[1];
68 mv_projection_compound_[2] = dsp->mv_projection_compound[2];
69 mv_projection_single_[0] = dsp->mv_projection_single[0];
70 mv_projection_single_[1] = dsp->mv_projection_single[1];
71 mv_projection_single_[2] = dsp->mv_projection_single[2];
72 }
73
74 void SetInputData(libvpx_test::ACMRandom* rnd);
75 void TestRandomValues(bool speed);
76
77 private:
78 MvProjectionCompoundFunc mv_projection_compound_[3];
79 MvProjectionSingleFunc mv_projection_single_[3];
80 int reference_offsets_[2];
81 alignas(kMaxAlignment)
82 MotionVector temporal_mvs_[kMaxTemporalMvCandidatesWithPadding];
83 int8_t temporal_reference_offsets_[kMaxTemporalMvCandidatesWithPadding];
84 CompoundMotionVector compound_mv_org_[kMaxTemporalMvCandidates + 1]
85 [kMaxTemporalMvCandidatesWithPadding];
86 alignas(kMaxAlignment)
87 CompoundMotionVector compound_mv_[kMaxTemporalMvCandidates + 1]
88 [kMaxTemporalMvCandidatesWithPadding];
89 MotionVector single_mv_org_[kMaxTemporalMvCandidates + 1]
90 [kMaxTemporalMvCandidatesWithPadding];
91 alignas(kMaxAlignment)
92 MotionVector single_mv_[kMaxTemporalMvCandidates + 1]
93 [kMaxTemporalMvCandidatesWithPadding];
94 };
95
SetInputData(libvpx_test::ACMRandom * const rnd)96 void MotionVectorSearchTest::SetInputData(libvpx_test::ACMRandom* const rnd) {
97 reference_offsets_[0] =
98 Clip3(rnd->Rand16(), -kMaxFrameDistance, kMaxFrameDistance);
99 reference_offsets_[1] =
100 Clip3(rnd->Rand16(), -kMaxFrameDistance, kMaxFrameDistance);
101 for (int i = 0; i < kMaxTemporalMvCandidatesWithPadding; ++i) {
102 temporal_reference_offsets_[i] = rnd->RandRange(kMaxFrameDistance);
103 for (auto& mv : temporal_mvs_[i].mv) {
104 mv = rnd->Rand16Signed() / 8;
105 }
106 }
107 for (int i = 0; i <= kMaxTemporalMvCandidates; ++i) {
108 for (int j = 0; j < kMaxTemporalMvCandidatesWithPadding; ++j) {
109 for (int k = 0; k < 2; ++k) {
110 single_mv_[i][j].mv[k] = rnd->Rand16Signed();
111 for (auto& mv : compound_mv_[i][j].mv[k].mv) {
112 mv = rnd->Rand16Signed();
113 }
114 }
115 compound_mv_org_[i][j] = compound_mv_[i][j];
116 single_mv_org_[i][j] = single_mv_[i][j];
117 }
118 }
119 }
120
TestRandomValues(bool speed)121 void MotionVectorSearchTest::TestRandomValues(bool speed) {
122 static const char* const kDigestCompound[3] = {
123 "74c055b06c3701b2e50f2c964a6130b9", "cab21dd54f0a1bf6e80b58cdcf1fe0a9",
124 "e42de30cd84fa4e7b8581a330ed08a8b"};
125 static const char* const kDigestSingle[3] = {
126 "265ffbb59d0895183f8e2d90b6652c71", "5068d980c4ce42ed3f11963b8aece6cc",
127 "7e699d58df3954a38ff11c8e34151e66"};
128 const int num_tests = speed ? 1000000 : 1;
129 libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
130 for (int function_index = 0; function_index < 3; ++function_index) {
131 SetInputData(&rnd);
132 if (mv_projection_compound_[function_index] == nullptr) continue;
133 const absl::Time start = absl::Now();
134 for (int count = 1; count <= kMaxTemporalMvCandidates; ++count) {
135 const int total_count = count + (count & 1);
136 for (int i = 0; i < num_tests; ++i) {
137 mv_projection_compound_[function_index](
138 temporal_mvs_, temporal_reference_offsets_, reference_offsets_,
139 count, compound_mv_[count]);
140 }
141 // One more element could be calculated in SIMD implementations.
142 // Restore the original values if any.
143 for (int i = count; i < total_count; ++i) {
144 compound_mv_[count][i] = compound_mv_org_[count][i];
145 }
146 }
147 const absl::Duration elapsed_time = absl::Now() - start;
148 test_utils::CheckMd5Digest(
149 "MvProjectionCompound",
150 absl::StrFormat("function_index %d", function_index).c_str(),
151 kDigestCompound[function_index], compound_mv_, sizeof(compound_mv_),
152 elapsed_time);
153 }
154 for (int function_index = 0; function_index < 3; ++function_index) {
155 SetInputData(&rnd);
156 if (mv_projection_single_[function_index] == nullptr) continue;
157 const absl::Time start = absl::Now();
158 for (int count = 1; count <= kMaxTemporalMvCandidates; ++count) {
159 const int total_count = (count + 3) & ~3;
160 for (int i = 0; i < num_tests; ++i) {
161 mv_projection_single_[function_index](
162 temporal_mvs_, temporal_reference_offsets_, reference_offsets_[0],
163 count, single_mv_[count]);
164 }
165 // Up to three more elements could be calculated in SIMD implementations.
166 // Restore the original values if any.
167 for (int i = count; i < total_count; ++i) {
168 single_mv_[count][i] = single_mv_org_[count][i];
169 }
170 }
171 const absl::Duration elapsed_time = absl::Now() - start;
172 test_utils::CheckMd5Digest(
173 "MvProjectionSingle",
174 absl::StrFormat("function_index %d", function_index).c_str(),
175 kDigestSingle[function_index], single_mv_, sizeof(single_mv_),
176 elapsed_time);
177 }
178 }
179
TEST_P(MotionVectorSearchTest,Correctness)180 TEST_P(MotionVectorSearchTest, Correctness) { TestRandomValues(false); }
181
TEST_P(MotionVectorSearchTest,DISABLED_Speed)182 TEST_P(MotionVectorSearchTest, DISABLED_Speed) { TestRandomValues(true); }
183
184 INSTANTIATE_TEST_SUITE_P(C, MotionVectorSearchTest, testing::Values(0));
185
186 #if LIBGAV1_ENABLE_NEON
187 INSTANTIATE_TEST_SUITE_P(NEON, MotionVectorSearchTest, testing::Values(0));
188 #endif
189
190 #if LIBGAV1_ENABLE_SSE4_1
191 INSTANTIATE_TEST_SUITE_P(SSE41, MotionVectorSearchTest, testing::Values(0));
192 #endif
193
194 } // namespace
195 } // namespace dsp
196 } // namespace libgav1
197