multicam: wizard + AFV + node-graph preview + performance
CI / Build & test (Linux) (push) Failing after 24s
CI / Build & test (Windows) (push) Failing after 3m38s

- wizard: angle multi-select, sync modes, auto-align, create sequence;
  keeps the host sequence current (host clip = the multicam clip)
- build_multicam_sequence: per-angle clip -> SOURCES_INPUT[element],
  array slot growth, audio angle tracks for AFV
- AFV: host linked audio clip follows the switched source (one undo),
  muted host audio track disables it
- node-graph preview: inline producer renders through the traverser
  (viewer/project-matched graph frames); sequence viewer uses the graph
- multicam node value(): element-tagged row keys (sources_in[i]),
  reads the current source; build_row keys array inputs by element
- angle grid: viewer=0 (single-track montage, not whole-graph)
- project explorer: rename (dialog) + delete (undoable) real items
- performance: decoded-frame LRU, per-process NVDEC quota (1 session,
  evict before open), GPU composite fail-once fallback, snapshot
  upload debounced on the engine tick, worker vram budget headroom
- timeline clip: multicam overlay via ClipDecorator
- wizard menu item moved to Sequence menu
This commit is contained in:
2026-09-01 20:27:59 +08:00
parent 8de9af705e
commit 1b0a15192e
25 changed files with 3528 additions and 65 deletions
+10 -1
View File
@@ -502,7 +502,16 @@ fn per_worker_gpu_budget(frame_size: (i32, i32), fps: u32) -> u64 {
let pixels = (frame_size.0.max(0) as f64) * (frame_size.1.max(0) as f64);
let pixel_ratio = pixels / (1920.0 * 1080.0);
let fps_factor = (fps.max(1) as f64 / 24.0).sqrt().max(1.0);
let peak = 1u64 << 30;
// Peak-vs-quiet spread: a worker sustains a NVDEC session (surface
// pool + an in-flight frame upload) plus the wgpu pipeline and a
// couple of montage uploads. The 1 GiB 1080p / 4 GiB 4K estimates
// have been shown to still exhaust a 16 GB card when the pool pushes
// them simultaneously on several sources (the CUDA_ERROR_OUT_OF_MEMORY
// mid-playback flood), so the budget includes an extra headroom
// factor for undetected spikes (shared decoder surface growth during
// long GOP scans at a keyframe miss).
let headroom = 2;
let peak = (1u64 << 30) * headroom;
let idle = 256u64 << 20;
((peak as f64 * pixel_ratio * fps_factor + idle as f64) as u64).max(1)
}