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.
88 lines
3.9 KiB
Markdown
88 lines
3.9 KiB
Markdown
# 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.
|