aom: rtc: Incorporate active_maps into the cyclic refresh

From 70c64d70437ac49fef461c1ff8b1c7a090acc4d0 Mon Sep 17 00:00:00 2001
From: Marco Paniconi <[EMAIL REDACTED]>
Date: Sat, 16 Mar 2024 23:45:34 -0700
Subject: [PATCH] rtc: Incorporate active_maps into the cyclic refresh

Incorporate the active_maps into the cyclic_refresh
to better target active areas, and reduce some compuations.

1) set the active_map first, followed by the cyclic_refresh.
2) set the percent_refresh based on active_region
3) enable/disable active_maps and cyclic_refresh based
   on perc_active_blocks
4) only enter cyclic_refresh_update_segment() for blocks
   labelled as active

This gives some speedup, ~2-3% observed in offline
test, is not bitexact, but makes the refresh more
targeted to active areas.

Change-Id: I79b7d13dd5370e57f0efbf6c51a4a50d2705b232
---
 av1/encoder/aq_cyclicrefresh.c  | 44 ++++++++++++++++++++++++++-------
 av1/encoder/encodeframe_utils.c |  2 ++
 av1/encoder/encoder.c           |  6 +----
 av1/encoder/encoder_utils.c     | 15 ++++-------
 4 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/av1/encoder/aq_cyclicrefresh.c b/av1/encoder/aq_cyclicrefresh.c
index d2fc0f7bb..11b6ea629 100644
--- a/av1/encoder/aq_cyclicrefresh.c
+++ b/av1/encoder/aq_cyclicrefresh.c
@@ -15,6 +15,7 @@
 #include "av1/common/pred_common.h"
 #include "av1/common/seg_common.h"
 #include "av1/encoder/aq_cyclicrefresh.h"
+#include "av1/encoder/encoder_utils.h"
 #include "av1/encoder/ratectrl.h"
 #include "av1/encoder/segmentation.h"
 #include "av1/encoder/tokenize.h"
@@ -295,6 +296,7 @@ static void cyclic_refresh_update_map(AV1_COMP *const cpi) {
   const CommonModeInfoParams *const mi_params = &cm->mi_params;
   CYCLIC_REFRESH *const cr = cpi->cyclic_refresh;
   unsigned char *const seg_map = cpi->enc_seg.map;
+  unsigned char *const active_map_4x4 = cpi->active_map.map;
   int i, block_count, bl_index, sb_rows, sb_cols, sbs_in_frame;
   int xmis, ymis, x, y;
   uint64_t sb_sad = 0;
@@ -302,7 +304,12 @@ static void cyclic_refresh_update_map(AV1_COMP *const cpi) {
   uint64_t thresh_sad = INT64_MAX;
   const int mi_rows = mi_params->mi_rows, mi_cols = mi_params->mi_cols;
   const int mi_stride = mi_cols;
-  memset(seg_map, CR_SEGMENT_ID_BASE, mi_rows * mi_cols);
+  // Don't set seg_map to 0 if active_maps is enabled. Active_maps will set
+  // seg_map to either 7 or 0 (AM_SEGMENT_ID_INACTIVE/ACTIVE), and cyclic
+  // refresh set below (segment 1 or 2) will only be set for ACTIVE blocks.
+  if (!cpi->active_map.enabled) {
+    memset(seg_map, CR_SEGMENT_ID_BASE, mi_rows * mi_cols);
+  }
   sb_cols = (mi_cols + cm->seq_params->mib_size - 1) / cm->seq_params->mib_size;
   sb_rows = (mi_rows + cm->seq_params->mib_size - 1) / cm->seq_params->mib_size;
   sbs_in_frame = sb_cols * sb_rows;
@@ -357,7 +364,10 @@ static void cyclic_refresh_update_map(AV1_COMP *const cpi) {
         // for possible boost/refresh (segment 1). The segment id may get
         // reset to 0 later if block gets coded anything other than low motion.
         // If the block_sad (sb_sad) is very low label it for refresh anyway.
-        if (cr->map[bl_index2] == 0 || sb_sad < thresh_sad_low) {
+        // If active_maps is enabled, only allow for setting on ACTIVE blocks.
+        if ((cr->map[bl_index2] == 0 || sb_sad < thresh_sad_low) &&
+            (!cpi->active_map.enabled ||
+             active_map_4x4[bl_index2] == AM_SEGMENT_ID_ACTIVE)) {
           sum_map += 4;
         } else if (cr->map[bl_index2] < 0) {
           cr->map[bl_index2]++;
@@ -380,7 +390,8 @@ static void cyclic_refresh_update_map(AV1_COMP *const cpi) {
   cr->sb_index = i;
   if (cr->target_num_seg_blocks == 0) {
     // Disable segmentation, seg_map is already set to 0 above.
-    av1_disable_segmentation(&cm->seg);
+    // Don't disable if active_map is being used.
+    if (!cpi->active_map.enabled) av1_disable_segmentation(&cm->seg);
   }
 }
 
@@ -448,6 +459,15 @@ void av1_cyclic_refresh_update_parameters(AV1_COMP *const cpi) {
   else
     cr->percent_refresh = 10 + cr->percent_refresh_adjustment;
 
+  if (cpi->active_map.enabled) {
+    // Scale down the percent_refresh to target the active blocks only.
+    cr->percent_refresh =
+        cr->percent_refresh * (100 - cpi->rc.percent_blocks_inactive) / 100;
+    if (cr->percent_refresh == 0) {
+      cr->apply_cyclic_refresh = 0;
+    }
+  }
+
   cr->max_qdelta_perc = 60;
   cr->time_for_refresh = 0;
   cr->use_block_sad_scene_det =
@@ -541,10 +561,14 @@ void av1_cyclic_refresh_setup(AV1_COMP *const cpi) {
 
   if (resolution_change) av1_cyclic_refresh_reset_resize(cpi);
   if (!cr->apply_cyclic_refresh) {
-    // Set segmentation map to 0 and disable.
-    unsigned char *const seg_map = cpi->enc_seg.map;
-    memset(seg_map, 0, cm->mi_params.mi_rows * cm->mi_params.mi_cols);
-    av1_disable_segmentation(&cm->seg);
+    // Don't disable and set seg_map to 0 if active_maps is enabled, unless
+    // whole frame is set as inactive (since we only apply cyclic_refresh to
+    // active blocks).
+    if (!cpi->active_map.enabled || cpi->rc.percent_blocks_inactive == 100) {
+      unsigned char *const seg_map = cpi->enc_seg.map;
+      memset(seg_map, 0, cm->mi_params.mi_rows * cm->mi_params.mi_cols);
+      av1_disable_segmentation(&cm->seg);
+    }
     if (frame_is_intra_only(cm) || scene_change_detected ||
         cpi->ppi->rtc_ref.bias_recovery_frame) {
       cr->sb_index = 0;
@@ -572,9 +596,11 @@ void av1_cyclic_refresh_setup(AV1_COMP *const cpi) {
       cr->thresh_rate_sb = INT64_MAX;
     }
     // Set up segmentation.
-    // Clear down the segment map.
     av1_enable_segmentation(&cm->seg);
-    av1_clearall_segfeatures(seg);
+    if (!cpi->active_map.enabled) {
+      // Clear down the segment map, only if active_maps is not enabled.
+      av1_clearall_segfeatures(seg);
+    }
 
     // Note: setting temporal_update has no effect, as the seg-map coding method
     // (temporal or spatial) is determined in
diff --git a/av1/encoder/encodeframe_utils.c b/av1/encoder/encodeframe_utils.c
index 947434c7e..a8e4a8839 100644
--- a/av1/encoder/encodeframe_utils.c
+++ b/av1/encoder/encodeframe_utils.c
@@ -15,6 +15,7 @@
 
 #include "av1/encoder/encoder.h"
 #include "av1/encoder/encodeframe_utils.h"
+#include "av1/encoder/encoder_utils.h"
 #include "av1/encoder/rdopt.h"
 
 void av1_set_ssim_rdmult(const AV1_COMP *const cpi, int *errorperbit,
@@ -306,6 +307,7 @@ void av1_update_state(const AV1_COMP *const cpi, ThreadData *td,
     // Else for cyclic refresh mode update the segment map, set the segment id
     // and then update the quantizer.
     if (cpi->oxcf.q_cfg.aq_mode == CYCLIC_REFRESH_AQ &&
+        mi_addr->segment_id != AM_SEGMENT_ID_INACTIVE &&
         !cpi->rc.rtc_external_ratectrl) {
       av1_cyclic_refresh_update_segment(cpi, x, mi_row, mi_col, bsize,
                                         ctx->rd_stats.rate, ctx->rd_stats.dist,
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 411eb7000..ed5f92b52 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2653,12 +2653,8 @@ static int encode_without_recode(AV1_COMP *cpi) {
         av1_setup_frame(cpi);
     }
   }
-
-  if (q_cfg->aq_mode == CYCLIC_REFRESH_AQ) {
-    suppress_active_map(cpi);
-    av1_cyclic_refresh_setup(cpi);
-  }
   av1_apply_active_map(cpi);
+  if (q_cfg->aq_mode == CYCLIC_REFRESH_AQ) av1_cyclic_refresh_setup(cpi);
   if (cm->seg.enabled) {
     if (!cm->seg.update_data && cm->prev_frame) {
       segfeatures_copy(&cm->seg, &cm->prev_frame->seg);
diff --git a/av1/encoder/encoder_utils.c b/av1/encoder/encoder_utils.c
index 5bf9f20e5..0736e5765 100644
--- a/av1/encoder/encoder_utils.c
+++ b/av1/encoder/encoder_utils.c
@@ -421,11 +421,13 @@ void av1_apply_active_map(AV1_COMP *cpi) {
   struct segmentation *const seg = &cpi->common.seg;
   unsigned char *const seg_map = cpi->enc_seg.map;
   const unsigned char *const active_map = cpi->active_map.map;
-  int i;
 
   assert(AM_SEGMENT_ID_ACTIVE == CR_SEGMENT_ID_BASE);
 
-  if (frame_is_intra_only(&cpi->common)) {
+  // Disable the active_maps on intra_only frames or if the
+  // input map for the current frame has no inactive blocks.
+  if (frame_is_intra_only(&cpi->common) ||
+      cpi->rc.percent_blocks_inactive == 0) {
     cpi->active_map.enabled = 0;
     cpi->active_map.update = 1;
   }
@@ -434,14 +436,7 @@ void av1_apply_active_map(AV1_COMP *cpi) {
     if (cpi->active_map.enabled) {
       const int num_mis =
           cpi->common.mi_params.mi_rows * cpi->common.mi_params.mi_cols;
-      for (i = 0; i < num_mis; ++i) {
-        // In active region: only unset segmentation map if cyclic refresh is
-        // not set.
-        if (active_map[i] == AM_SEGMENT_ID_INACTIVE ||
-            (seg_map[i] != CR_SEGMENT_ID_BOOST1 &&
-             seg_map[i] != CR_SEGMENT_ID_BOOST2))
-          seg_map[i] = active_map[i];
-      }
+      memcpy(seg_map, active_map, sizeof(active_map[0]) * num_mis);
       av1_enable_segmentation(seg);
       av1_enable_segfeature(seg, AM_SEGMENT_ID_INACTIVE, SEG_LVL_SKIP);
       av1_enable_segfeature(seg, AM_SEGMENT_ID_INACTIVE, SEG_LVL_ALT_LF_Y_H);