fix(viewer): UI flicker on camera move from interactive overlays + dirty-mark leaks#401
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ering the UI Every interactive item mounted a drei <Html occlude> overlay unconditionally — invisible (opacity 0) when no zone was selected, but still alive. With `occlude` as a bare boolean, drei raycasts the entire scene per overlay on every camera-move frame, and rewrites each element's z-index while toggling display when the occlusion flips. On scenes with hundreds of interactive items (recessed lights, ceiling fans) this starved the frame budget and made the whole DOM UI blink during camera moves while the WebGPU canvas stayed healthy. Overlays now mount only while a zone is selected and the item sits inside its polygon, fade in/out over 300ms (the child components stay rendered so the exit transition can play before the <Html> unmounts), and drop `occlude` entirely. eps=-1 works around a drei mount bug: its mount path writes the element transform without the distanceFactor scale, and with a static camera the eps guard never re-applies it, so freshly mounted overlays stayed mis-scaled until the camera moved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DIRTY now reads e.g. "29 (12 wall, 9 ceiling, 8 item)" — sorted by count, only non-zero kinds, with a "missing" bucket for dirty ids whose node no longer exists. Makes dirty-mark leaks attributable at a glance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dirtyNodes is consumed by GeometrySystem (def.geometry), FloorElevationSystem (capabilities.floorPlaced), and the legacy per-kind viewer systems. Site, building, level, zone, and guide match none of those, so their marks were never cleared: they accumulated for the whole session (every child create/delete dirties its parent), permanently defeated every consumer's empty-set early exit each frame, and polluted the perf overlay's DIRTY readout. NodeDefinition gains an explicit dirtyTracking?: boolean opt-out (default tracked — no derivable predicate exists since wall's dirty consumption lives in the viewer while zone/guide/level declare def.system for unrelated per-frame work). markDirty consults the registry; the five structural kinds opt out. Also fixes a second leak: deleteNodesAction never removed deleted ids from the dirty set, and every consumer skips missing nodes without clearing them, so marks on deleted nodes lived forever. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
biome's useIterableCallbackReturn rejects forEach callbacks that implicitly return a value (Set.add / clearDirty). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
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?
Fixes the whole-UI flicker / frame starvation on camera moves in large scenes (reported on a project with hundreds of interactive recessed lights and ceiling fans — DOM UI blinking while the WebGPU canvas stayed at a healthy GPU time).
InteractiveSystem): every interactive item mounted a drei<Html occlude>unconditionally — invisible atopacity: 0with no zone selected, but still raycasting the entire scene per overlay on every camera-move frame (bareocclude→raycaster.intersectObjects([scene], true)) and churning z-index/displayon hundreds ofbackdrop-filterDOM elements. Overlays now mount only while a zone is selected and the item is inside its polygon, fade in/out over 300ms, and dropocclude.eps={-1}works around a drei mount bug where freshly mounted overlays stayed mis-scaled until the camera moved (drei's mount path writes the transform without thedistanceFactorscale and the eps guard never re-applies it while the camera is static).def.geometry, no legacy system, nofloorPlaced), so their marks were never cleared — accumulating all session and permanently defeating every system'sdirtyNodes.size === 0early exit.NodeDefinitiongains adirtyTracking?: booleanopt-out (default tracked),markDirtyconsults the registry, and the five structural kinds opt out. Also fixesdeleteNodesActionleaving deleted ids in the dirty set forever.DIRTYnow breaks down by kind — e.g.29 (12 wall, 9 ceiling, 8 item)— non-zero kinds only, with amissingbucket, so future leaks are attributable at a glance.Docs: new
dirtyTrackingsection inwiki/architecture/node-definitions.md. Tests:use-scene-dirty-tracking.test.ts(opt-out skipped, default tracked, deletes clear marks).How to test
bun dev, open a large project with many interactive items (recessed lights / ceiling fans), append?perfto the URL.DIRTYsits at 0 when idle (previously stalezone/guide/level/site/buildingentries).DIRTYreturns to 0 (nomissingentries).bun testinpackages/coreandpackages/nodes;bun run check-types.Screenshots / screen recording
N/A — perf/behavior fix; before/after is best seen live via step 2 (reporter confirmed the flicker fix on the affected project).
Checklist
bun devbun checkto verify)mainbranch