Commit 77f51c64f6 for aom

commit 77f51c64f67322cc88b8852be80b529c51b95980
Author: Diksha Singh <diksha.singh@ittiam.com>
Date:   Thu Mar 12 10:59:49 2026 +0530

    Extend sf 'prune_single_ref' to speed 3, 4

    The speed feature 'prune_single_ref' is extended to speed 3, 4
    conservatively by disallowing pruning of certain reference frames
    based on the relative distance to the current frame and base_qindex.

    Encoder performance results averaged over all resolutions are as follows:

          Instruction Count           BD-Rate Loss(%)
    cpu     Reduction(%)      avg.psnr  ovr.psnr   ssim     vmaf   vmaf_neg
     3         2.33            0.0112   -0.0007  -0.0130  -0.0120  -0.0216
     4         1.57            0.0047    0.0079  -0.0047   0.0309   0.0014

    STATS_CHAGNED for speed=3,4

    Change-Id: I1aceac2f0107c88b16cd0df9713fa446b57170d3

diff --git a/av1/common/av1_common_int.h b/av1/common/av1_common_int.h
index 715fe2ced7..e083c2b0d5 100644
--- a/av1/common/av1_common_int.h
+++ b/av1/common/av1_common_int.h
@@ -139,6 +139,7 @@ typedef struct RefCntBuffer {
   unsigned int ref_display_order_hint[INTER_REFS_PER_FRAME];
   // Frame's level within the hierarchical structure.
   unsigned int pyramid_level;
+  int base_qindex;
   MV_REF *mvs;
   uint8_t *seg_map;
   struct segmentation seg;
diff --git a/av1/common/mvref_common.c b/av1/common/mvref_common.c
index fdb2c8d870..3f86c1ddb6 100644
--- a/av1/common/mvref_common.c
+++ b/av1/common/mvref_common.c
@@ -844,6 +844,7 @@ void av1_setup_frame_buf_refs(AV1_COMMON *cm) {
   cm->cur_frame->order_hint = cm->current_frame.order_hint;
   cm->cur_frame->display_order_hint = cm->current_frame.display_order_hint;
   cm->cur_frame->pyramid_level = cm->current_frame.pyramid_level;
+  cm->cur_frame->base_qindex = cm->quant_params.base_qindex;
   cm->cur_frame->filter_level[0] = -1;
   cm->cur_frame->filter_level[1] = -1;
   MV_REFERENCE_FRAME ref_frame;
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index 6f3bbc21aa..76c73aca98 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -1696,6 +1696,71 @@ static inline void set_default_interp_skip_flags(
                         : INTERP_SKIP_LUMA_SKIP_CHROMA;
 }

+/*!\cond */
+typedef struct {
+  // Scoring function for usefulness of references (the lower score, the more
+  // useful)
+  int score;
+  // Index in the reference buffer
+  int index;
+} RefScoreData;
+/*!\endcond */
+
+// Comparison function to sort reference frames in ascending score order.
+static int compare_score_data_asc(const void *a, const void *b) {
+  const RefScoreData *ra = (const RefScoreData *)a;
+  const RefScoreData *rb = (const RefScoreData *)b;
+
+  const int score_diff = ra->score - rb->score;
+  if (score_diff != 0) return score_diff;
+
+  return ra->index - rb->index;
+}
+
+// Determines whether a given reference frame is "good" based on temporal
+// distance and base_qindex. The "good" reference frames are not allowed to be
+// pruned by the speed feature "prune_single_ref" frame at block level.
+static inline void setup_keep_single_ref_frame_mask(AV1_COMP *cpi) {
+  const int prune_single_ref = cpi->sf.inter_sf.prune_single_ref;
+  const AV1_COMMON *const cm = &cpi->common;
+
+  if (prune_single_ref != 1 || frame_is_intra_only(cm)) {
+    cpi->keep_single_ref_frame_mask =
+        (prune_single_ref == 0) ? ((1 << REF_FRAMES) - 1) : 0;
+    return;
+  }
+  RefScoreData ref_score_data[INTER_REFS_PER_FRAME];
+  for (int i = 0; i < INTER_REFS_PER_FRAME; ++i) {
+    ref_score_data[i].score = INT_MAX;
+    ref_score_data[i].index = i;
+  }
+
+  // Calculate score for each reference frame based on relative distance to
+  // the current frame and its base_qindex. A lower score means that the
+  // reference is potentially more useful.
+  for (MV_REFERENCE_FRAME ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME;
+       ++ref_frame) {
+    if (cpi->ref_frame_flags & av1_ref_frame_flag_list[ref_frame]) {
+      const RefFrameDistanceInfo *const ref_frame_dist_info =
+          &cpi->ref_frame_dist_info;
+      const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame);
+      ref_score_data[ref_frame - LAST_FRAME].score =
+          abs(ref_frame_dist_info->ref_relative_dist[ref_frame - LAST_FRAME]) +
+          buf->base_qindex;
+    }
+  }
+
+  qsort(ref_score_data, INTER_REFS_PER_FRAME, sizeof(ref_score_data[0]),
+        compare_score_data_asc);
+
+  cpi->keep_single_ref_frame_mask = 0;
+  const int num_frames_to_keep = 3;
+  for (int i = 0; i < num_frames_to_keep; ++i) {
+    const int idx = ref_score_data[i].index;
+    cpi->keep_single_ref_frame_mask |= 1 << idx;
+  }
+}
+
 static inline void setup_prune_ref_frame_mask(AV1_COMP *cpi) {
   if ((!cpi->oxcf.ref_frm_cfg.enable_onesided_comp ||
        cpi->sf.inter_sf.disable_onesided_comp) &&
@@ -2263,6 +2328,9 @@ static inline void encode_frame_internal(AV1_COMP *cpi) {
   cpi->prune_ref_frame_mask = 0;
   // Figure out which ref frames can be skipped at frame level.
   setup_prune_ref_frame_mask(cpi);
+  // Disable certain reference frame pruning based on temporal distance and
+  // quality of that reference frame.
+  setup_keep_single_ref_frame_mask(cpi);

   x->txfm_search_info.txb_split_count = 0;
 #if CONFIG_SPEED_STATS
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 28c07c0659..f708c7e33a 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -3339,6 +3339,13 @@ typedef struct AV1_COMP {
    */
   int prune_ref_frame_mask;

+  /*!
+   * Mark the reference frames which are important (based on the temporal
+   * distance and quality) to prevent pruning the reference frame at block
+   * level.
+   */
+  int keep_single_ref_frame_mask;
+
   /*!
    * Loop Restoration context.
    */
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index dde46f697f..01e4bf7227 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -4180,8 +4180,8 @@ static inline void init_mode_skip_mask(mode_skip_mask_t *mask,
   // Prune reference frames which are not the closest to the current
   // frame and with large pred_mv_sad.
   if (inter_sf->prune_single_ref) {
-    assert(inter_sf->prune_single_ref > 0 && inter_sf->prune_single_ref < 3);
-    const double prune_threshes[2] = { 1.20, 1.05 };
+    assert(inter_sf->prune_single_ref > 0 && inter_sf->prune_single_ref < 4);
+    const double prune_threshes[3] = { 1.20, 1.20, 1.05 };

     for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
       const RefFrameDistanceInfo *const ref_frame_dist_info =
@@ -4189,8 +4189,10 @@ static inline void init_mode_skip_mask(mode_skip_mask_t *mask,
       const int is_closest_ref =
           (ref_frame == ref_frame_dist_info->nearest_past_ref) ||
           (ref_frame == ref_frame_dist_info->nearest_future_ref);
+      const int ref_idx = ref_frame - LAST_FRAME;

-      if (!is_closest_ref) {
+      if (!(cpi->keep_single_ref_frame_mask & (1 << ref_idx) ||
+            is_closest_ref)) {
         const int dir =
             (ref_frame_dist_info->ref_relative_dist[ref_frame - LAST_FRAME] < 0)
                 ? 0
diff --git a/av1/encoder/speed_features.c b/av1/encoder/speed_features.c
index 9537f0a2f8..876ed33e29 100644
--- a/av1/encoder/speed_features.c
+++ b/av1/encoder/speed_features.c
@@ -1285,6 +1285,7 @@ static void set_good_speed_features_framesize_independent(
     set_txfm_rd_gate_level(sf->inter_sf.txfm_rd_gate_level,
                            boosted ? 0 : (is_boosted_arf2_bwd_type ? 1 : 2));
     sf->inter_sf.inter_mode_txfm_breakout = boosted ? 0 : 2;
+    sf->inter_sf.prune_single_ref = 1;

     sf->interp_sf.adaptive_interp_filter_search = 2;

@@ -1401,7 +1402,7 @@ static void set_good_speed_features_framesize_independent(
     sf->mv_sf.warp_search_method = WARP_SEARCH_DIAMOND;

     sf->inter_sf.prune_inter_modes_if_skippable = 1;
-    sf->inter_sf.prune_single_ref = is_boosted_arf2_bwd_type ? 0 : 1;
+    sf->inter_sf.prune_single_ref = is_boosted_arf2_bwd_type ? 0 : 2;
     sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_DEFAULT] = boosted ? 0 : 4;
     sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_COMP_TYPE_MODE] = boosted ? 0 : 5;
     sf->inter_sf.enable_fast_compound_mode_search = 2;
@@ -1439,7 +1440,7 @@ static void set_good_speed_features_framesize_independent(

     sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 1 : 4;
     sf->inter_sf.selective_ref_frame = 6;
-    sf->inter_sf.prune_single_ref = is_boosted_arf2_bwd_type ? 0 : 2;
+    sf->inter_sf.prune_single_ref = is_boosted_arf2_bwd_type ? 0 : 3;
     sf->inter_sf.prune_ext_comp_using_neighbors = 3;

     sf->intra_sf.chroma_intra_pruning_with_hog = 4;
diff --git a/av1/encoder/speed_features.h b/av1/encoder/speed_features.h
index 7b87f3cd67..3d81c3b4f2 100644
--- a/av1/encoder/speed_features.h
+++ b/av1/encoder/speed_features.h
@@ -1023,7 +1023,7 @@ typedef struct INTER_MODE_SPEED_FEATURES {
   int alt_ref_search_fp;

   // Prune reference frames for single prediction modes based on temporal
-  // distance and pred MV SAD. Feasible values are 0, 1, 2. The feature is
+  // distance and pred MV SAD. Feasible values are 0, 1, 2, 3. The feature is
   // disabled for 0. An increasing value indicates more aggressive pruning
   // threshold.
   int prune_single_ref;