fix(render): cap the playback pre-render window to the slot headroom

Pressing play froze the app: the 120-frame pre-render window could
hold every shm slot in the pool (e.g. 8 workers x 3 F32 slots = 24 <
120). Once the wall-clock playhead outran the renders, the UI's
synchronous frame wait had no credit to dispatch, and the
slot-releasing cleanup runs on that same blocked UI thread — a hard
deadlock.

The window is now capped to (workers x slots - workers), reserving one
slot per worker so interactive (seek/sync display) and audio tickets
always dispatch. preview_window_capacity is exposed through
JobDispatch; a unit test pins the reserve math.
This commit is contained in:
2026-08-19 01:55:59 +08:00
parent 6dc8285f2a
commit 345c464e55
3 changed files with 55 additions and 0 deletions
+9
View File
@@ -1609,6 +1609,15 @@ impl RealEngine {
let forward = config_get_int(CONFIG_KEY_PREVIEW_WINDOW, DEFAULT_PREVIEW_WINDOW_FORWARD)
.clamp(8, 1200);
// Cap the window to the pool's slot headroom: a window that can
// hold every slot starves interactive (this frame's synchronous
// render) and audio tickets of credit, and since the slot-releasing
// cleanup runs on this same UI thread, a synchronous wait then
// deadlocks playback. Keep one slot per worker in reserve.
let forward = match m.dispatch.preview_window_capacity() {
Some(capacity) => forward.min(capacity as i64).max(1),
None => forward,
};
let end = length.max(playhead).min(playhead + forward);
// Reset / rebuild when the node changed or an invalidation bumped the