| name | camera-ubo-layout-sync |
| description | When adding a camera UBO shared between Rust and WGSL, verify field-by-field that the CPU layout matches the WGSL struct — especially packed vec4 fields where reordering silently breaks the shader |
| source | auto-skill |
| extracted_at | 2026-06-16T02:20:23.020Z |
When defining a camera uniform buffer struct shared between Rust and WGSL,
every field must be byte-exactly agreed on both sides. A mismatch produces
silent misbehavior — the shader reads garbage values for the affected fields.
Common failure pattern: A vec4 packed field on the CPU uses a different
field ordering than the WGSL struct expects. For example:
let flags = Vec4::new(screen_x, screen_y, max_hzb_mip, candidate_count);
// WGSL shader:
flags: vec4<f32>, // x=reverse_z, y=candidate_count, z=max_hzb_mip, w=unused
The CPU puts candidate_count at .w but the shader reads it from .y — the
shader gets screen_y (e.g. 720) as the candidate count, passing far more
threads than intended and producing wrong occlusion results.
Fix: Align the CPU to the WGSL struct, OR add a comment documenting the
exact layout on both sides. Prefer defining the layout once (in WGSL) and
matching it in Rust:
let flags = Vec4::new(0.0, candidate_count as f32, MAX_HZB_MIPS as f32, 0.0);
Verification: If candidate_count or similar packed flag fields produce
unexpected shader behavior, print the value on the CPU side and compare against
the expected WGSL value. A mismatch indicates a layout ordering error.