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
(cherry picked from commit ae410fe8b7bd45f3cf61dc8f112dc783e2a08089)
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 3842190..4485b49 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2499,7 +2499,9 @@
       !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 f43c8df..6c86499 100644
--- a/av1/encoder/nonrd_pickmode.c
+++ b/av1/encoder/nonrd_pickmode.c
@@ -1633,12 +1633,12 @@
   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 b0ea574..e8a6e83 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -3377,12 +3377,12 @@
   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 a5eeec6..4c4445e 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -1505,6 +1505,115 @@
                              /*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();