Commit 415a98a147 for aom
commit 415a98a147b854e71b1f73604e0ab62a80d769f3
Author: Ihor Buhaiov <freedbrt@gmail.com>
Date: Wed Oct 7 15:35:04 2026 +0200
rtc: Fix selective CDF when cyclic refresh is off
selective_disable_cdf_rtc() keeps CDF updates on while
counter_encode_maxq_scene_change is below 30. The counter only moves
on frames that apply cyclic refresh. If cyclic refresh stays off, the
counter never reaches 30 and CDF updates run on every frame. An active
map with most blocks inactive does this, since percent_refresh scales
down to 0. In the default build every CDF update also rebuilds the
coefficient, mode and MV cost tables per superblock, so speed 11 camera
content gets slower with an active map than without one.
When an active map is set and cyclic refresh is off on the frame,
check frames_since_scene_change instead. Streams without an active
map don't change.
Speed 11, camera content, aq 3, CBR, 1 thread, 300 frames, active map
with 3% of the blocks active. Encoder instructions, default build:
niklas_1280_720_30 at 1000 kbps: 22.7G before, 9.6G after
niklas_640_480_30 at 500 kbps: 8.3G before, 3.6G after
hantro_collage_w352h288 at 300 kbps: 3.7G before, 1.9G after
The realtime only build gains 3% to 5%, since it doesn't update cost
tables per superblock. BD rate on the active area over 4 rates is 1.2%
to 2.5% worse. Without an active map, selective_cdf_update already
costs 0.9% to 1.8% against CDF updates on every frame, so this is the
same trade.
Change-Id: I1d833fcf44aaaa5e5ac715d02b36fdc01a2d6a22
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index cd38437aa2..731ec35b98 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -4063,14 +4063,20 @@ static inline int selective_disable_cdf_rtc(const AV1_COMP *cpi) {
// For single layer.
if (cpi->svc.number_spatial_layers == 1 &&
cpi->svc.number_temporal_layers == 1) {
+ const CYCLIC_REFRESH *const cr = cpi->cyclic_refresh;
// Don't disable on intra_only, scene change (high_source_sad = 1),
// or resized frame. To avoid quality loss force enable at
// for ~30 frames after key or scene/slide change, and
// after 8 frames since last update if frame_source_sad > 0.
+ // The cyclic refresh counter only advances on frames that apply
+ // cyclic refresh. An active map can keep cyclic refresh off for the
+ // whole stream, so use frames_since_scene_change then.
if (frame_is_intra_only(cm) || is_frame_resize_pending(cpi) ||
rc->high_source_sad || rc->frames_since_key < 30 ||
(cpi->oxcf.q_cfg.aq_mode == CYCLIC_REFRESH_AQ &&
- cpi->cyclic_refresh->counter_encode_maxq_scene_change < 30) ||
+ (cr->apply_cyclic_refresh || !cpi->active_map.enabled
+ ? cr->counter_encode_maxq_scene_change
+ : rc->frames_since_scene_change) < 30) ||
(cpi->frames_since_last_update > 8 && cpi->rc.frame_source_sad > 0))
return 0;
else