Wall load fixes: treatment fan-out, one CSG cut per wall, capture draw leak, converter duplicates - #762
Merged
Merged
Conversation
…, subscribe per wall While any wall on a level was dirty, the treatment system recomputed the level's miters every frame (uncached, once per proud offset) and wrote a fresh object to its store; every wall renderer on the level subscribed to that object, so all of them re-rendered each frame — 65k WallTreatments renders in 12 s on the IFC castle, and the same fan-out on every wall drag. Now the system skips a level whose effective walls (identity, with live overrides cached per override) and proud offsets are unchanged, caches the per-proud miter data on those inputs, and each wall subscribes to a slice holding only its own endpoint intersections, compared structurally. Walls with no trim enabled mount no treatment work at all. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmmz2AMwTzcnKsSHHPEMhN
…their own frame Each opening was subtracted in turn from a result that grew with every cut, so a wall with 20 openings paid 20 passes over an ever-larger mesh (70 ms in one call on the IFC castle). The cutouts are now grouped by bounding-box overlap: disjoint ones are merged as-is, overlapping groups go through a real union, and the wall is cut once. The progressive rebuild loop also defers a wall with six or more cutouts when the frame has already rebuilt something, instead of checking the time budget only between walls. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmmz2AMwTzcnKsSHHPEMhN
The project thumbnail autosave fires 30–40 s after load and emits thumbnail:before-capture. The node batch answers by revealing every mesh held under the shared 'batched' reason — including the wall sources the merged wall batch was already drawing — and nothing re-hid them: every batched wall drew twice (and cast shadows twice) for the rest of the session. Measured on rich-4×: 4 494 → 5 662 draws, 11.8 → 17.1 ms, forever. Wall holds now carry their own reason, and the wall batch handles capture itself: sources come back for the capture (exports still prune off-layer meshes) and go under again on thumbnail:after-capture. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmmz2AMwTzcnKsSHHPEMhN
A real IFC import carries the same door eight times on one wall; coincident boxes are the worst input a boolean can get, and a union chain over them took 148 ms and then froze the page. Box cutouts fully inside another box are dropped before grouping, overlapping groups of up to four are unioned, and larger groups fall back to the bounded chained subtraction. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmmz2AMwTzcnKsSHHPEMhN
Every void relationship for a wall appended to its opening list, and the emission loop created a fresh node per visit, so a fill referenced by repeated relationships became eight identical doors on one wall (the sample castle). A fill belongs to one opening, which voids one host, so emitted fill ids are now tracked globally; spatial children use sets. Existing converted scenes keep their duplicates until reconverted. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmmz2AMwTzcnKsSHHPEMhN
…ay built A zero-length or fully cut wall rebuilds to as few vertices as the mount placeholder, and the sweep re-marked it every 30 frames — 21 castle walls rebuilt forever and their level's batch re-sewed with them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmmz2AMwTzcnKsSHHPEMhN
|
I hit an error while handling your request (Model unavailable on AI Gateway free tier: Free tier users do not have access to this model. Upgrade to paid credits at https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dtop-up for unrestricted…). Please try again, rephrase, or reach out if it keeps failing. Error id: 5e32fcc2-5966-4b9c-a3ab-43f3e2cc60ee |
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmmz2AMwTzcnKsSHHPEMhN
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.
Metrics (charter backlog rows 8, 9, 10)
Ifc2x3_SampleCastle.ifc, 932 walls / 464 openings / 6 levels — the production import that froze the editor): load-to-settled never (514 s) → 10.1 s, all 932 walls batched, frame 42 → 21 ms.run-scaling-matrix.ts, vs the Wall batching stays live in cutaway #761 build, same machine): load settled −13…−29 %; wall-move settle Maxi-8× −27 %, rich-2× −23 %; undo −18…−29 % (rich 2×/4×, tower); item-move −25…−28 %; Maxi-8× end-of-run dirt 162 → 0.What changes (one commit each)
WallTreatmentsrenders in 12 s on the castle; the same fan-out on every wall drag). Now: skip when the level's effective walls (identity, live overrides cached per override) and trim offsets are unchanged; per-(level, proud) miter cache; each wall subscribes to a slice holding only its own endpoint junction entries, compared structurally; no treatment work mounts for walls with no trim enabled.thumbnail:before-capture(fired by the thumbnail autosave 30–40 s after load) made the node batch's sweep reveal every mesh held under the shared'batched'reason, including the wall sources the merged wall batch already drew; nothing re-hid them. Wall holds now use'wall-batched'and the wall batch handles capture itself (reveal before, re-hide after — exports still prune off-layer meshes).userData.built.Gates
placement-preview-follows-cursorfails identically on main (pre-existing).Notes
project_IfcCastle2x3Smpl); its remaining cost is the steady-state frame (21 ms at 5 600 draws), not load.🤖 Generated with Claude Code
https://claude.ai/code/session_01Jmmz2AMwTzcnKsSHHPEMhN
Note
Medium Risk
Touches core wall CSG, treatment subscriptions, and scene-layer holds; behavior is heavily tested but geometry and render paths are easy to regress subtly.
Overview
Targets wall load and steady-state cost after large IFC imports: fewer redundant treatment updates, cheaper opening booleans, no permanent double-draw after thumbnails, and unique doors/windows from conversion.
Wall treatments skip level miter rebuilds when effective walls (with cached live overrides) and trim proud keys are unchanged; a per-level miter cache and per-wall zustand selectors (endpoint junction slices only) stop every wall on a level from re-rendering when one wall is dirty. Trim mounts only when
hasWallTreatmentsis true.Wall geometry groups opening cutouts by overlap, drops contained box duplicates, unions small groups (≤4) via
mergeWallCutoutBrushes, then does one CSG subtraction per wall (with chained fallback for large groups). Progressive rebuilds useshouldDeferWallRebuildso walls with ≥6 cutouts wait for a fresh frame. Rebuilt meshes getuserData.builtso degenerate walls are not swept as placeholders.Batching / visibility uses a distinct
'wall-batched'hold reason;WallBatchSystemreveals source meshes onthumbnail:before-captureand re-hides after capture so exports see geometry without leaving walls drawn twice.IFC converter dedupes spatial children with
Sets and emits each door/window fill once (expressIdToNodeIdguard + register after successful node creation), with fixture tests for repeated relationships.Reviewed by Cursor Bugbot for commit 5e2243b. Bugbot is set up for automated code reviews on this repo. Configure here.