Commit 66cc85977f for aom

commit 66cc85977f9efe5ccfa26475a472b19de16b5424
Author: Ihor Buhaiov <freedbrt@gmail.com>
Date:   Mon Oct 19 21:31:23 2026 +0200

    rtc: Sum segment pred costs over all tiles

    With one thread, encode_tiles() cleared seg_tmp_pred_cost before each
    tile, so only the last tile's costs chose the temporal_update flag of
    the segment map. The multithreaded paths add up the costs of all tiles.
    With tiles and a segment map, 1 thread could then code the map
    differently from 2 or 4 threads. The decoded frames match, but the
    bitstreams don't.

    encode_frame_internal() already clears the costs once per frame, so
    drop the per tile reset and let one thread add up all tiles too.

    The new test encodes 2x2 tiles with cyclic refresh and without error
    resilience at 1, 2 and 4 threads and expects the same stream. Without
    the fix it fails at speeds 7 and 9.

    Change-Id: Idab6f9f87fd013dcd6aae2893dbdc81a0997f787

diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index 8f8f5fef1a..bbb1d26c7f 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -1692,8 +1692,6 @@ static inline void encode_tiles(AV1_COMP *cpi) {
       cpi->td.intrabc_used = 0;
       cpi->td.deltaq_used = 0;
       cpi->td.abs_sum_level = 0;
-      cpi->td.rd_counts.seg_tmp_pred_cost[0] = 0;
-      cpi->td.rd_counts.seg_tmp_pred_cost[1] = 0;
       cpi->td.mb.e_mbd.tile_ctx = &this_tile->tctx;
       cpi->td.mb.tile_pb_ctx = &this_tile->tctx;
       av1_init_rtc_counters(&cpi->td.mb);
diff --git a/test/ethread_test.cc b/test/ethread_test.cc
index 215f740c9a..b0940bade9 100644
--- a/test/ethread_test.cc
+++ b/test/ethread_test.cc
@@ -532,6 +532,41 @@ TEST_P(AVxEncoderThreadRTIntraBCTest, SameOutput) {
 AV1_INSTANTIATE_TEST_SUITE(AVxEncoderThreadRTIntraBCTest,
                            ::testing::Range(7, 12));

+// Without error resilience the segment map may be coded with temporal
+// prediction, which is chosen from costs gathered over all tiles.
+class AVxEncoderThreadRTSegTest : public AVxEncoderThreadTest {
+ protected:
+  void SetUp() override {
+    AVxEncoderThreadTest::SetUp();
+    cfg_.g_error_resilient = 0;
+  }
+};
+
+TEST_P(AVxEncoderThreadRTSegTest, SameOutputForAnyThreadCount) {
+  ::libaom_test::YUVVideoSource video("niklas_640_480_30.yuv", AOM_IMG_FMT_I420,
+                                      640, 480, 30, 1, 0, 30);
+  cfg_.rc_target_bitrate = 1000;
+
+  std::vector<std::string> ref_md5_enc;
+  for (const unsigned int threads : { 1, 2, 4 }) {
+    cfg_.g_threads = threads;
+    size_enc_.clear();
+    md5_enc_.clear();
+    md5_dec_.clear();
+    ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+    if (threads == 1) {
+      ref_md5_enc = md5_enc_;
+    } else {
+      ASSERT_EQ(ref_md5_enc, md5_enc_) << "threads " << threads;
+    }
+  }
+}
+
+AV1_INSTANTIATE_TEST_SUITE(AVxEncoderThreadRTSegTest,
+                           ::testing::Values(::libaom_test::kRealTime),
+                           ::testing::Values(7, 9), ::testing::Values(1),
+                           ::testing::Values(1), ::testing::Values(0, 1));
+
 #if !CONFIG_REALTIME_ONLY

 // The AVxEncoderThreadTestLarge takes up ~14% of total run-time of the