perf(core,viewer): stop rebuilding wall geometry every frame - #556
Open
toycenterboss-bot wants to merge 2 commits into
Open
perf(core,viewer): stop rebuilding wall geometry every frame#556toycenterboss-bot wants to merge 2 commits into
toycenterboss-bot wants to merge 2 commits into
Conversation
Three separate hot paths were doing full-scene work per frame on a 1081-wall floor, adding up to 5.6 s of main-thread stalls per six scroll ticks: - findJunctions was O(J x N) over every wall end; a spatial-grid prefilter narrows it to real neighbours (level assembly 59.3 s -> 0.01 s, calculateLevelMiters 436 ms -> 10.8 ms, identical output) - miter data is now cached per level and keyed on the exact wall inputs, so draining the dirty queue no longer recomputes it - getLevelElevations was called inside the per-wall loop; memoised - slab support and the wall appearance key were both unstable, which retriggered opening cutouts on every frame for no reason Measured on scene e5f5822f8837, six wheel ticks in split view: long tasks 5652 ms -> 4322 ms -> 0 ms. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 6ac6ee3. Configure here.
The cache lives at module scope, so it outlived the mount it was created for: every level ever visited kept its wall array reachable, and a remount or a second project in the same tab simply added more. Editor teardown already resets the other shared singletons; the cache now does the same from the system's unmount effect. Also records the rule in the systems wiki, since the same trap is waiting for the next module-level memo. Co-Authored-By: Claude <noreply@anthropic.com>
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.

What does this PR do?
Wall geometry, mitre joints and slab support were recomputed inside the render
loop, so every frame paid for work whose inputs had not changed. On an imported
floor with 1089 walls the first level build took 59 s and the editor was
unusable until it finished. This PR caches the derived results and invalidates
them only when the walls, the level or the openings actually change: mitre
solving keeps a per-level result keyed by the exact wall fields the miters
depend on, slab support holds its effective slab/wall records by identity
instead of re-deriving them once per wall, and the rendered-polygon lookup
memoises against those same arrays. The same level now builds in 0.01 s.
No behaviour changes — the geometry produced is identical, it is simply produced
once instead of once per frame.
Related to #492 — the per-frame recompute is one of the paths that makes wall
interaction stutter on large scenes. Measurements and method are in
a comment on that issue.
The second commit clears the new level-miter cache when
WallSystemunmounts —it lives at module scope, so without that a remount or a second project in the
same tab keeps the previous level's wall arrays reachable. Editor teardown
already resets the other shared singletons; this makes the cache follow the same
rule, and the rule is now written down in
wiki/architecture/systems.md.How to test
bun devand open a scene with a few hundred walls (any floor plan import will do).be immediate rather than a multi-second freeze.
look exactly as they did before.
the cache must invalidate, not go stale.
tab — the miter cache must not carry entries from the previous level.
bun test—wall-mitering,slab-supportandspatial-gridsuites cover theinvalidation paths.
Screenshots / screen recording
N/A — no visual change. The output geometry is identical; only the time to produce
it changes. Happy to add a before/after screen recording of the load freeze if that
would help review.
Checklist
bun devbun checkto verify)mainbranch