Commit 513ccb1369 for aom

commit 513ccb13696c76acc434bb4c02a8c1b4886f5174
Author: Ihor Buhaiov <freedbrt@gmail.com>
Date:   Tue Oct 6 21:48:02 2026 +0200

    rtc: Add nonrd mode setup for segment skip blocks

    Fix an encoder assert for skip segments that use GOLDEN_FRAME or
    ALTREF_FRAME as their reference. An ROI map that sets skip and a
    GOLDEN or ALTREF reference on the same segment hits it.

    In nonrd, SEG_LVL_SKIP blocks went through the full mode search with
    force_zeromv_skip_for_blk set. There the SEG_LVL_SKIP check allows
    only GLOBALMV on LAST_FRAME, and the SEG_LVL_REF_FRAME check allows
    only the segment reference. With a GOLDEN or ALTREF skip segment no
    mode is left, so the block keeps the default NEARESTMV on LAST_FRAME
    and write_ref_frames() asserts that the reference matches the
    segment. Without asserts the encoder predicts from LAST_FRAME while
    the decoder infers GLOBALMV on the segment reference, so their
    reconstructions differ.

    Add av1_nonrd_pick_inter_mode_sb_seg_skip() and use it for these
    blocks, in place of the TODO in pick_sb_modes_nonrd(). It sets what
    the decoder infers: GLOBALMV, the segment reference or LAST_FRAME,
    the global motion vector and skip. The motion mode is still coded for
    these blocks, so its inputs, the overlappable neighbor count and
    num_proj_ref, are derived the same way the decoder does it.

    Other effects: with an active map the encoder runs about 2 percent
    fewer instructions. The output changes only for SEG_LVL_SKIP blocks.
    At speed 7 fewer inactive pixels change, because the zero cost makes
    the partition search keep inactive children split. Skip blocks no
    longer update the adaptive rd thresholds and are no longer passed to
    the temporal denoiser. With the denoiser on, quality in active areas
    at speed 7 did not change.

    Extend InactiveAreaTest with camera content and add RoiSkipGoldenRef,
    which uses a skip segment with GOLDEN_FRAME. Without this change 36
    of its 40 cases hit the assert in write_ref_frames(). The camera
    content runs cover the motion mode inputs.

    Change-Id: I3dfa5e3e64cdbcf3f1e0d4fadfd7080239f889ef

diff --git a/av1/encoder/nonrd_pickmode.c b/av1/encoder/nonrd_pickmode.c
index 6c86499228..7bf0a93fcb 100644
--- a/av1/encoder/nonrd_pickmode.c
+++ b/av1/encoder/nonrd_pickmode.c
@@ -3471,6 +3471,61 @@ static inline bool enable_palette(AV1_COMP *cpi, bool is_mode_intra,
   }
 }

+void av1_nonrd_pick_inter_mode_sb_seg_skip(const AV1_COMP *cpi, MACROBLOCK *x,
+                                           RD_STATS *rd_cost, BLOCK_SIZE bsize,
+                                           PICK_MODE_CONTEXT *ctx) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const FeatureFlags *const features = &cm->features;
+  const struct segmentation *const seg = &cm->seg;
+  MACROBLOCKD *const xd = &x->e_mbd;
+  MB_MODE_INFO *const mi = xd->mi[0];
+  const unsigned char segment_id = mi->segment_id;
+
+  assert(segfeature_active(seg, segment_id, SEG_LVL_SKIP));
+
+  // The decoder infers the mode, the reference and skip for this segment.
+  const MV_REFERENCE_FRAME ref_frame =
+      segfeature_active(seg, segment_id, SEG_LVL_REF_FRAME)
+          ? (MV_REFERENCE_FRAME)get_segdata(seg, segment_id, SEG_LVL_REF_FRAME)
+          : LAST_FRAME;
+  assert(ref_frame > INTRA_FRAME);
+
+  // av1_update_state() expects the block size of this pick.
+  mi->bsize = bsize;
+  init_mbmi_nonrd(mi, GLOBALMV, ref_frame, NONE_FRAME, cm);
+  mi->skip_mode = 0;
+  mi->mv[0].as_int =
+      gm_get_motion_vector(&cm->global_motion[ref_frame],
+                           features->allow_high_precision_mv, bsize, xd->mi_col,
+                           xd->mi_row, features->cur_frame_force_integer_mv)
+          .as_int;
+  mi->angle_delta[PLANE_TYPE_Y] = 0;
+  mi->angle_delta[PLANE_TYPE_UV] = 0;
+
+  // The decoder still reads the motion mode, so count what it counts.
+  if (features->switchable_motion_mode)
+    av1_count_overlappable_neighbors(cm, xd);
+  mi->num_proj_ref = 0;
+  if (warped_motion_update_num_proj_ref(cpi, mi)) {
+    int pts[SAMPLES_ARRAY_SIZE];
+    int pts_inref[SAMPLES_ARRAY_SIZE];
+    mi->num_proj_ref = av1_findSamples(cm, xd, pts, pts_inref);
+  }
+
+  // encode_superblock() sets the final transform size of skipped blocks.
+  mi->tx_size = max_txsize_lookup[bsize];
+  memset(mi->inter_tx_size, mi->tx_size, sizeof(mi->inter_tx_size));
+  memset(ctx->tx_type_map, DCT_DCT, ctx->num_4x4_blk);
+  x->txfm_search_info.skip_txfm = 1;
+  av1_init_rd_stats(rd_cost);
+
+#if CONFIG_INTERNAL_STATS
+  store_coding_context_nonrd(x, ctx, mi->mode);
+#else
+  store_coding_context_nonrd(x, ctx);
+#endif  // CONFIG_INTERNAL_STATS
+}
+
 /*!\brief AV1 inter mode selection based on Non-RD optimized model.
  *
  * \ingroup nonrd_mode_search
diff --git a/av1/encoder/partition_search.c b/av1/encoder/partition_search.c
index 55ff863b74..373116c06b 100644
--- a/av1/encoder/partition_search.c
+++ b/av1/encoder/partition_search.c
@@ -466,10 +466,11 @@ static void encode_superblock(const AV1_COMP *const cpi, TileDataEnc *tile_data,
     }
     // Predicted sample of inter mode (for Luma plane) cannot be reused if
     // nonrd_check_partition_split speed feature is enabled, Since in such cases
-    // the buffer may not contain the predicted sample of best mode.
+    // the buffer may not contain the predicted sample of best mode. Skipped
+    // segment blocks are picked without building a prediction.
     const int start_plane =
         (x->reuse_inter_pred && (!cpi->sf.rt_sf.nonrd_check_partition_split) &&
-         cm->seq_params->bit_depth == AOM_BITS_8)
+         !seg_skip && cm->seq_params->bit_depth == AOM_BITS_8)
             ? 1
             : 0;
     av1_enc_build_inter_predictor(cm, xd, mi_row, mi_col, NULL, bsize,
@@ -2333,12 +2334,10 @@ static void pick_sb_modes_nonrd(AV1_COMP *const cpi, TileDataEnc *tile_data,
     start_timing(cpi, nonrd_pick_inter_mode_sb_time);
 #endif
     if (seg_skip) {
-      x->force_zeromv_skip_for_blk = 1;
-      // TODO(marpan): Consider adding a function for nonrd:
-      // av1_nonrd_pick_inter_mode_sb_seg_skip(), instead of setting
-      // x->force_zeromv_skip flag and entering av1_nonrd_pick_inter_mode_sb().
+      av1_nonrd_pick_inter_mode_sb_seg_skip(cpi, x, rd_cost, bsize, ctx);
+    } else {
+      av1_nonrd_pick_inter_mode_sb(cpi, tile_data, x, rd_cost, bsize, ctx);
     }
-    av1_nonrd_pick_inter_mode_sb(cpi, tile_data, x, rd_cost, bsize, ctx);
 #if CONFIG_COLLECT_COMPONENT_TIMING
     end_timing(cpi, nonrd_pick_inter_mode_sb_time);
 #endif
diff --git a/av1/encoder/rdopt.h b/av1/encoder/rdopt.h
index 90eb31543e..655bbda99e 100644
--- a/av1/encoder/rdopt.h
+++ b/av1/encoder/rdopt.h
@@ -158,6 +158,32 @@ void av1_nonrd_pick_inter_mode_sb(struct AV1_COMP *cpi,
                                   struct RD_STATS *rd_cost, BLOCK_SIZE bsize,
                                   PICK_MODE_CONTEXT *ctx);

+/*!\brief Sets the mode of a SEG_LVL_SKIP block for non-RD, without a search.
+ *
+ * \ingroup nonrd_mode_search
+ * Used for blocks in a skip segment, set by the active map or by an ROI map.
+ * The block is coded as inter GLOBALMV from the segment reference, or from
+ * LAST_FRAME if the segment has none, with no residual. The decoder infers
+ * the mode, the reference and skip. The motion mode is still coded, so its
+ * inputs are derived from the neighbors the same way the decoder does it.
+ *
+ * \param[in]    cpi            Top-level encoder structure
+ * \param[in]    x              Pointer to structure holding all the data for
+ *                              the current macroblock
+ * \param[in]    rd_cost        Struct to keep track of the RD information
+ * \param[in]    bsize          Current block size
+ * \param[in]    ctx            Structure to hold snapshot of coding context
+ *                              during the mode picking process
+ *
+ * \remark Nothing is returned. The MB_MODE_INFO struct inside x and ctx are
+ * set to the mode above, and rd_cost is set to zero rate and distortion.
+ */
+void av1_nonrd_pick_inter_mode_sb_seg_skip(const struct AV1_COMP *cpi,
+                                           struct macroblock *x,
+                                           struct RD_STATS *rd_cost,
+                                           BLOCK_SIZE bsize,
+                                           PICK_MODE_CONTEXT *ctx);
+
 void av1_rd_pick_inter_mode_sb_seg_skip(
     const struct AV1_COMP *cpi, struct TileDataEnc *tile_data,
     struct macroblock *x, int mi_row, int mi_col, struct RD_STATS *rd_cost,
diff --git a/test/active_map_test.cc b/test/active_map_test.cc
index 47fcb19280..be0bbb41a3 100644
--- a/test/active_map_test.cc
+++ b/test/active_map_test.cc
@@ -233,10 +233,10 @@ int GetPixel(const aom_image_t &img, int plane, int x, int y) {
   return row[x];
 }

-// Params: test mode, speed, aq_mode and number of threads.
+// Params: test mode, speed, aq_mode, number of threads and content type.
 class InactiveAreaTest
-    : public ::libaom_test::CodecTestWith4Params<libaom_test::TestMode, int,
-                                                 int, int>,
+    : public ::libaom_test::CodecTestWith5Params<libaom_test::TestMode, int,
+                                                 int, int, int>,
       public ::libaom_test::EncoderTest {
  protected:
   InactiveAreaTest() : EncoderTest(GET_PARAM(0)) {}
@@ -247,6 +247,7 @@ class InactiveAreaTest
     cpu_used_ = GET_PARAM(2);
     aq_mode_ = GET_PARAM(3);
     cfg_.g_threads = GET_PARAM(4);
+    content_ = GET_PARAM(5);
     cfg_.g_lag_in_frames = 0;
     cfg_.rc_target_bitrate = 300;
     cfg_.rc_end_usage = AOM_CBR;
@@ -260,7 +261,7 @@ class InactiveAreaTest
     if (frame == 0) {
       encoder->Control(AOME_SET_CPUUSED, cpu_used_);
       encoder->Control(AV1E_SET_AQ_MODE, aq_mode_);
-      encoder->Control(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN);
+      encoder->Control(AV1E_SET_TUNE_CONTENT, content_);
       encoder->Control(AV1E_SET_ROW_MT, cfg_.g_threads > 1);
       return;
     }
@@ -284,6 +285,7 @@ class InactiveAreaTest
       roi.cols = mi_cols;
       for (int i = 0; i < AOM_MAX_SEGMENTS; ++i) roi.ref_frame[i] = -1;
       roi.skip[3] = 1;
+      if (skip_ref_golden_) roi.ref_frame[3] = 4;
       encoder->Control(AOME_SET_ROI_MAP, &roi);
     } else {
       aom_active_map_t active_map = {};
@@ -297,7 +299,7 @@ class InactiveAreaTest
   void DecompressedFrameHook(const aom_image_t &img,
                              aom_codec_pts_t pts) override {
     const int frame = static_cast<int>(pts);
-    if (frame > 0) {
+    if (frame > 0 && !skip_ref_golden_) {
       uint8_t map[kMapRows * kMapCols];
       BuildMap(frame, map);
       std::vector<uint8_t> near_active(kSrcWidth * kSrcHeight, 0);
@@ -351,8 +353,10 @@ class InactiveAreaTest
     }
   }

-  void DoTest(bool use_roi, bool map_once = false) {
+  void DoTest(bool use_roi, bool skip_ref_golden = false,
+              bool map_once = false) {
     use_roi_ = use_roi;
+    skip_ref_golden_ = skip_ref_golden;
     map_once_ = map_once;
     // More bits let q drop low enough while the map stays set.
     if (map_once) cfg_.rc_target_bitrate = 1000;
@@ -362,7 +366,9 @@ class InactiveAreaTest

   int cpu_used_;
   int aq_mode_;
+  int content_;
   bool use_roi_ = false;
+  bool skip_ref_golden_ = false;
   bool map_once_ = false;
   std::vector<uint8_t> prev_[3];
 };
@@ -371,12 +377,14 @@ TEST_P(InactiveAreaTest, ActiveMap) { DoTest(false); }

 TEST_P(InactiveAreaTest, RoiSkip) { DoTest(true); }

+TEST_P(InactiveAreaTest, RoiSkipGoldenRef) { DoTest(true, true); }
+
 // The map stays set while cyclic refresh turns off and q drops.
-TEST_P(InactiveAreaTest, ActiveMapSetOnce) { DoTest(false, true); }
+TEST_P(InactiveAreaTest, ActiveMapSetOnce) { DoTest(false, false, true); }

-AV1_INSTANTIATE_TEST_SUITE(InactiveAreaTest,
-                           ::testing::Values(::libaom_test::kRealTime),
-                           ::testing::Range(7, 12), ::testing::Values(0, 3),
-                           ::testing::Values(1, 4));
+AV1_INSTANTIATE_TEST_SUITE(
+    InactiveAreaTest, ::testing::Values(::libaom_test::kRealTime),
+    ::testing::Range(7, 12), ::testing::Values(0, 3), ::testing::Values(1, 4),
+    ::testing::Values(AOM_CONTENT_DEFAULT, AOM_CONTENT_SCREEN));

 }  // namespace