Commit 6f8adbe6fd for aom

commit 6f8adbe6fd82d0776ef8c740a49c54027b620ea1
Author: Lin Zheng <linzhen@google.com>
Date:   Mon Oct 26 21:34:36 2026 +0000

    Tune motion search, TPL, and transform speed features

    Refine GOOD mode speed features for speed >= 3.

    Combine with the previous 3 CLs, the BD-Rate and speed impact is:

                               Q-mode                     VBR-mode
    speed                  psnr1411:speed_up        psnr1411:vmaf:  speed_up
    ========================================================================
    1       {MEDIAN}        0.0400  5.543           0.0348  0.0350  4.946
            {OVERALL}       0.0518  6.544           0.0440  0.0456  5.410
    ========================================================================
    2       {MEDIAN}        0.0278  4.044           0.0390  0.0418  3.910
            {OVERALL}       0.0194  4.276           0.0260  0.0388  3.621
    ========================================================================
    3       {MEDIAN}        0.1080  6.152           0.0944  0.1282  5.937
            {OVERALL}       0.0946  6.526           0.0972  0.1194  6.850
    ========================================================================
    4       {MEDIAN}        -0.1222 9.713           -0.0346 0.2472  8.754
            {OVERALL}       -0.1916 14.746          -0.0428 0.1776  13.615
    ========================================================================
    5       {MEDIAN}        0.8130  12.782          0.8346  0.8708  11.185
            {OVERALL}       1.5898  24.017          1.6108  1.5626  21.648
    ========================================================================

    Change-Id: If673bf5b001ee9b90e26e67e6030265e4fda883c

diff --git a/av1/encoder/encodeframe_utils.h b/av1/encoder/encodeframe_utils.h
index 8cd163ad18..7d879df1ce 100644
--- a/av1/encoder/encodeframe_utils.h
+++ b/av1/encoder/encodeframe_utils.h
@@ -522,9 +522,17 @@ static inline unsigned int get_num_refs_to_disable(
         const FIRSTPASS_STATS *const this_frame_stats =
             read_one_frame_stats(&cpi->ppi->twopass, cur_frame_display_index);
         const double coded_error_per_mb = this_frame_stats->coded_error;
+        // Retain LAST2_FRAME on mixed static/high-motion frames at speeds 3-4
+        // to avoid quality loss.
+        const bool is_partial_high_motion =
+            cpi->oxcf.speed > 2 && cpi->oxcf.speed <= 4 &&
+            this_frame_stats->pcnt_motion >= 0.1 &&
+            this_frame_stats->pcnt_motion < 0.5 &&
+            this_frame_stats->sr_coded_error > 2.0 * coded_error_per_mb;
         // Disable LAST2_FRAME if the coded error of the current frame based on
         // first pass stats is very low.
-        if (coded_error_per_mb < 100.0) num_refs_to_disable++;
+        if (coded_error_per_mb < 100.0 && !is_partial_high_motion)
+          num_refs_to_disable++;
       }
 #endif  // CONFIG_REALTIME_ONLY
     }
diff --git a/av1/encoder/speed_features.c b/av1/encoder/speed_features.c
index 249c9d2234..09e9108e6a 100644
--- a/av1/encoder/speed_features.c
+++ b/av1/encoder/speed_features.c
@@ -950,7 +950,12 @@ static void set_good_speed_feature_framesize_dependent(
   }

   if (speed >= 4) {
-    sf->tx_sf.tx_type_search.winner_mode_tx_type_pruning = 2;
+    const int is_top_pyr =
+        boosted || (cpi->ppi->gf_group.max_layer_depth >= 6 &&
+                    cpi->ppi->gf_group.layer_depth[cpi->gf_frame_index] <= 2);
+    const int high_q = cpi->oxcf.rc_cfg.cq_level > 145;
+    sf->tx_sf.tx_type_search.winner_mode_tx_type_pruning =
+        (speed >= 5 || !boosted || !high_q) ? 2 : 1;
     sf->winner_mode_sf.enable_winner_mode_for_tx_size_srch = 1;
     if (is_720p_or_larger) {
       sf->part_sf.partition_search_breakout_dist_thr = (1 << 26);
@@ -960,7 +965,8 @@ static void set_good_speed_feature_framesize_dependent(
     sf->part_sf.early_term_after_none_split = 1;

     if (is_480p_or_larger) {
-      sf->tx_sf.tx_type_search.prune_tx_type_using_stats = 2;
+      sf->tx_sf.tx_type_search.prune_tx_type_using_stats =
+          (speed >= 5 || !boosted || !high_q) ? 2 : 1;
     } else {
       sf->mv_sf.skip_fullpel_search_using_startmv_refmv = boosted ? 0 : 1;
     }
@@ -970,10 +976,22 @@ static void set_good_speed_feature_framesize_dependent(
     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_720p_or_larger) {
-      sf->inter_sf.prune_comp_ref_frames = 2;
-    } else if (is_480p_or_larger) {
-      sf->inter_sf.prune_comp_ref_frames = is_boosted_arf2_bwd_type ? 0 : 2;
+    if (speed >= 5) {
+      if (is_720p_or_larger) {
+        sf->inter_sf.prune_comp_ref_frames = 2;
+      } else if (is_480p_or_larger) {
+        sf->inter_sf.prune_comp_ref_frames = is_boosted_arf2_bwd_type ? 0 : 2;
+      }
+      sf->lpf_sf.cdef_pick_method = CDEF_FAST_SEARCH_LVL3;
+    } else {
+      const int is_deep_pyr =
+          !boosted &&
+          (!high_q || cpi->ppi->gf_group.layer_depth[cpi->gf_frame_index] >=
+                          cpi->ppi->gf_group.max_layer_depth - 2);
+      sf->inter_sf.prune_comp_ref_frames =
+          (is_480p_or_lesser || is_top_pyr) ? 0 : (is_deep_pyr ? 2 : 1);
+      sf->lpf_sf.cdef_pick_method =
+          is_top_pyr ? CDEF_FAST_SEARCH_LVL1 : CDEF_FAST_SEARCH_LVL3;
     }

     if (is_720p_or_larger)
@@ -982,8 +1000,6 @@ static void set_good_speed_feature_framesize_dependent(
       sf->hl_sf.recode_tolerance = 55;

     sf->intra_sf.skip_intra_in_interframe = 4;
-
-    sf->lpf_sf.cdef_pick_method = CDEF_FAST_SEARCH_LVL3;
   }

   if (speed >= 5) {
@@ -1368,6 +1384,7 @@ static void set_good_speed_features_framesize_independent(
   }

   if (speed >= 4) {
+    sf->hl_sf.disable_extra_sc_testing = 1;
     sf->mv_sf.subpel_search_method = SUBPEL_TREE_PRUNED_MORE;

     sf->gm_sf.prune_zero_mv_with_sse = 2;
@@ -1382,21 +1399,14 @@ static void set_good_speed_features_framesize_independent(
         (allow_screen_content_tools || frame_is_intra_only(&cpi->common)) ? 0
                                                                           : 1;

-    sf->inter_sf.alt_ref_search_fp = 2;
-    sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_DEFAULT] = boosted ? 0 : 3;
-    sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_MOTION_MODE] = boosted ? 0 : 5;
-    sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_COMP_TYPE_MODE] = boosted ? 0 : 3;
+    sf->mv_sf.simple_motion_subpel_force_stop = HALF_PEL;
+    sf->mv_sf.prune_mesh_search = PRUNE_MESH_SEARCH_LVL_2;

-    sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 1 : 3;
+    sf->inter_sf.alt_ref_search_fp = 2;
     sf->inter_sf.prune_ext_comp_using_neighbors = 2;
     sf->inter_sf.prune_obmc_prob_thresh = INT_MAX;
     sf->inter_sf.disable_interinter_wedge_var_thresh = UINT_MAX;

-    sf->interp_sf.cb_pred_filter_search = 1;
-    sf->interp_sf.skip_sharp_interp_filter_search = 1;
-    sf->interp_sf.use_interp_filter = 2;
-    sf->interp_sf.use_more_sharp_interp = 0;
-
     sf->intra_sf.intra_uv_mode_mask[TX_16X16] = UV_INTRA_DC_H_V_CFL;
     sf->intra_sf.intra_uv_mode_mask[TX_32X32] = UV_INTRA_DC_H_V_CFL;
     sf->intra_sf.intra_uv_mode_mask[TX_64X64] = UV_INTRA_DC_H_V_CFL;
@@ -1406,14 +1416,9 @@ static void set_good_speed_features_framesize_independent(
     // sf->intra_sf.intra_y_mode_mask[TX_64X64] = INTRA_DC_H_V;
     sf->intra_sf.skip_intra_in_interframe = 4;

-    sf->mv_sf.simple_motion_subpel_force_stop = HALF_PEL;
-    sf->mv_sf.prune_mesh_search = PRUNE_MESH_SEARCH_LVL_2;
-
-    sf->tpl_sf.subpel_force_stop = HALF_PEL;
-    sf->tpl_sf.search_method = FAST_BIGDIA;
-    sf->tpl_sf.use_sad_for_mode_decision = 1;
-
-    sf->tx_sf.tx_type_search.fast_intra_tx_type_search = 1;
+    const int high_q = cpi->oxcf.rc_cfg.cq_level > 145;
+    sf->tx_sf.tx_type_search.fast_intra_tx_type_search =
+        (speed >= 5 || !boosted || !high_q) ? 1 : 0;

     sf->rd_sf.perform_coeff_opt = is_boosted_arf2_bwd_type ? 5 : 7;

@@ -1422,11 +1427,68 @@ static void set_good_speed_features_framesize_independent(
         frame_is_intra_only(&cpi->common) ? MULTI_WINNER_MODE_DEFAULT
                                           : MULTI_WINNER_MODE_OFF;
     sf->winner_mode_sf.dc_blk_pred_level = boosted ? 0 : 2;
+    sf->lpf_sf.lpf_pick = (speed >= 5 || !boosted || !high_q)
+                              ? LPF_PICK_FROM_FULL_IMAGE_NON_DUAL
+                              : LPF_PICK_FROM_FULL_IMAGE;

-    sf->lpf_sf.lpf_pick = LPF_PICK_FROM_FULL_IMAGE_NON_DUAL;
+    if (speed >= 5) {
+      sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_DEFAULT] = boosted ? 0 : 3;
+      sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_MOTION_MODE] = boosted ? 0 : 5;
+      sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_COMP_TYPE_MODE] =
+          boosted ? 0 : 3;
+
+      sf->inter_sf.prune_inter_modes_based_on_tpl = boosted ? 1 : 3;
+
+      sf->interp_sf.cb_pred_filter_search = 1;
+      sf->interp_sf.skip_sharp_interp_filter_search = 1;
+      sf->interp_sf.use_interp_filter = 2;
+      sf->interp_sf.use_more_sharp_interp = 0;
+
+      sf->tpl_sf.subpel_force_stop = HALF_PEL;
+      sf->tpl_sf.search_method = FAST_BIGDIA;
+      sf->tpl_sf.use_sad_for_mode_decision = 1;
+    } else {
+      // At speed 4, relax transform/loop-filter/interpolation pruning on top
+      // pyramid layers and low-QP frames while pruning deeper leaf layers
+      // more aggressively.
+      const int is_top_pyr =
+          boosted || (gf_group->max_layer_depth >= 6 &&
+                      gf_group->layer_depth[cpi->gf_frame_index] <= 2);
+      const int is_deep_pyr =
+          !boosted && (!high_q || gf_group->layer_depth[cpi->gf_frame_index] >=
+                                      gf_group->max_layer_depth - 2);
+      const int is_top_interp = high_q ? is_top_pyr : boosted;
+      sf->hl_sf.weight_calc_level_in_tf = 0;
+
+      sf->tpl_sf.subpel_force_stop = HALF_PEL;
+      sf->tpl_sf.prune_starting_mv = 2;
+      sf->tpl_sf.skip_alike_starting_mv = 1;
+      sf->tpl_sf.reduce_first_step_size = 4;
+      sf->tpl_sf.use_sad_for_mode_decision = 1;
+
+      sf->lpf_sf.enable_sgr_ep_pruning = boosted ? 1 : 4;
+      sf->lpf_sf.disable_wiener_coeff_refine_search = !boosted;
+
+      sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_DEFAULT] = is_top_pyr ? 0 : 3;
+      sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_MOTION_MODE] =
+          is_top_pyr ? 0 : 5;
+      sf->inter_sf.txfm_rd_gate_level[TX_SEARCH_COMP_TYPE_MODE] =
+          is_top_pyr ? 0 : 3;
+      sf->inter_sf.prune_inter_modes_based_on_tpl =
+          is_top_pyr ? 1 : (is_deep_pyr ? 3 : 2);
+
+      sf->interp_sf.cb_pred_filter_search = is_top_interp ? 0 : 1;
+      sf->interp_sf.skip_sharp_interp_filter_search = is_top_interp ? 0 : 1;
+      sf->interp_sf.use_interp_filter = is_top_interp ? 1 : 2;
+      sf->interp_sf.use_more_sharp_interp = 0;
+
+      sf->rd_sf.perform_coeff_opt =
+          boosted ? 3 : (is_boosted_arf2_bwd_type ? 5 : 7);
+    }
   }

   if (speed >= 5) {
+    sf->hl_sf.weight_calc_level_in_tf = 1;
     sf->hl_sf.adjust_num_frames_for_arf_filtering =
         allow_screen_content_tools ? 0 : 2;

@@ -1473,7 +1535,7 @@ static void set_good_speed_features_framesize_independent(
   }

   if (speed >= 6) {
-    sf->hl_sf.disable_extra_sc_testing = 1;
+    sf->hl_sf.disable_extra_sc_testing = 2;
     sf->hl_sf.second_alt_ref_filtering = 0;

     sf->gm_sf.downsample_level = 2;
@@ -3065,6 +3127,15 @@ void av1_set_speed_features_qindex_dependent(AV1_COMP *cpi, int speed) {
         sf->lpf_sf.disable_wiener_coeff_refine_search = true;
       }
     }
+    if (speed == 4) {
+      // Prune loop restoration filters on boosted frames at low/medium QP
+      // based on Wiener filter and source variance.
+      const int qthresh_lr = frame_is_intra_only(cm) ? 90 : 115;
+      if (boosted && cm->quant_params.base_qindex <= qthresh_lr) {
+        sf->lpf_sf.prune_sgr_based_on_wiener = 1;
+        sf->lpf_sf.prune_wiener_based_on_src_var = 1;
+      }
+    }
     sf->part_sf.less_rectangular_check_level = 2;
   }

@@ -3131,6 +3202,25 @@ void av1_set_speed_features_qindex_dependent(AV1_COMP *cpi, int speed) {
                            cpi->oxcf.unit_test_cfg.motion_vector_unit_test,
                            sf->mv_sf.subpel_search_method);

+  // Apply faster motion search and downsampled SAD for GOOD mode at speed > 2.
+  const int is_lf_frame =
+      cpi->ppi->gf_group.update_type[cpi->gf_frame_index] == LF_UPDATE;
+  if (cpi->oxcf.mode == GOOD && speed > 2 && !frame_is_intra_only(cm)) {
+    if (is_lf_frame) {
+      sf->mv_sf.search_method = DIAMOND;
+    }
+
+    sf->mv_sf.prune_mesh_search = PRUNE_MESH_SEARCH_LVL_2;
+
+    if (!boosted && sf->mv_sf.skip_fullpel_search_using_startmv_refmv == 0) {
+      sf->mv_sf.skip_fullpel_search_using_startmv_refmv = 2;
+    }
+
+    if (sf->mv_sf.use_downsampled_sad == 0) {
+      sf->mv_sf.use_downsampled_sad = 2;
+    }
+  }
+
   if (cpi->oxcf.enable_low_complexity_decode && cpi->oxcf.mode == GOOD)
     set_good_speed_features_lc_dec_qindex_dependent(cpi, sf, speed);
 }
diff --git a/av1/encoder/tpl_model.c b/av1/encoder/tpl_model.c
index a02adc7649..246193f5c0 100644
--- a/av1/encoder/tpl_model.c
+++ b/av1/encoder/tpl_model.c
@@ -162,6 +162,7 @@ void av1_setup_tpl_buffers(AV1_PRIMARY *const ppi,

   const int alloc_y_plane_only =
       ppi->cpi->sf.tpl_sf.use_y_only_rate_distortion ? 1 : 0;
+  tpl_data->tpl_stats_buffer[0].use_pred_sad = 1;
   for (int frame = 0; frame < MAX_LENGTH_TPL_FRAME_STATS; ++frame) {
     const int mi_cols =
         ALIGN_POWER_OF_TWO(mi_params->mi_cols, MAX_MIB_SIZE_LOG2);
@@ -1940,7 +1941,7 @@ static inline int eval_gop_length(double *beta,
       // Allow larger GOP size if the base layer ARF has higher dependency
       // factor than the intermediate ARF and both ARFs have reasonably high
       // dependency factors.
-      if ((beta[0] < beta[1] + 0.1) || beta[0] <= 1.4)
+      if ((beta[0] < beta[1] + 0.09) || beta[0] <= 1.40)
         return 0;  // Shorten the gf interval
       else
         return 1;  // Don't shorten the gf interval
@@ -2176,6 +2177,27 @@ int av1_tpl_setup_stats(AV1_COMP *cpi, int approx_gop_eval,
   av1_fill_mv_costs(&cm->fc->nmvc, cm->features.cur_frame_force_integer_mv,
                     cm->features.allow_high_precision_mv, cpi->td.mb.mv_costs);

+  const TPL_SPEED_FEATURES saved_tpl_sf = cpi->sf.tpl_sf;
+  if (cpi->oxcf.speed == 4) {
+    if (approx_gop_eval) {
+      // Use faster half-pel diamond search during approximate GOP length
+      // evaluation, and restrict RD to luma unless keyframe stats indicate
+      // high motion (use_pred_sad).
+      cpi->sf.tpl_sf.subpel_force_stop = HALF_PEL;
+      cpi->sf.tpl_sf.search_method = FAST_BIGDIA;
+      cpi->sf.tpl_sf.prune_starting_mv = 2;
+      cpi->sf.tpl_sf.skip_alike_starting_mv = 2;
+      cpi->sf.tpl_sf.reduce_first_step_size = 6;
+      cpi->sf.tpl_sf.use_sad_for_mode_decision = 1;
+      if (frame_params->frame_type != KEY_FRAME &&
+          !tpl_data->tpl_stats_buffer[0].use_pred_sad) {
+        cpi->sf.tpl_sf.use_y_only_rate_distortion = 1;
+      }
+    } else if (!tpl_data->tpl_stats_buffer[0].use_pred_sad) {
+      cpi->sf.tpl_sf.use_y_only_rate_distortion = 1;
+    }
+  }
+
   const int num_planes =
       cpi->sf.tpl_sf.use_y_only_rate_distortion ? 1 : av1_num_planes(cm);
   // As tpl module is called before the setting of speed features at frame
@@ -2259,6 +2281,7 @@ int av1_tpl_setup_stats(AV1_COMP *cpi, int approx_gop_eval,
     end_timing(cpi, av1_tpl_setup_stats_time);
 #endif

+  cpi->sf.tpl_sf = saved_tpl_sf;
   tpl_dealloc_temp_buffers(tpl_tmp_buffers);

   if (!approx_gop_eval) {
@@ -2275,6 +2298,15 @@ int av1_tpl_setup_stats(AV1_COMP *cpi, int approx_gop_eval,
       AOMMIN(tpl_gf_group_frames - 1, gf_group->arf_index + 1);
   beta[0] = get_frame_importance(tpl_data, frame_idx_0);
   beta[1] = get_frame_importance(tpl_data, frame_idx_1);
+  if (approx_gop_eval && cpi->oxcf.speed == 4) {
+    if (frame_params->frame_type == KEY_FRAME) {
+      tpl_data->tpl_stats_buffer[0].use_pred_sad =
+          (cpi->ppi->p_rc.baseline_gf_interval >= 32 && beta[0] <= 1.48);
+    }
+    if (!tpl_data->tpl_stats_buffer[0].use_pred_sad && beta[0] <= 1.42) {
+      beta[0] = 1.40;
+    }
+  }
 #if CONFIG_COLLECT_COMPONENT_TIMING
   end_timing(cpi, av1_tpl_setup_stats_time);
 #endif