Compare revision shader builds and pipeline interfaces across local games, and run available smoke routes with crash, hang, timing and image checks. Add the developer loop and share the measuring lock with bench.py. Keep all local games in static checks and honor --no-smoke. Include shader bytes and fallback pairs in cache keys, recheck interfaces on reuse, and reuse smoke baselines only for matching inputs and run settings. Detect failed prebuilds, missing or intermittently bad screenshots, and Vulkan build failures even when no MSL was emitted. Keep game-derived files and writable profile copies inside each game's NSP work folder. Validation: tool tests and the lint ratchet pass. Full shader rebuilds and gameplay comparisons remain unverified; the 26 discovered Mac builds have no smoke routes in this checkout. Co-authored-by: v0lumehi <aljic.zijad@gmail.com>
9.2 KiB
06 — The developer loop: which tool when, and before a pull request
One page for people and agents: the tools this repository has for building, running, debugging, measuring and checking a change, when to use which, and what a pull request needs. It links to where each tool is described instead of repeating it. Game files, keys, builds and runs stay outside the repository (AGENTS.md).
1. Build and run on the Mac
| Step | Tool | Details |
|---|---|---|
| Pack a game for the Mac | scripts/build_ipa_from_nsp.py <NSP folder> -k <prod.keys> --platform mac |
Extraction, rewriter, shader split and chain, the Mac host; prints the command that runs the game. A Mac-only build |
| A double-clickable app | the same with --platform mac-app (adds the offline pipeline archive) |
A Mac app |
| An iPhone build | the same without --platform (an unsigned IPA the user sideloads) |
The packing script in detail |
| Rebuild only the host | make -C dm/host GAME=<id> |
Build by hand (Mac) |
| Rebuild only the shaders | DM_GAME=<id> dm/gfx/tools/dmsh_build.py (after a chain change: dmsh_chain.py --header first) |
the chain procedure in AGENTS.md; 04 |
| Run with scripted input | DM_KEYS (button presses at fixed seconds or frames), DM_CTL (presses, screenshots and dumps while it runs) |
05, Scripted input |
Start Mac runs with the Metal performance HUD (DM_METAL_HUD=1). A new title starts from the checklist and 03.
2. When something is wrong
- Search the symptom index for the
[RED]line, abort text or what you see; it leads to the reference entry and the games it was found on. - Crashes: the host's
CRASH sig=…report, the macOS crash report, lldb scripts, Ghidra / objdump on the rewritten modules: 05. - Hangs and threads:
DM_WATCH=<s>/DM_WATCH_STALL=<s>dump every thread (03, Reading the log);DM_THR=1,DM_SVC=1,DM_FS=1log thread SVCs, memory SVCs and file opens;sample/xctracename the guest threads during a hitch (05);DM_HLECOUNT=<file>counts every HLE function and SVC the game called. - Guest state without a debugger:
DM_PROBEand its watch and heap variants (05). - Wrong picture:
DM_NVN_SHOTscreenshots,DM_NVN_PASSDUMP(every pass, its attachments, bindings and draw state),DM_NVN_TEXDUMP/DM_NVN_TEXSNAP(render targets as PNG),DM_NVN_CBDUMP(uniform buffers, vertex streams),DM_NVN_HILITE/DM_NVN_DEPTHOVR/DM_NVN_SKIP_FS(isolate a draw),DM_NVN_KEYDUMPandDM_NVN_MAGSCANon the phone: 04, Diagnostic switches and 05, Intermittent rendering glitches. - Slow frames: the
[longframe]lines and the hitch dump (DM_HITCH_DUMP), pipeline compile waits (05), GPU time per pass (DM_NVN_GPUPROF,DM_ABA/B phases,DM_GPU_CAPTUREfor Xcode; read withdm/tools/gpuprof.py): 05, GPU time on the device. Attachment traffic:dm/gfx/tools/tbdr_*.pyover a pass dump. - Memory: the host's memory line,
footprint,vmmap,DM_NVN_ALLOCPROF=1: 05, Memory growth. - Shaders:
dm/gfx/tools/shader_regressions.py(shaders that built in the previous chain and fail now),DM_NVN_SHDUMP(programs missing from the map), the offline link check (05). - On the iPhone: switches in
Documents/env.txt, results inDocuments/log.txt(05, On-device diagnostics). - Game updates and fixed addresses:
dm/tools/nso_fnmap.py,nso_syms.py,insn_census.py,sdis.py,nso2elf.py(Tools).
3. Measuring
scripts/bench.py <id>_build --route <name> runs a route of the profile's bench.tsv from a fixed save and gives frame-time percentiles, GPU time and screenshots; variants (--variant) compare two hosts, shader libraries or sets of switches in one invocation, which is the only comparison it supports (A benchmark on the Mac). Power and energy per frame: Power. Pass --lock <file> when other agents or people measure on the same Mac: the lock is held only while a game runs, and heavy builds should wait for it.
4. Checking a change on every game: scripts/regress.py
One chain and one host serve every profile, and each contributor owns only some games (GitHub issue #272). scripts/regress.py compares a baseline revision (by default the merge base with main) with a candidate (by default this checkout, uncommitted files included) on every game the Mac has a --platform mac build of. Its shader count uses the rule of shader_regressions.py, which the NSP script applies to a game you rebuild; the harness adds the other games, the MSL, the pipeline interface and a run. Full description: The regression harness.
| Command | What runs | Cost |
|---|---|---|
regress.py chain |
Static: each revision's shader build, compared shader by shader (built or failed, MSL / reflection / variant hashes), and the vertex / pixel interface of the pipeline archive's bases (issue #283) | nothing when the chain did not change; a changed chain translates every shader again (14 min for Breath of the Wild's 19,592 on an M2 Max) |
regress.py check --quick |
The above plus a short boot per game: the route's quick seconds, no pipeline prebuild |
about a minute per game and run |
regress.py check |
The whole route with the pipeline prebuild first (DM_PSO_PREBUILD=tool) under a watchdog |
Breath of the Wild about 5 min, Tears of the Kingdom about 7 |
regress.py baseline --rev main --runs 2 |
Records a baseline (a missing one is recorded by check itself); two runs give the noise |
as check, per run |
The routes are dm/games/<id>/smoke.tsv; a game without one takes part in the chain checks only. Add one when you own a game: a fixed save, the presses that load it, screenshots only while the player stands (where a walk ends moves from run to run), and the frame it must reach.
Reading the report (report.md beside report.json in <root>/reports/): one row per game, then the details. PASS and CHANGED (MSL or numbers moved, nothing worse) exit 0; NEW-FAIL (a shader, the interface check, a crash, a hang or the frame that worked in the baseline fails) and REGRESSION (new [RED] lines, a screenshot, frame time, memory or prepare time beyond the tolerances) exit 1; ERROR (the harness could not check a game) exits 2. Failures the baseline has too are listed as known and do not fail the check. Changed MSL is expected after a chain change: say in the pull request why it changed. A screenshot difference may be a fix: look at the run's screenshots in <NSP folder>/<id>-work/regress/runs/ before calling it either. The report holds only numbers, hashes, shader keys and normalized [RED] texts, so it can be pasted as it is.
5. Continuous integration
.github/workflows/tests.yml runs on every pull request: the host's unit tests and ThreadSanitizer targets, the CPU-only graphics tests, the Python tool tests (dm/tools/tests, dm/ios/tests, dm/gfx/tests) including the translator's synthetic-shader tests, Mac host builds for an NVN, a Vulkan and an OpenGL profile, and the iOS source compile and smoke project (Continuous integration). GitHub's Macs have no Metal 4 and no game files, so these stay local: make -C dm/gfx/tests GAME=<id> run (the Metal graphics tests), the translator tests with DMSH_TEST_METAL=1 / DMSH_TEST_GPU=1, profile_guess.py --check, and everything regress.py does.
6. Before you open a pull request
By what the change touches (the largest that applies):
- Docs only:
python3 -m unittest dm/tools/tests/test_docs.py; after a change todocs/reference,python3 dm/tools/docs_symptoms.py. - One game's profile or note (
dm/games/<id>/): the Python tool tests; a full shader build of that game after a change to its shader inputs; the game on the Mac (or the iPhone) along its route, withscripts/bench.pynumbers when the change is about speed; the finding in the game's note (AGENTS.md). - The host, the chain or the tools (
dm/host,dm/gfx,dm/tools,dm/gfx/tools,dm/ios,scripts): everything above,make -C dm/gfx/tests GAME=<id> run,dmsh_chain.py --headerafter a chain change, thenpython3 scripts/regress.py checkover every local game (--quickwhile iterating, the full run before the pull request), and itsreport.mdpasted into the pull request with a word on everyCHANGED,NEW-FAILorREGRESSION. Name the games the report covers: the others are unchecked.