Commit 6f75fec9dc for aom
commit 6f75fec9dc36361af93dd6bab9cfeea1f1c67c40
Author: Ranjit Kumar Tulabandu <ranjit.tulabandu@ittiam.com>
Date: Wed May 13 12:29:43 2026 +0530
Avoid unintended RD evaluation of second MV
The function find_fractional_mv_step() exits early during second MV
search in av1_single_motion_search(), if sub-pel ME is converging
to same result as that of first MV. However, av1_estimate_txfm_yrd()
was still called to choose best of these MVs in parent version
though second MV is partially evaluated.
In this CL, av1_estimate_txfm_yrd() is avoided for both the MVs in
these scenarios and the CL is applicable when
cpi->sf.mv_sf.disable_second_mv = 0. The CL is not bit-exact as
second sub-pel MV was a result of partial evaluation and was
winning occasionally though not intended which is avoided in this
CL.
Instruction Count BD-Rate Loss(%)
cpu Reduction(%) avg.psnr ovr.psnr ssim vmaf vmaf_neg
1 0.534 -0.0015 -0.0028 0.0084 0.0115 -0.0002
2 0.027 -0.0016 0.0007 -0.0109 -0.0277 -0.0285
3 0.008 0.0066 0.0147 0.0244 0.0098 0.0101
4 0.021 0.0094 0.0142 0.0145 0.0135 0.0090
5 -0.005 -0.0108 -0.0106 0.0001 0.0097 0.0138
6 0.039 0.0008 0.0017 -0.0001 0.0018 0.0009
STATS_CHANGED
Change-Id: I0759bd3e3e38668f859949e350963e7ede38c9c2
diff --git a/av1/encoder/motion_search_facade.c b/av1/encoder/motion_search_facade.c
index 0bd7d9db73..4c6d4f8b4f 100644
--- a/av1/encoder/motion_search_facade.c
+++ b/av1/encoder/motion_search_facade.c
@@ -426,23 +426,6 @@ void av1_single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
{ p[0].dst.buf, p[1].dst.buf, p[2].dst.buf },
{ p[0].dst.stride, p[1].dst.stride, p[2].dst.stride },
};
- int64_t rd = INT64_MAX;
- if (!mv_sf->disable_second_mv) {
- // Calculate actual rd cost.
- mbmi->mv[0].as_mv = best_mv->as_mv;
- av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, &orig_dst,
- bsize, 0, 0);
- av1_subtract_plane(x, bsize, 0);
- RD_STATS this_rd_stats;
- av1_init_rd_stats(&this_rd_stats);
- av1_estimate_txfm_yrd(cpi, x, &this_rd_stats, INT64_MAX, bsize,
- max_txsize_rect_lookup[bsize]);
- int this_mv_rate = av1_mv_bit_cost(
- &best_mv->as_mv, &ref_mv, mv_costs->nmv_joint_cost,
- mv_costs->mv_cost_stack, MV_COST_WEIGHT);
- rd = RDCOST(x->rdmult, this_mv_rate + this_rd_stats.rate,
- this_rd_stats.dist);
- }
MV this_best_mv;
subpel_start_mv = get_mv_from_fullmv(&second_best_mv.as_fullmv);
@@ -453,9 +436,26 @@ void av1_single_motion_search(const AV1_COMP *const cpi, MACROBLOCK *x,
xd, cm, &ms_params, subpel_start_mv, NULL, &this_best_mv,
&dis, &sse, fractional_ms_list);
- if (!mv_sf->disable_second_mv) {
- // If cpi->sf.mv_sf.disable_second_mv is 0, use actual rd cost
- // to choose the better MV.
+ // If cpi->sf.mv_sf.disable_second_mv is 0 and both MVs are valid,
+ // use actual rd cost to choose the better MV.
+ if (!mv_sf->disable_second_mv && this_var != INT32_MAX) {
+ // Calculate rd cost of first MV.
+ mbmi->mv[0].as_mv = best_mv->as_mv;
+ av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, &orig_dst,
+ bsize, 0, 0);
+ av1_subtract_plane(x, bsize, 0);
+ RD_STATS this_rd_stats;
+ av1_init_rd_stats(&this_rd_stats);
+ av1_estimate_txfm_yrd(cpi, x, &this_rd_stats, INT64_MAX, bsize,
+ max_txsize_rect_lookup[bsize]);
+ int this_mv_rate = av1_mv_bit_cost(
+ &best_mv->as_mv, &ref_mv, mv_costs->nmv_joint_cost,
+ mv_costs->mv_cost_stack, MV_COST_WEIGHT);
+ const int64_t rd =
+ RDCOST(x->rdmult, this_mv_rate + this_rd_stats.rate,
+ this_rd_stats.dist);
+
+ // Calculate rd cost of second MV.
mbmi->mv[0].as_mv = this_best_mv;
av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, &orig_dst,
bsize, 0, 0);