Skip to content

perf(lod): cache display bounds per graph revision and skip idle LOD passes - #45

Merged
aka-blackboots merged 3 commits into
mainfrom
task/display-bounds-cache
Nov 3, 2026
Merged

aka-blackboots merged 3 commits into
mainfrom
task/display-bounds-cache

Conversation

@aka-blackboots

@aka-blackboots aka-blackboots commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

What

The LOD pass read each displayed body's bounds from the kernel every 100 ms, and each read re-validated the whole stored B-rep. With a few thousand bodies the pass took about 100 ms, so it ran back to back and the main thread never rested, with the camera still or moving.

  • The kernel's bounds queries enclose the stored B-rep without validating it again (every shape enters the store through the commit path, which validates it).
  • The SDK keeps each body's display bounds until the graph revision moves (rendering/lod/display-bounds.ts).
  • A non-forced pass is skipped while nothing it reads has changed: graph and flushed revisions, camera, moving state, displayed set and LOD state edits. Forced passes and the 100 ms interval are unchanged, so the chosen buckets match the uncached run.
Kernel Camera still Camera orbiting Main thread blocked in 3 s
3.0.0 (current pin) 9 FPS 9 FPS 3,000 ms
This branch 60 FPS 60 FPS 0 ms

One LOD pass on the current kernel: 2,313 bounds reads, 99 ms. Same picture and draw calls on both.

Verification (after rebase onto 5998d0b)

  • cargo test --test world_graph: 61 passed, 1 ignored (new display_bounds.rs).
  • npm run typecheck, npm run lint:check, npm run build: pass.
  • opengeometry-three/tests/node/display-bounds.test.mjs: 5 of 5 (bounds read once per graph revision; idle passes skipped).
  • npm run test:node: 209 of 209.

… are skipped

The LOD pass reads every displayed body's bounds from the kernel on every
pass, and the kernel re-validates each stored B-rep on every read. These
tests pin the intended behaviour: one bounds read per body per graph
revision, no pass while nothing it reads has changed, the same buckets as
an uncached run, and kernel bounds that stay the enclosure recorded from
the validating code on the storey and pan-and-inset scenes.
Every LOD pass read each displayed body's bounds from the kernel, and the
kernel re-validated the whole stored B-rep on each read, so a 2,500 body
scene spent about 90 ms per pass even with a still camera. Every shape
enters the store through the commit path, which validates it, so the
bounds queries now enclose the stored B-rep without validating it again.
The SDK keeps each body's bounds until the graph revision moves, and a
non-forced pass is skipped while nothing it reads has changed: the graph
and flushed revisions, the camera, the moving state, the displayed set
and the LOD state edits. Forced passes and the 100 ms interval are
unchanged, so the chosen buckets match the uncached run.
Every other kernel call goes through call(), which poisons the runtime
when the kernel traps. The revision read added for the pass staleness
check called the getter directly, so a trap there would escape the
guard. It now uses the same wrapper as the flush revision read.
@aka-blackboots
aka-blackboots merged commit 9b20149 into main Nov 3, 2026
0 of 4 checks passed
@aka-blackboots
aka-blackboots deleted the task/display-bounds-cache branch November 3, 2026 22:21
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