scenario-unity-performance

Installation
SKILL.md

Unity performance (performance engineer)

Expert performance work is a measurement discipline: a budget in milliseconds, the bounding thread named before any change, the biggest measured cost fixed first, structure before tuning, and every fix proven with before/after numbers from a development player. An agent does all of it headless: AgentProfile in Editor Play mode to iterate, PerfProbe in a batch-mode player for verdicts, PerfCapture to attribute costs from a Profiler capture, metal_trace for GPU time, PerfBuild for size. Target: Unity 6000.3.21f1, URP 17.3, macOS Apple Silicon. If a sibling skill named here is missing from your available skills, ask the user to install it (npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and flag the gap.

REQUIRED BACKGROUND: scenario-unity-expert (channels, review loop, 6.3 traps, ut_* toolkit).

Stance (the expert delta)

  1. Name the bound before touching anything. "If you speed up the GPU when your profile looks like this it's not going to run any faster" (Peter Hall, Unity Profiler team, _cV1B2hqXGI [00:05:50]). Observed: removing 1.36 MB of garbage per frame did not move a frame bound by 5,946 SetPass calls. GPU bound needs GPU timing; the Frame Debugger has none (Hall [frame 00:09:18]).
  2. Players decide, the Editor iterates. The authority is a development build on the lowest target device (profiling e-book p. 18). GRD: 4.4x in a 2M-instance stress test, 2.07x in the Editor but 1.45x standalone in a real game (Oc6T4hh5gaI [frame 00:12:41]); here the Editor swung 1.5x to 3.0x, players 1.9x to 2.8x on a stress fixture. Forecast about 1.5x.
  3. GC.Alloc sample time is not the cost. Unity records only timestamp and size (e-book p. 27): 54,021 allocations shown as 1.01 ms caused 19.97 ms of GC.Collect (Hall [frame 00:21:52]). Observed: 27,000 allocations per frame displayed 0.8 to 1.1 ms; the allocating code cost 3.8 to 5.4 ms. Count bytes and calls, measure collections, target 0 B per gameplay frame, and keep the probe itself allocation-free (git-amend ham_w48aRJ4 [00:11:30]).
  4. 6.3 URP draw calls = SRP Batcher + GPU Resident Drawer; the 2024 advice is Built-in-only. Material "Enable GPU Instancing" off, static batching off, no MaterialPropertyBlock (6.3 Manual table). GRD silently does nothing unless BatchRendererGroup Variants = Keep All and every renderer of the URP asset is Forward+ or Deferred+ (URP 17.3 source, observed). Then prove coverage: GRD draws are named "Hybrid Batch Group" in the Frame Debugger (GUI editor only), each other draw is a renderer that fell out (MPB, LPPV, realtime GI, non-DOTS shader, skinned; 6.3 Manual); headless, check eligibility per renderer after Start.
  5. Data layout first; spreading work over frames hides cost. 200,000 objects: MonoBehaviour Update 27 ms to 0.13 ms through manager, structs, Burst, SoA; "it's just spreading the work around" (Jason Booth, NAVbI1HIzCE [00:06:38]-[00:18:17]).
  6. Attribute every hitch before fixing it, with markers kept in code. Streaming and spawn systems carry their own ProfilerMarkers and counters; Deep Profile only finds what fills a gap (Hall [00:18:39]-[00:19:12]). Compiles show as Shader.CreateGPUProgram (warm a traced GraphicsStateCollection during loading, Nicolas Borromeo CmD8MVGkDxQ [00:11:14]-[00:14:35]); here warm-up removed all 18 first-use compiles, yet the first frame stayed about 250 ms on Mono.JIT.
  7. Resident memory kills, untracked usually does not. 0.75 GB untracked was 140 MB resident (Borromeo [00:32:22]); Editor memory is about 5x the player's (Uuzd39AjFWQ [00:10:03]). On a laptop, paging and VRAM overflow are stutter causes of their own.
  8. Counts are exact, times drift [added, observed]. On a shared machine (load average 50 to 210 here), interleave A/B runs and require a consistent sign.

Establish first

Installs
165
GitHub Stars
1.0K
First Seen
Sep 26, 2026