unity-performance

v2026.09.24

Unity performance optimization for slow games, stutters, and FPS drops. PROACTIVELY activate for: (1) game is slow, stuttering, or has FPS drops, (2) Unity Profiler analysis, (3) Frame Debugger and draw-call investigation, (4) batching (static, dynamic, GPU instancing, SRP Batcher), (5) object pooling to reduce GC pressure, (6) garbage collection spikes and allocation hot paths, (7) memory leaks and Memory Profiler usage, (8) LOD and occlusion culling, (9) texture compression and mipmap settings, (10) build size reduction. Provides: profiling workflow, batching patterns, object-pool implementations, GC reduction techniques, LOD/culling setup, and texture-compression matrix.

GitHub
Install command
npx skhub add josiahsiegel/unity-performance
Markdown
SKILL.md

Unity Performance Optimization

Overview

Systematic approach to profiling and optimizing Unity games. Covers profiling tools, CPU/GPU optimization, memory management, rendering optimization, and platform-specific considerations.

Profiling Tools

ToolWhat It ShowsWhen to Use
Unity ProfilerCPU, GPU, memory, audio, physics per frameFirst stop for any perf issue
Frame DebuggerDraw call breakdown, shader/material stateRendering bottlenecks
Memory ProfilerHeap snapshots, texture/mesh memoryMemory leaks, bloat
Profile AnalyzerCompare captures, statistical analysisBefore/after optimization
Physics DebuggerCollider visualization, contact pointsPhysics performance

Profiler Workflow

  1. Build with Development Build + Autoconnect Profiler enabled
  2. Profile on target device (not in Editor -- Editor overhead distorts results)
  3. Identify the bottleneck category: CPU-bound, GPU-bound, or memory pressure
  4. Drill into the specific system causing the issue
  5. Optimize, re-profile, and compare

Reading the Profiler

If frame time > 16.6ms (60 FPS target):
  CPU timeline > GPU timeline -> CPU-bound
  GPU timeline > CPU timeline -> GPU-bound
  GC.Alloc column shows per-frame allocations -> GC pressure

Look for spikes (single bad frames) vs. sustained high times (baseline too heavy).

CPU Optimization

Reduce Per-Frame Allocations (GC)

Anti-PatternFix
string + string in UpdateUse StringBuilder or cache
new List<T>() every frameAllocate once, Clear() and reuse
LINQ in hot pathsReplace with manual loops
GetComponent<T>() per frameCache in Awake/Start
GameObject.Find() per frameCache reference or use events
foreach on non-generic collectionsUse for loop or generic collections
SendMessage() / BroadcastMessage()Use direct calls, events, or interfaces
Boxing value typesUse generic collections, avoid object casts

Object Pooling

public class ObjectPool<T> where T : Component
{
    readonly Queue<T> _pool = new();
    readonly T _prefab;
    readonly Transform _parent;

    public ObjectPool(T prefab, int preWarm, Transform parent = null)
    {
        _prefab = prefab;
        _parent = parent;
        for (int i = 0; i < preWarm; i++)
            _pool.Enqueue(CreateInstance());
    }

    public T Get(Vector3 position, Quaternion rotation)
    {
        var obj = _pool.Count > 0 ? _pool.Dequeue() : CreateInstance();
        obj.transform.SetPositionAndRotation(position, rotation);
        obj.gameObject.SetActive(true);
        return obj;
    }

    public void Return(T obj)
    {
        obj.gameObject.SetActive(false);
        _pool.Enqueue(obj);
    }

    T CreateInstance()
    {
        var obj = Object.Instantiate(_prefab, _parent);
        obj.gameObject.SetActive(false);
        return obj;
    }
}

Pool bullets, particles, enemies, UI elements -- anything instantiated/destroyed frequently. Unity 2021+ also has UnityEngine.Pool.ObjectPool<T> built-in.

Update Optimization

TechniqueDescription
Stagger updatesDon't update all AI every frame; use tick groups
Distance-based LODReduce update frequency for distant objects
Event-drivenReplace polling with events where possible
Disable unused scriptsenabled = false on off-screen components
Use InvokeRepeatingFor periodic checks (cheaper than coroutine yielding)

GPU / Rendering Optimization

Draw Call Reduction

TechniqueHowSavings
Static BatchingMark non-moving objects as StaticCombines meshes at build time
Dynamic BatchingAutomatic for small meshes (<300 verts)URP/Built-in only
GPU InstancingEnable on materials for repeated objectsTrees, grass, rocks
SRP BatcherEnabled by default in URP/HDRPReduces SetPass calls
Texture AtlasingCombine textures into atlasFewer material switches
Mesh CombiningCombineMeshes() at runtimeCustom batching

LOD (Level of Detail)

LOD Group Setup:
  LOD 0 (0-30%):  Full-detail mesh (5000 tris)
  LOD 1 (30-60%): Medium mesh (2000 tris)
  LOD 2 (60-90%): Low mesh (500 tris)
  Culled (90%+):  Not rendered

Use LOD for meshes, but also reduce script complexity, particle counts, and physics at distance.

Occlusion Culling

Bake occlusion data for indoor/complex scenes. Mark large static occluders (walls, floors). Configure cell size based on scene scale. Use the Occlusion Culling window to visualize and test.

Shader Optimization

IssueSolution
Complex fragment shadersReduce texture samples, simplify math
OverdrawMinimize transparent objects, use opaque when possible
Too many variantsStrip unused shader variants in build settings
Expensive post-processingDisable effects on mobile, use cheaper alternatives

Memory Management

Common Memory Issues

IssueSymptomFix
Texture bloatHigh memory, long loadsCompress textures, reduce max size per platform
Unloaded scenes holding refsMemory climbs over timeUse Resources.UnloadUnusedAssets() after scene transitions
Addressables not releasedBundles stay in memoryCall Addressables.Release(handle)
Audio clips uncompressedHuge memory footprintUse compressed in memory for music, decompress on load for SFX
Mesh read/write enabledDouble memory per meshDisable Read/Write if not needed at runtime

Texture Compression Per Platform

PlatformFormatNotes
PC/ConsoleBC7 (DXT)Best quality/size ratio
AndroidASTC 6x6Universal, scalable quality
iOSASTC 6x6Same as Android
WebGLETC2 / DXTDepends on target GPU

Use "Override for [Platform]" in texture import settings. Set max texture size to the minimum needed (512 for UI icons, 1024 for props, 2048 for hero assets).

Platform-Specific Considerations

PlatformKey Constraints
MobileThermal throttling, limited memory, battery drain, fill-rate limited
WebGLNo threads (pre-Unity 6), large download size, no compute shaders
ConsoleCertification requirements, fixed hardware, memory budgets
VR/XR72-90 FPS minimum, stereo rendering cost, motion sickness from drops

Addressables and Asset Loading

Use Addressables for async asset loading to avoid load-time hitches:

// Preload during loading screen
var handle = Addressables.LoadAssetAsync<GameObject>("enemy_boss");
await handle;

// Release when done
Addressables.Release(handle);

Use Addressable groups to control bundle granularity. Mark infrequently used assets as remote/on-demand. Profile bundle memory with the Addressables Event Viewer.

Quick Optimization Checklist

  • Profile on target device, not in Editor
  • Zero per-frame GC allocations in gameplay code
  • Object pooling for all frequently spawned objects
  • Static batching enabled for non-moving objects
  • LOD groups on all 3D models visible at varying distances
  • Textures compressed per platform with appropriate max sizes
  • Disable Read/Write on meshes and textures not modified at runtime
  • Audio clips use appropriate compression settings
  • Occlusion culling baked for indoor/complex scenes
  • Shader variants stripped in build settings

Additional Resources

Reference Files

  • references/profiling-deep-dive.md -- Advanced Profiler usage, memory profiler snapshots, frame-by-frame analysis, custom profiler markers, automated performance testing, build size analysis, ECS/DOTS performance patterns
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

plugins/unity-master/skills/unity-performance

Default branch

main

Latest commit

5a1b112

Tree SHA

376c8e0