From fceee4fd80da9503212a6ed205df216a0edd4ac4 Mon Sep 17 00:00:00 2001 From: Diksha Singh Date: Fri, 11 Sep 2026 21:09:52 +0530 Subject: [PATCH] Skip chroma RD evaluation of interpolation filter search This patch introduces the speed feature 'skip_model_rd_uv' to skip the RD evaluation of chroma planes during the interpolation filter search. The chroma predictors are built later using the best interpolation filter selected by the luma RD. This speed feature is enabled for speed >= 1. This optimization has been taken from AV1: https://aomedia-review.googlesource.com/c/aom/+/210801 Results for RA CTC, A1 17 frames, A2 33 frames, speed 1: (Anchor: [12c553ad5] (https://github.com/AOMediaCodec/avm/commit/12c553ad5648cd26360866cd90ec1a1877d32335)) ``` +-----+------+-------+------+-------+---------------+ |Class| Y | U | V | YUV |EncInstCount(%)| +-----+------+-------+------+-------+---------------+ | A1 | 0.01 | -0.11 |-0.21 | 0.00 | 99.3 | | A2 | 0.00 | 0.23 | 0.40 | 0.03 | 98.6 | +-----+------+-------+------+-------+---------------+ ``` STATS_CHANGED for speed >= 1 Change-Id: I0b791230d5a5cf3228653635dd0332ad401e91bc --- av2/common/filter.h | 3 +- av2/encoder/interp_search.c | 53 +++++++++++++++++++++--------------- av2/encoder/rdopt.c | 51 ++++++++++++++++++---------------- av2/encoder/speed_features.c | 3 ++ av2/encoder/speed_features.h | 4 +++ 5 files changed, 67 insertions(+), 47 deletions(-) diff --git a/av2/common/filter.h b/av2/common/filter.h index 33885685a7..3c433c5d4f 100644 --- a/av2/common/filter.h +++ b/av2/common/filter.h @@ -51,7 +51,8 @@ enum { enum { INTERP_EVAL_LUMA_EVAL_CHROMA = 0, INTERP_SKIP_LUMA_EVAL_CHROMA, - INTERP_EVAL_INVALID, + INTERP_EVAL_LUMA_SKIP_CHROMA, // Valid only when skip_model_rd_uv speed + // feature is enabled INTERP_SKIP_LUMA_SKIP_CHROMA, } UENUM1BYTE(INTERP_EVAL_PLANE); diff --git a/av2/encoder/interp_search.c b/av2/encoder/interp_search.c index 75973f2e90..cd34a4803f 100644 --- a/av2/encoder/interp_search.c +++ b/av2/encoder/interp_search.c @@ -193,7 +193,6 @@ static INLINE int64_t interpolation_filter_rd( (void)tile_data; - assert(skip_pred != 2); assert((rd_stats_luma->rate >= 0) && (rd_stats->rate >= 0)); assert((rd_stats_luma->dist >= 0) && (rd_stats->dist >= 0)); assert((rd_stats_luma->sse >= 0) && (rd_stats->sse >= 0)); @@ -213,11 +212,16 @@ static INLINE int64_t interpolation_filter_rd( (skip_pred == interp_search_flags->default_interp_skip_flags) ? INTERP_SKIP_LUMA_SKIP_CHROMA : skip_pred; - - switch (tmp_skip_pred) { - case INTERP_EVAL_LUMA_EVAL_CHROMA: - // skip_pred = 0: Evaluate both luma and chroma. - // Luma MC + assert(IMPLIES(tmp_skip_pred == INTERP_EVAL_LUMA_SKIP_CHROMA, + cpi->sf.interp_sf.skip_model_rd_uv)); + + if (tmp_skip_pred == INTERP_SKIP_LUMA_SKIP_CHROMA) { + // Both luma and chroma evaluations are skipped. + this_rd_stats = *rd_stats; + } else { + // Evaluate Luma + if (tmp_skip_pred == INTERP_EVAL_LUMA_EVAL_CHROMA || + tmp_skip_pred == INTERP_EVAL_LUMA_SKIP_CHROMA) { interp_model_rd_eval(x, cpi, bsize, orig_dst, AVM_PLANE_Y, AVM_PLANE_Y, &this_rd_stats_luma, 0); this_rd_stats = this_rd_stats_luma; @@ -227,10 +231,10 @@ static INLINE int64_t interpolation_filter_rd( INT64_MAX); PrintPredictionUnitStats(cpi, tile_data, x, &rd_stats_y, bsize); #endif // CONFIG_COLLECT_RD_STATS == 3 - AVM_FALLTHROUGH_INTENDED; - case INTERP_SKIP_LUMA_EVAL_CHROMA: - // skip_pred = 1: skip luma evaluation (retain previous best luma stats) - // and do chroma evaluation. + } + // Evaluate Chroma + if (tmp_skip_pred == INTERP_EVAL_LUMA_EVAL_CHROMA || + tmp_skip_pred == INTERP_SKIP_LUMA_EVAL_CHROMA) { for (int plane = 1; plane < num_planes; ++plane) { int64_t tmp_rd = RDCOST(x->rdmult, tmp_rs + this_rd_stats.rate, this_rd_stats.dist); @@ -241,13 +245,7 @@ static INLINE int64_t interpolation_filter_rd( interp_model_rd_eval(x, cpi, bsize, orig_dst, plane, plane, &this_rd_stats, 0); } - break; - case INTERP_SKIP_LUMA_SKIP_CHROMA: - // both luma and chroma evaluation is skipped - this_rd_stats = *rd_stats; - break; - case INTERP_EVAL_INVALID: - default: assert(0); return 0; + } } int64_t tmp_rd = RDCOST(x->rdmult, tmp_rs + this_rd_stats.rate, this_rd_stats.dist); @@ -344,6 +342,13 @@ static INLINE void calc_interp_skip_pred_flag(MACROBLOCK *const x, assert(mbmi->comp_group_idx == 1); if (*skip_hor == 0 && *skip_ver == 1) *skip_ver = 0; } + + // Configure flags to skip chroma RD evaluation when skip_model_rd_uv is + // enabled + if (cpi->sf.interp_sf.skip_model_rd_uv && num_planes > 1) { + *skip_hor |= INTERP_EVAL_LUMA_SKIP_CHROMA; + *skip_ver |= INTERP_EVAL_LUMA_SKIP_CHROMA; + } } /*!\brief AV2 interpolation filter search @@ -368,7 +373,7 @@ static INLINE void calc_interp_skip_pred_flag(MACROBLOCK *const x, * \param[in,out] switchable_rate The rate associated with using a SWITCHABLE * filter mode. * \param[in,out] skip_build_pred Indicates whether or not to build the inter - * predictor. If this is 0, the inter predictor + * predictor. If this is 3, the inter predictor * has already been built and thus we can avoid * repeating computation. * \param[in] args HandleInterModeArgs struct holding @@ -396,6 +401,7 @@ int64_t av2_interpolation_filter_search( MB_MODE_INFO *const mbmi = xd->mi[0]; const int need_search = av2_is_interp_needed(cm, xd); const int ref_frame = COMPACT_INDEX0_NRS(xd->mi[0]->ref_frame[0]); + const int skip_model_rd_uv = cpi->sf.interp_sf.skip_model_rd_uv; RD_STATS rd_stats_luma, rd_stats; if (mbmi->mode == WARPMV) return 0; @@ -415,6 +421,7 @@ int64_t av2_interpolation_filter_search( *rd = args->interp_filter_stats[match_found_idx].rd; x->pred_sse[ref_frame] = args->interp_filter_stats[match_found_idx].pred_sse; + *skip_build_pred = INTERP_EVAL_LUMA_EVAL_CHROMA; return 0; } @@ -436,11 +443,13 @@ int64_t av2_interpolation_filter_search( PrintPredictionUnitStats(cpi, tile_data, x, &rd_stats_y, bsize); #endif // CONFIG_COLLECT_RD_STATS == 3 // Chroma MC - if (num_planes > 1) { + if (num_planes > 1 && !skip_model_rd_uv) { interp_model_rd_eval(x, cpi, bsize, orig_dst, AVM_PLANE_U, AVM_PLANE_V, &rd_stats, *skip_build_pred); } - *skip_build_pred = 1; + *skip_build_pred = num_planes > 1 && skip_model_rd_uv + ? INTERP_SKIP_LUMA_EVAL_CHROMA + : INTERP_SKIP_LUMA_SKIP_CHROMA; av2_merge_rd_stats(&rd_stats, &rd_stats_luma); @@ -489,8 +498,8 @@ int64_t av2_interpolation_filter_search( // Setting 0th flag corresonds to skipping luma MC and setting 1st bt // corresponds to skipping chroma MC skip_flag=0 corresponds to "Don't skip // luma and chroma MC" Skip flag=1 corresponds to "Skip Luma MC only" - // Skip_flag=2 is not a valid case - // skip_flag=3 corresponds to "Skip both luma and chroma MC" + // Skip_flag=2 corresponds to "Skip chroma MC only". This is valid only when + // skip_model_rd_uv speed feature is enabled int skip_hor = interp_search_flags->default_interp_skip_flags; int skip_ver = interp_search_flags->default_interp_skip_flags; calc_interp_skip_pred_flag(x, cpi, &skip_hor, &skip_ver); diff --git a/av2/encoder/rdopt.c b/av2/encoder/rdopt.c index 69ceaa9d0f..0c82e1fcf8 100644 --- a/av2/encoder/rdopt.c +++ b/av2/encoder/rdopt.c @@ -4490,26 +4490,18 @@ static INLINE bool prune_comp_eval_using_est_rd( * done in the motion mode search. * \param[in,out] rd_stats Struct to keep track of the overall RD * information. - * \param[in,out] skip_rd An array of length 2 where skip_rd[0] is - the + * \param[in,out] skip_rd An array of length 2 where skip_rd[0] is the * best total RD for a skip mode so far, and - * skip_rd[1] is the best RD for a skip mode - so - * far in luma. This is used as a speed - feature - * to skip the transform search if the - computed + * skip_rd[1] is the best RD for a skip mode so + * far in luma. This is used as a speed feature + * to skip the transform search if the computed * skip RD for the current mode is not better * than the best skip_rd so far. - * \param[in,out] skip_build_pred Indicates whether or not to build the - inter - * predictor. If this is 0, the inter - predictor - * has already been built and thus we can - avoid - * repeating computation. - * \return Returns 1 if this mode is worse than one already seen and 0 if it - is + * \param[out] skip_build_pred Indicates whether or not to build the inter + * predictor during/after interpolation + * filter search. + + * \return Returns 1 if this mode is worse than one already seen and 0 if it is * a viable candidate. */ static int process_compound_inter_mode( @@ -4587,7 +4579,7 @@ static int process_compound_inter_mode( av2_enc_build_inter_predictor(cm, xd, mi_row, mi_col, orig_dst, bsize, AVM_PLANE_U, num_planes - 1); } - *skip_build_pred = 1; + *skip_build_pred = INTERP_SKIP_LUMA_SKIP_CHROMA; } return 0; } @@ -5023,7 +5015,9 @@ static void evaluate_inter_predictor(AV2_COMP *const cpi, mbmi, cpi->sf.inter_sf.prune_ref_mv_idx_search)) return; - int skip_build_pred = 0; + // Flag to indicate whether to skip av1_enc_build_inter_predictor() after + // interpolation filter search + int skip_build_pred = INTERP_EVAL_LUMA_EVAL_CHROMA; const int mi_row = xd->mi_row; const int mi_col = xd->mi_col; @@ -5104,15 +5098,24 @@ static void evaluate_inter_predictor(AV2_COMP *const cpi, } } rd_stats->rate += compmode_interinter_cost; - if ((skip_build_pred != 1 && (mbmi->mode != WARPMV)) || is_comp_pred) { - // Build this inter predictor if it has not been - // previously built + if ((skip_build_pred != INTERP_SKIP_LUMA_SKIP_CHROMA && + (mbmi->mode != WARPMV)) || + is_comp_pred) { + // Chroma plane of COMPOUND_DIFFWTD mode shares the segment mask of luma + // which is stored in xd->seg_mask. Hence, the predictor is populated for + // all planes. This should avoid usage of incorrect segment mask when the + // call is made only for chroma. + const int skip_luma_plane = + skip_build_pred == INTERP_SKIP_LUMA_EVAL_CHROMA && + mbmi->interinter_comp.type != COMPOUND_DIFFWTD; + const int start_plane = skip_luma_plane ? AVM_PLANE_U : AVM_PLANE_Y; av2_enc_build_inter_predictor(cm, xd, mi_row, mi_col, env->orig_dst, bsize, - 0, av2_num_planes(cm) - 1); + start_plane, av2_num_planes(cm) - 1); } // So far we did not make prediction for WARPMV mode - assert(IMPLIES(mbmi->mode == WARPMV, skip_build_pred != 1)); + assert(IMPLIES(mbmi->mode == WARPMV, + skip_build_pred == INTERP_EVAL_LUMA_EVAL_CHROMA)); int rate2_nocoeff = rd_stats->rate; assert(IMPLIES(mbmi->mode == WARPMV, diff --git a/av2/encoder/speed_features.c b/av2/encoder/speed_features.c index 301f73447b..d29e192891 100644 --- a/av2/encoder/speed_features.c +++ b/av2/encoder/speed_features.c @@ -439,6 +439,8 @@ static void set_good_speed_features_framesize_independent( sf->inter_sf.skip_temporary_pred_for_opfl = 1; sf->inter_sf.enable_warp_inter_intra_in_winner = 1; + sf->interp_sf.skip_model_rd_uv = 1; + // Enable the optimized inter-SDP fast method (requires >=1 intra coded // block, prunes when inter-mode ratio exceeds 50%, and early skips when // the current best partitioning is PARTITION_NONE). @@ -974,6 +976,7 @@ static AVM_INLINE void init_inter_sf(INTER_MODE_SPEED_FEATURES *inter_sf) { static AVM_INLINE void init_interp_sf(INTERP_FILTER_SPEED_FEATURES *interp_sf) { interp_sf->use_interp_filter = 0; + interp_sf->skip_model_rd_uv = 0; } static AVM_INLINE void init_intra_sf(INTRA_MODE_SPEED_FEATURES *intra_sf) { diff --git a/av2/encoder/speed_features.h b/av2/encoder/speed_features.h index 8c3dc19658..0fb792a5ee 100644 --- a/av2/encoder/speed_features.h +++ b/av2/encoder/speed_features.h @@ -871,6 +871,10 @@ typedef struct INTERP_FILTER_SPEED_FEATURES { // Check mv and ref_frames before search, if they are very close with previous // saved results, filter search can be skipped. int use_interp_filter; + + // Skip model RD evaluation of chroma planes during interpolation filter + // search. Enabled for speed >= 1. + int skip_model_rd_uv; } INTERP_FILTER_SPEED_FEATURES; typedef struct INTRA_MODE_SPEED_FEATURES {