1
0
Commit Graph

3 Commits

Author SHA1 Message Date
karti 687c34d123 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.
2026-08-24 20:10:05 -07:00
karti 4ba9b08a1b fix: cull a district by where it stands, not by where its footprint is at sea level
Two independent reviewers found the same defect in `a2a04e0`, which is
the only reason it is being fixed rather than shipped.

`DetailRange.r` is `reach` from the lattice sweep: the half-diagonal of
the district's lat/lng bounding box plus a lot. That is a **plan**
radius, and it is a good one — measured over all 59,166 instances, every
lot is inside its own district's `r` of the centre with metres to spare.
It was then used as the radius of a sphere pinned at `y = 0`, which is a
different claim, and one this board does not support: a lot's local y is
the ground under it times `verticalExaggeration`, and that is 15 on the
merged board. **39 of 97 district ranges had lots outside their own cull
sphere**, the worst by 4.78 units — 9.2 km of scene height, in the San
Gabriel foothills. Swept over 32,400 poses built the way `scene.ts`
builds them, 61 dropped lots that project strictly inside the NDC cube.
Hillside buildings vanishing while you look at them.

So the range carries `y0`/`y1` — walked once per district at build, over
lots that were just pushed — and the test is `Frustum.intersectsBox`
instead of `intersectsSphere`. Same six plane tests, and exact for the
volume being described rather than right in x/z and wrong in y.

Measured, SF 2.5 km: 215,598 → 216,392 triangles. The fix *adds* 794,
which is the shape of a correct answer here — it stops discarding
geometry that was on screen. The whole-board pose is unchanged and draws
are unchanged at 437.

The doc comments that asserted the invariant this violated are rewritten
rather than deleted; they were the part most likely to survive a fix and
mislead. Also corrected there: the pack declares 99 detail districts but
ten emit no lot and carry no range, so the quoted reach statistics are
over 89, not 99 — the file and the hand-off report disagreed.

`blocksDetail.test.ts` gains a peak board, because the existing fixture
cannot express this: it exaggerates by 2 with a 200 m hill, so every
district's height is a rounding error next to its plan radius and a
y=0 volume contains it by accident. The new board is one small district
on a 3,000 m peak at 15x — lots at y 36.09-39.28 on a 2.70-unit
footprint. Verified to fail against the old volume and pass against the
box, rather than assumed to.
2026-08-24 19:16:08 -07:00
karti a2a04e09c1 perf: the city is packed against the frustum, not just against a reach
`createBlocks` returns one `InstancedMesh` for every anonymous building on
the board and three culls per *object*, so its bounding sphere contains
California and nothing about it is ever rejected. Standing 2.5 km over
Los Angeles the merged board packed 48,081 detail lots and 2,839 base
lots — **480,810 triangles of buildings alone against a 400,000 cap**, two
thirds of them behind the camera or off the sides. No budget cell stands
there, which is why nothing had measured it. It was a live breach.

`DetailRange` now carries the district's `reach` — already computed to
size the lattice sweep and thrown away — and a range is recorded for
*every* district that emitted a lot, base ones included. A range is packed
when it is within reach and its sphere is in frustum, which drops the base
set at any close pose: it used to be a fixed prefix of the buffer and was
therefore drawn wherever the camera stood.

Measured at 2.5 km of stand-off, reach only against reach and frustum:
San Francisco 3,546 detail + 2,839 base becomes 1,987 + 0; Los Angeles
48,081 + 2,839 becomes 15,067 + 0. **For zero extra draw calls** — this is
the one place where culling is free, because `WebGLIndexedBufferRenderer`
returns on `primcount === 0`, so a repack to nothing is cheaper than one
draw and six chunk meshes would have cost five draws to buy the same
28,390 triangles.

The pad is 2% of the stand-off rather than a distance, because it covers
one frame of lag and a frame is a long way at 400 km and nothing at 2.5.
Measured, a fixed 4 km pad packs 8,079 lots at 2.5 km over Los Angeles for
nothing; 2% packs what no pad packs, and is worth 966 lots at 7.7 km.

Two hazards that come with a moving packing, both handled: `count` starts
at 0 rather than at `baseCount`, so `hasDetail` asks the store whether it
exists instead of subtracting (the old expression would be negative); and
`InstancedMesh.boundingSphere` is nulled on every repack, because
`Frustum.intersectsObject` computes it once over whatever `count` held and
never again — a sphere computed over San Francisco would have hidden Los
Angeles for the rest of the session.

`updateBlocksDetail` with no frustum behaves exactly as it did, which is
what every board that is not the merged one gets, and what the one
up-front call at scene build gets so the opening frame is not a district
short. It does not fix the 110 km pose over Los Angeles, where all 42
in-reach districts genuinely are in frustum: closing that needs a coarser
`DETAIL_LOT_METRES` at long stand-off, which is separate work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 18:46:32 -07:00