Repository navigation
The kernel's layouts in its source: accessors, WESL blocks and constants, the loop guard on the syntax tree - #71
Open
yujiaxu-ops wants to merge 1 commit into
Open
yujiaxu-ops wants to merge 1 commit into
yujiaxu-ops wants to merge 1 commit into
Conversation
…ors, WESL blocks and constants, the loop guard on the syntax tree The browser's merged layout, Android's uniform tables and the loop guard were made by rewriting the kernel's text: merged.rs scanned the WGSL for every use of thirteen buffer names, followed shadowing locals through their scopes, rewrote each use, and put a counter after the brace of every loop it found by keyword. - The kernel reads its seven tables and uses its six group-1 buffers only through functions (material_at, ..., cache_key_claim, cache_acc_add, refl_store, direct_len, ...): 87 table reads, 40 group-1 uses. - Each layout is a block of declarations in pathtrace.wgsl declaring the buffers and those functions: @if(merged) { .. } @else { @if(uniform_tables) { .. } @else { .. } } (WESL compound declarations). - The numbers a variant needs are WESL constants the source imports (import constants::..): the merged arrays' lengths, the uniform arrays', the loop guard's limit. gpu::KernelVariant gives a variant's flags and constants; kernel_wgsl resolves the kernel module and the constants package. - The loop guard's count goes into every loop, the scene's functions' too, by a pass over the variant's syntax tree (gpu::guard_loops); its declarations are in the source under @if(guard). - merged.rs loses its scanner, its rewriter, the uniform tables' rewrite, the guard's text pass and their tests (637 -> 302 lines); Member keeps only what the Rust side's buffers need. Checks: render-identity against main with the guard (native, merged, uniform tables, every switch on) and native without it: 0 of 190 changed; web-hello in Chrome passes; native hardware traversal costs the same (Bistro, Kabukichō), software traversal -0.2 ms as written and +0.2 ms merged (the compiler's arrangement; the Metal source is the same in meaning). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The browser's merged binding layout, Android's uniform tables and the loop guard were made by rewriting the kernel's text:
merged.rsscanned the WGSL for every use of thirteen buffer names, followed the locals that shadowed them through their scopes, rewrote each use (MT.materials[,min(u32(i), last)), and put a counter after the{of every loop it found by its keyword. An edit of the kernel could break the rewrite at run time, and nothing in the source said a layout existed. This puts the layouts in the source.What changes
material_at,light_at,emitter_at,tex_info_at,object_at,instance_at,medium_at;cache_key_load/_claim/_clear,cache_acc_add/_load/_clear,cache_val_load/_store,refl_store,motion_store,direct_load/_store,*_len. That is 87 reads of the tables and 40 uses of group 1.@if(merged) { .. } @else { @if(uniform_tables) { .. } @else { .. } }. These are WESL's compound declarations (the spec's@if/@elseblocks). The merged block declares none of the buffers by name, so a read past the accessors does not compile there, and the tests read the merged variants with naga.import constants::..): the merged arrays' lengths, the uniform arrays' lengths, and the loop guard's limit.gpu::KernelVariantgives a variant's 15 flags and its constants.kernel_wgslcompiles through a small resolver of our own: the kernel module plus theconstantspackage.gpu::guard_loops):if (loop_guard()) { break; }goes at the start of everyloop/for/whilebody, including the scene's functions. The guard's declarations are in the source under@if(guard). (I had planned to write the guard into the kernel's loops by hand. It has to bound the scene's functions' loops too, so a pass over the tree is the one place that covers both.)Checks
cargo test --releasewith the loop guard on (GPU tests included): 125 passed. That is main's 126, less the rewriter's three tests, plus two for the constants.render-identity against main, 190 outputs each. The four guarded runs come first, because a changed kernel and the software traversal run guarded before anything else:
The guarded runs show that the tree pass bounds the same loops as the text scan did (52 in the merged kernel either way).
tools/web-hello.tsin Chrome: pass. The page runs with eight storage buffers a stage, the guard at 8192, and the guard stopped nothing in 826 frames.Cost: per-pass profile at 1440p, render scale 0.375. Runs alternate between main and this branch, in both orders, three prints a run.
I compared the Metal source naga writes for main's merged kernel and for this one, function by function. With naga's names normalized, the two mean the same thing. Two experiments did not move the merged +0.2: accessors that read one field, and declarations placed where main had them (main's parameter order). The sign flips with the layout, so I read it as the Metal compiler arranging the code around the accessors' boundaries differently (±0.5 %), not a cost of the structure.
docs/RUNTIME.md ("The engine in a browser", "The kernel's source", the Android uniform tables) and a ROADMAP record are updated.
🤖 Generated with Claude Code
Note
Medium Risk
Changes how every browser/Android kernel variant is compiled and instrumented (merged binding layout, uniform table clamps, loop guard), though tests claim render-identical output and unchanged traversal cost within noise.
Overview
Moves browser merged layouts, Android uniform tables, and the loop guard out of
merged.rstext rewriting and into the kernel WGSL (pathtrace.wgsl), with WESL@if(merged|uniform_tables|guard)blocks, accessor functions for scene tables and group-1 live buffers, andimport constants::for array lengths and guard limits.Rust compilation path: replaces
kernel_flags/ string patches withKernelVariant(flags + constants),wesl_wgsl+ a smallconstantsresolver, andguard_loops(syntax-tree instrumentation) instead ofmerged::guard. Shader call sites switch from direct buffer indexing tomaterial_at,instance_at,cache_acc_add,refl_store, etc.merged.rsshrinks to layout metadata, offsets, and naga validation tests; the WGSL scanner/rewriter (~637 lines) is removed. Docs (RUNTIME.md,ROADMAP.md) describe the new model.Reviewed by Cursor Bugbot for commit 94e7ffc. Bugbot is set up for automated code reviews on this repo. Configure here.