| /* | |
| * 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(¶m_, &pre_quant_, &coeff_ctx_, blk_pos_, diag_ctx_, | |
| eob_rate_, &rate_ref_); | |
| ASM_REGISTER_STATE_CHECK(params_.tst_func(¶m_, &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(¶m_, &pre_quant_, &coeff_ctx_, blk_pos_, diag_ctx_, | |
| eob_rate_, coeff_sign_, &rate_ref_); | |
| ASM_REGISTER_STATE_CHECK(params_.tst_func(¶m_, &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 |