perf(maps): 42% off the walk frame — cache survival and culling before projection - #45
Merged
Merged
Conversation
…before projection
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.
First-person walk mode ran at 30 fps in a dense city. It now runs at 60. Renderer main-thread time per frame 36.2 ms → 21.0 ms, a 41.8% reduction; frame p50 33.4 → 16.7 ms; dropped frames 260 of 274 → 60 of 274.
Every cell is byte-identical. Two fidelity digests held at all eleven checkpoints: the existing globe digest, and a new walk-scene digest over nine settled street-level poses carrying buildings, shadows, the sky dome and the perspective camera. The shipped digest alone is blind to all of this — not one of its waypoints has a building in it, which is why the new one exists.
The frame was not where we thought. A Chrome trace of the real interaction (new harness at
bench/maps-trace/) putbase-rasterat 51% of a walk frame rather than the 99% the orbit bench had measured, with 11.9 ms sitting outside the render entirely and therefore invisible tobench/maps-render. Style, layout, paint and compositor together are 1.51 ms — the DOM is not a limiting factor.The structural change. The wall cull expressed its per-frame verdict by handing glyphcss a fresh 45,726-polygon array, destroying four identity-keyed caches — the cross-frame shade cache, the texture-sampler walk, the cull-chunk map and its normal cones — in order to communicate a difference of one to seven polygons. Skipping the write when the verdict is unchanged was measured first and recovers almost nothing: the survivor set genuinely moves on 251 of 263 walking frames. The fix is the shape, not the frequency — the cull now writes
Polygon.hiddenin place, and the shadow map honours it on both its passes, the depth raster and the fitted light volume.The rest is culling before projection rather than after. A cull run wholly behind the near plane is provably invisible and is now rejected whole, where before 75.9% of runs were accepted on a NaN corner and their triangles projected one at a time. The globe horizon is resolved on the walker so projection is paid for only by what it admits. Two exact pre-rejects run ahead of the haversine, and a Lipschitz bound on a cached distance rules out 85% of walls with a single compare. Smaller: the shadow map builds into scratch instead of allocating 512 KB and 166,200 tuples per frame, the stroke stamp hoists three closures per run to per stamp, and the per-render mesh flattening is memoized.
Twelve changes, each measured on its own against a re-measured baseline, medians of four to five headed runs. Five things that did not pay are recorded with their numbers, including two that looked certain on paper.
Six new test files; every load-bearing line mutation-checked, including both halves of the shadow
hiddencontract separately, since each catches only its own mutant.Two correctness defects the trace exposed are reported and deliberately not fixed here, in
docs/design/performance.md: in orbit at city zoom the camera targets the datum while terrain stands atelevation × exaggeration, so the real 408 m Zürich ground projects to row −336 of a 63-row grid and 108,101 submitted polygons draw ten triangles; andgetMaxTilt()answers 85 at every tilt whilecamera.rotXreaches 127.6, blanking all 8,820 cells at tilt 80. Both are camera-model changes that move the picture and reopencenterForCamera's inversion and every pose pinned inwidget.tiltPivot.test.ts— they belong in their own change, and the right tilt ceiling cannot be settled while the first stands.