WebGPURenderer: Track frontFaceCW in needsRenderUpdate.#33780
Merged
Conversation
`WebGPUBackend.getRenderCacheKey` includes `frontFaceCW = object.matrixWorld.determinant() < 0` in the pipeline cache key because the GPU `frontFace` enum is baked into the pre-compiled render pipeline. The sibling `WebGPUBackend.needsRenderUpdate` did not track the same signal, so when an existing mesh transitioned between positive and negative determinant (typical case: editor toggles a mirror/flip via negative scale on an axis) `Pipelines.getForRender` short-circuited on the cached `renderObject.pipeline` and never re-evaluated the cache key. The mesh continued to draw with the stale `frontFace`, culling the visually-outward fragments. For lit opaque materials this resulted in a fully-black render. This adds `frontFaceCW` to `needsRenderUpdate`'s dirty check so the two sides of the cache surface stay symmetric.
📦 Bundle sizeFull ESM build, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
Mugen87
approved these changes
Jun 12, 2026
Collaborator
|
I can confirm the bug is real and that your suggested fix solves the problem. This makes the WebGPU backend in line with the WebGL backend which does not have this issue since the check is already honored correctly. |
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.
Description
Fixes #33779.
WebGPUBackend.getRenderCacheKeyincludesfrontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0in the pipeline cache key because the GPUfrontFaceenum is baked into the pre-compiled render pipeline. However, the siblingWebGPUBackend.needsRenderUpdatedoesn't track the same signal, so when an existing mesh transitions between positive and negative determinant (typical case: editor toggles a mirror/flip via negative scale on an axis),Pipelines.getForRendershort-circuits on the cachedrenderObject.pipelineand never re-evaluates the cache key. The mesh continues to draw with the stalefrontFace, culling the visually-outward fragments. For lit opaque materials this results in a fully-black render.This PR adds
frontFaceCWtoneedsRenderUpdate's dirty check so the two sides of the cache surface stay symmetric.Reproduction
See linked issue for the JSFiddle live demo (https://jsfiddle.net/fvpbt90h/23/). Before this patch, clicking "flip" causes the
TorusKnotto disappear (closed convex geometry → all faces culled by stalefrontFace) and clicking "workaround" makes it reappear. After this patch, "flip" works correctly on its own without needing the manualmaterial.needsUpdate = truebump.Notes
needsRenderUpdate.matrixWorld.determinant()never changes sign — the new comparison just stores a stablefalse(or stabletrue) indata.frontFaceCW.npm run lintandnpm run test-unit(1308 pass / 0 fail) both clean locally.WebGLShadowMapalong the same lines: Fix: wrong face cull for shadows of freshly inserted mirrored objects. #15825.