Phase 2 updates

Phase 2 · Slice 4 ·

Frame pipelining

  • Render thread: the player extracts each frame on the main thread into a self-contained FrameExtract (FrameBuilder), and a render thread uploads, encodes, submits and presents it (FrameRenderer). Two extracts circulate, so frame N+1 simulates while frame N renders, with no per-frame allocation.
  • Extraction: FxHash maps and per-material cached linear colors cut extraction from 1.43 ms to 0.39 ms for 10,000 renderers (about 40 ns each), with 0 allocations per frame.
  • Measured (dotnet/samples/scenes/stress.json: 10,000 scripted spinning cubes, RTX 4070 SUPER, release, no vsync): frame interval 0.77 ms (simulate 0.33 ms + extract 0.39 ms on the main thread; render thread 0.43 ms overlapped), down from 1.88 ms.
  • Allocations: 0 per frame in simulation and extraction; about 62 per frame remain inside wgpu (command encoding, render pass recording, buffer writes, submission), and about 40 more for acquiring and presenting the surface.
  • Tools: --stats reports frame interval, simulation, render and wait times; --headless --render renders offscreen every frame and splits allocations between extraction and rendering.
  • Deferred: a render graph waits until there is more than one pass (shadows, post-processing); with a single forward pass it would only add indirection.
  • Next: FBX import, sprite batching across textures (atlases), texture sharing across imported files, and reducing wgpu's per-frame allocations (staging belt, reused encoders where wgpu allows).