feat(vegas): render plugin content off the render thread, and keep it off the GIL when the panel needs it (#630)

DisplayManager.offscreen() gives a thread its own canvas, so Vegas renders every plugin's ticker content on its prefetch thread instead of pausing the scroll for canvas-bound plugins on the render thread. A render gate (src/common/render_gate.py, vegas_scroll.prefetch_gate, on by default with the GIL-releasing binding) lets the prefetch thread run Python only while the render thread waits in SwapOnVSync: on hdpi, frames 2+ refreshes late fell eightfold and late frames overall from 0.90% to 0.60%. See docs/OFFSCREEN_RENDERING.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-24 19:57:03 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 865d62f67b
commit 9964dd2183
13 changed files with 1903 additions and 89 deletions
+11 -1
View File
@@ -688,6 +688,10 @@ class StreamManager:
Ordered list of (plugin_id, images). ``images`` is None when the
plugin could not be served under ``offscreen_only``, so the caller
can fetch just those on the render thread while keeping the order.
That only happens with ``offscreen_prefetch`` switched off: every
content path now draws on a canvas of its own, so a background
fetch that comes back empty had nothing to show, and ``images``
is an empty list rather than a request for the render thread.
"""
if count is None:
count = self.config.plugins_per_cycle
@@ -705,6 +709,9 @@ class StreamManager:
plugins = getattr(self.plugin_manager, 'plugins', {})
group: List[Tuple[str, Optional[List[Image.Image]]]] = []
# Only the old contract hands anything back to the render thread.
defer_empty = offscreen_only and not getattr(
self.config, 'offscreen_prefetch', True)
for plugin_id in ids:
plugin = plugins.get(plugin_id)
@@ -719,7 +726,10 @@ class StreamManager:
continue
if images:
self.stats['segments_fetched'] += 1
group.append((plugin_id, images if images else None))
if images:
group.append((plugin_id, images))
else:
group.append((plugin_id, None if defer_empty else []))
return group