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.
87 lines
3.7 KiB
Markdown
87 lines
3.7 KiB
Markdown
# Render budget
|
||
|
||
`apps/arcade/BUDGETS.md` is the authority. This is the working summary and the method.
|
||
|
||
## What the numbers say
|
||
|
||
Measured on an M-class Mac, release. The Quest column in BUDGETS.md is a conservative **estimate** until run on device — do not quote it as measured.
|
||
|
||
| thing | cost |
|
||
| --- | --- |
|
||
| 100 movers + 50 rigid bodies + 65×65 terrain | **0.038 ms/tick** |
|
||
| 60 Hz frame budget | 16.6 ms |
|
||
| script per tick | ≤ 2 ms, one cumulative 500k-instruction pool |
|
||
| entity lookup | O(log n) |
|
||
|
||
**Simulation is ~0.2% of the frame. It is never your performance problem.** Draw calls and CPU skinning are.
|
||
|
||
## Bandwidth
|
||
|
||
| stream | before | after |
|
||
| --- | --- | --- |
|
||
| cube instance | 44 floats / 176 B | **32 floats / 128 B** (−27%) |
|
||
| skinned character vertex | 16 floats / 64 B | **6 floats / 24 B** (−62%) |
|
||
| shadow mesh vertex | 16 floats / 64 B | **6 floats / 24 B** (−62%) |
|
||
| terrain vertex | 16 floats / 64 B | unchanged (uploaded once per revision) |
|
||
|
||
The skinned vertex matters most because it re-uploads **every frame**: the Knight at 3716 verts went from 238 KB/frame to 89 KB/frame. On a bandwidth-bound tiler that is the largest single saving available.
|
||
|
||
The instance saving came from moving `sun_color`, `sun_sky`, `sun_ground`, `fog_color` off the instance stream into uniforms — 12 floats of pure per-cube duplication.
|
||
|
||
**Quest is bandwidth-bound before it is ALU-bound.** Optimise bytes per frame first.
|
||
|
||
## Measuring
|
||
|
||
Never estimate. Read it:
|
||
|
||
```rust
|
||
renderer.report_frame_ms(ms); // -> bool: did adaptive quality change?
|
||
renderer.frame_p90_ms(); // Option<f32> - report p90, not mean
|
||
renderer.quality_level();
|
||
renderer.quality_reason();
|
||
stats.instance_floats; // RenderStats: from the compiled shader
|
||
renderer.model_triangles(id);
|
||
```
|
||
|
||
Report **p90, not average**. A 16 ms average with a 40 ms p90 stutters, and the average hides it.
|
||
|
||
Headless evidence:
|
||
|
||
```bash
|
||
cargo run -p makepad-arcade --example bigworld_probe --release
|
||
```
|
||
|
||
Always measure in `--release`. A debug-build frame time tells you nothing about shipping.
|
||
|
||
## Cutting, in order
|
||
|
||
1. **Instance/vertex bytes** — packed layout everywhere, batch-constant values as uniforms.
|
||
2. **Draw calls** — batch by material and mesh. Many small draws beat nothing.
|
||
3. **Skinned character count** — the most expensive category per unit. Cap it; degrade distant characters.
|
||
4. **Shadow casters** — `set_shadow_budget(n)`; bake AO for the rest.
|
||
5. **Terrain resolution** — uploaded once per revision, so this is a memory and vertex-count question more than a per-frame one.
|
||
6. **Particle count** — cheap individually, unbounded collectively. Cap the pool.
|
||
7. **Fragment work** — last, and only with a measurement showing you are ALU-bound.
|
||
|
||
## Adaptive quality
|
||
|
||
`GameRenderer` owns it: `report_frame_ms` each frame, `set_refresh_hz` for the device (72 Hz on Quest, not 60), `set_shadow_budget`, `Stage` / `StageMode`, and `quality_reason()` to explain a drop.
|
||
|
||
**Do not build a second frame pacer.** Two controllers oscillate against each other and produce worse frame times than either alone. If quality is dropping unexpectedly, read `quality_reason()` — that is what it is for.
|
||
|
||
## Budget template
|
||
|
||
Fill this in for the target device before optimising, so you know when to stop:
|
||
|
||
```
|
||
target device Quest 2, 72 Hz -> 13.9 ms
|
||
sim 0.1 ms (measured)
|
||
skinned chars 8 x ? ms
|
||
shadow casters 24 x ? ms
|
||
opaque draws ? calls ? ms
|
||
particles ? max ? ms
|
||
HUD ? ms
|
||
headroom >= 20%
|
||
```
|
||
|
||
Twenty percent headroom is not padding: it is what absorbs the worst wave, the busiest camera angle, and thermal throttling on a mobile part after ten minutes of play.
|