Commit ae410fe8b7 for aom

commit ae410fe8b7bd45f3cf61dc8f112dc783e2a08089
Author: Marco Paniconi <marpan@google.com>
Date:   Thu Oct 1 00:08:11 2026 +0000

    Fix stale search_site_config in IntraBC motion search

    Use av1_get_search_site_config() in rd_pick_intrabc_mode_sb()
    and av1_search_intrabc_nonrd() instead of reading
    search_site_cfg[SS_CFG_LOOKAHEAD] directly, and also
    check y_stride_src in init_motion_estimation().

    Fix is suggested by Yuan Tong.
    Unittests written by Yuan Tong.

    Bug: 568044446
    Change-Id: Id25d82bed48b4fa4bdcc2175af4e6448e2ab05fc

diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 6ee258a762..359c5294ba 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2520,7 +2520,9 @@ static void init_motion_estimation(AV1_COMP *cpi) {
       !mv_search_params->search_site_cfg[SS_CFG_SRC][DIAMOND].stride ||
       !mv_search_params->search_site_cfg[SS_CFG_LOOKAHEAD][DIAMOND].stride ||
       (y_stride !=
-       mv_search_params->search_site_cfg[SS_CFG_SRC][DIAMOND].stride);
+       mv_search_params->search_site_cfg[SS_CFG_SRC][DIAMOND].stride) ||
+      (y_stride_src !=
+       mv_search_params->search_site_cfg[SS_CFG_LOOKAHEAD][DIAMOND].stride);

   if (!should_update) {
     return;
diff --git a/av1/encoder/nonrd_pickmode.c b/av1/encoder/nonrd_pickmode.c
index f43c8df473..6c86499228 100644
--- a/av1/encoder/nonrd_pickmode.c
+++ b/av1/encoder/nonrd_pickmode.c
@@ -1633,12 +1633,12 @@ static void av1_search_intrabc_nonrd(AV1_COMP *cpi, MACROBLOCK *x,
   FULLPEL_MOTION_SEARCH_PARAMS fullms_params;
   const SEARCH_METHODS search_method =
       av1_get_default_mv_search_method(x, &cpi->sf.mv_sf, bsize);
-  const search_site_config *lookahead_search_sites =
-      cpi->mv_search_params.search_site_cfg[SS_CFG_LOOKAHEAD];
+  const search_site_config *src_search_sites =
+      av1_get_search_site_config(cpi, x, search_method);
   const FULLPEL_MV start_mv = get_fullmv_from_mv(&dv_ref.as_mv);
   av1_make_default_fullpel_ms_params(&fullms_params, cpi, x, bsize,
-                                     &dv_ref.as_mv, start_mv,
-                                     lookahead_search_sites, search_method,
+                                     &dv_ref.as_mv, start_mv, src_search_sites,
+                                     search_method,
                                      /*fine_search_interval=*/0);
   av1_set_ms_to_intra_mode(&fullms_params, x->dv_costs);
   fullms_params.mv_limits.col_min = (xd->tile.mi_col_start - mi_col) * MI_SIZE;
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 77259ba3bd..89806021ed 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -3515,12 +3515,12 @@ static int64_t rd_pick_intrabc_mode_sb(const AV1_COMP *cpi, MACROBLOCK *x,
   FULLPEL_MOTION_SEARCH_PARAMS fullms_params;
   const SEARCH_METHODS search_method =
       av1_get_default_mv_search_method(x, &cpi->sf.mv_sf, bsize);
-  const search_site_config *lookahead_search_sites =
-      cpi->mv_search_params.search_site_cfg[SS_CFG_LOOKAHEAD];
+  const search_site_config *src_search_sites =
+      av1_get_search_site_config(cpi, x, search_method);
   const FULLPEL_MV start_mv = get_fullmv_from_mv(&dv_ref.as_mv);
   av1_make_default_fullpel_ms_params(&fullms_params, cpi, x, bsize,
-                                     &dv_ref.as_mv, start_mv,
-                                     lookahead_search_sites, search_method,
+                                     &dv_ref.as_mv, start_mv, src_search_sites,
+                                     search_method,
                                      /*fine_search_interval=*/0);
   const IntraBCMVCosts *const dv_costs = x->dv_costs;
   av1_set_ms_to_intra_mode(&fullms_params, dv_costs);
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index 2f470c25fd..7ac5498719 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -1508,6 +1508,115 @@ INSTANTIATE_TEST_SUITE_P(All, EncodeAPIParameterized,
                              /*speed=*/testing::Values(6, 7, 10),
                              /*aq_mode=*/testing::Values(0, 1, 2, 3)));

+// Triggers the IntraBC motion search by using screen content tools to encode a
+// key frame.
+// Note: The speed must be low enough to run the IntraBC pixel search.
+void IntraBCFrameSizeIncreaseTest(unsigned int usage, int speed) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, usage), AOM_CODEC_OK);
+  cfg.g_w = 64;
+  cfg.g_h = 16;
+  cfg.g_forced_max_frame_width = 64;
+  cfg.g_forced_max_frame_height = 64;
+  cfg.g_lag_in_frames = 0;
+  cfg.g_threads = 1;
+
+  aom_codec_ctx_t enc;
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, speed), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN),
+            AOM_CODEC_OK);
+
+  aom_image_t *img_small = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 64, 16, 1);
+  ASSERT_NE(img_small, nullptr);
+  FillImageRandom(img_small);
+  aom_image_t *img_large = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 64, 64, 1);
+  ASSERT_NE(img_large, nullptr);
+  FillImageRandom(img_large);
+
+  EncodeOne(&enc, img_small, 0);
+
+  // Only the height grows, so the reconstructed frame keeps its stride while
+  // the source buffer gets a new one.
+  cfg.g_h = 64;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_encode(&enc, img_large, 1, 1, AOM_EFLAG_FORCE_KF),
+            AOM_CODEC_OK);
+  bool got_key_frame = false;
+  aom_codec_iter_t iter = nullptr;
+  while (const aom_codec_cx_pkt_t *pkt = aom_codec_get_cx_data(&enc, &iter)) {
+    if (pkt->kind == AOM_CODEC_CX_FRAME_PKT &&
+        (pkt->data.frame.flags & AOM_FRAME_IS_KEY)) {
+      got_key_frame = true;
+    }
+  }
+  EXPECT_TRUE(got_key_frame);
+
+  aom_img_free(img_small);
+  aom_img_free(img_large);
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+
+#if !CONFIG_REALTIME_ONLY
+TEST(EncodeAPI, IntraBCFrameSizeIncreaseGoodQuality) {
+  IntraBCFrameSizeIncreaseTest(AOM_USAGE_GOOD_QUALITY, 2);
+}
+
+TEST(EncodeAPI, IntraBCFrameSizeIncreaseAllIntra) {
+  IntraBCFrameSizeIncreaseTest(AOM_USAGE_ALL_INTRA, 5);
+}
+#endif  // !CONFIG_REALTIME_ONLY
+
+TEST(EncodeAPI, IntraBCFrameSizeIncreaseRealtime) {
+  IntraBCFrameSizeIncreaseTest(AOM_USAGE_REALTIME, 7);
+}
+
+// Encodes a narrow key frame after a wide one with screen content tools in
+// realtime mode. The source buffer of the narrow frame has a smaller stride
+// than the buffer of the wide frame, so the IntraBC motion search must not use
+// search sites built for the wider stride. The assertion in
+// av1_full_pixel_search() checks that the strides match.
+TEST(EncodeAPI, IntraBCFrameSizeDecreaseRealtime) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME),
+            AOM_CODEC_OK);
+  cfg.g_w = 1024;
+  cfg.g_h = 512;
+  cfg.g_lag_in_frames = 0;
+  cfg.g_threads = 1;
+
+  aom_codec_ctx_t enc;
+  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, 7), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN),
+            AOM_CODEC_OK);
+
+  aom_image_t *img_wide =
+      aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 1024, 512, 1);
+  ASSERT_NE(img_wide, nullptr);
+  FillImageRandom(img_wide);
+  aom_image_t *img_narrow =
+      aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 32, 512, 1);
+  ASSERT_NE(img_narrow, nullptr);
+  FillImageRandom(img_narrow);
+
+  EncodeOne(&enc, img_wide, 0);
+
+  cfg.g_w = 32;
+  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_encode(&enc, img_narrow, 1, 1, AOM_EFLAG_FORCE_KF),
+            AOM_CODEC_OK);
+  aom_codec_iter_t iter = nullptr;
+  while (aom_codec_get_cx_data(&enc, &iter) != nullptr) {
+  }
+
+  aom_img_free(img_wide);
+  aom_img_free(img_narrow);
+  ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK);
+}
+
 #if !CONFIG_REALTIME_ONLY
 TEST(EncodeAPI, AllIntraMode) {
   aom_codec_iface_t *iface = aom_codec_av1_cx();