Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2213,6 +2213,28 @@ is still the merged one, for all passes, until those three are rearranged. Check
build before, native and merged, nothing changed; the passes after the trace 6.03-6.09 ms on both (Bistro, 1440p at
0.375, alternated).

**The live passes in a browser as natively (2026-11-14, after it).** The three passes over a browser's eight storage
buffers, rearranged so that each binds at most eight, and the browser's layout is now the native one, a layout a
pass:
- `post` (13): the view modes are passes of their own before it, which a frame in the lit picture does not run
(`view_raw`: base colour, normal, history length into the output as they are; `view_hdr` and `view_sample`: the
signals in place of the image `post` exposes). An HDR snapshot in a view mode is now that signal (it was the
lit image).
- `compose` (11): the upscaler's state of each sample (`Flicker`, `Lock`) is a pass of its own after it, `track`.
- `dtemporal` (10): the surfaces' compact copy (`sb`) is written by `refl`, which reads each sample whole anyway.
- The sample's parts are read apart (`sample_diffuse`, `_specular`, `_emission`): a pass binds the resolved reflection
only if it takes the specular part.
- merged.rs's live half is gone (the slabs, the offsets' uniform, the live source's rewrite); the kernel's outputs the
passes read start at multiples of 256 bytes in its merged buffer (`@align(256)`), so a page binds those ranges.
`every_pass_fits_a_browser` holds every pass to eight.

Checks: render-identity against the build before, native: 3 of 190 outputs differ by rounding
(`particles-motion-repose` 277 pixels, at most ~3e-6 nits on dark ones; `wind-pivot-sway-live` and
`voxel-cut-body-push` ~3e-5 relative); `THREERT_MERGED=1`: 3 (at most 7.5e-5 nits on values of 1 to 4). The view modes
1-8: every PNG identical. `tools/web-hello.ts` in Chrome: pass. Cost +0.18 ms (Bistro, 1440p at 0.375, alternated:
the passes after the trace 6.00-6.08 -> 6.19-6.24; `track` 0.27 less `compose`'s 0.08; the surfaces' copy in `refl`
+0.02, a pass of its own read the samples again: 0.18).

## 2. Temporal fixes

Targets (2026-11-08): bench-temporal, bench-ghosting and their UE MRQ references move to Bistro night (§ 0 step 7); the
Expand Down
20 changes: 9 additions & 11 deletions docs/RUNTIME.md
Original file line number Diff line number Diff line change
Expand Up @@ -688,22 +688,20 @@ tree (ROADMAP § 6).
the node host as `GameSession` does, and sends the canvas' clicks down the same path. `node tools/build-web.ts
[game.ts] --serve` builds and serves it (needs the wasm-bindgen CLI of the crate's version).
- **The merged binding layout** (`renderer/src/merged.rs`). WebGPU gives a stage 8 storage buffers by default; the
kernel binds 19 and the live passes 25. (Natively each live pass has a layout of its own, of the bindings its
entry point uses: `renderer/src/live/layout.rs`, from live.wgsl as build.rs reads it; three passes use more than
eight, so the browser's layout is still one for all, merged.) There is one source: the browser's shaders are written from
`pathtrace.wgsl` and `live.wgsl` as text when a module is compiled, and the native ones are untouched.
kernel binds 19. (The live passes need none of it: each has a layout of its own, of the bindings its entry point
uses, `renderer/src/live/layout.rs` from live.wgsl as build.rs reads it, and none binds more than eight, a unit
test's check; a page binds them as natively.) There is one source: the browser's kernel is written from
`pathtrace.wgsl` as text when a module is compiled, and the native one is untouched.
- The kernel's seven tables become the arrays of one struct in one buffer; their lengths are in the source, each a
power of two above a floor, so a kernel is compiled again only when a table doubles. The renderer keeps writing
its own buffers and a frame copies them into the struct's.
- The kernel's six group-1 buffers (the radiance cache, what the live passes read of a trace) are ranges of one
buffer, the arrays of a second struct.
- The live passes' buffers of one element type are ranges of a slab each (the two atomic ones, the exposure
histogram and the carried thin-feature locks, share one), indexed from offsets in a uniform. A
lasting buffer's two halves (this frame's and the last, `LiveParams.state`) lie in its range, as natively.
buffer, the arrays of a second struct; what the live passes read of it starts at multiples of 256 bytes, so they
bind those ranges as buffers of their own.
- A use of a merged name the generator does not know, a declaration not found exactly once, a name it would add
that is taken: each stops the renderer, with the line. Locals that shadow a name are followed through their
scopes. A test reads the result with naga: valid WGSL, 8 storage buffers for the kernel and for the live passes
(19 and 25 as written), the structs' arrays where the Rust side puts them.
scopes. A test reads the result with naga: valid WGSL, 8 storage buffers for the kernel (19 as written), the
structs' arrays where the Rust side puts them.
- `THREERT_MERGED=1` runs it natively (software traversal, the device held to 8).
- **The loop guard** (`merged::guard`). The kernel's traversals follow indices they read from buffers and end only on
sound data. On 2026-11-11 two dispatches that did not return took this machine's display down (WindowServer's
Expand Down Expand Up @@ -1211,7 +1209,7 @@ on the device", and differs where it says so.
| `accum` (11), group 1 (radiance cache, the trace's outputs, the loop guard's count) | per pixel, per cache entry | written by the kernel: cannot be uniform |
| group 2, the translucency pass (`timg`, `vpts`, `vcount`, `tlow`) | per pixel | written by the pass |
| the particle passes' `systems`, `spawns`, `grid`; the grid programs' `srcs` | small, read-only | storage: other shaders, of eleven bindings at most and no acceleration structure; not known to misread (the fix on the device left them alone) |
| the live passes (`live.wgsl`: 25 storage buffers, `ae_bins` and `ae_state` among them) | per pixel; two tiny | storage: written by the passes; the device showed their output right |
| the live passes (`live.wgsl`: 25 storage buffers, `ae_bins` and `ae_state` among them; at most eight a pass) | per pixel; two tiny | storage: written by the passes; the device showed their output right |

Two of these are what a device run should look at first: `prev_verts` is the one storage buffer left after the
acceleration structure's binding (13) and feeds the motion vectors of moving objects and the vertex colours;
Expand Down
Loading