Attribute the fixed per-frame render cost #31

Closed
opened 2026-09-13 17:23:02 +00:00 by icub3d · 0 comments
Owner

The thing ADR 0005 says
to measure next.

#29 established what the mission view's frame is not spent on. On a Pixel Tablet at
2560x1600, free-running:

Configuration Full-res frame Attributable
As shipped 19.43 ms —
Shadows off 18.50 ms shadows 0.93 ms
MSAA off 15.84 ms MSAA 4x 3.59 ms
Pixel count (fit across 4.10 → 0.37 Mpx) — ≤1.42 ms

That leaves roughly 15 ms per frame that responds to neither pixel count nor shading
cost
, on a scene of 96 terrain columns, four units, and one light, with nothing
animating and the camera still.

An almost empty scene costing 15 ms that is insensitive to everything the GPU does more
of is the shape of a CPU bound, not a GPU one. Until it is attributed, no rendering
decision can be justified on performance grounds — which is exactly the mistake ADR 0004
made.

Where to look

  • CPU or GPU? Settle this first; everything else follows. Whether the GPU is idle
    waiting on the CPU is the single most informative bit available.
  • Bevy's per-frame CPU work. Extract, queue, prepare. A mobile CPU is not a desktop
    one, and 8 cores at tablet clocks change what "cheap per entity" means.
  • The CPU clustering fallback. Bevy logs GPU clustering isn't supported on this device; falling back to CPU clustering on Mali. Whatever that costs, it is paid every
    frame regardless of resolution — which fits the shape of what we are looking for.
  • Draw calls. ~100 for the terrain and units. Worth confirming they are batched
    rather than assuming.
  • Present and surface handling. A fixed stall per frame acquiring or presenting the
    swapchain would look exactly like this, and would be nobody's shader.

Tools

src/world/perf.rs behind the perf feature already sweeps and reports; extend it
rather than starting over. android/build.sh --install --features perf builds it with
vsync off.

Measure free-running milliseconds, not vsync-locked fps — ADR 0005 § Consequences on why
two configurations 2.7 ms apart both read as "37 fps".

Done when

The ~15 ms is attributed to named costs with numbers against them, and it is known
whether 60 fps is reachable at all — so the frame-rate target stops being an assumption.

The thing [ADR 0005](../src/branch/main/docs/adr/0005-render-cost-is-per-frame.md) says to measure next. #29 established what the mission view's frame is *not* spent on. On a Pixel Tablet at 2560x1600, free-running: | Configuration | Full-res frame | Attributable | | --- | --- | --- | | As shipped | 19.43 ms | — | | Shadows off | 18.50 ms | shadows 0.93 ms | | MSAA off | 15.84 ms | MSAA 4x 3.59 ms | | Pixel count (fit across 4.10 → 0.37 Mpx) | — | ≤1.42 ms | That leaves roughly **15 ms per frame that responds to neither pixel count nor shading cost**, on a scene of 96 terrain columns, four units, and one light, with nothing animating and the camera still. An almost empty scene costing 15 ms that is insensitive to everything the GPU does more of is the shape of a CPU bound, not a GPU one. Until it is attributed, no rendering decision can be justified on performance grounds — which is exactly the mistake ADR 0004 made. ## Where to look - **CPU or GPU?** Settle this first; everything else follows. Whether the GPU is idle waiting on the CPU is the single most informative bit available. - **Bevy's per-frame CPU work.** Extract, queue, prepare. A mobile CPU is not a desktop one, and 8 cores at tablet clocks change what "cheap per entity" means. - **The CPU clustering fallback.** Bevy logs `GPU clustering isn't supported on this device; falling back to CPU clustering` on Mali. Whatever that costs, it is paid every frame regardless of resolution — which fits the shape of what we are looking for. - **Draw calls.** ~100 for the terrain and units. Worth confirming they are batched rather than assuming. - **Present and surface handling.** A fixed stall per frame acquiring or presenting the swapchain would look exactly like this, and would be nobody's shader. ## Tools `src/world/perf.rs` behind the `perf` feature already sweeps and reports; extend it rather than starting over. `android/build.sh --install --features perf` builds it with vsync off. Measure free-running milliseconds, not vsync-locked fps — ADR 0005 § Consequences on why two configurations 2.7 ms apart both read as "37 fps". ## Done when The ~15 ms is attributed to named costs with numbers against them, and it is known whether 60 fps is reachable at all — so the frame-rate target stops being an assumption.
Sign in to join this conversation.
No description provided.