perf: lot size is a ladder the camera climbs, not a constant
`DETAIL_LOT_METRES = 160` was one number for a board that stopped being looked at from one distance, and it was wrong at both ends of the range it is now looked at from. Measured with `scripts/cost-at.mjs` on the merged board against a 400,000-triangle cap: | pose | before | after | lot | | --- | --- | --- | --- | | Los Angeles, 13 km | **688,536** | 343,372 | 400 m | | Los Angeles, 12.5 km | **678,546** | 343,372 | 400 m | | Los Angeles, 11 km | **654,656** | 390,602 | 250 m | | Los Angeles, 9 km | **635,806** | 380,762 | 250 m | | **Los Angeles, 7.7 km** | **621,866** | **375,196** | 250 m | | Los Angeles, 2.5 km | **415,780** | 345,340 | 200 m | | Los Angeles, 0.9 km | 297,148 | 363,454 | 40 m | | San Francisco, 7.7 km | 222,286 | 300,782 | 80 m | | San Francisco, 2.5 km | 216,292 | 279,158 | 80 m | | San Francisco, 0.6 km | 200,114 | 347,830 | 40 m | | San Francisco, 400 km | 355,763 | 355,763 | — | | San Francisco, 45 km | 270,377 | 270,377 | — | **Six poses were over the cap and five of them had never been measured.** Only the 7.7 km one was on record; 2.5 km over Los Angeles was at 415,780, and the whole band from 9 km up to where `DETAIL_STANDOFF_M` switches the metros off at 13.3 km ran 635,806 to 688,536, because the frustum there holds essentially the entire Southland. None of them is over now. San Francisco goes the other way — 4.2x the buildings at the poses the complaint was about, and 15,613 at the closest one against 849. No cap moved. **Zero new draw calls, and that is a bound rather than a sample.** It is the same `InstancedMesh`, the same geometry and the same material; a district is built at 40, 80, 160, 200, 250 and 400 m, all six rungs live in the store, and the live buffers hold whichever one the board chose. Draws are unchanged to the digit at every pose above. A test asserts the mesh identity across four rung changes. The lever is a **lot count, not a lot size**, because a lot size is the wrong thing to hang on the camera: at the same 22.6 km of engine stand-off San Francisco has 1,987 lots in frustum and Los Angeles has 23,000, so any rule in metres of lot or kilometres of reach is a different bill in the two cities. The board packs the finest rung whose *visible* sum fits 19,000 lots, which is 400,000 minus the 184,000 to 205,000 triangles everything-but-the-city measured at across the whole band this runs in — flat to 10%, which is why there is no stand-off ramp and why the one that was here first came out. Costs, honestly. `createBlocks` goes 264 ms -> 1,236 ms and its store 4.7 MiB -> 18.9 MiB, because every rung is walked and kept; the live instance buffers get *smaller*, 59,166 -> 19,000, since the mesh no longer has to be able to draw a rung nothing can afford. A rung change pops — each rung is its own survey, not a subdivision — and cross-fading would cost the draw call this board does not have. Los Angeles at 7.7 km is visibly thinner than the 621,866-triangle version it replaces. `TODO.md`'s LA section is rewritten around what is left: no budget cell stands at any of these poses, which was the first item on it and still is. 1,723 tests.
This commit is contained in:
@@ -879,48 +879,58 @@ The content programme this opens up — what a single cohesive California is sti
|
||||
missing, and in what order — is **`CALIFORNIA.md`**, kept separate because none
|
||||
of it needs a new mechanism and all of it can be worked in parallel.
|
||||
|
||||
## Los Angeles at 7.7 km is over the triangle cap, and no budget cell stands there
|
||||
## Los Angeles at 7.7 km was over the triangle cap — done, and here is what is left
|
||||
|
||||
Found by the review of the culling work, 2026-08-24, and recorded here because
|
||||
the commit record for that work names a *different* LA pose — the 110 km one,
|
||||
which after the terrain split measures 310,841 and is comfortably under. The pose
|
||||
that is over the cap is not in any commit message, and this is the correction.
|
||||
Found by the review of the culling work, 2026-08-24, and fixed the same day by
|
||||
`DETAIL_LOT_TIERS`: a detail district is built at six lot sizes and the board
|
||||
draws whichever of them its *visible* lots fit a 19,000-lot budget at. Measured
|
||||
with `scripts/cost-at.mjs` on the merged board, 34.05, -118.24:
|
||||
|
||||
Measured with `scripts/cost-at.mjs` on the merged board at 34.05, -118.24:
|
||||
| seek | before culling | after culling | after the ladder | lot | cap |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 110 km | 341,803 | 310,841 | 314,265 | — | 400,000 |
|
||||
| 13 km | — | **688,536** | **343,372** | 400 m | 400,000 |
|
||||
| 12.5 km | — | **678,546** | **343,372** | 400 m | 400,000 |
|
||||
| 11 km | — | **654,656** | **390,602** | 250 m | 400,000 |
|
||||
| 9 km | — | **635,806** | **380,762** | 250 m | 400,000 |
|
||||
| **7.7 km** | **773,282** | **604,332 / 621,866** | **375,196** | 250 m | 400,000 |
|
||||
| 2.5 km | 761,648 | **415,780** | **345,340** | 200 m | 400,000 |
|
||||
| 1.2 km | — | 311,564 (160 m either way) | 311,564 | 160 m | 400,000 |
|
||||
| 0.9 km | — | 297,148 | 363,454 | 40 m | 400,000 |
|
||||
|
||||
| stand-off | before culling | after culling | cap |
|
||||
| --- | --- | --- | --- |
|
||||
| 110 km | 341,803 | 310,841 | 400,000 |
|
||||
| **7.7 km** | **773,282** | **604,332** | **400,000** |
|
||||
| 2.5 km | 761,648 | 335,186 | 400,000 |
|
||||
The 604,332 in the 7.7 km row is the figure this section was written with; the
|
||||
same pose re-measured on the day of the fix was 621,866, and both are the same
|
||||
defect. **Six rows were over the cap and only one of them had ever been named.**
|
||||
2.5 km was over at 415,780, and the whole band from about 9 km up to where
|
||||
`DETAIL_STANDOFF_M` switches the metros off at 13.3 km was 635,806 to 688,536 —
|
||||
the frustum there holds essentially the entire Southland. The 400 m rung on the
|
||||
ladder exists for the top of that band; every other rung is under 260 m and
|
||||
keeps its street lattice.
|
||||
|
||||
So the culling work **closed the 2.5 km breach and reduced the 7.7 km one by
|
||||
22%**, and 7.7 km is still 51% over. It is a pre-existing defect that this work
|
||||
improved rather than one it introduced — but it is live, it is reachable from the
|
||||
board's own chapters, and it was never measured because `california-one` poses at
|
||||
`california-overview` and nothing else does.
|
||||
The 0.9 km row goes the other way, and is the near-field half of the same fault:
|
||||
the board was drawing 11,306 lots at 160 m where it had room for 17,929 at 40 m.
|
||||
|
||||
**Frustum culling cannot fix it.** At that stand-off 40,025 detail lots are
|
||||
genuinely inside the frustum. The Southland is a basin ninety kilometres across
|
||||
and at 7.7 km you are looking down the length of it; there is no camera geometry
|
||||
that makes those buildings not on screen. What is wrong is the *lot size*: 160 m
|
||||
is chosen for a metro seen from its own stand-off, and at 7.7 km over the basin
|
||||
the board is drawing a hundred kilometres of city at that density.
|
||||
**Frustum culling could not fix any of it.** At those stand-offs the lots are
|
||||
genuinely inside the frustum: the Southland is a basin ninety kilometres across
|
||||
and you are looking down the length of it. What was wrong was the *lot size*,
|
||||
which was one number for a board that is no longer looked at from one distance.
|
||||
|
||||
The fix is a lot-size level of detail — the same mechanism the near-field 40 m
|
||||
tier needs, pointing the other way. Two shapes, and the choice needs measuring
|
||||
rather than arguing:
|
||||
### What is left
|
||||
|
||||
- **A stride.** Pack every Nth instance of a range, N from the stand-off. One
|
||||
copy loop, no new geometry, no new draw call — the store already holds every
|
||||
instance. The risk is shimmer: instance order within a range is lattice order,
|
||||
so a stride is a spatially regular thinning and regular thinning of a grid is
|
||||
exactly what aliases.
|
||||
- **A second, coarser instance set per district**, built at 480 m and swapped in
|
||||
by stand-off. No shimmer, and it costs build time and memory for lots that are
|
||||
usually not drawn.
|
||||
|
||||
**Two things to do first, in this order.** Add a budget cell that stands at that
|
||||
pose — `no budget cell stands there` is the actual defect, and a cap nobody
|
||||
measures is not a cap. Expect it to fail on the day it lands; that is the point,
|
||||
and it must not be answered by raising a number. Then fix it.
|
||||
- **No budget cell stands at any of these poses.** This was the first item in the
|
||||
original list and it is still open: `performance-budget.mjs` measures
|
||||
`california-one` at `california-overview` and nowhere else, and a cap nobody
|
||||
measures is not a cap. The ladder is now the only thing holding these poses
|
||||
under 400,000 and nothing in CI would notice if it stopped.
|
||||
- **The rung change pops.** Each rung is an independent survey — a different
|
||||
lattice, different addresses, different heights — so stepping from 250 m to
|
||||
200 m replaces the city rather than subdividing it. It lands during camera
|
||||
motion and a still board never re-lots, which is what makes it liveable, but
|
||||
it is visible if you are watching for it. A cross-fade is the obvious answer
|
||||
and it costs a draw call, which this board does not have.
|
||||
- **The ladder costs about a second of boot.** `createBlocks` on the merged
|
||||
board went from 264 ms to 1,236 ms and its store from 4.7 MiB to 18.9 MiB,
|
||||
because every rung of every district is walked and kept. The live instance
|
||||
buffers got *smaller* — 59,166 instances to 19,000 — so this is boot and
|
||||
memory, not frame time. Building the fine rungs lazily, on the first pose that
|
||||
asks for one, would move most of it off the boot path.
|
||||
|
||||
Reference in New Issue
Block a user