blob: 2a6d1993ed886a41021a3512ce568cb55ebb3064 [file] [log] [blame] [edit]
/*
* Copyright (c) 2025, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 3-Clause Clear License
* and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
* License was not distributed with this source code in the LICENSE file, you
* can obtain it at aomedia.org/license/software-license/bsd-3-c-c/. If the
* Alliance for Open Media Patent License 1.0 was not distributed with this
* source code in the PATENTS file, you can obtain it at
* aomedia.org/license/patent-license/.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
#include "test/register_state_check.h"
#include "test/function_equivalence_test.h"
#include "config/aom_config.h"
#include "config/aom_dsp_rtcd.h"
#include "config/av1_rtcd.h"
#include "aom/aom_integer.h"
#include "av1/common/enums.h"
#include "av1/encoder/trellis_quant.h"
using libaom_test::FunctionEquivalenceTest;
namespace {
template <typename F, typename T>
class TcqRateTest : public FunctionEquivalenceTest<F> {
protected:
static const int kIterations = 100000;
virtual ~TcqRateTest() {}
virtual void Execute(T *rate_tst) = 0;
void Common() {
Execute(&rate_tst_);
ASSERT_EQ(rate_ref_.rate_zero[0], rate_tst_.rate_zero[0]);
ASSERT_EQ(rate_ref_.rate_zero[1], rate_tst_.rate_zero[1]);
ASSERT_EQ(rate_ref_.rate_eob[0], rate_tst_.rate_eob[0]);
ASSERT_EQ(rate_ref_.rate_eob[1], rate_tst_.rate_eob[1]);
for (int i = 0; i < 8; i++) {
ASSERT_EQ(rate_ref_.rate[i], rate_tst_.rate[i]);
}
}
T rate_ref_;
T rate_tst_;
};
//////////////////////////////////////////////////////////////////////////////
// TCQ Rate calculation functions.
//////////////////////////////////////////////////////////////////////////////
typedef void (*TcqRateLuma)(const struct tcq_param_t *p,
const struct prequant_t *pq,
const struct tcq_coeff_ctx_t *coeff_ctx,
int blk_pos, int diag_ctx, int eob_rate,
struct tcq_rate_t *rd);
typedef libaom_test::FuncParam<TcqRateLuma> TcqRateLumaTestFuncs;
class TcqRateLumaTest : public TcqRateTest<TcqRateLuma, tcq_rate_t> {
protected:
void Execute(tcq_rate_t *rate_tst) {
params_.ref_func(&param_, &pre_quant_, &coeff_ctx_, blk_pos_, diag_ctx_,
eob_rate_, &rate_ref_);
ASM_REGISTER_STATE_CHECK(params_.tst_func(&param_, &pre_quant_, &coeff_ctx_,
blk_pos_, diag_ctx_, eob_rate_,
rate_tst));
}
tcq_param_t param_;
LV_MAP_COEFF_COST txb_costs_;
prequant_t pre_quant_;
tcq_coeff_ctx_t coeff_ctx_;
int blk_pos_;
int diag_ctx_;
int eob_rate_;
};
typedef void (*TcqRateLfLuma)(const struct tcq_param_t *p,
const struct prequant_t *pq,
const struct tcq_coeff_ctx_t *coeff_ctx,
int blk_pos, int diag_ctx, int eob_rate,
int coeff_sign, struct tcq_rate_t *rd);
typedef libaom_test::FuncParam<TcqRateLfLuma> TcqRateLfLumaTestFuncs;
class TcqRateLfLumaTest : public TcqRateTest<TcqRateLfLuma, tcq_rate_t> {
protected:
void Execute(tcq_rate_t *rate_tst) {
params_.ref_func(&param_, &pre_quant_, &coeff_ctx_, blk_pos_, diag_ctx_,
eob_rate_, coeff_sign_, &rate_ref_);
ASM_REGISTER_STATE_CHECK(params_.tst_func(&param_, &pre_quant_, &coeff_ctx_,
blk_pos_, diag_ctx_, eob_rate_,
coeff_sign_, rate_tst));
}
tcq_param_t param_;
LV_MAP_COEFF_COST txb_costs_;
prequant_t pre_quant_;
tcq_coeff_ctx_t coeff_ctx_;
int blk_pos_;
int diag_ctx_;
int eob_rate_;
int coeff_sign_;
int tmp_sign_[1024];
};
static int generate_random_q_idx(libaom_test::ACMRandom *rng) {
int r1 = rng->Rand8() & 15;
int r2 = (r1 == 15) ? rng->Rand8() & 15 : 0;
int r3 = (r2 == 15) ? rng->Rand16() & 8191 : 0;
int r = r1 + r2 + r3;
return r;
}
// Init coeff syntax costs randomly
// - base_cost[], lps_cost[], base_eob_cost[]
// - base_cost_zero[], base_cost_low_tbl[], base_eob_cost_tbl[], mid_cost_tbl[]
static void generate_random_cost_tables(libaom_test::ACMRandom *rng,
LV_MAP_COEFF_COST *txb_costs) {
int max = 2048 - 1;
int n;
int *p0;
// Init sign costs
n = sizeof(txb_costs->dc_sign_cost) / sizeof(txb_costs->dc_sign_cost[0][0]);
p0 = txb_costs->dc_sign_cost[0][0];
for (int i = 0; i < n; i++) {
*p0++ = rng->Rand16() & max;
}
// Init base costs
n = sizeof(txb_costs->base_cost) / sizeof(txb_costs->base_cost[0][0][0]);
p0 = &txb_costs->base_cost[0][0][0];
for (int i = 0; i < n; i++) {
*p0++ = rng->Rand16() & max;
}
n = sizeof(txb_costs->base_lf_cost) /
sizeof(txb_costs->base_lf_cost[0][0][0]);
p0 = &txb_costs->base_lf_cost[0][0][0];
for (int i = 0; i < n; i++) {
*p0++ = rng->Rand16() & max;
}
// Init mid-range (lps) costs
n = sizeof(txb_costs->lps_cost) / sizeof(txb_costs->lps_cost[0][0]);
p0 = &txb_costs->lps_cost[0][0];
for (int i = 0; i < n; i++) {
*p0++ = rng->Rand16() & max;
}
n = sizeof(txb_costs->lps_lf_cost) / sizeof(txb_costs->lps_lf_cost[0][0]);
p0 = &txb_costs->lps_lf_cost[0][0];
for (int i = 0; i < n; i++) {
*p0++ = rng->Rand16() & max;
}
// Init base_eob costs
n = sizeof(txb_costs->base_eob_cost) / sizeof(txb_costs->base_eob_cost[0][0]);
p0 = &txb_costs->base_eob_cost[0][0];
for (int i = 0; i < n; i++) {
*p0++ = rng->Rand16() & max;
}
n = sizeof(txb_costs->base_lf_eob_cost) /
sizeof(txb_costs->base_lf_eob_cost[0][0]);
p0 = &txb_costs->base_lf_eob_cost[0][0];
for (int i = 0; i < n; i++) {
*p0++ = rng->Rand16() & max;
}
// Rearrange costs into base_cost_zero[] array for quicker access.
// (from av1/encoder/rd.c)
for (int q_i = 0; q_i < TCQ_CTXS; q_i++) {
for (int ctx = 0; ctx < SIG_COEF_CONTEXTS; ++ctx) {
txb_costs->base_cost_zero[q_i][ctx] = txb_costs->base_cost[ctx][q_i][0];
}
}
// Rearrange costs into base_lf_cost_zero[] array for quicker access.
for (int q_i = 0; q_i < TCQ_CTXS; q_i++) {
for (int ctx = 0; ctx < LF_SIG_COEF_CONTEXTS; ++ctx) {
txb_costs->base_lf_cost_zero[q_i][ctx] =
txb_costs->base_lf_cost[ctx][q_i][0];
}
}
// Precompute some base_costs for trellis, interleaved for quick access.
// Look-up take to retrive data from precomputed cost array
static const uint8_t trel_abslev[15][4] = {
{ 2, 1, 1, 2 }, // qIdx=1
{ 2, 3, 1, 2 }, // qidx=2
{ 2, 3, 3, 2 }, // qidx=3
{ 2, 3, 3, 4 }, // qidx=4
{ 4, 3, 3, 4 }, // qidx=5
{ 4, 5, 3, 4 }, // qidx=6
{ 4, 5, 5, 4 }, // qidx=7
{ 4, 5, 5, 6 }, // qidx=8
{ 6, 5, 5, 6 }, // qidx=9
{ 6, 7, 5, 6 }, // qidx=10
{ 6, 7, 7, 6 }, // qidx=11
{ 6, 7, 7, 8 }, // qidx=12
{ 8, 7, 7, 8 }, // qidx=13
{ 8, 9, 7, 8 }, // qidx=14
{ 8, 9, 9, 8 }, // qidx=15
};
for (int idx = 0; idx < 5; idx++) {
int a0 = AOMMIN(trel_abslev[idx][0], 3);
int a1 = AOMMIN(trel_abslev[idx][1], 3);
int a2 = AOMMIN(trel_abslev[idx][2], 3);
int a3 = AOMMIN(trel_abslev[idx][3], 3);
for (int ctx = 0; ctx < SIG_COEF_CONTEXTS; ++ctx) {
// Q0, absLev 0 / 2
txb_costs->base_cost_low_tbl[idx][ctx][0][0] =
txb_costs->base_cost[ctx][0][a0] + av1_cost_literal(1);
txb_costs->base_cost_low_tbl[idx][ctx][0][1] =
txb_costs->base_cost[ctx][0][a2] + av1_cost_literal(1);
// Q1, absLev 1 / 3
txb_costs->base_cost_low_tbl[idx][ctx][1][0] =
txb_costs->base_cost[ctx][1][a1] + av1_cost_literal(1);
txb_costs->base_cost_low_tbl[idx][ctx][1][1] =
txb_costs->base_cost[ctx][1][a3] + av1_cost_literal(1);
}
for (int ctx = 0; ctx < SIG_COEF_CONTEXTS_EOB; ++ctx) {
// EOB coeff, absLev 0 / 2
txb_costs->base_eob_cost_tbl[idx][ctx][0] =
txb_costs->base_eob_cost[ctx][a0 - 1] + av1_cost_literal(1);
txb_costs->base_eob_cost_tbl[idx][ctx][1] =
txb_costs->base_eob_cost[ctx][a2 - 1] + av1_cost_literal(1);
}
}
for (int idx = 0; idx < 9; idx++) {
int max = LF_BASE_SYMBOLS - 1;
int a0 = AOMMIN(trel_abslev[idx][0], max);
int a1 = AOMMIN(trel_abslev[idx][1], max);
int a2 = AOMMIN(trel_abslev[idx][2], max);
int a3 = AOMMIN(trel_abslev[idx][3], max);
for (int ctx = 0; ctx < LF_SIG_COEF_CONTEXTS; ++ctx) {
// Q0, absLev 0 / 2
txb_costs->base_lf_cost_low_tbl[idx][ctx][0][0] =
txb_costs->base_lf_cost[ctx][0][a0] + av1_cost_literal(1);
txb_costs->base_lf_cost_low_tbl[idx][ctx][0][1] =
txb_costs->base_lf_cost[ctx][0][a2] + av1_cost_literal(1);
// Q1, absLev 1 / 3
txb_costs->base_lf_cost_low_tbl[idx][ctx][1][0] =
txb_costs->base_lf_cost[ctx][1][a1] + av1_cost_literal(1);
txb_costs->base_lf_cost_low_tbl[idx][ctx][1][1] =
txb_costs->base_lf_cost[ctx][1][a3] + av1_cost_literal(1);
}
for (int ctx = 0; ctx < SIG_COEF_CONTEXTS_EOB; ++ctx) {
// EOB coeff, absLev 0 / 2
txb_costs->base_lf_eob_cost_tbl[idx][ctx][0] =
txb_costs->base_lf_eob_cost[ctx][a0 - 1] + av1_cost_literal(1);
txb_costs->base_lf_eob_cost_tbl[idx][ctx][1] =
txb_costs->base_lf_eob_cost[ctx][a2 - 1] + av1_cost_literal(1);
}
}
// Precalc mid costs for default region.
for (int idx = 0; idx < 5 + 2 * COEFF_BASE_RANGE; idx++) {
int a0 = get_low_range(trel_abslev[idx][0], 0);
int a1 = get_low_range(trel_abslev[idx][1], 0);
int a2 = get_low_range(trel_abslev[idx][2], 0);
int a3 = get_low_range(trel_abslev[idx][3], 0);
for (int ctx = 0; ctx < LEVEL_CONTEXTS; ++ctx) {
// Q0, absLev 0 / 2
txb_costs->mid_cost_tbl[idx][ctx][0][0] =
a0 < 0 ? 0 : txb_costs->lps_cost[ctx][a0];
txb_costs->mid_cost_tbl[idx][ctx][0][1] =
a2 < 0 ? 0 : txb_costs->lps_cost[ctx][a2];
// Q1, absLev 1 / 3
txb_costs->mid_cost_tbl[idx][ctx][1][0] =
a1 < 0 ? 0 : txb_costs->lps_cost[ctx][a1];
txb_costs->mid_cost_tbl[idx][ctx][1][1] =
a3 < 0 ? 0 : txb_costs->lps_cost[ctx][a3];
}
}
// Precalc mid costs for default region.
for (int idx = 0; idx < 9 + 2 * COEFF_BASE_RANGE; idx++) {
int a0 = get_low_range(trel_abslev[idx][0], 1);
int a1 = get_low_range(trel_abslev[idx][1], 1);
int a2 = get_low_range(trel_abslev[idx][2], 1);
int a3 = get_low_range(trel_abslev[idx][3], 1);
for (int ctx = 0; ctx < LF_LEVEL_CONTEXTS; ++ctx) {
// Q0, absLev 0 / 2
txb_costs->mid_lf_cost_tbl[idx][ctx][0][0] =
a0 < 0 ? 0 : txb_costs->lps_lf_cost[ctx][a0];
txb_costs->mid_lf_cost_tbl[idx][ctx][0][1] =
a2 < 0 ? 0 : txb_costs->lps_lf_cost[ctx][a2];
// Q1, absLev 1 / 3
txb_costs->mid_lf_cost_tbl[idx][ctx][1][0] =
a1 < 0 ? 0 : txb_costs->lps_lf_cost[ctx][a1];
txb_costs->mid_lf_cost_tbl[idx][ctx][1][1] =
a3 < 0 ? 0 : txb_costs->lps_lf_cost[ctx][a3];
}
}
}
TEST_P(TcqRateLumaTest, RandomValues) {
for (int iter = 0; iter < kIterations && !HasFatalFailure(); ++iter) {
int log_scale = 1;
int shift = 16 - log_scale + QUANT_FP_BITS;
const int32_t quant[2] = { 1 << shift, 1 << shift };
int dqv = 1 << QUANT_TABLE_BITS;
int tqc = iter < 16000 ? iter : generate_random_q_idx(&rng_);
// Initialize param structure.
int bwl = 2 + (rng_.Rand8() & 3);
int height = 1 << bwl;
int max = (1 << bwl) - 1;
int row = rng_.Rand8() & max;
int col = rng_.Rand8() & max;
row = AOMMAX(row, 4);
col = AOMMAX(col, 4);
int blk_pos = (row << bwl) + col;
int scan_pos = blk_pos;
int diag_ctx = get_nz_map_ctx_from_stats(0, blk_pos, bwl, TX_CLASS_2D, 0);
blk_pos_ = blk_pos;
diag_ctx_ = diag_ctx;
param_.bwl = bwl;
param_.txb_height = height;
param_.tx_class = 0;
param_.txb_costs = &txb_costs_;
// Generate random syntax costs.
generate_random_cost_tables(&rng_, &txb_costs_);
// Generate pre_quant info with random coeff.
av1_pre_quant_c(tqc, &pre_quant_, quant, dqv, log_scale, scan_pos);
eob_rate_ = rng_(512 * 4);
// Generate random coeff_ctx
for (int i = 0; i < 8; i++) {
coeff_ctx_.coef[i] = (rng_(4) << 4) + rng_(4);
}
coeff_ctx_.coef_eob = get_lower_levels_ctx_eob(bwl, height, scan_pos);
coeff_ctx_.pad[0] = 0;
coeff_ctx_.pad[1] = 0;
coeff_ctx_.pad[2] = 0;
Common();
}
}
TEST_P(TcqRateLfLumaTest, RandomValues) {
for (int iter = 0; iter < kIterations && !HasFatalFailure(); ++iter) {
int log_scale = 1;
int shift = 16 - log_scale + QUANT_FP_BITS;
const int32_t quant[2] = { 1 << shift, 1 << shift };
int dqv = 1 << QUANT_TABLE_BITS;
int tqc = iter < 16000 ? iter : generate_random_q_idx(&rng_);
// Initialize param structure.
int bwl = 2 + (rng_.Rand8() & 3);
int height = 1 << bwl;
int diag = rng_.Rand8() & 3;
int row = rng_.Rand8() % (diag + 1);
int col = diag - row;
int blk_pos = (row << bwl) + col;
int scan_pos = blk_pos;
int diag_ctx = get_nz_map_ctx_from_stats_lf(0, blk_pos, bwl, TX_CLASS_2D);
if (scan_pos > 0) {
diag_ctx += 7 << 8;
}
blk_pos_ = blk_pos;
diag_ctx_ = diag_ctx;
coeff_sign_ = rng_.Rand8() & 1;
param_.bwl = bwl;
param_.txb_height = height;
param_.tx_class = 0;
param_.txb_costs = &txb_costs_;
param_.tmp_sign = tmp_sign_;
param_.dc_sign_ctx = rng_.Rand8() % DC_SIGN_CONTEXTS;
tmp_sign_[blk_pos] = rng_.Rand8() % CROSS_COMPONENT_CONTEXTS;
// Generate random syntax costs.
generate_random_cost_tables(&rng_, &txb_costs_);
// Generate pre_quant info with random coeff.
av1_pre_quant_c(tqc, &pre_quant_, quant, dqv, log_scale, scan_pos);
eob_rate_ = rng_(512 * 4);
// Generate random coeff_ctx
for (int i = 0; i < 8; i++) {
coeff_ctx_.coef[i] = (rng_(4) << 4) + rng_(4);
}
coeff_ctx_.coef_eob = get_lower_levels_ctx_eob(bwl, height, scan_pos);
coeff_ctx_.pad[0] = 0;
coeff_ctx_.pad[1] = 0;
coeff_ctx_.pad[2] = 0;
Common();
}
}
#if HAVE_AVX2
INSTANTIATE_TEST_SUITE_P(
AVX2, TcqRateLumaTest,
::testing::Values(TcqRateLumaTestFuncs(av1_get_rate_dist_def_luma_c,
av1_get_rate_dist_def_luma_avx2)));
INSTANTIATE_TEST_SUITE_P(
AVX2, TcqRateLfLumaTest,
::testing::Values(TcqRateLfLumaTestFuncs(av1_get_rate_dist_lf_luma_c,
av1_get_rate_dist_lf_luma_avx2)));
#endif // HAVE_AVX2
} // namespace