mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-10 17:16:36 +00:00
fix(vegas): lock the scroll to the panel refresh; encode the preview off the render thread
Vegas advanced by elapsed time, blended neighbouring columns every frame, and paced itself with a sleep to target_fps. On hdpi (4x128x64 on one chain, a 120Hz cap the chain cannot reach, ~95-100Hz real) that ran at 73fps with target 90 and ~89fps with target 125: the sleep drifted against the refresh and missed a vsync every few frames, and the blend read as shimmer on the panel (and as "anti-aliased" text in the preview). smooth_scroll now means the crisp pacing the plugin tickers already use: a whole number of pixels per presented frame, each held for frame_hold refreshes, with SwapOnVSync as the clock. The speed is solved against the panel's measured refresh, timed from our own swaps once scrolling starts, because the configured limit is only a cap -- at "120Hz" 90px/s solves to 3px every 4 refreshes, at the real ~97Hz to 1px every refresh. The old blend stays available as sub_pixel_blend (default off). With the web preview open, the render thread also PNG-encoded the whole 512x64 frame five times a second, 12-14ms each -- longer than a refresh. Mid-scroll that encode now runs on a single-slot writer thread (Pillow releases the GIL while compressing); static frames still write inline. Measured on hdpi, 3-minute soak with the preview open: 3 of 17,280 frames held an extra refresh (0.02%), down from ~6-20%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -420,7 +420,6 @@ class VegasModeCoordinator:
|
||||
# Update static mode plugin list on iteration start
|
||||
self._update_static_mode_plugins()
|
||||
|
||||
frame_interval = self.vegas_config.get_frame_interval()
|
||||
if self.vegas_config.continuous_scroll:
|
||||
# The strip is continuously extended and trimmed, so its width says
|
||||
# nothing about how long to run. This is only how often control
|
||||
@@ -488,7 +487,10 @@ class VegasModeCoordinator:
|
||||
# quarter of the budget spent not rendering. Subtracting the work
|
||||
# already done keeps the pacing target while reclaiming that time,
|
||||
# and yields the GIL either way so other threads still run.
|
||||
# Read every frame: a config change applied mid-iteration can
|
||||
# switch between crisp and blended pacing.
|
||||
frame_elapsed = time.monotonic() - frame_started
|
||||
frame_interval = self.render_pipeline.frame_interval
|
||||
time.sleep(max(0.0, frame_interval - frame_elapsed))
|
||||
|
||||
# Measured before the sleep: time spent working, not pacing.
|
||||
@@ -518,20 +520,20 @@ class VegasModeCoordinator:
|
||||
if current_time - last_fps_log_time >= fps_log_interval:
|
||||
fps = fps_frame_count / (current_time - last_fps_log_time)
|
||||
p99 = _percentile(sorted(frame_times), 0.99)
|
||||
target = self.vegas_config.target_fps
|
||||
target = self.render_pipeline.target_fps
|
||||
degraded = target > 0 and fps < target * _FPS_HEALTHY_FRACTION
|
||||
due = (current_time - self._fps_last_health_log
|
||||
>= _FPS_HEARTBEAT_INTERVAL)
|
||||
if degraded or self._fps_was_degraded or due:
|
||||
logger.info(
|
||||
"Vegas FPS: %.1f (target: %d, frames: %d) p99 %.1fms worst %.1fms",
|
||||
"Vegas FPS: %.1f (target: %.0f, frames: %d) p99 %.1fms worst %.1fms",
|
||||
fps, target, fps_frame_count,
|
||||
p99 * 1000.0, frame_worst * 1000.0
|
||||
)
|
||||
self._fps_last_health_log = current_time
|
||||
else:
|
||||
logger.debug(
|
||||
"Vegas FPS: %.1f (target: %d, frames: %d) p99 %.1fms worst %.1fms",
|
||||
"Vegas FPS: %.1f (target: %.0f, frames: %d) p99 %.1fms worst %.1fms",
|
||||
fps, target, fps_frame_count,
|
||||
p99 * 1000.0, frame_worst * 1000.0
|
||||
)
|
||||
@@ -559,7 +561,7 @@ class VegasModeCoordinator:
|
||||
# main loop's _tick_plugin_updates() finds all intervals already
|
||||
# satisfied on return, so the inter-iteration gap is <1 ms and the
|
||||
# display never shows a frozen frame between iterations.
|
||||
_UPDATE_TICK_FRAMES = max(1, int(self.vegas_config.target_fps * 4)) # every 4 s regardless of FPS
|
||||
_UPDATE_TICK_FRAMES = max(1, int(self.render_pipeline.target_fps * 4)) # every 4 s regardless of FPS
|
||||
if (self._update_callback and
|
||||
frame_count % _UPDATE_TICK_FRAMES == 0 and
|
||||
not self._update_tick_running):
|
||||
|
||||
Reference in New Issue
Block a user