Unify shader sources: collapse TS duplication, embed C++ shader from a real file, add layout parity tests - #458
Conversation
… files - src/webgpu/shaders/pbrBlocks.ts was marked @deprecated as a "thin compatibility re-export" but actually held its own independently-drifted 675-line copy of the block shader, only exercised by tests/shader-optimizations.test.ts (which imported it directly, bypassing the real production path). Collapse it into an actual re-export of block/blockShader.ts, and repoint the test at the canonical shader (every assertion verified to still hold against it). - Delete src/webgpu/shaders/_legacy/ (zero external imports, README already said unused). - Delete the dead ParticleMaterialInteractionWGSL/applyMaterialParticleInteraction WGSL in src/webgpu/particleMaterialInteraction.ts — only pbrBlocks.ts/_legacy called it; the CPU-side ParticleMaterialInteraction class is untouched. - Flag (don't fix) a real bug the drift uncovered: viewMaterials.ts writes fUniforms.particleMaterialType every frame but the shipping fragment shader never reads it, so the gold/chrome/cyber tinted-particle-hit visual it was meant to drive is dead. Left as a KNOWN BUG comment for a follow-up that can actually review the visual change. - Extract the static (non-parametric) block shader WGSL bodies — vertex, fragment main, PBR functions, the FragmentUniforms struct, and the particle-material-interaction functions — into real .wgsl files under shaders/wgsl/block/, loaded via Vite's ?raw (vite/client types already in tsconfig.json). getSimpleTextureSamplingWGSL stays TS-generated since it interpolates runtime BlockTextureConfig values. Verified: typecheck, lint, and all 202 tests pass; production build succeeds. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NsXNcchXYG3RQU5hpoT5Ar
…t parity tests - Move gpu_renderer.cpp's inline kBlockWgsl string into cpp/src/shaders/block/block.wgsl, a real file (matching the TS side's shaders/wgsl/block/*.wgsl from the previous commit). - Add scripts/generate-cpp-shaders.mjs: pure Node, no emcc required, embeds every cpp/src/shaders/**/*.wgsl into gitignored cpp/src/generated/shader_sources.h. Wired into scripts/build-cpp.mjs's main() (runs even without emcc) and cpp/CMakeLists.txt (generate_cpp_shaders custom target). - This does not make the TS and C++ block shaders the same program — they're intentionally different (C++'s 128-byte UniformData vs TS's 224-byte FragmentUniforms, no PBR/ghost/dissolve/particle-interaction on the C++ side). What changes is that each shader now has exactly one real file as its source, instead of a hand-copied string being the only copy. - Add tests/block-uniform-layout.test.ts: parses the WGSL FragmentUniforms struct's byte-offset comments and cross-checks them against BLOCK_FRAGMENT_UNIFORM_OFFSETS (fails if they drift apart), and separately parses the C++ UniformData struct and the WGSL Uniforms struct to confirm they describe the same byte size (128). - Extend tests/render-metrics.test.ts with a parity check between cpp/src/board_metrics.h's constexpr constants and renderMetrics.ts/renderConfig.ts. - Document the new layout, the "how to add a uniform" workflow for both backends, and mark the relevant roadmap items done in cpp/README.md. Verified: typecheck, lint, all 211 tests, npm run build and build:all (without emcc) all pass; node scripts/generate-cpp-shaders.mjs runs standalone. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NsXNcchXYG3RQU5hpoT5Ar
|
Warning Review limit reached
Next review available in: 6 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (31)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Follow-up to issue #434 (closed without actually fixing the drift). The drift turned out to be worse than the issue text suggested — real, verified duplication existed within the TS side alone, before even getting to C++:
src/webgpu/shaders/pbrBlocks.ts(675 lines) was marked@deprecated ... thin compatibility re-exportbut actually held its own independently-drifted copy of the block shader. It was dead at runtime, buttests/shader-optimizations.test.tsimported it directly, bypassing the real production path — so those regression tests were silently validating a shader that doesn't ship. Collapsed to an actual re-export; test repointed to the canonicalblock/blockShader.ts(every assertion verified to still hold).applyMaterialParticleInteraction. Chasing that down surfaced a genuine, separate bug:viewMaterials.tswritesfUniforms.particleMaterialTypeevery frame, but the shipping fragment shader never reads it — the gold/chrome/cyber tinted-particle-hit visual it was meant to drive is silently dead. Flagged with aKNOWN BUGcomment, not fixed — changing shipped visual behavior needs a human to actually look at the result, not a plumbing refactor.src/webgpu/shaders/_legacy/(4 files, confirmed zero external imports) deleted.ParticleMaterialInteractionWGSLcopy (dead, unusedapplyMaterialParticleInteractionWGSL) removed fromsrc/webgpu/particleMaterialInteraction.ts; its CPU-sideParticleMaterialInteractionclass (actually used byviewWebGPU.ts) is untouched.With that cleaned up, "true single source" work:
main(), PBR functions, theFragmentUniformsstruct, particle-material-interaction — extracted into real.wgslfiles undersrc/webgpu/shaders/wgsl/block/, loaded via Vite's?raw(vite/clienttypes already present, no new config).getSimpleTextureSamplingWGSL()stays TS-generated since it interpolates runtime config values into the shader text — genuinely not static.gpu_renderer.cpp's inlinekBlockWgslstring moved to a real file,cpp/src/shaders/block/block.wgsl. Newscripts/generate-cpp-shaders.mjs(pure Node, noemccneeded) embeds everycpp/src/shaders/**/*.wgslinto a gitignored, generatedcpp/src/generated/shader_sources.h, wired into bothscripts/build-cpp.mjsandcpp/CMakeLists.txt.UniformDatavs TS's 224-byteFragmentUniforms; no PBR/ghost/dissolve/audio-glow/particle-interaction on the C++ side). That's documented as intentional scope reduction incpp/README.mdalready, and full parity is explicitly the issue's own "Phase B/C." What changed is that each shader now has exactly one real file as its source on both sides, so future parity work has real files to diff against instead of re-deriving WGSL from a JS/C++ string.tests/block-uniform-layout.test.ts: parses the WGSL struct's byte-offset comments and cross-checks them againstBLOCK_FRAGMENT_UNIFORM_OFFSETS(fails on drift), and separately parses the C++UniformDatastruct and the WGSLUniformsstruct to confirm matching byte sizes (128).tests/render-metrics.test.tswith a parity check betweencpp/src/board_metrics.h's constants andrenderMetrics.ts/renderConfig.ts— closes the "partially synced" gap called out in the issue.cpp/README.md: new "Shader source of truth" section, "Adding a block-shader uniform" workflow for both backends, updated directory layout, roadmap items marked done.Explicit non-goals (matching the issue's own framing): no attempt at full PBR parity in the C++ shader (that's the stated later phase); no WGSL validator (naga/tint) in CI — no clean, light npm-installable option found, and it was marked optional/lowest-priority in the issue.
Test plan
npm run typecheck— passesnpx eslint .— passesnpm test— 211/211 passing (202 existing + 9 new: uniform-layout parity, C++/TS render-metrics parity, build-cpp wiring checks)npm run build— production bundle builds successfully with?rawimports resolvingnpm run build:all— still exits 0 withoutemccpresent; confirmsgenerate-cpp-shaders.mjsdoesn't require the toolchainnode scripts/generate-cpp-shaders.mjs— runs standalone, producescpp/src/generated/shader_sources.hwith the shader text verbatimnpm run cpp:releasecompile with a real emsdk 5.0.7 install (needs CI or a machine with emsdk — not available in this sandbox)🤖 Generated with Claude Code
https://claude.ai/code/session_01NsXNcchXYG3RQU5hpoT5Ar
Generated by Claude Code