diff --git a/gnommo/renderer.py b/gnommo/renderer.py index 8451b32..2868fc7 100644 --- a/gnommo/renderer.py +++ b/gnommo/renderer.py @@ -364,11 +364,20 @@ def build_ffmpeg_command(plan: RenderPlan, output_path: Path) -> list[str]: # Global thread limits before any -i. Without this, each format=rgba conversion # in the filter graph (one per video layer) spawns one swscaler thread per CPU core, - # causing OOM on Apple Silicon where av_cpu_count() returns 10-11. + # causing OOM on Apple Silicon where av_cpu_count() returns 10-11 — and OOM-killing + # the whole WSL2 VM on Windows (Wsl/Service/E_UNEXPECTED), since the VM's RAM is + # capped well below the host's. + # + # CRITICAL: this render uses -filter_complex, whose parallelism is governed by + # -filter_complex_threads. -filter_threads only applies to SIMPLE (-vf) graphs and + # is silently ignored here, so without -filter_complex_threads the graph runs one + # thread per core no matter what — the real cause of the render-stage memory blowup. from .cache import get_ffmpeg_thread_count _tc = str(get_ffmpeg_thread_count()) - cmd.extend(["-threads", _tc, "-filter_threads", _tc]) + cmd.extend( + ["-threads", _tc, "-filter_threads", _tc, "-filter_complex_threads", _tc] + ) # Resolve paths to absolute project_path = plan.project_path.resolve()