aom: rtc-svc: Rework RC on scene change for spatial layers.

From 0293acbeaa62506386f0c7c57f471043f7337719 Mon Sep 17 00:00:00 2001
From: Marco Paniconi <[EMAIL REDACTED]>
Date: Fri, 19 Jan 2024 17:56:55 +0000
Subject: [PATCH] rtc-svc: Rework RC on scene change for spatial layers.

On scene/slide changes: the reset and max-qp should only
need to be done for non-zero spatial layers if no inter-layer
(spatial) prediction is done.

The CL checks for this and fixes the reset/max-qp logic.

This improves quality with minimal overshoot on slide changes
for screen with quality layers.

Change-Id: I637a23a90bc2734c0f0ae2930fe72d2ebe6ab519
---
 av1/encoder/ratectrl.c         | 34 +++++++++++++++++++++++++++-------
 av1/encoder/svc_layercontext.c | 12 +++++++-----
 av1/encoder/svc_layercontext.h | 15 +++++++++++++++
 3 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index 2f0cd263d0..b7cc06e8e7 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -3520,6 +3520,10 @@ void av1_get_one_pass_rt_params(AV1_COMP *cpi, FRAME_TYPE *const frame_type,
   }
 }
 
+#define CHECK_INTER_LAYER_PRED(ref_frame)                         \
+  ((cpi->ref_frame_flags & av1_ref_frame_flag_list[ref_frame]) && \
+   (av1_check_ref_is_low_spatial_res_super_frame(cpi, ref_frame)))
+
 int av1_encodedframe_overshoot_cbr(AV1_COMP *cpi, int *q) {
   AV1_COMMON *const cm = &cpi->common;
   PRIMARY_RATE_CONTROL *const p_rc = &cpi->ppi->p_rc;
@@ -3530,12 +3534,26 @@ int av1_encodedframe_overshoot_cbr(AV1_COMP *cpi, int *q) {
   int target_bits_per_mb;
   double q2;
   int enumerator;
+  int inter_layer_pred_on = 0;
   int is_screen_content = (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN);
-  *q = (3 * cpi->rc.worst_quality + *q) >> 2;
-  // For screen content use the max-q set by the user to allow for less
-  // overshoot on slide changes.
-  if (is_screen_content) *q = cpi->rc.worst_quality;
   cpi->cyclic_refresh->counter_encode_maxq_scene_change = 0;
+  if (cpi->svc.spatial_layer_id > 0) {
+    // For spatial layers: check if inter-layer (spatial) prediction is used
+    // (check if any reference is being used that is the lower spatial layer),
+    inter_layer_pred_on = CHECK_INTER_LAYER_PRED(LAST_FRAME) ||
+                          CHECK_INTER_LAYER_PRED(GOLDEN_FRAME) ||
+                          CHECK_INTER_LAYER_PRED(ALTREF_FRAME);
+  }
+  // If inter-layer prediction is on: we expect to pull up the quality from
+  // the lower spatial layer, so we can use a lower q.
+  if (cpi->svc.spatial_layer_id > 0 && inter_layer_pred_on) {
+    *q = (cpi->rc.worst_quality + *q) >> 1;
+  } else {
+    *q = (3 * cpi->rc.worst_quality + *q) >> 2;
+    // For screen content use the max-q set by the user to allow for less
+    // overshoot on slide changes.
+    if (is_screen_content) *q = cpi->rc.worst_quality;
+  }
   // Adjust avg_frame_qindex, buffer_level, and rate correction factors, as
   // these parameters will affect QP selection for subsequent frames. If they
   // have settled down to a very different (low QP) state, then not adjusting
@@ -3564,8 +3582,10 @@ int av1_encodedframe_overshoot_cbr(AV1_COMP *cpi, int *q) {
         rate_correction_factor;
   }
   // For temporal layers: reset the rate control parameters across all
-  // temporal layers.
-  if (cpi->svc.number_temporal_layers > 1) {
+  // temporal layers. Only do it for spatial enhancement layers when
+  // inter_layer_pred_on is not set (off).
+  if (cpi->svc.number_temporal_layers > 1 &&
+      (cpi->svc.spatial_layer_id == 0 || inter_layer_pred_on == 0)) {
     SVC *svc = &cpi->svc;
     for (int tl = 0; tl < svc->number_temporal_layers; ++tl) {
       int sl = svc->spatial_layer_id;
@@ -3582,4 +3602,4 @@ int av1_encodedframe_overshoot_cbr(AV1_COMP *cpi, int *q) {
     }
   }
   return 1;
-}
+}
\ No newline at end of file
diff --git a/av1/encoder/svc_layercontext.c b/av1/encoder/svc_layercontext.c
index 2c99cb89b8..1ac81ce6ee 100644
--- a/av1/encoder/svc_layercontext.c
+++ b/av1/encoder/svc_layercontext.c
@@ -203,8 +203,10 @@ void av1_update_temporal_layer_framerate(AV1_COMP *const cpi) {
   }
 }
 
-static AOM_INLINE bool check_ref_is_low_spatial_res_super_frame(
-    int ref_frame, const SVC *svc, const RTC_REF *rtc_ref) {
+AOM_INLINE bool av1_check_ref_is_low_spatial_res_super_frame(
+    AV1_COMP *const cpi, int ref_frame) {
+  SVC *svc = &cpi->svc;
+  RTC_REF *const rtc_ref = &cpi->ppi->rtc_ref;
   int ref_frame_idx = rtc_ref->ref_idx[ref_frame - 1];
   return rtc_ref->buffer_time_index[ref_frame_idx] == svc->current_superframe &&
          rtc_ref->buffer_spatial_layer[ref_frame_idx] <=
@@ -253,13 +255,13 @@ void av1_restore_layer_context(AV1_COMP *const cpi) {
   // previous spatial layer(s) at the same time (current_superframe).
   if (rtc_ref->set_ref_frame_config && svc->force_zero_mode_spatial_ref &&
       cpi->sf.rt_sf.use_nonrd_pick_mode) {
-    if (check_ref_is_low_spatial_res_super_frame(LAST_FRAME, svc, rtc_ref)) {
+    if (av1_check_ref_is_low_spatial_res_super_frame(cpi, LAST_FRAME)) {
       svc->skip_mvsearch_last = 1;
     }
-    if (check_ref_is_low_spatial_res_super_frame(GOLDEN_FRAME, svc, rtc_ref)) {
+    if (av1_check_ref_is_low_spatial_res_super_frame(cpi, GOLDEN_FRAME)) {
       svc->skip_mvsearch_gf = 1;
     }
-    if (check_ref_is_low_spatial_res_super_frame(ALTREF_FRAME, svc, rtc_ref)) {
+    if (av1_check_ref_is_low_spatial_res_super_frame(cpi, ALTREF_FRAME)) {
       svc->skip_mvsearch_altref = 1;
     }
   }
diff --git a/av1/encoder/svc_layercontext.h b/av1/encoder/svc_layercontext.h
index 93118be2d4..9c133e03fc 100644
--- a/av1/encoder/svc_layercontext.h
+++ b/av1/encoder/svc_layercontext.h
@@ -223,6 +223,21 @@ void av1_update_layer_context_change_config(struct AV1_COMP *const cpi,
  */
 void av1_update_temporal_layer_framerate(struct AV1_COMP *const cpi);
 
+/*!\brief Prior to check if reference is lower spatial layer at the same
+ *        timestamp/superframe.
+ *
+ * \ingroup SVC
+ * \callgraph
+ * \callergraph
+ *
+ * \param[in]       cpi  Top level encoder structure
+ * \param[in]       ref_frame Reference frame
+ *
+ * \return  True if the ref_frame if lower spatial layer, otherwise fals.
+ */
+bool av1_check_ref_is_low_spatial_res_super_frame(struct AV1_COMP *const cpi,
+                                                  int ref_frame);
+
 /*!\brief Prior to encoding the frame, set the layer context, for the current
  layer to be encoded, to the cpi struct.
  *