Commit 870508a1a3 for aom
commit 870508a1a31858b773bc53caf6b1fc9fa458ef8c
Author: Remya Prakasan <remya.prakasan@ittiam.com>
Date: Tue Oct 13 00:02:07 2026 +0530
Skip NEWMV based on modelRD after subpel search
This CL introduces an intermediate level for the speed
feature 'skip_newmv_in_drl' to enable pruning of NEWMV
modes based on modelRD cost computed after subpel motion
search. This level is enabled for speed >= 2 for non-boosted
frames and tuned to make the pruning less aggressive for
refmv indices < 2 for resolutions < 720p in speed 2.
Encoder performance results averaged over all resolutions are
as follows:
Instruction Count BD-Rate Loss(%)
cpu Reduction(%) avg.psnr ssim vmaf vmaf_neg uvq_1p5
2 1.937 0.0133 0.0123 0.047 -0.0086 0.1586
3 0.828 0.0157 0.0121 0.0077 0.0194 0.1394
4 0.569 0.0137 0.0113 -0.0257 -0.0146 0.287
5 0.173 0.0018 0.0162 -0.0048 0.0051 0.0033
6 0.123 0.0185 0.0119 -0.0034 0.0122 0.1728
STATS_CHANGED for speed >= 2
Change-Id: I47ab34080ae805d1b37121c0c9aac603e45e093d
diff --git a/av1/encoder/motion_search_facade.c b/av1/encoder/motion_search_facade.c
index f09c9112d9..dab1f13957 100644
--- a/av1/encoder/motion_search_facade.c
+++ b/av1/encoder/motion_search_facade.c
@@ -15,6 +15,7 @@
#include "av1/encoder/encoder.h"
#include "av1/encoder/interp_search.h"
#include "av1/encoder/mcomp.h"
+#include "av1/encoder/model_rd.h"
#include "av1/encoder/motion_search_facade.h"
#include "av1/encoder/partition_strategy.h"
#include "av1/encoder/reconinter_enc.h"
@@ -117,6 +118,15 @@ static inline void get_mv_candidate_from_tpl(const AV1_COMP *const cpi,
}
}
+static inline int64_t compute_modeled_rd(const AV1_COMP *const cpi,
+ const MACROBLOCK *x, BLOCK_SIZE bsize,
+ unsigned int sse, int rate_cost) {
+ int res_rate = 0;
+ int64_t dist = 0;
+ model_rd_from_sse(cpi, x, bsize, AOM_PLANE_Y, sse, 0, &res_rate, &dist);
+ return RDCOST(x->rdmult, res_rate + rate_cost, dist);
+}
+
void av1_single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
BLOCK_SIZE bsize, int ref_idx, int *rate_mv,
int search_range, inter_mode_info *mode_info,
@@ -342,9 +352,9 @@ void av1_single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
}
// Terminate search with the current ref_idx based on fullpel mv, rate cost,
- // and other know cost.
- if (cpi->sf.inter_sf.skip_newmv_in_drl >= 2 &&
- mbmi->motion_mode == SIMPLE_TRANSLATION &&
+ // and other known cost.
+ int skip_newmv_in_drl = cpi->sf.inter_sf.skip_newmv_in_drl;
+ if (skip_newmv_in_drl >= 4 && mbmi->motion_mode == SIMPLE_TRANSLATION &&
best_mv->as_int != INVALID_MV) {
int_mv this_mv;
this_mv.as_mv = get_mv_from_fullmv(&best_mv->as_fullmv);
@@ -380,9 +390,8 @@ void av1_single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
// drl_cost.
const int psme = mode_info[prev_ref_idx].full_mv_bestsme;
if (psme == INT_MAX) continue;
- const int thr =
- cpi->sf.inter_sf.skip_newmv_in_drl == 3 ? (psme + (psme >> 2)) : psme;
- if (cpi->sf.inter_sf.skip_newmv_in_drl >= 3 &&
+ const int thr = skip_newmv_in_drl == 5 ? (psme + (psme >> 2)) : psme;
+ if (skip_newmv_in_drl >= 5 &&
mode_info[ref_mv_idx].full_mv_bestsme > thr &&
mode_info[prev_ref_idx].drl_cost < mode_info[ref_mv_idx].drl_cost) {
best_mv->as_int = INVALID_MV;
@@ -496,9 +505,9 @@ void av1_single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
default: assert(0 && "Invalid motion mode!\n");
}
- // Terminate search with the current ref_idx based on subpel mv and rate
- // cost.
- if (cpi->sf.inter_sf.skip_newmv_in_drl >= 1 && args != NULL &&
+ // Terminate search with the current ref_idx based on subpel mv, pred_sse,
+ // and rate cost.
+ if (skip_newmv_in_drl && args != NULL &&
mbmi->motion_mode == SIMPLE_TRANSLATION &&
best_mv->as_int != INVALID_MV) {
const int ref_mv_idx = mbmi->ref_mv_idx;
@@ -507,29 +516,60 @@ void av1_single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
mv_costs->mv_cost_stack, MV_COST_WEIGHT);
mv_rate_calculated = 1;
+ const unsigned int this_sse = x->pred_sse[ref];
+ mode_info[ref_mv_idx].pred_sse = this_sse;
+ mode_info[ref_mv_idx].model_rd = INT64_MAX;
+ const int this_rate_cost = best_mv_rate + mode_info[ref_mv_idx].drl_cost;
+ int64_t this_model_rd = INT64_MAX;
+
for (int prev_ref_idx = 0; prev_ref_idx < ref_mv_idx; ++prev_ref_idx) {
if (!args->single_newmv_valid[prev_ref_idx][ref]) continue;
- // Check if the motion vectors are the same.
+
+ const int prev_rate_cost = args->single_newmv_rate[prev_ref_idx][ref] +
+ mode_info[prev_ref_idx].drl_cost;
+
+ // Case 1: If identical MVs, prune if worse/equal rate.
if (best_mv->as_int == args->single_newmv[prev_ref_idx][ref].as_int) {
// Skip this evaluation if the previous one is skipped.
if (mode_info[prev_ref_idx].skip) {
mode_info[ref_mv_idx].skip = 1;
break;
}
- // Compare the rate cost that we current know.
- const int prev_rate_cost =
- args->single_newmv_rate[prev_ref_idx][ref] +
- mode_info[prev_ref_idx].drl_cost;
- const int this_rate_cost =
- best_mv_rate + mode_info[ref_mv_idx].drl_cost;
-
if (prev_rate_cost <= this_rate_cost) {
- // If the current rate_cost is worse than the previous rate_cost,
- // then we terminate the search for this ref_mv_idx.
mode_info[ref_mv_idx].skip = 1;
break;
}
}
+
+ // Case 2: Prune based on modeled rd.
+ if (skip_newmv_in_drl > 1) {
+ const unsigned int prev_sse = mode_info[prev_ref_idx].pred_sse;
+ if (prev_sse != UINT_MAX && prev_rate_cost < this_rate_cost) {
+ if (this_model_rd == INT64_MAX) {
+ this_model_rd =
+ compute_modeled_rd(cpi, x, bsize, this_sse, this_rate_cost);
+ mode_info[ref_mv_idx].model_rd = this_model_rd;
+ }
+
+ int64_t prev_model_rd = mode_info[prev_ref_idx].model_rd;
+ if (prev_model_rd == INT64_MAX) {
+ prev_model_rd =
+ compute_modeled_rd(cpi, x, bsize, prev_sse, prev_rate_cost);
+ mode_info[prev_ref_idx].model_rd = prev_model_rd;
+ }
+
+ int64_t rd_thresh = prev_model_rd;
+ // Prune less aggressively for lower refmv indices.
+ if (skip_newmv_in_drl == 2 && ref_mv_idx < 2) {
+ rd_thresh += prev_model_rd >> 3;
+ }
+
+ if (this_model_rd > rd_thresh) {
+ mode_info[ref_mv_idx].skip = 1;
+ break;
+ }
+ }
+ }
}
}
}
diff --git a/av1/encoder/motion_search_facade.h b/av1/encoder/motion_search_facade.h
index e5b4dd2dc0..9c3118295d 100644
--- a/av1/encoder/motion_search_facade.h
+++ b/av1/encoder/motion_search_facade.h
@@ -23,10 +23,12 @@ extern "C" {
// TODO(any): rename this struct to something else. There is already another
// struct called inter_modes_info, which makes this terribly confusing.
typedef struct {
+ int64_t model_rd;
int drl_cost;
int_mv full_search_mv;
int full_mv_rate;
int full_mv_bestsme;
+ unsigned int pred_sse;
int skip;
} inter_mode_info;
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 5e0afbc8ef..0414900f8b 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -3280,6 +3280,8 @@ static int64_t handle_inter_mode(
mode_info[ref_mv_idx].full_search_mv.as_int = INVALID_MV;
mode_info[ref_mv_idx].full_mv_bestsme = INT_MAX;
+ mode_info[ref_mv_idx].pred_sse = UINT_MAX;
+ mode_info[ref_mv_idx].model_rd = INT64_MAX;
const int drl_cost = get_drl_cost(
mbmi, mbmi_ext, mode_costs->drl_mode_cost0, ref_frame_type);
mode_info[ref_mv_idx].drl_cost = drl_cost;
diff --git a/av1/encoder/speed_features.c b/av1/encoder/speed_features.c
index 09e9108e6a..4f2752d666 100644
--- a/av1/encoder/speed_features.c
+++ b/av1/encoder/speed_features.c
@@ -814,6 +814,7 @@ static void set_good_speed_feature_framesize_dependent(
}
if (speed >= 2) {
+ sf->inter_sf.skip_newmv_in_drl = boosted ? 1 : !is_720p_or_larger ? 2 : 3;
sf->part_sf.ml_4_partition_search_level_index = 2;
if (is_720p_or_larger) {
sf->part_sf.use_square_partition_only_threshold = BLOCK_64X64;
@@ -887,7 +888,7 @@ static void set_good_speed_feature_framesize_dependent(
if (speed >= 3) {
sf->inter_sf.enable_fast_wedge_mask_search = 1;
- sf->inter_sf.skip_newmv_in_drl = 2;
+ sf->inter_sf.skip_newmv_in_drl = 4;
sf->inter_sf.skip_ext_comp_nearmv_mode = 1;
sf->inter_sf.limit_inter_mode_cands = is_lf_frame ? 3 : 0;
sf->inter_sf.disable_interinter_wedge_newmv_search = boosted ? 0 : 1;
@@ -974,7 +975,7 @@ static void set_good_speed_feature_framesize_dependent(
sf->inter_sf.disable_interinter_wedge_var_thresh = UINT_MAX;
sf->inter_sf.prune_obmc_prob_thresh = INT_MAX;
sf->inter_sf.limit_txfm_eval_per_mode = boosted ? 0 : 2;
- if (is_480p_or_lesser) sf->inter_sf.skip_newmv_in_drl = 3;
+ if (is_480p_or_lesser) sf->inter_sf.skip_newmv_in_drl = 5;
if (speed >= 5) {
if (is_720p_or_larger) {
@@ -1010,7 +1011,7 @@ static void set_good_speed_feature_framesize_dependent(
}
if (is_720p_or_larger) sf->hl_sf.recode_tolerance = 40;
- sf->inter_sf.skip_newmv_in_drl = 4;
+ sf->inter_sf.skip_newmv_in_drl = 6;
sf->inter_sf.prune_comp_ref_frames = 2;
sf->mv_sf.skip_fullpel_search_using_startmv_refmv = boosted ? 0 : 1;
@@ -2060,7 +2061,7 @@ static void set_rt_speed_features_framesize_independent(AV1_COMP *cpi,
sf->inter_sf.prune_inter_modes_if_skippable = 1;
sf->inter_sf.prune_nearmv_using_neighbors = PRUNE_NEARMV_LEVEL3;
sf->inter_sf.reduce_inter_modes = boosted ? 1 : 3;
- sf->inter_sf.skip_newmv_in_drl = 4;
+ sf->inter_sf.skip_newmv_in_drl = 6;
sf->interp_sf.use_fast_interpolation_filter_search = 1;
sf->interp_sf.use_interp_filter = 1;
diff --git a/av1/encoder/speed_features.h b/av1/encoder/speed_features.h
index c095780a0c..b1b1aa4921 100644
--- a/av1/encoder/speed_features.h
+++ b/av1/encoder/speed_features.h
@@ -1053,18 +1053,20 @@ typedef struct INTER_MODE_SPEED_FEATURES {
// current frame in both past and future direction.
int prune_comp_ref_frames;
- // Skip the current ref_mv in NEW_MV mode based on mv, rate cost, etc.
- // This speed feature equaling 0 means no skipping.
- // If the speed feature equals 1 or 2, skip the current ref_mv in NEW_MV mode
- // if we have already encountered ref_mv in the drl such that:
- // 1. The other drl has the same mv during the SIMPLE_TRANSLATION search
- // process as the current mv.
- // 2. The rate needed to encode the current mv is larger than that for the
- // other ref_mv.
- // The speed feature equaling 1 means using subpel mv in the comparison.
- // The speed feature equaling 2 means using fullpel mv in the comparison.
- // If the speed feature >= 3, skip the current ref_mv in NEW_MV mode based on
- // known full_mv bestsme and drl cost.
+ // Skip the evaluation of the current ref_mv in NEW_MV mode based on motion
+ // vectors, rate cost, modeled RD cost, and search error:
+ // 0: Disabled.
+ // 1: After subpel search, skip if subpel MV is identical to an earlier DRL
+ // candidate and rate cost is worse/equal.
+ // 2, 3: In addition to 1, prune based on modeled RD cost when rate is worse
+ // (level 2: relaxed threshold for ref_mv_idx < 2 and resolution < 720p, level
+ // 3: strict threshold). 4: In addition to post-subpel pruning, skip before
+ // subpel search if the
+ // full-pel search MV is identical to an earlier DRL candidate and full-pel
+ // rate cost is worse/equal.
+ // 5, 6: In addition to 4, skip before subpel search if full-pel search error
+ // and drl_cost are worse (level 5: relaxed threshold with 25% margin,
+ // level 6: strict threshold).
int skip_newmv_in_drl;
// This speed feature checks duplicate ref MVs among NEARESTMV, NEARMV,