blob: a7b2ec825fbe2b158c4888f2f1eca496ddb8066f [file] [log] [blame]
// Copyright 2021 The libgav1 Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/dsp/motion_vector_search.h"
#include <cstdint>
#include <string>
#include "absl/strings/match.h"
#include "absl/strings/str_format.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "gtest/gtest.h"
#include "src/dsp/dsp.h"
#include "src/utils/common.h"
#include "src/utils/constants.h"
#include "src/utils/cpu.h"
#include "src/utils/memory.h"
#include "src/utils/types.h"
#include "tests/third_party/libvpx/acm_random.h"
#include "tests/utils.h"
namespace libgav1 {
namespace dsp {
namespace {
// The 'int' parameter is unused but required to allow for instantiations of C,
// NEON, etc.
class MotionVectorSearchTest : public testing::TestWithParam<int>,
public test_utils::MaxAlignedAllocable {
public:
MotionVectorSearchTest() = default;
MotionVectorSearchTest(const MotionVectorSearchTest&) = delete;
MotionVectorSearchTest& operator=(const MotionVectorSearchTest&) = delete;
~MotionVectorSearchTest() override = default;
void SetUp() override {
test_utils::ResetDspTable(8);
MotionVectorSearchInit_C();
const testing::TestInfo* const test_info =
testing::UnitTest::GetInstance()->current_test_info();
const char* const test_case = test_info->test_suite_name();
if (absl::StartsWith(test_case, "C/")) {
} else if (absl::StartsWith(test_case, "NEON/")) {
MotionVectorSearchInit_NEON();
} else if (absl::StartsWith(test_case, "SSE41/")) {
if ((GetCpuInfo() & kSSE4_1) != 0) {
MotionVectorSearchInit_SSE4_1();
}
} else {
FAIL() << "Unrecognized architecture prefix in test case name: "
<< test_case;
}
const Dsp* const dsp = GetDspTable(8);
ASSERT_NE(dsp, nullptr);
mv_projection_compound_[0] = dsp->mv_projection_compound[0];
mv_projection_compound_[1] = dsp->mv_projection_compound[1];
mv_projection_compound_[2] = dsp->mv_projection_compound[2];
mv_projection_single_[0] = dsp->mv_projection_single[0];
mv_projection_single_[1] = dsp->mv_projection_single[1];
mv_projection_single_[2] = dsp->mv_projection_single[2];
}
void SetInputData(libvpx_test::ACMRandom* rnd);
void TestRandomValues(bool speed);
private:
MvProjectionCompoundFunc mv_projection_compound_[3];
MvProjectionSingleFunc mv_projection_single_[3];
int reference_offsets_[2];
alignas(kMaxAlignment)
MotionVector temporal_mvs_[kMaxTemporalMvCandidatesWithPadding];
int8_t temporal_reference_offsets_[kMaxTemporalMvCandidatesWithPadding];
CompoundMotionVector compound_mv_org_[kMaxTemporalMvCandidates + 1]
[kMaxTemporalMvCandidatesWithPadding];
alignas(kMaxAlignment)
CompoundMotionVector compound_mv_[kMaxTemporalMvCandidates + 1]
[kMaxTemporalMvCandidatesWithPadding];
MotionVector single_mv_org_[kMaxTemporalMvCandidates + 1]
[kMaxTemporalMvCandidatesWithPadding];
alignas(kMaxAlignment)
MotionVector single_mv_[kMaxTemporalMvCandidates + 1]
[kMaxTemporalMvCandidatesWithPadding];
};
void MotionVectorSearchTest::SetInputData(libvpx_test::ACMRandom* const rnd) {
reference_offsets_[0] =
Clip3(rnd->Rand16(), -kMaxFrameDistance, kMaxFrameDistance);
reference_offsets_[1] =
Clip3(rnd->Rand16(), -kMaxFrameDistance, kMaxFrameDistance);
for (int i = 0; i < kMaxTemporalMvCandidatesWithPadding; ++i) {
temporal_reference_offsets_[i] = rnd->RandRange(kMaxFrameDistance);
for (auto& mv : temporal_mvs_[i].mv) {
mv = rnd->Rand16Signed() / 8;
}
}
for (int i = 0; i <= kMaxTemporalMvCandidates; ++i) {
for (int j = 0; j < kMaxTemporalMvCandidatesWithPadding; ++j) {
for (int k = 0; k < 2; ++k) {
single_mv_[i][j].mv[k] = rnd->Rand16Signed();
for (auto& mv : compound_mv_[i][j].mv[k].mv) {
mv = rnd->Rand16Signed();
}
}
compound_mv_org_[i][j] = compound_mv_[i][j];
single_mv_org_[i][j] = single_mv_[i][j];
}
}
}
void MotionVectorSearchTest::TestRandomValues(bool speed) {
static const char* const kDigestCompound[3] = {
"74c055b06c3701b2e50f2c964a6130b9", "cab21dd54f0a1bf6e80b58cdcf1fe0a9",
"e42de30cd84fa4e7b8581a330ed08a8b"};
static const char* const kDigestSingle[3] = {
"265ffbb59d0895183f8e2d90b6652c71", "5068d980c4ce42ed3f11963b8aece6cc",
"7e699d58df3954a38ff11c8e34151e66"};
const int num_tests = speed ? 1000000 : 1;
libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
for (int function_index = 0; function_index < 3; ++function_index) {
SetInputData(&rnd);
if (mv_projection_compound_[function_index] == nullptr) continue;
const absl::Time start = absl::Now();
for (int count = 1; count <= kMaxTemporalMvCandidates; ++count) {
const int total_count = count + (count & 1);
for (int i = 0; i < num_tests; ++i) {
mv_projection_compound_[function_index](
temporal_mvs_, temporal_reference_offsets_, reference_offsets_,
count, compound_mv_[count]);
}
// One more element could be calculated in SIMD implementations.
// Restore the original values if any.
for (int i = count; i < total_count; ++i) {
compound_mv_[count][i] = compound_mv_org_[count][i];
}
}
const absl::Duration elapsed_time = absl::Now() - start;
test_utils::CheckMd5Digest(
"MvProjectionCompound",
absl::StrFormat("function_index %d", function_index).c_str(),
kDigestCompound[function_index], compound_mv_, sizeof(compound_mv_),
elapsed_time);
}
for (int function_index = 0; function_index < 3; ++function_index) {
SetInputData(&rnd);
if (mv_projection_single_[function_index] == nullptr) continue;
const absl::Time start = absl::Now();
for (int count = 1; count <= kMaxTemporalMvCandidates; ++count) {
const int total_count = (count + 3) & ~3;
for (int i = 0; i < num_tests; ++i) {
mv_projection_single_[function_index](
temporal_mvs_, temporal_reference_offsets_, reference_offsets_[0],
count, single_mv_[count]);
}
// Up to three more elements could be calculated in SIMD implementations.
// Restore the original values if any.
for (int i = count; i < total_count; ++i) {
single_mv_[count][i] = single_mv_org_[count][i];
}
}
const absl::Duration elapsed_time = absl::Now() - start;
test_utils::CheckMd5Digest(
"MvProjectionSingle",
absl::StrFormat("function_index %d", function_index).c_str(),
kDigestSingle[function_index], single_mv_, sizeof(single_mv_),
elapsed_time);
}
}
TEST_P(MotionVectorSearchTest, Correctness) { TestRandomValues(false); }
TEST_P(MotionVectorSearchTest, DISABLED_Speed) { TestRandomValues(true); }
INSTANTIATE_TEST_SUITE_P(C, MotionVectorSearchTest, testing::Values(0));
#if LIBGAV1_ENABLE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, MotionVectorSearchTest, testing::Values(0));
#endif
#if LIBGAV1_ENABLE_SSE4_1
INSTANTIATE_TEST_SUITE_P(SSE41, MotionVectorSearchTest, testing::Values(0));
#endif
} // namespace
} // namespace dsp
} // namespace libgav1