Skip to content

viewer/nodes: fix WebGPU pipeline poisoning from missing uv2 on slabs and empty roofs#464

Open
connorsoohoo wants to merge 1 commit into
pascalorg:mainfrom
connorsoohoo:fix/ifc-slab-roof-uv2-webgpu
Open

viewer/nodes: fix WebGPU pipeline poisoning from missing uv2 on slabs and empty roofs#464
connorsoohoo wants to merge 1 commit into
pascalorg:mainfrom
connorsoohoo:fix/ifc-slab-roof-uv2-webgpu

Conversation

@connorsoohoo

@connorsoohoo connorsoohoo commented Jul 6, 2026

Copy link
Copy Markdown

What does this PR do?

Fixes the WebGPU error cascade (Color target has no corresponding fragment stage outputInvalid RenderPipelineInvalid CommandBuffer on every queue submit) that fires when a scene contains slabs or segment-less roofs whose default catalog finish carries an aoMap — most visibly when loading IFC files in apps/ifc-converter.

Root cause: two geometry producers ship meshes without a uv2 attribute while their slot material samples uv2 (aoMap):

  • buildSlabGeometry's top/side face split rebuilt geometries with only position + uv; the default surface finish library:wood-woodplank48 has an AO map.
  • The children-less merged-roof fallback assigned BoxGeometry(0, 0, 0), which has uv but no uv2; roofs imported from IFC have no segments, so they all hit this branch.

three r184's WebGPU renderer throws mid scene pass on the referenced-but-missing attribute (Cannot read properties of undefined (reading 'id') in RenderObject.getAttributes), which leaves the post-processing MRT render target bound on the renderer — every pipeline compiled afterwards validates against the wrong 3-attachment framebuffer, and post FX permanently falls back once the retry budget is exhausted.

Changes:

  • packages/nodes/src/slab/geometry.ts — emit uv2 (copy of uv) on the split top/side geometries, matching the existing ensureUv2Attribute convention.
  • packages/viewer/src/systems/roof/roof-system.tsx — replace the BoxGeometry(0,0,0) fallback with ensureRenderableGeometryAttributes(new BufferGeometry()), which carries all four attributes and keeps a valid position for Drei's BVH.
  • packages/viewer/src/components/viewer/post-processing.tsx — defense-in-depth: reset the render target when a render pass throws, so any future mid-pass exception costs one frame instead of poisoning every pipeline for the session.

How to test

  1. bun install && bun dev, then open the IFC converter app (port 3003) and load the "Revit Architectural" example (?file=07-revit-architectural.ifc) — any example with slabs reproduces.
  2. Before this PR: the console fills with THREE.AttributeNode: Vertex attribute "uv2" not found, [viewer/post-processing] Render pass failed., and repeating WebGPU uncaptured error: Invalid RenderPipeline / Invalid CommandBuffer entries; post FX (SSGI/ink) drops out after 3 retries.
  3. After this PR: console is clean and the model renders with post-processing active.
  4. Editor regression check: place a slab and a roof in the editor app, paint the slab top, delete all segments from a roof — no console errors, slab finish still tiles correctly (uv2 mirrors uv).

Screenshots / screen recording

N/A — the change is "errors gone": before, the converter console streams GPUValidationError spam and the viewer loses SSGI/ink; after, the same model loads with a clean console and full post FX.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

🤖 Generated with Claude Code


Note

Medium Risk
Touches WebGPU rendering and geometry attributes on common paths (IFC slabs/roofs); changes are narrow and align with existing ensureRenderableGeometryAttributes patterns, with low regression risk outside WebGPU/post-FX.

Overview
Fixes a WebGPU error cascade when slab or segment-less roof meshes use default finishes whose aoMap samples uv2, but the geometry only had position + uv.

Slab split geometry now mirrors uv into uv2 on top/side buffers (same convention as ensureRenderableGeometryAttributes). Empty merged roofs no longer use BoxGeometry(0,0,0); they use ensureRenderableGeometryAttributes(new BufferGeometry()) so BVH-safe position and uv2 are present.

Post-processing resets the render target to null when a render pass throws, so a mid-pass failure does not leave the MRT bound and poison subsequent pipeline compiles.

Reviewed by Cursor Bugbot for commit 8d883ee. Bugbot is set up for automated code reviews on this repo. Configure here.

… and empty roofs

Slab surface/side meshes (split in buildSlabGeometry) and the empty-roof
merged-mesh fallback shipped geometry without a uv2 attribute while their
default catalog finishes (woodplank floor, roof shingles) carry an aoMap
that samples uv2. three r184's WebGPU renderer crashes mid scene pass on
the referenced-but-missing attribute, leaving the post-processing MRT
render target bound — every pipeline compiled afterwards validates
against the wrong 3-attachment framebuffer and fails ("Color target has
no corresponding fragment stage output" → Invalid RenderPipeline →
Invalid CommandBuffer on every submit), and post FX permanently falls
back after retries.

- slab/geometry.ts: emit uv2 (copy of uv) on the split top/side
  geometries, matching the ensureUv2Attribute convention.
- roof-system.tsx: the children-less merged-roof fallback used
  BoxGeometry(0,0,0), which has uv but no uv2; use
  ensureRenderableGeometryAttributes(new BufferGeometry()) instead.
- post-processing.tsx: reset the render target when a render pass throws
  so a future bad mesh costs one frame, not the whole session.

Reproduced with apps/ifc-converter loading 07-revit-architectural.ifc
(35 slabs + 2 segment-less roofs); clean console after the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant