makepad/skills/makepad-debug-profiler/references/profiling.md
Arena Agent fe21c07d84 skills: add Makepad game skills pack ported from threejs-game-skills
Nine agent skills for building Makepad/Rust games, ported from
majidmanzarpour/threejs-game-skills. Same director-routed workflow and
premium bar; runtime rewritten for this fork's game crates.

- makepad-game-director        entrypoint, routing, continuity, asset probe
- makepad-gameplay-systems     loop, movers vs rigid bodies, input, camera, netplay
- makepad-aaa-graphics-builder lighting, shaders, budget, visual scorecard
- makepad-game-ui-designer     HUD, menus, touch and XR UI
- makepad-debug-profiler       defect bisection and profiling
- makepad-qa-release           verification ladder, evidence, packaging
- makepad-3d-generator         CC0 model search, casts, procedural geometry
- makepad-image-generator      texgen textures, palettes, sky, icons
- makepad-audio-generator      sample bank, mixer, material impacts, 3D audio

Written against the real APIs in libs/game/*, libs/sim and apps/arcade:
the game.* verb table, GameRenderer adaptive quality, the packed 6-float
GameMeshVertex layout, script_mod! splash styling, makepad-test driving,
and the BUDGETS.md numbers. No paid generation API is required - the CC0
library plus seeded makepad-game-gen replaces them.

Includes install.sh (Codex/Claude), validate-skills.sh and a repo-aware
probe_assets.sh; both scripts verified against this checkout.
2026-09-05 21:18:58 +00:00

88 lines
3.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Profiling
## Rules
1. **Measure first.** Optimising an unmeasured guess usually makes something else slower.
2. **`--release` only.** Debug frame times are meaningless for shipping decisions.
3. **Report p90, not mean.** A 16 ms mean with a 40 ms p90 stutters, and the mean hides it.
4. **Measure on the target device class.** The BUDGETS Quest column is an estimate until it runs on device.
## Instruments
```rust
renderer.report_frame_ms(ms); // feed every frame; returns whether quality changed
renderer.frame_p90_ms(); // Option<f32>
renderer.quality_level();
renderer.quality_reason(); // why quality dropped - read this before debugging "missing" effects
renderer.shadow_budget();
stats.instance_floats; // RenderStats, from the compiled shader
renderer.model_triangles(id);
```
```bash
cargo run -p makepad-arcade --example bigworld_probe --release
```
## Where time goes
From `apps/arcade/BUDGETS.md`:
- Sim: 100 movers + 50 rigid bodies + 65×65 terrain = **0.038 ms/tick** against 16.6 ms. **Gameplay code is essentially never the problem.**
- Script: ≤ 2 ms, one cumulative 500k-instruction pool per tick.
- The real costs are **draw calls** and **CPU skinning**.
Skinning is CPU-side, so a skinned character's entire vertex buffer re-uploads every frame. The Knight: 3716 verts × 64 B = 238 KB/frame unpacked, 89 KB packed. Skinned character count is the highest-leverage number in most scenes.
## Bandwidth first
Quest is bandwidth-bound before ALU-bound.
| stream | packed |
| --- | --- |
| cube instance | 32 floats / 128 B (was 44 / 176 B) |
| skinned vertex | 6 floats / 24 B (was 16 / 64 B) |
| shadow mesh vertex | 6 floats / 24 B |
`geom.GameMeshVertex`: position 3 exact f32, octahedral normal 1 lane, uv 2×f16 1 lane, colour 4×unorm8 1 lane. Attributes are f32-only in this engine; compression is bit-packing plus `unpack2f16` / `unpack4u8` in-shader.
Anything constant across a batch belongs in a uniform. Moving `sun_color`, `sun_sky`, `sun_ground`, `fog_color` off the instance stream saved 27% per cube.
## Cut order
1. Instance/vertex bytes (packed layout, batch constants → uniforms)
2. Draw calls (batch by material and mesh)
3. Skinned character count (cap; degrade distant ones)
4. Shadow casters (`set_shadow_budget`; bake AO for the rest)
5. Particle pool cap
6. Terrain resolution (uploaded per revision, not per frame)
7. Fragment work — last, and only with evidence you are ALU-bound
## Stutter
Different problem from low framerate. Causes, in order of likelihood: allocation inside the frame loop, model loading during play, terrain regeneration on a gameplay event, container rehash in `step`, unbounded `Vec` growth (particles, `BulletDecal`, logs, the gen `cache`), and GC-like bulk frees.
Fix by preloading and pooling. Anything created per frame should be reused per frame.
## Memory
Watch: model cache (`makepad-game-gen::cache`), decal buffers (cap and recycle — `DecalBuffer` has `clear`, `push`, `len`), particle pools, audio voices (`Mixer` has `Priority` for a reason — cap voices and let priority evict), and log buffers.
The asset index itself is cheap: ~2.1 MB heap and ~120 ms build for 4,999 entries in release. It is not your leak.
## Two-controller oscillation
`GameRenderer` already implements adaptive quality. A second frame pacer or a settings slider that overrides it will oscillate and produce worse frame times than either alone. Surface `quality_level` / `set_shadow_budget` / `set_refresh_hz` as a *ceiling* and let the adaptive controller work beneath it.
## Reporting a performance result
Always give before and after, in release, on a named device, with p90:
```
device M1 Air, release
before p90 24.3 ms, q1 (reason: frame_time), 41 skinned, 96 casters
after p90 11.8 ms, q3, 12 skinned (distant LOD'd), 24 casters
change packed skinned vertex 64B->24B; shadow budget 96->24; batched crates
budget 16.6 ms @ 60 Hz - 29% headroom
```
A performance claim without a before number is not a claim.