experiment(vegas): vegas_scroll.prefetch_gate runs the prefetch only while the render thread waits on vsync

The render thread spends most of each refresh in SwapOnVSync with the GIL
released, then needs it back the moment the swap returns. With plugin
rendering on the prefetch thread, it often has to wait for it -- behind
bytecode for up to the switch interval, behind a GIL-holding C call for as
long as that takes -- and hdpi's late frames of 2-5 refreshes went up.

src/common/render_gate.py opens a window around each swap, up to just
before the refresh the swap will return on, and a profile hook on the
prefetch thread parks it outside that window. It is never parked holding a
lock the render thread also takes (the Vegas buffer, cache and state
locks, logging, threading, importlib, the cache), never when no frame has
been swapped for 50ms, and never for more than 50ms at a time.

Off by default and ignored on a binding that keeps the GIL in
SwapOnVSync, where the window would never let the prefetch run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-24 14:23:09 -04:00
co-authored by Claude Opus 5.5
parent 55a2760892
commit 4f30bc62ca
6 changed files with 656 additions and 1 deletions
+10
View File
@@ -90,6 +90,14 @@ class VegasModeConfig:
# Experimental: measured with scripts/frame_soak.py before it gets a default.
switch_interval_ms: float = 0.0
# Let the prefetch thread run Python only while the render thread is
# blocked waiting for vsync, and park it the rest of the time, so the
# render thread never waits for the GIL when its refresh comes round. Needs
# a binding that releases the GIL in SwapOnVSync; ignored otherwise.
# Experimental: see src/common/render_gate.py and measure with
# scripts/frame_soak.py before it gets a default.
prefetch_gate: bool = False
# Keep one continuous strip, extending it with the next group of plugins as
# the scroll approaches the end, instead of composing a fresh strip and
# swapping it in. A swap stops the motion, substitutes every pixel at once
@@ -221,6 +229,7 @@ class VegasModeConfig:
continuous_scroll=vegas_config.get('continuous_scroll', True),
offscreen_prefetch=bool(vegas_config.get('offscreen_prefetch', True)),
switch_interval_ms=float(vegas_config.get('switch_interval_ms', 0.0) or 0.0),
prefetch_gate=bool(vegas_config.get('prefetch_gate', False)),
extend_threshold_screens=float(
vegas_config.get('extend_threshold_screens', 2.0)),
auto_trim=vegas_config.get('auto_trim', True),
@@ -264,6 +273,7 @@ class VegasModeConfig:
'continuous_scroll': self.continuous_scroll,
'offscreen_prefetch': self.offscreen_prefetch,
'switch_interval_ms': self.switch_interval_ms,
'prefetch_gate': self.prefetch_gate,
'extend_threshold_screens': self.extend_threshold_screens,
'auto_trim': self.auto_trim,
'trim_threshold': self.trim_threshold,