Skip to content

The live passes from one place: live.wgsl's declarations read at build time, a table for the rest, a layout a pass - #68

Merged
yujiaxu-ops merged 1 commit into
mainfrom
live-table
Nov 13, 2026
Merged

yujiaxu-ops merged 1 commit into
mainfrom
live-table

Conversation

@yujiaxu-ops

@yujiaxu-ops yujiaxu-ops commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

Step 1 of the plan for the live passes' layout. Adding a buffer and a pass in #67 took seven edits that had to agree with one another:

  1. the declaration in live.wgsl;
  2. the bind group layout's entries;
  3. the pass list;
  4. the buffers' sizes and order;
  5. the bind group's entries;
  6. the dispatch grid, which existed in two copies;
  7. the merged layout's table.

Tier R1: no picture changes.

The change

  • renderer/build.rs reads shaders/live.wgsl with naga when the crate is built. naga is the compiler wgpu uses, at the same version (30). It is a build dependency, so the browser's wasm build carries none of it. From the shader it gets each binding's name, number, kind and element stride, and the bindings each entry point uses.
  • renderer/src/live/layout.rs holds only what the shader cannot say:
    • RESOURCES: what each binding is bound to (a buffer of the passes' own, the frame's parameters, the trace's samples, the kernel's outputs, the LUT), and how many elements an own buffer holds (per output pixel ×k, quarter grid, bloom pyramid, or the length the shader declares);
    • PASSES: the passes' order and the grid each runs over.
  • layout::check holds the table to the shader: every binding has one resource of a fitting kind, and every entry point is a pass, each exactly once. It runs as a unit test and at every view's start, and names what disagrees.
  • Natively each pass has its own bind group layout and bind group, covering only the bindings its entry point uses. Before, every pass shared one group holding every binding.
  • The browser keeps the merged layout for all passes for now. 36 of the 39 passes bind at most the browser's eight storage buffers. The other three are post (13, most of them its debug view modes), compose (11) and dtemporal (10). Step 2 rearranges those three; then the browser can use the same layout and merged.rs's live half goes.
  • Targets allocates from the table (buf(name)), and pass_grid became layout::Grid::size.

From now on, a buffer or a pass more means its declaration in live.wgsl and a line in live/layout.rs.

Merge gate (R1)

check result
cargo test --release 125 passed (the new one: the table and the shader agree)
render-identity vs the build before, native 190 outputs: 171 byte-identical, 19 within run-to-run spread, 0 changed
render-identity, THREERT_MERGED=1 172 byte-identical, 18 within spread, 0 changed
cost the passes after the trace 6.03–6.09 ms on both builds (Bistro, 1440p at 0.375, alternated, six prints each)
wasm32 library, web crate build (the web crate's lock gains naga as a build dependency)
npm not touched

ROADMAP § 1a records the step.

🤖 Generated with Claude Code


Note

Medium Risk
Refactors core live-rendering bind layouts and buffer wiring across all passes; mitigated by layout/shader validation and reported render-identity parity, but mistakes here would surface as subtle GPU bugs.

Overview
Centralizes live GPU pass layout so live.wgsl is the source of truth for bindings and entry-point usage, with a small Rust table only for what WGSL cannot express (buffer sizes, pass order, dispatch grids).

Adds renderer/build.rs (naga build dependency) to generate BINDINGS and ENTRY_POINTS from the shader at compile time. renderer/src/live/layout.rs holds RESOURCES, PASSES, and layout::check to keep the table and shader in sync (unit test + called at view startup). pass_grid and scattered manual layout tables are replaced by layout::Grid and generated metadata.

Natively, each of the 39 compute passes now gets its own bind group layout and bind group (only the bindings that entry point uses); dispatch uses the per-pass grid from PASSES. Web builds still use the merged single layout until post, compose, and dtemporal are refactored.

Targets stops hard-coding one field per buffer: allocation and bind groups are driven from the layout table (buf(name), bind_groups). LoadedScene carries bgs: Vec<_> instead of one shared bg; frame.rs binds self.sc.bgs[k] per pass. Docs (ROADMAP, RUNTIME) note the change; wasm lockfile picks up naga as a build-only dep.

Reviewed by Cursor Bugbot for commit 74c6daa. Bugbot is set up for automated code reviews on this repo. Configure here.

…d time, a table for the rest, a layout a pass

A buffer and a pass more took seven edits that had to agree (the declaration, the layout's entries, the pass list, the
buffers' sizes and order, the bind group's entries, the dispatch grid in two copies, the merged layout's table). Now:

- build.rs reads live.wgsl with naga (a build dependency at wgpu's version, 30: the browser's build carries none of
  it) into each binding's name, number, kind and element stride, and the bindings each entry point uses.
- live/layout.rs holds what the shader cannot say: what each binding is bound to and how many elements a buffer of
  the passes' own holds (RESOURCES), the passes' order and grids (PASSES); layout::check holds it to the shader (a
  unit test, and every view as it starts).
- Natively each pass has a bind group layout and a bind group of its own, of the bindings its entry point uses. The
  browser's layout is still the merged one for all passes: post binds 13 storage buffers, compose 11, dtemporal 10
  (the other 36 at most eight).
- Targets allocates from the table (`buf(name)`); `pass_grid` is `layout::Grid::size`.

Checks: cargo test 125 passed; render-identity against the build before, native (171 identical, 19 within spread)
and THREERT_MERGED=1 (172, 18): nothing changed; the passes after the trace 6.03-6.09 ms on both builds (Bistro,
1440p at 0.375, alternated). The wasm32 library and the web crate build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yujiaxu-ops
yujiaxu-ops merged commit b239ea9 into main Nov 13, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant