Contents

ADR-0008: Rendering: frame server first, Movie Maker as a checked alternative

  • Status: Accepted (2026-09-29)
  • Date: 2026-09-29

Context

The brief specifies Godot's Movie Maker mode (--write-movie), headless-capable, with ffmpeg for muxing. Research against Godot 4.7.2 source and test renders on macOS (Metal) and Ubuntu 24.04 (Mesa) found:

  1. --headless --write-movie crashes (null texture). The headless display driver has no real renderer. Linux render boxes therefore run under xvfb-run with Mesa (lavapipe Vulkan or llvmpipe GL), which works and is bit-identical between runs. Godot's own CI does the same.
  2. Movie Maker on macOS silently duplicates frames when the window is minimized or covered. In the test, 188 of 200 frames were copies of the last visible frame.
  3. Three confirmed Movie Maker bugs in 4.7.2:
    • the output folder must already exist;
    • old frames are never cleared;
    • delta jitters unless physics/common/physics_jitter_fix=0.
  4. Output size is reliable only with stretch/mode=viewport and the viewport set to the target size.
  5. A SubViewport rendered with RenderingServer.force_draw() and read back with get_texture().get_image() is synchronous, exact-size, and keeps working while the window is minimized. Custom _draw() output lags one frame unless the capture is deferred.
  6. Polygon2D.antialiased is ignored in 4.7.2. 2D MSAA exists only in Forward+ and Mobile.

Decision

  • Primary capture is the frame server. For each frame: evaluate(frame), deferred capture, force_draw with the fixed step, read back, then write PNG (RGBA for alpha deliverables). The render loop is disabled, and the output size comes from the SubViewport regardless of the window or screen. The same code renders full episodes, frame ranges (chunked or parallel renders), stills, storyboards, model sheets and golden frames.
  • Movie Maker is a supported alternative (reelwright render --capture moviemaker) with every workaround applied. CI renders the same frames both ways and requires identical output.
  • Forward+ is the canonical renderer (Metal on macOS, Vulkan elsewhere) for 2D MSAA and better 3D. Compatibility stays a fallback for low-end preview only.
  • Linux: xvfb-run -a -s "-screen 0 1280x720x24" with lavapipe. The CLI adds it automatically when there is no display. The startup banner is checked, because Godot silently falls back to OpenGL when Vulkan is missing.
  • Audio never goes through Godot for final renders. The core mixes it from the Timeline's audio plan with ffmpeg (sample-exact, byte-reproducible) and muxes it with the frames. Godot audio is for editor preview only.
  • Encoding presets:
    • H.264: scale=out_color_matrix=bt709:out_range=tv, yuv420p, setparams tags for BT.709, CRF 16, +faststart.
    • ProRes 422 HQ and 4444: prores_ks.
    • -fflags +bitexact and no creation timestamps.

Alternatives considered

  • Movie Maker only, as in the brief. Rejected as the only path: it cannot render a single frame or a range, silently corrupts renders on macOS when the window is hidden, and needs three workarounds. It is kept, and CI proves it produces the same frames.
  • A custom MovieWriter. It would need a GDExtension (registration happens before scripts load). Not worth it while the frame server covers every need.

Consequences

  • Golden frames are exact for CI's lavapipe Forward+; Metal locally is compared for information (mean difference under 0.1 levels, a few antialiased edge pixels differ more) for Metal locally.
  • Readback stalls the GPU each frame. That is acceptable for offline quality; interactive previews use the live viewport instead.
  • Linux CI renders at 640×360 for goldens and 720p for the demo artifact to keep software rendering time reasonable.