Tags: unity, performance, optimization, lod, occlusion-culling, object-pooling, draw-calls, batching, game-dev Last updated: 2026-06-27

Performance Optimization Cheatsheet

Quick Reference

TechniqueSolvesImpact
LOD (Level of Detail)High poly counts at distanceHigh
Occlusion CullingRendering hidden geometryHigh
Object PoolingInstantiation GC spikesHigh
Static/Dynamic BatchingDraw call countMedium–High
GPU InstancingRepeated meshes (trees, rocks)Medium
Texture AtlasingMaterial batchingMedium

LOD (Level of Detail)

LOD Group Setup

  1. Add LOD Group component to mesh parent.
  2. Define LOD levels (0 = full detail, 1/2/3 = reduced).
  3. Assign lower-detail meshes per level.
  4. Set transition percentages.
LODScreen %Triangle Count (example)
LOD0100–60%Full (10,000)
LOD160–30%Medium (3,000)
LOD230–10%Low (500)
Culled<10%0
LODGroup lodGroup = GetComponent<LODGroup>();
LOD[] lods = lodGroup.GetLODs();
lods[0].screenRelativeTransitionHeight = 0.1f;
lodGroup.SetLODs(lods);

Occlusion Culling

Setup

  1. Window → Rendering → Occlusion Culling.
  2. Mark static objects as Occluder Static / Occludee Static.
  3. Bake occlusion data.
  4. Enable Occlusion Culling on Camera.

Key Settings

SettingEffect
Smallest OccluderMinimum size to block (smaller = more occluders, higher bake)
Smallest HoleGap size that counts as transparent
Backface Threshold% backfaces to consider occluder

Manual Checks

if (Vector3.Distance(player.position, transform.position) > cullDistance)
    return;

if (!IsVisibleFrom(renderer, Camera.main))
    return;

Object Pooling

Basic Pool

public class ObjectPool<T> where T : Component {
    Queue<T> pool = new();
    T prefab;

    public ObjectPool(T prefab, int initialSize) {
        this.prefab = prefab;
        for (int i = 0; i < initialSize; i++) {
            T obj = GameObject.Instantiate(prefab);
            obj.gameObject.SetActive(false);
            pool.Enqueue(obj);
        }
    }

    public T Get(Vector3 pos, Quaternion rot) {
        T obj = pool.Count > 0 ? pool.Dequeue() :
            GameObject.Instantiate(prefab);
        obj.transform.SetPositionAndRotation(pos, rot);
        obj.gameObject.SetActive(true);
        return obj;
    }

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

Unity Built-In Pool (2021+)

using UnityEngine.Pool;

ObjectPool<Bullet> bulletPool = new(
    createFunc:  () => Instantiate(bulletPrefab),
    actionOnGet: (b) => b.gameObject.SetActive(true),
    actionOnRelease: (b) => b.gameObject.SetActive(false),
    actionOnDestroy: (b) => Destroy(b.gameObject),
    defaultCapacity: 20
);

Bullet b = bulletPool.Get();
bulletPool.Release(b);

Pooling Tips

Draw Call Batching

Static Batching

Dynamic Batching

GPU Instancing

Graphics.DrawMeshInstanced(mesh, 0, material, matrices, count);
MethodMax ObjectsMoving?Memory
Static BatchingUnlimitedNoHigher memory
Dynamic BatchingPer-frame limitYesHigher CPU
GPU Instancing~1023/batchYesLower memory

SRP Batcher (URP/HDRP)

Profiling

Tools

ToolWhat It Shows
ProfilerCPU/GPU/Rendering/Memory per frame
Frame DebuggerEvery draw call and why it happened
Memory ProfilerAll allocations, managed + native
Rendering Debugger (URP/HDRP)Overdraw, light count, shadow maps
Physics DebuggerCollider geometry, contacts

Key Metrics

MetricTarget (PC)Target (Mobile)
Draw Calls<2000<200
SetPass Calls<50<20
Triangles<1M visible<100K visible
Batches (SRP)<500<100
GPU ms<16ms (60fps)<33ms (30fps)
Managed Alloc/frame<1KB goal<1KB goal
Profiler.BeginSample("MyExpensiveCode");
// Code to profile
Profiler.EndSample();

Common Bottleneck Fixes

SymptomCheckFix
High draw callsFrame DebuggerEnable SRP Batcher, atlasing, instancing
CPU spikesProfiler → CPU UsageObject pool, cache GetComponent, reduce Find()
GPU boundGPU profilerReduce overdraw, shadows, post-processing res
GC alloc spikesMemory ProfilerPool objects, cache lists, avoid string concat
Physics spikesPhysics DebuggerReduce collider count, lower solver iterations
Loading timeProfiler → LoadingSplit into additive scenes, async loading