perf(viewer): give the initial wall build a 48 ms frame budget - #834
perf(viewer): give the initial wall build a 48 ms frame budget#834RIRuhAI wants to merge 1 commit into
Conversation
pascalorg#800 lifted the 8-walls-per-frame cap during initial build but kept the 8 ms time budget, which is tuned to protect a drag. Initial build has no gesture to protect, and each of its frames also pays a full render of the still-unbatched scene (the level wall batch only merges once a level stops changing). Draining under a 48 ms budget (about three display frames, still two orders of magnitude under a perceptible freeze) cuts the frames spent rendering an unbatched scene several-fold. The interactive 8 ms / 8-wall tiers are unchanged, and a heavy (>= 6 openings) wall still takes its own frame. Indicative measurement: an equivalent change (a 48 ms / 128-wall tier for 64+ pending walls) applied to the published @pascal-app/viewer 1.0.0-beta.5 — which predates pascalorg#800 and has no initial-build state — on a 1,600-wall scene in Chrome (WebGPU on Metal) halved time to a ready model in an interleaved A/B (17.6/18.3 s -> 8.2/8.8 s) and cut main-thread long-task time ~3x. This PR applies the wider budget only inside pascalorg#800's initial build; the numbers were not re-taken on this source. shouldDeferWallRebuild gains an optional initialBuild flag so the test pins both tiers; the lifecycle probes simulate 24 ms per wall (was 4) so the budget exit is still exercised; the systems wiki records the new budget. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
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: 9afeb7d8-0d20-4886-bc87-8ffc58cdcfdb |
Aymericr
left a comment
There was a problem hiding this comment.
The code is focused, but the evidence does not yet establish this 48 ms budget on the code being merged. The reported A/B used beta.5 plus a different 48 ms / 128-wall rule; this branch applies an uncapped 48 ms tier inside the newer #800 lifecycle. The test pins the constant and control flow, but cannot tell us whether current main improves time-to-ready without creating visible 50+ ms long tasks (the check occurs between walls, so an individual rebuild can overshoot the budget). Please rerun the real Chrome A/B on current source with the same scene and report at least time to first-clean/ready, maximum main-thread task, wallDrain frames/walls per frame, and one post-load drag check. I’m adding ready-for-human because choosing a deliberate multi-frame main-thread budget is a maintainer performance/UX tradeoff once those measurements are available.
What does this PR do?
#800 lifted the 8-walls-per-frame cap during the initial wall build but kept the 8 ms time budget, which exists to protect a drag. The initial build has no gesture to protect, and each of its frames also pays a full render of the still-unbatched scene (the level wall batch merges only once a level stops changing), so every extra frame spent draining walls costs a whole scene render.
This gives the initial build its own budget, 48 ms (about three display frames, still two orders of magnitude under a perceptible freeze), checked between walls exactly as before. The interactive tiers (8 ms / 8 walls) are unchanged, and a heavy wall (≥ 6 openings) still takes its own frame.
shouldDeferWallRebuildgains an optionalinitialBuildflag so the test can pin both tiers, andwiki/architecture/systems.mdrecords the budget.Measurement caveat: the numbers come from an equivalent change (a 48 ms / 128-wall tier for 64+ pending walls) applied to the published
@pascal-app/viewer@1.0.0-beta.5, which predates #800 and has no initial-build state; this PR is narrower and applies the wider budget only inside #800's initial build. On a 1,600-wall scene in Chrome (WebGPU on Metal) that change halved time to a ready model in an interleaved A/B (17.6/18.3 s → 8.2/8.8 s) and cut main-thread long-task time roughly 3× (~12 s → 4 s). Not re-taken on this source; happy to re-run on your fixture.How to test
bun test --cwd packages/viewer src/systems/wall/— new case inwall-progressive-budget.test.ts; the lifecycle probes now simulate 24 ms per wall (was 4) so the budget exit is still exercised, and the recorded span expectation moves 28 → 68 accordingly. Full viewer suite: 311 pass.?perf;__pascalPerf.batchStats().wallDrainshows walls consumed per frame rising from ~6 to ~35 during initial build, and first-clean arriving in a fraction of the frames.A constant is the smallest change. A frame-time-adaptive budget (spend as long as the last render took) would self-tune but needs a render-time signal in the wall system; glad to follow up either way.
Screenshots / screen recording
No visual change; timing only.
Checklist
bun dev— verified through the package test suites,tsc --buildandbun checkinstead; no editor session was run for this changebun checkto verify)mainbranch🤖 Generated with Claude Code
Note
Low Risk
Timing-only change scoped to initial-build hydration; interactive 8 ms / 8-wall limits and heavy-wall rules are unchanged.
Overview
Initial wall hydration now drains dirty walls with a 48 ms per-frame time budget (checked between walls, still no 8-walls/frame cap), while interactive progressive rebuilds keep 8 ms and 8 walls/frame. The 8 ms limit exists to protect drags; initial load has no gesture and pays a full unbatched render each frame, so spending more wall work per frame reduces total frames to readiness.
wallRebuildExitReason/shouldDeferWallRebuildtake an optionalinitialBuildflag to select the budget; heavy walls (≥6 openings) still defer to their own frame. Tests lock both tiers and bump simulated rebuild cost so budget-exit scenarios still match the wider allowance; architecture docs record the split.Reviewed by Cursor Bugbot for commit d61e07c. Bugbot is set up for automated code reviews on this repo. Configure here.