| /* |
| * 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 |