Skip to content

perf(FragmentsModels): fill tiles in size-sorted sample order to cut visibility runs - #282

Open
rihokirss wants to merge 1 commit into
ThatOpen:mainfrom
rihokirss:perf/tile-sample-order
Open

perf(FragmentsModels): fill tiles in size-sorted sample order to cut visibility runs#282
rihokirss wants to merge 1 commit into
ThatOpen:mainfrom
rihokirss:perf/tile-sample-order

Conversation

@rihokirss

@rihokirss rihokirss commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Part of #278.

generate() fills tiles by walking samples 0..N (file order), while fetchLodLevel decides per sample from its screen size. The two orders are unrelated, so the geometry / wires / invisible cut through a tile lands as dozens of interleaved visibility runs — and every run becomes a geometry.group, i.e. a draw call.

_samplesDimensions already exists and is already sorted big → small (it is the order updateTiles sweeps in, built in processSamplesDimension() during init(), which runs before generate()). Feeding tile construction that same order keeps samples of similar size — and therefore of the same LOD level at any given camera distance — adjacent inside a tile, so the cut is one or two runs.

Tile membership, tile ids, geometry and the per-sample classification are unchanged; only the order of samples inside a tile's index buffer differs.

Measurements

Headless Chromium, Radeon 780M, 1920×1080, postproduction off, 50 FPS cap, medians of 3 runs.

before after
draw calls, 1 model, idle / orbit 5 619 / 4 691 4 101 / 3 035
geometry.groups, 1 model 3 670 2 356
FPS, 1 model, orbit 37.1 40.5
draw calls, 8 models, idle / orbit 6 783 / 6 456 4 408 / 3 843
geometry.groups, 8 models 4 527 2 439
render CPU ms, 8 models, orbit 42.2 33.3
FPS, 8 models, orbit 22.7 28.2
long tasks per 5 s idle, 8 models 818 ms 206 ms
settle after orbit, 1 model 685 ms 514 ms

Rendered content is the same; the screenshot diff is ≤0.08 % of pixels and is entirely one façade strip where two coplanar surfaces z-fight — the tile that arrives first wins, and this changes tile order. No difference anywhere else.

This composes with the compacted-index PR (which removes the remaining runs on the main thread): together the 8-model scene runs at 1 817 draw calls and the 50 FPS cap.


The companion PR for #278 is #281 (main-thread index compaction). They are independent — either can go in alone — and compose.

🤖 Generated with Claude Code

https://claude.ai/code/session_013rtNQqhSQRM2t6E98DESNE

…visibility runs

A tile's index buffer is laid out in the order samples were appended and
the per-sample LOD decision depends on screen size, so with file-ordered
samples the geometry / wires / invisible cut through a tile produced dozens
of interleaved visibility runs. Each run becomes a geometry.groups entry on
the main thread, i.e. its own draw call. Feeding generate() the existing
size-sorted _samplesDimensions order keeps the cut to one or two runs.
Tile membership and geometry are unchanged.
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