blob: 8d4fa63db56bc15c63db4a7de130e1d69dba5635 [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/intrapred_directional.h"
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <ostream>
#include "absl/strings/match.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "gtest/gtest.h"
#include "src/dsp/constants.h"
#include "src/dsp/dsp.h"
#include "src/utils/common.h"
#include "src/utils/compiler_attributes.h"
#include "src/utils/constants.h"
#include "src/utils/cpu.h"
#include "src/utils/memory.h"
#include "tests/block_utils.h"
#include "tests/third_party/libvpx/acm_random.h"
#include "tests/utils.h"
namespace libgav1 {
namespace dsp {
namespace {
constexpr int kMaxBlockSize = 64;
constexpr int kTotalPixels = kMaxBlockSize * kMaxBlockSize;
constexpr int kNumDirectionalIntraPredictors = 3;
constexpr int kBaseAngles[] = {45, 67, 90, 113, 135, 157, 180, 203};
const char* const kDirectionalPredNames[kNumDirectionalIntraPredictors] = {
"kDirectionalIntraPredictorZone1", "kDirectionalIntraPredictorZone2",
"kDirectionalIntraPredictorZone3"};
int16_t GetDirectionalIntraPredictorDerivative(const int angle) {
EXPECT_GE(angle, 3);
EXPECT_LE(angle, 87);
return kDirectionalIntraPredictorDerivative[DivideBy2(angle) - 1];
}
template <int bitdepth, typename Pixel>
class IntraPredTestBase : public testing::TestWithParam<TransformSize>,
public test_utils::MaxAlignedAllocable {
public:
static_assert(bitdepth >= kBitdepth8 && bitdepth <= LIBGAV1_MAX_BITDEPTH, "");
IntraPredTestBase() {
switch (tx_size_) {
case kNumTransformSizes:
EXPECT_NE(tx_size_, kNumTransformSizes);
break;
default:
block_width_ = kTransformWidth[tx_size_];
block_height_ = kTransformHeight[tx_size_];
break;
}
}
IntraPredTestBase(const IntraPredTestBase&) = delete;
IntraPredTestBase& operator=(const IntraPredTestBase&) = delete;
~IntraPredTestBase() override = default;
protected:
struct IntraPredMem {
void Reset(libvpx_test::ACMRandom* rnd) {
ASSERT_NE(rnd, nullptr);
#if LIBGAV1_MSAN
// Match the behavior of Tile::IntraPrediction to prevent warnings due to
// assembly code (safely) overreading to fill a register.
memset(left_mem, 0, sizeof(left_mem));
memset(top_mem, 0, sizeof(top_mem));
#endif // LIBGAV1_MSAN
Pixel* const left = left_mem + 16;
Pixel* const top = top_mem + 16;
const int mask = (1 << bitdepth) - 1;
for (auto& r : ref_src) r = rnd->Rand16() & mask;
for (int i = 0; i < kMaxBlockSize; ++i) left[i] = rnd->Rand16() & mask;
for (int i = -1; i < kMaxBlockSize; ++i) top[i] = rnd->Rand16() & mask;
// Some directional predictors require top-right, bottom-left.
for (int i = kMaxBlockSize; i < 2 * kMaxBlockSize; ++i) {
left[i] = rnd->Rand16() & mask;
top[i] = rnd->Rand16() & mask;
}
// TODO(jzern): reorder this and regenerate the digests after switching
// random number generators.
// Upsampling in the directional predictors extends left/top[-1] to [-2].
left[-1] = rnd->Rand16() & mask;
left[-2] = rnd->Rand16() & mask;
top[-2] = rnd->Rand16() & mask;
memset(left_mem, 0, sizeof(left_mem[0]) * 14);
memset(top_mem, 0, sizeof(top_mem[0]) * 14);
memset(top_mem + kMaxBlockSize * 2 + 16, 0,
sizeof(top_mem[0]) * kTopMemPadding);
}
// Set ref_src, top-left, top and left to |pixel|.
void Set(const Pixel pixel) {
#if LIBGAV1_MSAN
// Match the behavior of Tile::IntraPrediction to prevent warnings due to
// assembly code (safely) overreading to fill a register.
memset(left_mem, 0, sizeof(left_mem));
memset(top_mem, 0, sizeof(top_mem));
#endif // LIBGAV1_MSAN
Pixel* const left = left_mem + 16;
Pixel* const top = top_mem + 16;
for (auto& r : ref_src) r = pixel;
// Upsampling in the directional predictors extends left/top[-1] to [-2].
for (int i = -2; i < 2 * kMaxBlockSize; ++i) {
left[i] = top[i] = pixel;
}
}
// DirectionalZone1_Large() overreads up to 7 pixels in |top_mem|.
static constexpr int kTopMemPadding = 7;
alignas(kMaxAlignment) Pixel dst[kTotalPixels];
alignas(kMaxAlignment) Pixel ref_src[kTotalPixels];
alignas(kMaxAlignment) Pixel left_mem[kMaxBlockSize * 2 + 16];
alignas(
kMaxAlignment) Pixel top_mem[kMaxBlockSize * 2 + 16 + kTopMemPadding];
};
void SetUp() override { test_utils::ResetDspTable(bitdepth); }
const TransformSize tx_size_ = GetParam();
int block_width_;
int block_height_;
IntraPredMem intra_pred_mem_;
};
//------------------------------------------------------------------------------
// DirectionalIntraPredTest
template <int bitdepth, typename Pixel>
class DirectionalIntraPredTest : public IntraPredTestBase<bitdepth, Pixel> {
public:
static_assert(bitdepth >= kBitdepth8 && bitdepth <= LIBGAV1_MAX_BITDEPTH, "");
DirectionalIntraPredTest() = default;
DirectionalIntraPredTest(const DirectionalIntraPredTest&) = delete;
DirectionalIntraPredTest& operator=(const DirectionalIntraPredTest&) = delete;
~DirectionalIntraPredTest() override = default;
protected:
using IntraPredTestBase<bitdepth, Pixel>::tx_size_;
using IntraPredTestBase<bitdepth, Pixel>::block_width_;
using IntraPredTestBase<bitdepth, Pixel>::block_height_;
using IntraPredTestBase<bitdepth, Pixel>::intra_pred_mem_;
enum Zone { kZone1, kZone2, kZone3, kNumZones };
enum { kAngleDeltaStart = -9, kAngleDeltaStop = 9, kAngleDeltaStep = 3 };
void SetUp() override {
IntraPredTestBase<bitdepth, Pixel>::SetUp();
IntraPredDirectionalInit_C();
const Dsp* const dsp = GetDspTable(bitdepth);
ASSERT_NE(dsp, nullptr);
base_directional_intra_pred_zone1_ = dsp->directional_intra_predictor_zone1;
base_directional_intra_pred_zone2_ = dsp->directional_intra_predictor_zone2;
base_directional_intra_pred_zone3_ = dsp->directional_intra_predictor_zone3;
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/")) {
base_directional_intra_pred_zone1_ = nullptr;
base_directional_intra_pred_zone2_ = nullptr;
base_directional_intra_pred_zone3_ = nullptr;
} else if (absl::StartsWith(test_case, "NEON/")) {
IntraPredDirectionalInit_NEON();
} else if (absl::StartsWith(test_case, "SSE41/")) {
if ((GetCpuInfo() & kSSE4_1) != 0) {
IntraPredDirectionalInit_SSE4_1();
}
} else {
FAIL() << "Unrecognized architecture prefix in test case name: "
<< test_case;
}
cur_directional_intra_pred_zone1_ = dsp->directional_intra_predictor_zone1;
cur_directional_intra_pred_zone2_ = dsp->directional_intra_predictor_zone2;
cur_directional_intra_pred_zone3_ = dsp->directional_intra_predictor_zone3;
// Skip functions that haven't been specialized for this particular
// architecture.
if (cur_directional_intra_pred_zone1_ ==
base_directional_intra_pred_zone1_) {
cur_directional_intra_pred_zone1_ = nullptr;
}
if (cur_directional_intra_pred_zone2_ ==
base_directional_intra_pred_zone2_) {
cur_directional_intra_pred_zone2_ = nullptr;
}
if (cur_directional_intra_pred_zone3_ ==
base_directional_intra_pred_zone3_) {
cur_directional_intra_pred_zone3_ = nullptr;
}
}
bool IsEdgeUpsampled(int delta, const int filter_type) const {
delta = std::abs(delta);
if (delta == 0 || delta >= 40) return false;
const int block_wh = block_width_ + block_height_;
return (filter_type == 1) ? block_wh <= 8 : block_wh <= 16;
}
// Returns the minimum and maximum (exclusive) range of angles that the
// predictor should be applied to.
void GetZoneAngleRange(const Zone zone, int* const min_angle,
int* const max_angle) const {
ASSERT_NE(min_angle, nullptr);
ASSERT_NE(max_angle, nullptr);
switch (zone) {
// The overall minimum angle comes from mode D45_PRED, yielding:
// min_angle = 45-(MAX_ANGLE_DELTA*ANGLE_STEP) = 36
// The overall maximum angle comes from mode D203_PRED, yielding:
// max_angle = 203+(MAX_ANGLE_DELTA*ANGLE_STEP) = 212
// The angles 180 and 90 are not permitted because they correspond to
// V_PRED and H_PRED, which are handled in distinct functions.
case kZone1:
*min_angle = 36;
*max_angle = 87;
break;
case kZone2:
*min_angle = 93;
*max_angle = 177;
break;
case kZone3:
*min_angle = 183;
*max_angle = 212;
break;
case kNumZones:
FAIL() << "Invalid zone value: " << zone;
break;
}
}
// These tests modify intra_pred_mem_.
void TestSpeed(const char* const digests[kNumDirectionalIntraPredictors],
Zone zone, int num_runs);
void TestSaturatedValues();
void TestRandomValues();
DirectionalIntraPredictorZone1Func base_directional_intra_pred_zone1_;
DirectionalIntraPredictorZone2Func base_directional_intra_pred_zone2_;
DirectionalIntraPredictorZone3Func base_directional_intra_pred_zone3_;
DirectionalIntraPredictorZone1Func cur_directional_intra_pred_zone1_;
DirectionalIntraPredictorZone2Func cur_directional_intra_pred_zone2_;
DirectionalIntraPredictorZone3Func cur_directional_intra_pred_zone3_;
};
template <int bitdepth, typename Pixel>
void DirectionalIntraPredTest<bitdepth, Pixel>::TestSpeed(
const char* const digests[kNumDirectionalIntraPredictors], const Zone zone,
const int num_runs) {
switch (zone) {
case kZone1:
if (cur_directional_intra_pred_zone1_ == nullptr) return;
break;
case kZone2:
if (cur_directional_intra_pred_zone2_ == nullptr) return;
break;
case kZone3:
if (cur_directional_intra_pred_zone3_ == nullptr) return;
break;
case kNumZones:
FAIL() << "Invalid zone value: " << zone;
break;
}
ASSERT_NE(digests, nullptr);
const Pixel* const left = intra_pred_mem_.left_mem + 16;
const Pixel* const top = intra_pred_mem_.top_mem + 16;
libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
intra_pred_mem_.Reset(&rnd);
// Allocate separate blocks for each angle + filter + upsampled combination.
// Add a 1 pixel right border to test for overwrites.
static constexpr int kMaxZoneAngles = 27; // zone 2
static constexpr int kMaxFilterTypes = 2;
static constexpr int kBlockBorder = 1;
static constexpr int kBorderSize =
kBlockBorder * kMaxZoneAngles * kMaxFilterTypes;
const int ref_stride =
kMaxZoneAngles * kMaxFilterTypes * block_width_ + kBorderSize;
const size_t ref_alloc_size = sizeof(Pixel) * ref_stride * block_height_;
using AlignedPtr = std::unique_ptr<Pixel[], decltype(&AlignedFree)>;
AlignedPtr ref_src(static_cast<Pixel*>(AlignedAlloc(16, ref_alloc_size)),
&AlignedFree);
AlignedPtr dest(static_cast<Pixel*>(AlignedAlloc(16, ref_alloc_size)),
&AlignedFree);
ASSERT_NE(ref_src, nullptr);
ASSERT_NE(dest, nullptr);
const int mask = (1 << bitdepth) - 1;
for (size_t i = 0; i < ref_alloc_size / sizeof(ref_src[0]); ++i) {
ref_src[i] = rnd.Rand16() & mask;
}
int min_angle = 0, max_angle = 0;
ASSERT_NO_FATAL_FAILURE(GetZoneAngleRange(zone, &min_angle, &max_angle));
absl::Duration elapsed_time;
for (int run = 0; run < num_runs; ++run) {
Pixel* dst = dest.get();
memcpy(dst, ref_src.get(), ref_alloc_size);
for (const auto& base_angle : kBaseAngles) {
for (int filter_type = 0; filter_type <= 1; ++filter_type) {
for (int angle_delta = kAngleDeltaStart; angle_delta <= kAngleDeltaStop;
angle_delta += kAngleDeltaStep) {
const int predictor_angle = base_angle + angle_delta;
if (predictor_angle < min_angle || predictor_angle > max_angle) {
continue;
}
ASSERT_GT(predictor_angle, 0) << "base_angle: " << base_angle
<< " angle_delta: " << angle_delta;
const bool upsampled_left =
IsEdgeUpsampled(predictor_angle - 180, filter_type);
const bool upsampled_top =
IsEdgeUpsampled(predictor_angle - 90, filter_type);
const ptrdiff_t stride = ref_stride * sizeof(ref_src[0]);
if (predictor_angle < 90) {
ASSERT_EQ(zone, kZone1);
const int xstep =
GetDirectionalIntraPredictorDerivative(predictor_angle);
const absl::Time start = absl::Now();
cur_directional_intra_pred_zone1_(dst, stride, top, block_width_,
block_height_, xstep,
upsampled_top);
elapsed_time += absl::Now() - start;
} else if (predictor_angle < 180) {
ASSERT_EQ(zone, kZone2);
const int xstep =
GetDirectionalIntraPredictorDerivative(180 - predictor_angle);
const int ystep =
GetDirectionalIntraPredictorDerivative(predictor_angle - 90);
const absl::Time start = absl::Now();
cur_directional_intra_pred_zone2_(
dst, stride, top, left, block_width_, block_height_, xstep,
ystep, upsampled_top, upsampled_left);
elapsed_time += absl::Now() - start;
} else {
ASSERT_EQ(zone, kZone3);
ASSERT_LT(predictor_angle, 270);
const int ystep =
GetDirectionalIntraPredictorDerivative(270 - predictor_angle);
const absl::Time start = absl::Now();
cur_directional_intra_pred_zone3_(dst, stride, left, block_width_,
block_height_, ystep,
upsampled_left);
elapsed_time += absl::Now() - start;
}
dst += block_width_ + kBlockBorder;
}
}
}
}
test_utils::CheckMd5Digest(ToString(tx_size_), kDirectionalPredNames[zone],
digests[zone], dest.get(), ref_alloc_size,
elapsed_time);
}
template <int bitdepth, typename Pixel>
void DirectionalIntraPredTest<bitdepth, Pixel>::TestSaturatedValues() {
const Pixel* const left = intra_pred_mem_.left_mem + 16;
const Pixel* const top = intra_pred_mem_.top_mem + 16;
const auto kMaxPixel = static_cast<Pixel>((1 << bitdepth) - 1);
intra_pred_mem_.Set(kMaxPixel);
for (int i = kZone1; i < kNumZones; ++i) {
switch (i) {
case kZone1:
if (cur_directional_intra_pred_zone1_ == nullptr) continue;
break;
case kZone2:
if (cur_directional_intra_pred_zone2_ == nullptr) continue;
break;
case kZone3:
if (cur_directional_intra_pred_zone3_ == nullptr) continue;
break;
case kNumZones:
FAIL() << "Invalid zone value: " << i;
break;
}
int min_angle = 0, max_angle = 0;
ASSERT_NO_FATAL_FAILURE(
GetZoneAngleRange(static_cast<Zone>(i), &min_angle, &max_angle));
for (const auto& base_angle : kBaseAngles) {
for (int filter_type = 0; filter_type <= 1; ++filter_type) {
for (int angle_delta = kAngleDeltaStart; angle_delta <= kAngleDeltaStop;
angle_delta += kAngleDeltaStep) {
const int predictor_angle = base_angle + angle_delta;
if (predictor_angle <= min_angle || predictor_angle >= max_angle) {
continue;
}
ASSERT_GT(predictor_angle, 0) << "base_angle: " << base_angle
<< " angle_delta: " << angle_delta;
memcpy(intra_pred_mem_.dst, intra_pred_mem_.ref_src,
sizeof(intra_pred_mem_.dst));
const bool upsampled_left =
IsEdgeUpsampled(predictor_angle - 180, filter_type);
const bool upsampled_top =
IsEdgeUpsampled(predictor_angle - 90, filter_type);
const ptrdiff_t stride = kMaxBlockSize * sizeof(Pixel);
if (predictor_angle < 90) {
const int xstep =
GetDirectionalIntraPredictorDerivative(predictor_angle);
cur_directional_intra_pred_zone1_(intra_pred_mem_.dst, stride, top,
block_width_, block_height_,
xstep, upsampled_top);
} else if (predictor_angle < 180) {
const int xstep =
GetDirectionalIntraPredictorDerivative(180 - predictor_angle);
const int ystep =
GetDirectionalIntraPredictorDerivative(predictor_angle - 90);
cur_directional_intra_pred_zone2_(
intra_pred_mem_.dst, stride, top, left, block_width_,
block_height_, xstep, ystep, upsampled_top, upsampled_left);
} else {
ASSERT_LT(predictor_angle, 270);
const int ystep =
GetDirectionalIntraPredictorDerivative(270 - predictor_angle);
cur_directional_intra_pred_zone3_(intra_pred_mem_.dst, stride, left,
block_width_, block_height_,
ystep, upsampled_left);
}
if (!test_utils::CompareBlocks(
intra_pred_mem_.dst, intra_pred_mem_.ref_src, block_width_,
block_height_, kMaxBlockSize, kMaxBlockSize, true)) {
ADD_FAILURE() << "Expected " << kDirectionalPredNames[i]
<< " (angle: " << predictor_angle
<< " filter type: " << filter_type
<< ") to produce a block containing '"
<< static_cast<int>(kMaxPixel) << "'";
return;
}
}
}
}
}
}
template <int bitdepth, typename Pixel>
void DirectionalIntraPredTest<bitdepth, Pixel>::TestRandomValues() {
const Pixel* const left = intra_pred_mem_.left_mem + 16;
const Pixel* const top = intra_pred_mem_.top_mem + 16;
// Use an alternate seed to differentiate this test from TestSpeed().
libvpx_test::ACMRandom rnd(test_utils::kAlternateDeterministicSeed);
for (int i = kZone1; i < kNumZones; ++i) {
// Only run when there is a reference version (base) and a different
// optimized version (cur).
switch (i) {
case kZone1:
if (base_directional_intra_pred_zone1_ == nullptr ||
cur_directional_intra_pred_zone1_ == nullptr) {
continue;
}
break;
case kZone2:
if (base_directional_intra_pred_zone2_ == nullptr ||
cur_directional_intra_pred_zone2_ == nullptr) {
continue;
}
break;
case kZone3:
if (base_directional_intra_pred_zone3_ == nullptr ||
cur_directional_intra_pred_zone3_ == nullptr) {
continue;
}
break;
case kNumZones:
FAIL() << "Invalid zone value: " << i;
break;
}
int min_angle = 0, max_angle = 0;
ASSERT_NO_FATAL_FAILURE(
GetZoneAngleRange(static_cast<Zone>(i), &min_angle, &max_angle));
for (const auto& base_angle : kBaseAngles) {
for (int n = 0; n < 1000; ++n) {
for (int filter_type = 0; filter_type <= 1; ++filter_type) {
for (int angle_delta = kAngleDeltaStart;
angle_delta <= kAngleDeltaStop; angle_delta += kAngleDeltaStep) {
const int predictor_angle = base_angle + angle_delta;
if (predictor_angle <= min_angle || predictor_angle >= max_angle) {
continue;
}
ASSERT_GT(predictor_angle, 0) << "base_angle: " << base_angle
<< " angle_delta: " << angle_delta;
intra_pred_mem_.Reset(&rnd);
memcpy(intra_pred_mem_.dst, intra_pred_mem_.ref_src,
sizeof(intra_pred_mem_.dst));
const bool upsampled_left =
IsEdgeUpsampled(predictor_angle - 180, filter_type);
const bool upsampled_top =
IsEdgeUpsampled(predictor_angle - 90, filter_type);
const ptrdiff_t stride = kMaxBlockSize * sizeof(Pixel);
if (predictor_angle < 90) {
const int xstep =
GetDirectionalIntraPredictorDerivative(predictor_angle);
base_directional_intra_pred_zone1_(
intra_pred_mem_.ref_src, stride, top, block_width_,
block_height_, xstep, upsampled_top);
cur_directional_intra_pred_zone1_(
intra_pred_mem_.dst, stride, top, block_width_, block_height_,
xstep, upsampled_top);
} else if (predictor_angle < 180) {
const int xstep =
GetDirectionalIntraPredictorDerivative(180 - predictor_angle);
const int ystep =
GetDirectionalIntraPredictorDerivative(predictor_angle - 90);
base_directional_intra_pred_zone2_(
intra_pred_mem_.ref_src, stride, top, left, block_width_,
block_height_, xstep, ystep, upsampled_top, upsampled_left);
cur_directional_intra_pred_zone2_(
intra_pred_mem_.dst, stride, top, left, block_width_,
block_height_, xstep, ystep, upsampled_top, upsampled_left);
} else {
ASSERT_LT(predictor_angle, 270);
const int ystep =
GetDirectionalIntraPredictorDerivative(270 - predictor_angle);
base_directional_intra_pred_zone3_(
intra_pred_mem_.ref_src, stride, left, block_width_,
block_height_, ystep, upsampled_left);
cur_directional_intra_pred_zone3_(
intra_pred_mem_.dst, stride, left, block_width_,
block_height_, ystep, upsampled_left);
}
if (!test_utils::CompareBlocks(
intra_pred_mem_.dst, intra_pred_mem_.ref_src, block_width_,
block_height_, kMaxBlockSize, kMaxBlockSize, true)) {
ADD_FAILURE() << "Result from optimized version of "
<< kDirectionalPredNames[i]
<< " differs from reference at angle "
<< predictor_angle << " with filter type "
<< filter_type << " in iteration #" << n;
return;
}
}
}
}
}
}
}
using DirectionalIntraPredTest8bpp = DirectionalIntraPredTest<8, uint8_t>;
const char* const* GetDirectionalIntraPredDigests8bpp(TransformSize tx_size) {
static const char* const kDigests4x4[kNumDirectionalIntraPredictors] = {
"9cfc1da729ad08682e165826c29b280b",
"bb73539c7afbda7bddd2184723b932d6",
"9d2882800ffe948196e984a26a2da72c",
};
static const char* const kDigests4x8[kNumDirectionalIntraPredictors] = {
"090efe6f83cc6fa301f65d3bbd5c38d2",
"d0fba4cdfb90f8bd293a94cae9db1a15",
"f7ad0eeab4389d0baa485d30fec87617",
};
static const char* const kDigests4x16[kNumDirectionalIntraPredictors] = {
"1d32b33c75fe85248c48cdc8caa78d84",
"7000e18159443d366129a6cc6ef8fcee",
"06c02fac5f8575f687abb3f634eb0b4c",
};
static const char* const kDigests8x4[kNumDirectionalIntraPredictors] = {
"1b591799685bc135982114b731293f78",
"5cd9099acb9f7b2618dafa6712666580",
"d023883efede88f99c19d006044d9fa1",
};
static const char* const kDigests8x8[kNumDirectionalIntraPredictors] = {
"f1e46ecf62a2516852f30c5025adb7ea",
"864442a209c16998065af28d8cdd839a",
"411a6e554868982af577de69e53f12e8",
};
static const char* const kDigests8x16[kNumDirectionalIntraPredictors] = {
"89278302be913a85cfb06feaea339459",
"6c42f1a9493490cd4529fd40729cec3c",
"2516b5e1c681e5dcb1acedd5f3d41106",
};
static const char* const kDigests8x32[kNumDirectionalIntraPredictors] = {
"aea7078f3eeaa8afbfe6c959c9e676f1",
"cad30babf12729dda5010362223ba65c",
"ff384ebdc832007775af418a2aae1463",
};
static const char* const kDigests16x4[kNumDirectionalIntraPredictors] = {
"964a821c313c831e12f4d32e616c0b55",
"adf6dad3a84ab4d16c16eea218bec57a",
"a54fa008d43895e523474686c48a81c2",
};
static const char* const kDigests16x8[kNumDirectionalIntraPredictors] = {
"fe2851b4e4f9fcf924cf17d50415a4c0",
"50a0e279c481437ff315d08eb904c733",
"0682065c8fb6cbf9be4949316c87c9e5",
};
static const char* const kDigests16x16[kNumDirectionalIntraPredictors] = {
"ef15503b1943642e7a0bace1616c0e11",
"bf1a4d3f855f1072a902a88ec6ce0350",
"7e87a03e29cd7fd843fd71b729a18f3f",
};
static const char* const kDigests16x32[kNumDirectionalIntraPredictors] = {
"f7b636615d2e5bf289b5db452a6f188d",
"e95858c532c10d00b0ce7a02a02121dd",
"34a18ccf58ef490f32268e85ce8c7de4",
};
static const char* const kDigests16x64[kNumDirectionalIntraPredictors] = {
"b250099986c2fab9670748598058846b",
"f25d80af4da862a9b6b72979f1e17cb4",
"5347dc7bc346733b4887f6c8ad5e0898",
};
static const char* const kDigests32x8[kNumDirectionalIntraPredictors] = {
"72e4c9f8af043b1cb1263490351818ab",
"1fc010d2df011b9e4e3d0957107c78df",
"f4cbfa3ca941ef08b972a68d7e7bafc4",
};
static const char* const kDigests32x16[kNumDirectionalIntraPredictors] = {
"37e5a1aaf7549d2bce08eece9d20f0f6",
"6a2794025d0aca414ab17baa3cf8251a",
"63dd37a6efdc91eeefef166c99ce2db1",
};
static const char* const kDigests32x32[kNumDirectionalIntraPredictors] = {
"198aabc958992eb49cceab97d1acb43e",
"aee88b6c8bacfcf38799fe338e6c66e7",
"01e8f8f96696636f6d79d33951907a16",
};
static const char* const kDigests32x64[kNumDirectionalIntraPredictors] = {
"0611390202c4f90f7add7aec763ded58",
"960240c7ceda2ccfac7c90b71460578a",
"7e7d97594aab8ad56e8c01c340335607",
};
static const char* const kDigests64x16[kNumDirectionalIntraPredictors] = {
"7e1f567e7fc510757f2d89d638bc826f",
"c929d687352ce40a58670be2ce3c8c90",
"f6881e6a9ba3c3d3d730b425732656b1",
};
static const char* const kDigests64x32[kNumDirectionalIntraPredictors] = {
"27b4c2a7081d4139f22003ba8b6dfdf2",
"301e82740866b9274108a04c872fa848",
"98d3aa4fef838f4abf00dac33806659f",
};
static const char* const kDigests64x64[kNumDirectionalIntraPredictors] = {
"b31816db8fade3accfd975b21aa264c7",
"2adce01a03b9452633d5830e1a9b4e23",
"7b988fadba8b07c36e88d7be6b270494",
};
switch (tx_size) {
case kTransformSize4x4:
return kDigests4x4;
case kTransformSize4x8:
return kDigests4x8;
case kTransformSize4x16:
return kDigests4x16;
case kTransformSize8x4:
return kDigests8x4;
case kTransformSize8x8:
return kDigests8x8;
case kTransformSize8x16:
return kDigests8x16;
case kTransformSize8x32:
return kDigests8x32;
case kTransformSize16x4:
return kDigests16x4;
case kTransformSize16x8:
return kDigests16x8;
case kTransformSize16x16:
return kDigests16x16;
case kTransformSize16x32:
return kDigests16x32;
case kTransformSize16x64:
return kDigests16x64;
case kTransformSize32x8:
return kDigests32x8;
case kTransformSize32x16:
return kDigests32x16;
case kTransformSize32x32:
return kDigests32x32;
case kTransformSize32x64:
return kDigests32x64;
case kTransformSize64x16:
return kDigests64x16;
case kTransformSize64x32:
return kDigests64x32;
case kTransformSize64x64:
return kDigests64x64;
default:
ADD_FAILURE() << "Unknown transform size: " << tx_size;
return nullptr;
}
}
TEST_P(DirectionalIntraPredTest8bpp, DISABLED_Speed) {
#if LIBGAV1_ENABLE_NEON
const auto num_runs = static_cast<int>(2e5 / (block_width_ * block_height_));
#else
const int num_runs = static_cast<int>(4e7 / (block_width_ * block_height_));
#endif
for (int i = kZone1; i < kNumZones; ++i) {
TestSpeed(GetDirectionalIntraPredDigests8bpp(tx_size_),
static_cast<Zone>(i), num_runs);
}
}
TEST_P(DirectionalIntraPredTest8bpp, FixedInput) {
for (int i = kZone1; i < kNumZones; ++i) {
TestSpeed(GetDirectionalIntraPredDigests8bpp(tx_size_),
static_cast<Zone>(i), 1);
}
}
TEST_P(DirectionalIntraPredTest8bpp, Overflow) { TestSaturatedValues(); }
TEST_P(DirectionalIntraPredTest8bpp, Random) { TestRandomValues(); }
//------------------------------------------------------------------------------
#if LIBGAV1_MAX_BITDEPTH >= 10
using DirectionalIntraPredTest10bpp = DirectionalIntraPredTest<10, uint16_t>;
const char* const* GetDirectionalIntraPredDigests10bpp(TransformSize tx_size) {
static const char* const kDigests4x4[kNumDirectionalIntraPredictors] = {
"a683f4d7ccd978737615f61ecb4d638d",
"90c94374eaf7e9501f197863937b8639",
"0d3969cd081523ac6a906eecc7980c43",
};
static const char* const kDigests4x8[kNumDirectionalIntraPredictors] = {
"c3ffa2979b325644e4a56c882fe27347",
"1f61f5ee413a9a3b8d1d93869ec2aee0",
"4795ea944779ec4a783408769394d874",
};
static const char* const kDigests4x16[kNumDirectionalIntraPredictors] = {
"45c3282c9aa51024c1d64a40f230aa45",
"5cd47dd69f8bd0b15365a0c5cfc0a49a",
"06336c507b05f98c1d6a21abc43e6182",
};
static const char* const kDigests8x4[kNumDirectionalIntraPredictors] = {
"7370476ff0abbdc5e92f811b8879c861",
"a239a50adb28a4791b52a0dfff3bee06",
"4779a17f958a9ca04e8ec08c5aba1d36",
};
static const char* const kDigests8x8[kNumDirectionalIntraPredictors] = {
"305463f346c376594f82aad8304e0362",
"0cd481e5bda286c87a645417569fd948",
"48c7899dc9b7163b0b1f61b3a2b4b73e",
};
static const char* const kDigests8x16[kNumDirectionalIntraPredictors] = {
"5c18fd5339be90628c82b1fb6af50d5e",
"35eaa566ebd3bb7c903cfead5dc9ac78",
"9fdb0e790e5965810d02c02713c84071",
};
static const char* const kDigests8x32[kNumDirectionalIntraPredictors] = {
"2168d6cc858c704748b7b343ced2ac3a",
"1d3ce273107447faafd2e55877e48ffb",
"d344164049d1fe9b65a3ae8764bbbd37",
};
static const char* const kDigests16x4[kNumDirectionalIntraPredictors] = {
"dcef2cf51abe3fe150f388a14c762d30",
"6a810b289b1c14f8eab8ca1274e91ecd",
"c94da7c11f3fb11963d85c8804fce2d9",
};
static const char* const kDigests16x8[kNumDirectionalIntraPredictors] = {
"50a0d08b0d99b7a574bad2cfb36efc39",
"2dcb55874db39da70c8ca1318559f9fe",
"6390bcd30ff3bc389ecc0a0952bea531",
};
static const char* const kDigests16x16[kNumDirectionalIntraPredictors] = {
"7146c83c2620935606d49f3cb5876f41",
"2318ddf30c070a53c9b9cf199cd1b2c5",
"e9042e2124925aa7c1b6110617cb10e8",
};
static const char* const kDigests16x32[kNumDirectionalIntraPredictors] = {
"c970f401de7b7c5bb4e3ad447fcbef8f",
"a18cc70730eecdaa31dbcf4306ff490f",
"32c1528ad4a576a2210399d6b4ccd46e",
};
static const char* const kDigests16x64[kNumDirectionalIntraPredictors] = {
"00b3f0007da2e5d01380594a3d7162d5",
"1971af519e4a18967b7311f93efdd1b8",
"e6139769ce5a9c4982cfab9363004516",
};
static const char* const kDigests32x8[kNumDirectionalIntraPredictors] = {
"08107ad971179cc9f465ae5966bd4901",
"b215212a3c0dfe9182c4f2e903d731f7",
"791274416a0da87c674e1ae318b3ce09",
};
static const char* const kDigests32x16[kNumDirectionalIntraPredictors] = {
"94ea6cccae35b5d08799aa003ac08ccf",
"ae105e20e63fb55d4fd9d9e59dc62dde",
"973d0b2358ea585e4f486e7e645c5310",
};
static const char* const kDigests32x32[kNumDirectionalIntraPredictors] = {
"d14c695c4853ddf5e5d8256bc1d1ed60",
"6bd0ebeb53adecc11442b1218b870cb7",
"e03bc402a9999aba8272275dce93e89f",
};
static const char* const kDigests32x64[kNumDirectionalIntraPredictors] = {
"b21a8a8723758392ee659eeeae518a1e",
"e50285454896210ce44d6f04dfde05a7",
"f0f8ea0c6c2acc8d7d390927c3a90370",
};
static const char* const kDigests64x16[kNumDirectionalIntraPredictors] = {
"ce51db16fd4fa56e601631397b098c89",
"aa87a8635e02c1e91d13158c61e443f6",
"4c1ee3afd46ef34bd711a34d0bf86f13",
};
static const char* const kDigests64x32[kNumDirectionalIntraPredictors] = {
"25aaf5971e24e543e3e69a47254af777",
"eb6f444b3df127d69460778ab5bf8fc1",
"2f846cc0d506f90c0a58438600819817",
};
static const char* const kDigests64x64[kNumDirectionalIntraPredictors] = {
"b26ce5b5f4b5d4a438b52e5987877fb8",
"35721a00a70938111939cf69988d928e",
"0af7ec35939483fac82c246a13845806",
};
switch (tx_size) {
case kTransformSize4x4:
return kDigests4x4;
case kTransformSize4x8:
return kDigests4x8;
case kTransformSize4x16:
return kDigests4x16;
case kTransformSize8x4:
return kDigests8x4;
case kTransformSize8x8:
return kDigests8x8;
case kTransformSize8x16:
return kDigests8x16;
case kTransformSize8x32:
return kDigests8x32;
case kTransformSize16x4:
return kDigests16x4;
case kTransformSize16x8:
return kDigests16x8;
case kTransformSize16x16:
return kDigests16x16;
case kTransformSize16x32:
return kDigests16x32;
case kTransformSize16x64:
return kDigests16x64;
case kTransformSize32x8:
return kDigests32x8;
case kTransformSize32x16:
return kDigests32x16;
case kTransformSize32x32:
return kDigests32x32;
case kTransformSize32x64:
return kDigests32x64;
case kTransformSize64x16:
return kDigests64x16;
case kTransformSize64x32:
return kDigests64x32;
case kTransformSize64x64:
return kDigests64x64;
default:
ADD_FAILURE() << "Unknown transform size: " << tx_size;
return nullptr;
}
}
TEST_P(DirectionalIntraPredTest10bpp, DISABLED_Speed) {
#if LIBGAV1_ENABLE_NEON
const int num_runs = static_cast<int>(2e5 / (block_width_ * block_height_));
#else
const int num_runs = static_cast<int>(4e7 / (block_width_ * block_height_));
#endif
for (int i = kZone1; i < kNumZones; ++i) {
TestSpeed(GetDirectionalIntraPredDigests10bpp(tx_size_),
static_cast<Zone>(i), num_runs);
}
}
TEST_P(DirectionalIntraPredTest10bpp, FixedInput) {
for (int i = kZone1; i < kNumZones; ++i) {
TestSpeed(GetDirectionalIntraPredDigests10bpp(tx_size_),
static_cast<Zone>(i), 1);
}
}
TEST_P(DirectionalIntraPredTest10bpp, Overflow) { TestSaturatedValues(); }
TEST_P(DirectionalIntraPredTest10bpp, Random) { TestRandomValues(); }
#endif // LIBGAV1_MAX_BITDEPTH >= 10
//------------------------------------------------------------------------------
#if LIBGAV1_MAX_BITDEPTH == 12
using DirectionalIntraPredTest12bpp = DirectionalIntraPredTest<12, uint16_t>;
const char* const* GetDirectionalIntraPredDigests12bpp(TransformSize tx_size) {
static const char* const kDigests4x4[kNumDirectionalIntraPredictors] = {
"78f3297743f75e928e755b6ffa2d3050",
"7315da39861c6e3ef2e47c913e3be349",
"5609cb40b575f24d05880df202a60bd3",
};
static const char* const kDigests4x8[kNumDirectionalIntraPredictors] = {
"efb2363d3c25427abe198806c8ba4d6b",
"b5aaa41665a10e7e7944fb7fc90fd59a",
"5a85610342339ca3109d775fa18dc25c",
};
static const char* const kDigests4x16[kNumDirectionalIntraPredictors] = {
"9045679914980ea1f579d84509397b6e",
"f9f50bdc9f81a93095fd9d6998174aa7",
"46c1f82e85b8ba5b03bab41a2f561483",
};
static const char* const kDigests8x4[kNumDirectionalIntraPredictors] = {
"a0ae0956b2b667c528b7803d733d49da",
"5d9f60ef8904c4faedb6cfc19e54418a",
"4ffdcbbbcb23bca8286f1c286b9cb3e8",
};
static const char* const kDigests8x8[kNumDirectionalIntraPredictors] = {
"086116c6b116613b8b47a086726566ea",
"141dca7fcae0e4d4b88887a618271ea1",
"3575a34278aa0fb1eed934290982f4a7",
};
static const char* const kDigests8x16[kNumDirectionalIntraPredictors] = {
"7922f40216c78a40abaf675667e79493",
"55d20588240171df2e24d105ee1563ad",
"674b4d8f4dbf514d22e21cc4baeda1d3",
};
static const char* const kDigests8x32[kNumDirectionalIntraPredictors] = {
"32d4d7e256d3b304026ddb5430cf6a09",
"72f4be2569f4e067c252d51ff4030de3",
"6779a132e1bac0ac43c2373f56553ed8",
};
static const char* const kDigests16x4[kNumDirectionalIntraPredictors] = {
"1be2e0efc1403f9e22cfb8aeb28763d9",
"558c8a5418ac91d21a5839c454a9391f",
"7693ebef9b86416ebd6e78e98fcafba7",
};
static const char* const kDigests16x8[kNumDirectionalIntraPredictors] = {
"e6217ed1c673ae42e84f8757316b580d",
"028aa582c11a9733f0cd693211a067c5",
"082de9fc7c4bc80a8ec8522b5a5cb52c",
};
static const char* const kDigests16x16[kNumDirectionalIntraPredictors] = {
"e3b293c09bdc9c5c543ad046a3f0d64f",
"2de5803a6ed497c1039c8e6d675c1dd3",
"05742f807560f5d5206e54b70097dc4a",
};
static const char* const kDigests16x32[kNumDirectionalIntraPredictors] = {
"57f2ca4ba56be253eff7e6b73df5003d",
"ef8bea00437e01fb798a22cda59f0191",
"989ff38c96600c2f108d6e6fa381fd13",
};
static const char* const kDigests16x64[kNumDirectionalIntraPredictors] = {
"f5540f4874c02aa2222a3ba75106f841",
"17e5d20f798a96c39abc8a81e7aa7bc6",
"0fe9ea14c9dcae466b4a36f1c7db6978",
};
static const char* const kDigests32x8[kNumDirectionalIntraPredictors] = {
"aff9429951ab1885c0d9ed29aa1b6a9f",
"4b686e2a879bf0b4aadd06b412e0eb48",
"39325d71cddc272bfa1dd2dc80d09ffe",
};
static const char* const kDigests32x16[kNumDirectionalIntraPredictors] = {
"b83dffdf8bad2b7c3808925b6138ca1e",
"3656b58c7aaf2025979b4a3ed8a2841e",
"cfcc0c6ae3fa5e7d45dec581479459f6",
};
static const char* const kDigests32x32[kNumDirectionalIntraPredictors] = {
"3c91b3b9e2df73ffb718e0bf53c5a5c2",
"0dbe27603e111158e70d99e181befb83",
"edecbffb32ae1e49b66b6e55ad0af6c6",
};
static const char* const kDigests32x64[kNumDirectionalIntraPredictors] = {
"a3290917f755c7ccdc7b77eb3c6c89a7",
"42f89db41fbb366ddb78ef79a043f3e3",
"7f7bcbe33aa003b166677c68d12490e9",
};
static const char* const kDigests64x16[kNumDirectionalIntraPredictors] = {
"d4f4c6b70a82695f843e9227bd7d9cc8",
"550a0bd87936801651d552e229b683e9",
"a4c730ad71f566a930c5672e1b2f48f1",
};
static const char* const kDigests64x32[kNumDirectionalIntraPredictors] = {
"2087c9264c4c5fea9a6fe20dcedbe2b9",
"d4dd51d9578a3fc2eb75086fba867c22",
"6121a67d63e40107e780d0938aeb3d21",
};
static const char* const kDigests64x64[kNumDirectionalIntraPredictors] = {
"09c3818a07bc54467634c2bfce66f58f",
"8da453b8d72d73d71ba15a14ddd59db4",
"9bc939aa54445722469b120b8a505cb3",
};
switch (tx_size) {
case kTransformSize4x4:
return kDigests4x4;
case kTransformSize4x8:
return kDigests4x8;
case kTransformSize4x16:
return kDigests4x16;
case kTransformSize8x4:
return kDigests8x4;
case kTransformSize8x8:
return kDigests8x8;
case kTransformSize8x16:
return kDigests8x16;
case kTransformSize8x32:
return kDigests8x32;
case kTransformSize16x4:
return kDigests16x4;
case kTransformSize16x8:
return kDigests16x8;
case kTransformSize16x16:
return kDigests16x16;
case kTransformSize16x32:
return kDigests16x32;
case kTransformSize16x64:
return kDigests16x64;
case kTransformSize32x8:
return kDigests32x8;
case kTransformSize32x16:
return kDigests32x16;
case kTransformSize32x32:
return kDigests32x32;
case kTransformSize32x64:
return kDigests32x64;
case kTransformSize64x16:
return kDigests64x16;
case kTransformSize64x32:
return kDigests64x32;
case kTransformSize64x64:
return kDigests64x64;
default:
ADD_FAILURE() << "Unknown transform size: " << tx_size;
return nullptr;
}
}
TEST_P(DirectionalIntraPredTest12bpp, DISABLED_Speed) {
#if LIBGAV1_ENABLE_NEON
const int num_runs = static_cast<int>(2e7 / (block_width_ * block_height_));
#else
const int num_runs = static_cast<int>(4e7 / (block_width_ * block_height_));
#endif
for (int i = kZone1; i < kNumZones; ++i) {
TestSpeed(GetDirectionalIntraPredDigests12bpp(tx_size_),
static_cast<Zone>(i), num_runs);
}
}
TEST_P(DirectionalIntraPredTest12bpp, FixedInput) {
for (int i = kZone1; i < kNumZones; ++i) {
TestSpeed(GetDirectionalIntraPredDigests12bpp(tx_size_),
static_cast<Zone>(i), 1);
}
}
TEST_P(DirectionalIntraPredTest12bpp, Overflow) { TestSaturatedValues(); }
TEST_P(DirectionalIntraPredTest12bpp, Random) { TestRandomValues(); }
#endif // LIBGAV1_MAX_BITDEPTH == 12
constexpr TransformSize kTransformSizes[] = {
kTransformSize4x4, kTransformSize4x8, kTransformSize4x16,
kTransformSize8x4, kTransformSize8x8, kTransformSize8x16,
kTransformSize8x32, kTransformSize16x4, kTransformSize16x8,
kTransformSize16x16, kTransformSize16x32, kTransformSize16x64,
kTransformSize32x8, kTransformSize32x16, kTransformSize32x32,
kTransformSize32x64, kTransformSize64x16, kTransformSize64x32,
kTransformSize64x64};
INSTANTIATE_TEST_SUITE_P(C, DirectionalIntraPredTest8bpp,
testing::ValuesIn(kTransformSizes));
#if LIBGAV1_ENABLE_SSE4_1
INSTANTIATE_TEST_SUITE_P(SSE41, DirectionalIntraPredTest8bpp,
testing::ValuesIn(kTransformSizes));
#endif // LIBGAV1_ENABLE_SSE4_1
#if LIBGAV1_ENABLE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, DirectionalIntraPredTest8bpp,
testing::ValuesIn(kTransformSizes));
#endif // LIBGAV1_ENABLE_NEON
#if LIBGAV1_MAX_BITDEPTH >= 10
INSTANTIATE_TEST_SUITE_P(C, DirectionalIntraPredTest10bpp,
testing::ValuesIn(kTransformSizes));
#if LIBGAV1_ENABLE_SSE4_1
INSTANTIATE_TEST_SUITE_P(SSE41, DirectionalIntraPredTest10bpp,
testing::ValuesIn(kTransformSizes));
#endif // LIBGAV1_ENABLE_SSE4_1
#if LIBGAV1_ENABLE_NEON
INSTANTIATE_TEST_SUITE_P(NEON, DirectionalIntraPredTest10bpp,
testing::ValuesIn(kTransformSizes));
#endif // LIBGAV1_ENABLE_NEON
#endif // LIBGAV1_MAX_BITDEPTH >= 10
#if LIBGAV1_MAX_BITDEPTH == 12
INSTANTIATE_TEST_SUITE_P(C, DirectionalIntraPredTest12bpp,
testing::ValuesIn(kTransformSizes));
#endif // LIBGAV1_MAX_BITDEPTH == 12
} // namespace
} // namespace dsp
static std::ostream& operator<<(std::ostream& os, const TransformSize tx_size) {
return os << ToString(tx_size);
}
} // namespace libgav1