Skip to content
Development
Skill

/performance-optimization

Find and fix game performance problems methodically — measure with the engine profiler first, reason about the frame-time budget, locate the CPU-vs-GPU bottleneck, then apply the right fix: object pooling, draw-call batching, fewer allocations/GC spikes, and asset budgets.

From plugin
awesome-gamedev-agent-skills
6.3k77 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill performance-optimization --agent claude-code

SKILL.md

performance-optimization.SKILL.md
name: performance-optimization
description: >
  Finds and fixes game frame-rate problems by measurement in Godot, Unity, Unreal or Roblox: CPU-
  vs GPU-bound, then the biggest fix (draw calls, pooling, GC spikes). Use for low FPS, stutter, a
  first-use hitch, or requests to optimize or profile. Not for build size (`unity-build-size`),
  physics jitter (`physics-tuning`) or network lag (`roblox-networking`, `godot-multiplayer`).

Performance optimization

The loop: **set a budget, measure, find the one bottleneck, fix it, measure again, report the numbers.** Guesses usually target the wrong system, so nothing changes until a profiler or a frame-time capture says where the time goes. This skill is the cross-engine method and the fixes that recur; each engine's profiler is listed below and in the reference.

When to use

  • The frame rate is low or uneven, or a target (60 FPS desktop, 30 or 60 on mobile) is missed.
  • Hitches: first-time effects, loading mid-game, periodic GC pauses, big fights.
  • Choosing what to optimize, setting budgets, or adding a performance check to CI.

**Not this skill:** physics stability (`physics-tuning`), install or download size (`unity-build-size`), network latency (`roblox-networking`, `godot-multiplayer`), shader maths (`shader-programming`).

Core workflow

1. **Turn the complaint into a number.** Target FPS gives the budget: 60 FPS is 16.67 ms, 30 is 33.3 ms, 120 is 8.33 ms. Find a repeatable worst-case scene and save it (a test level, a recorded input, a fixed seed). 2. **Uncap before measuring.** VSync and frame caps hide headroom: a 4 ms frame and a 15 ms frame both show 60 FPS. Godot `--disable-vsync` (and `--max-fps 0`); Unity `QualitySettings.vSyncCount = 0` with `Application.targetFrameRate = -1` on desktop; Unreal `r.VSync 0` and `t.MaxFPS 0`. Then read frame time in ms, not FPS. 3. **Profile a release-like build on target hardware.** Editor and debug builds add overhead and skip compiler optimization; use a Godot release export, a Unity Development build with the profiler attached, or an Unreal Development or Test build. 4. **Split CPU from GPU.** Compare main-thread, render-thread and GPU time. The side closest to the frame time is the bottleneck; fixing the other side changes nothing. 5. **Fix the single biggest cost**, preferring doing less work (cache, partition space, run less often) over polishing a hot line. Then apply the matching fix below. 6. **Re-measure the same scene on the same machine**, three runs each, and keep or revert on the numbers. 7. **Set budgets** per subsystem and per asset so the fix stays fixed, and add the frame-time check to CI where the engine allows it. 8. **Report** the scene, hardware, build type, before and after (average and p95 or p99 frame time), the bottleneck and the fix. Say what was measured headless or in the editor and what still needs the user's device.

Patterns

1. Frame budget split

@60 FPS = 16.67 ms. The slower of CPU frame and GPU frame sets the frame rate.
Example sub-budgets: gameplay/scripts ~5 ms · physics ~3 ms · render submit ~4 ms · UI ~2 ms · slack.
Mobile: keep 20-30% headroom so the game survives thermal throttling.

2. Where each engine reports the split

Godot 4.7     : Debugger > Profiler (script and physics), Visual Profiler (CPU vs GPU render time
                per pass), Monitors (FPS, draw calls, memory, pipeline compilations).
                Performance.get_monitor(TIME_PROCESS) is the worst frame of the last second in
                SECONDS, refreshed once a second; time frames yourself for per-frame spikes.
Unity 6.3 LTS : Profiler window + Frame Debugger. ProfilerRecorder on "CPU Main Thread Frame Time"
                and "GPU Frame Time" (release players need Frame Timing Stats in Player Settings).
Unreal 5.8    : stat unit (Frame / Game / Draw / RHIT / GPU), stat scenerendering (draw calls),
                Unreal Insights; `csvprofile frames=600` writes a CSV capture you can read.
Roblox        : MicroProfiler (Ctrl+F6 in Studio and the desktop client; mobile via its web UI).
# Game and Draw are CPU threads; GPU is the graphics card. Whichever is nearest Frame wins.

**Roblox:** profile on a mid-range phone, because a capped desktop hides the problem. With Studio Assistant, Roblox's built-in `rbx-perf-profiling` (reads MicroProfiler captures) and `rbx-scene-analysis` (rendering cost, memory, leaked instances) give real data; use `roblox-studio-workflow` to run checks in Studio, `roblox-physics` for assembly and simulation cost, and `roblox-networking` when "lag" means network delay rather than frame time.

3. Headless CPU frame-time check (Godot)

# tools/frame_stats.gd: CPU frame time of one scene, headless (no GPU work is measured).
# godot --headless --path . --script res://tools/frame_stats.gd -- res://levels/worst.tscn 600 16.6
extends SceneTree

func _initialize() -> void:
    var args := OS.get_cmdline_user_args()
    var scene_path := args[0] if args.size() > 0 else "res://main.tscn"
    var frames := int(args[1]) if args.size() > 1 else 600
    var budget_ms := float(args[2]) if args.size() > 2 else 16.6
    OS.low_processor_usage_mode_sleep_usec = 0          # headless otherwise sleeps ~6.9 ms a frame
    root.add_child(load(scene_path).instantiate())
    for i in 60:                                       # skip loading and first-use spikes
        await process_frame
    var ms := PackedFloat64Array()
    var last := Time.get_ticks_usec()
    for i in frames:
        await process_frame
        var now := Time.get_ticks_usec()
        ms.append((now - last) / 1000.0)               # whole frame: scripts, physics, engine
        last = now
    ms.sort()
    var avg := 0.0
    for v in ms:
        avg += v / ms.size()
    var p95 := ms[int(ms.size() * 0.95)]
    var p99 := ms[int(ms.size() * 0.99)]
    var over := ms.size() - ms.bsearch(budget_ms, false)
Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
6.3k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about performance-optimization.

Which coding agents can use performance-optimization?
performance-optimization installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.