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