makepad/skills/makepad-debug-profiler/SKILL.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

6.8 KiB

name description
makepad-debug-profiler Diagnose and fix Makepad game defects: black or empty viewport, missing models, broken shaders, wrong lighting, input that does nothing, stutter and frame spikes, memory growth, netplay desync, script errors, and mobile/XR-specific failures. Use for 'nothing renders', 'it compiles but crashes on launch', 'assets don't appear', 'it's slow', and profiling work.

Makepad Debug Profiler

Find the actual cause. The most expensive debugging in this codebase comes from fixing a symptom one layer above the fault.

Method

  1. Reproduce deterministically. Fix the seed, fix the tick count, note the platform and build profile. An intermittent bug you cannot reproduce is a bug you cannot verify fixed.
  2. Bisect the pipeline, not the source file. The layers are: sim state → scene state → instance data → draw call → shader → pass composite. Find the last layer where the data is right.
  3. Print the data, not the code. Assert what the values actually are at that boundary.
  4. Fix the cause, then add the test that would have caught it.

Layer bisection for "nothing renders"

Work down; the first failing check is your bug.

// 1. does the sim have anything?
println!("entities: {}", world.entities.len());

// 2. is it where you think?
println!("player pos: {:?}", player.pos);

// 3. is the camera looking at it?
println!("cam: {:?} target {:?}", rig.pos, rig.target);

// 4. did instances reach the renderer?
println!("models: {} particles: {}", models.len(), particles.len());

// 5. did the model actually load?
println!("loaded: {}", renderer.model_is_loaded("kenney/racing/vehicle-truck-yellow"));
println!("bounds: {:?}", renderer.model_bounds(id));

// 6. is quality having silently dropped things?
println!("q{} because {}", renderer.quality_level(), renderer.quality_reason());

Most common causes, in the order they actually occur:

Symptom Usual cause
Black viewport camera inside geometry, or near/far plane wrong, or the pass never composited
Everything invisible but HUD fine camera target not updated from the sim; rig at origin
Model missing, no error wrong asset id — it degrades to a primitive silently. Assert model_is_loaded
Model present but tiny/huge scale mismatch between the kit and the player capsule
Model at origin transform not applied; ModelInstance::new(.., transform) got identity
Flat/wrong lighting light_dir set on some draw types and not others
Shadows vanished under load adaptive quality dropped the budget — read quality_reason()
Fine in cargo check, broken on launch the splash DSL resolves at runtime

That last row is the most important fact in this skill: cargo check cannot validate the DSL. A malformed style, a missing widget id, or a bad inheritance path fails only when the app runs. Any UI or style change must be launched.

Silent asset fallback

The library is designed to degrade gracefully — with no assets downloaded the game runs on primitives and asset-dependent tests skip. Convenient for CI, dangerous when debugging: a typo'd id looks like a design choice.

bash skills/makepad-game-director/scripts/probe_assets.sh

Then assert, don't eyeball:

for id in scene_model_ids { assert!(renderer.model_is_loaded(id), "missing {id}"); }

Input that does nothing

Bisect the same way: does the platform event arrive → does the mapping layer produce an action → does the sim consume it → does the entity respond?

Specific traps: a radial deadzone applied per-axis; a ground raycast that hits the player's own capsule (so on_floor is always false, so jump never fires); an input buffer that is never cleared (double jumps); touch handled only as mouse (so nothing works on device); XR controller pose read as gamepad axes.

Performance

Measure before changing anything, in --release.

renderer.frame_p90_ms();     // report p90, not mean
renderer.quality_level();
renderer.quality_reason();
stats.instance_floats;
cargo run -p makepad-arcade --example bigworld_probe --release

Given the sim is ~0.2% of frame time, a slow frame is almost never gameplay code. Check, in order: skinned character count (CPU skinning re-uploads every vertex every frame), draw-call count, shadow casters, particle pool size, instance stream width, and only then fragment work.

Stutter is not the same as low framerate. A 16 ms mean with a 40 ms p90 is a stutter problem: look for per-frame allocation, model loading during play, terrain regeneration, or a HashMap rehash in the step loop. Preload and pool.

Two systems fighting is a classic here: if quality oscillates, check you have not added a frame pacer beside GameRenderer's adaptive controller.

Netplay desync

Host-authoritative, so a divergence means a client simulated something it should not have, or the sim is not deterministic. Check: variable dt anywhere; std float transcendentals instead of makepad-game-math; unseeded RNG instead of the seeded rng; HashMap iteration order inside step; clients sending state instead of input; content replicated as vertex data instead of (preset, seed, position).

Bisect by logging a per-tick state hash on host and client and finding the first differing tick.

Script route

Errors surface in EvalReport. Constraints that present as bugs: the 500k-instruction cumulative pool per tick shared by on_tick + timers + touch (symptom: handlers silently stop running late in a tick), and typo-guarded option keys (symptom: a verb rejects a key — the key is wrong, do not work around it). Untrusted scripts must pass strip_capabilities with a Trust level; a capability failure is the sandbox working.

Memory

Growth over time comes from unbounded Vecs: particle instances, decals (BulletDecal — cap and recycle), log buffers, model cache (makepad-game-gen::cache), or per-frame allocations never reused. Pool anything created per frame.

Tooling

cargo check -p <crate> for the fast loop; cargo test -p <crate> for Cx-free logic; cargo clippy (the repo has clippy.toml); makepad-test for driving the real app with widget_dump(), widget_snapshot(), screenshot(), wait_for_log_contains(); RUST_BACKTRACE=1 for panics; the ao_render probe to judge baked AO by eye.

When stuck

Re-read the actual API rather than guessing at a signature — makepad_game_script::api_text() prints the authoritative verb list, and the crate sources are in the tree. State what you have ruled out. Say plainly what you could not reproduce; an unverified fix reported as fixed is worse than an open bug.

Required reading

  • references/debug-playbook.md — symptom→cause tables and bisection recipes.
  • references/profiling.md — measuring, budgets, what to cut.