Skip to content

Draw calls scale with wall count: batch a level's static walls per material #588

Description

@Aymericr

Split out of #492 as cause 2. #556 fixed cause 1 (level-wide recompute per frame); this is the remaining half and a design change rather than a regression.

Measurement

All numbers from @toycenterboss-bot's instrumented profiling in #492. macOS, Chrome, WebGPU renderer (three r185), frameloop="never" — so 50 FPS is a ceiling, and a 50.x reading means "hit the ceiling", not "hit the hardware limit".

Scene Nodes Contents Draw calls Orbit FPS
empty 0 grid and helpers only 24 50.3
zones 142 flat polygons 130 50.2
outer walls 88 perimeter only 534 50.0
real plan 310 walls and rooms 1362 50.2
full import 1089 outer + inner walls 6520 42.7

Cost tracks visible meshes, not node count — a 142-node zone scene costs 130 calls, an 88-node wall scene costs 534. Every scene under roughly 1400 calls holds the ceiling; the 1089-wall import falls off it.

A scene-graph walk on that scene found 8 meshes with one material group, 1081 with three, and 24 with six: 3395 draw units, doubled by the shadow pass → 6520 calls.

Direction

Merging a level's static walls per material collapses those 3243 units to three, which took the same scene to 25 draw calls in the reporter's experiment.

The constraint to design around: the merge has to invalidate cleanly on edit, and walls are exactly the geometry users mutate most. So the win only holds if the merged batch

That ordering is why this is a follow-up to #556 rather than part of it.

Also worth checking while in here: the shadow pass doubling every unit means anything that reduces shadow-casting mesh count pays twice.

Fixture

A 1089-wall scene JSON reproducing the last row has been offered in #492 and should be added as a profiling fixture so this is measurable in CI rather than by hand.

Credit to @toycenterboss-bot for the profiling and the decomposition — separating "work repeated per frame with unchanged inputs" from "draw calls scale with mesh count" is what made both halves actionable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions