← Back to Tutorials
Intermediate~16 min readUE 5.5 / 5.6

Memory & VRAM Optimization

Memory beats FPS for shipping. OOM crashes get refunds; framerate drops get bad reviews; the latter is recoverable. This tutorial walks the senior memory budget, the texture-pool / mesh-streaming / virtual-texture / render-target pools and how each is sized, allocator-side tuning (VLPA hoarding, UObject hash arrays), and the per-platform memory budget table that decides every other call.

1

Why memory beats FPS for shipping

Three failure modes ship a UE5 title:

  • OOM crash — player loses progress, refunds.
  • Eviction stutter — texture pop-in, mesh LOD blowups.
  • TRC / cert failure — submission rejected.

All three trace to memory budget violations. Frame time can recover at runtime; memory generally cannot — once you're past the budget, the only options are crash or visible quality regression.

2

Memory budget by platform tier

Set hard caps per-platform on day one. Reasonable splits:

Pool% of game-accessible memory
Texture pool40–60%
Render targets20–25%
Nanite stream pool8–12%
VT pool8–12%
Headroom10%

Reference budgets per platform: PS5/XSX ~12.5 / 13.5 GB game-accessible; XSS ~7.5 GB; Steam Deck ~8 GB shared with GPU; Quest 3 ~7 GB; Switch 1 ~3.2 GB; Switch 2 ~9 GB. The XSS floor sets the worst case for cross-platform UE5 titles.

3

Reading memreport -full like a senior

Trigger memreport -full in a packaged Test/Shipping cook (editor reports inflated AnimSequence and similar sizes, ~2× per Tom Looman). Files land in <Project>/Saved/Profiling/MemReports/. Walk the canonical sections:

  • Platform memory header — OS-side numbers (Used Physical, Used Virtual).
  • obj list -alphasortNumKBytes (UObject body) + ExclusiveResKBytes (owned resources). Sort by ExclusiveResKBytes for memory-by-asset-type.
  • rhi.DumpMemory — GPU-side resource memory (RHI textures, vertex/index buffers).
  • ListTextures / ListSounds / r.DumpRenderTargetPoolMemory — per-asset-class drilldown.

The [MemReportCommands] and [MemReportFullCommands] sections in BaseEngine.ini can be extended with project-specific dumps. Add your custom subsystem memory reports here.

4

Texture pool tuning

DefaultEngine.ini
[/Script/Engine.RendererSettings]
r.Streaming.PoolSize=3000          ; MB; -1 = engine-managed (avoid on shipped)
r.Streaming.UseFixedPoolSize=1     ; Required for shippable platforms
r.Streaming.LimitPoolSizeToVRAM=1  ; Clamp to VRAM minus RT/non-streaming
r.Streaming.MipBias=0              ; Per-platform; 0.5+ on memory-constrained tiers

Per CVar wiki: scalability tier defaults run 400 MB (low) → 3000 MB (cinematic) in BaseScalability.ini. Default r.Streaming.PoolSize=-1 uses engine-managed sizing — not appropriate for shipping. Set explicit values per device profile.

Audit oversized textures with ListTextures; common offenders are 4K UI atlases on a 1080p phone, lightmaps not in mobile-specific groups, and per-character normal maps that should be shared.

5

Mesh streaming & Nanite pool

  • r.Streaming.PoolSizeForMeshes — default -1 means meshes share the texture pool. Set positive (MB) to isolate. Mesh and texture sharing is the #1 reason "my texture pool keeps overflowing" on Nanite-heavy scenes.
  • r.Nanite.Streaming.StreamingPoolSize — runtime-resizable in 5.5+. Sized to ~10% of VRAM is typical.
  • r.Nanite.Streaming.ReservedResources=1 — uses Reserved Resource heaps to avoid spike on resize (5.5+).
  • UE 5.6 s.UseUnifiedTimeBudgetForStreaming — unified time budget across ProcessAsyncLoading and UpdateLevelStreaming; donates unused budget to whichever needs it.
6

Virtual texturing pools

Per-format VT pools: ~64 MB default per format, 8 formats common ≈ ~512 MB resident if all used.

  • r.VT.PoolSize — per-format pool size.
  • r.VT.PoolSizeScale — DP-scaled, requires "Allow Size Scale" per pool config or it silently no-ops.
  • r.VT.SpaceReleaseFrames — UE 5.7 default 150 frames idle before release.

UE 5.6 added CPU distance-based VT mip streaming budget — useful for memory-constrained tiers. UE 5.4+ has a known issue where VT pool auto-grow constantly logs unless you pin pool sizes per format (UE-183918).

7

Render target & transient allocator hygiene

r.RenderTargetPoolMin default ~400 MB. On 8 GB consoles that's ~5% of RAM permanently reserved. Tune down on memory-bound platforms; the cost is more frequent RT pool churn (allocations during the frame).

For RDG (Render Dependency Graph) transient allocations, the engine handles most of this internally. The user-facing levers are r.RDG.TransientAllocator (default on; high-watermark sizing) and r.RDG.AsyncCompute.

8

PSO and shader memory

UE 5.6 added ListShaders command for runtime shader memory + load analysis — mirrors ListTextures. Use to diagnose unexpected shader residency growth.

5.6 also tracks ShaderTypeStats CSV (per Tom Looman); dump and diff between builds to catch shader memory regressions.

For PSO memory specifically, see the PSO Precaching tutorial: r.PSOPrecache.KeepInMemoryGraphicsMaxNum caps retained graphics PSOs, r.PSOPrecache.KeepInMemoryComputeMaxNum caps compute. Tuning these trades VRAM for cache-hit reliability.

9

Allocator-side tuning & UE 5.6 wins

  • VLPA hoardingVeryLargePageAllocator.MaxCommittedPageCountDefault + MaxCommittedPageCountSmallPool (UE 5.5) prevent VLPA from permanently hoarding pages after peak usage. Pre-5.5 hoarded indefinitely.
  • UE_UOBJECT_HASH_USES_ARRAYS — UE 5.6 build define; ~20% UObject hash memory reduction.
  • Oodle 2.9.13 — UE 5.6; 25–30% faster BC7-RDO encode (cook time + on-disk size).
  • ACL animation compression — recommended for UE5; ~50% animation memory reduction, 8.4× faster decompression, 56× faster compression vs stock.

Per-platform memory budget table

PlatformGame RAMTexture poolNanite poolVT poolRT pool
PS5~12.5 GB6 GB1.2 GB512 MB400 MB
XSX~13.5 GB6 GB1.2 GB512 MB400 MB
XSS~7.5 GB3.5 GB512 MB256 MB256 MB
Steam Deck~8 GB shared3 GB512 MB256 MB256 MB
Quest 3~7 GB2.5 GBn/an/a192 MB
Switch 1~3.2 GB512 MBn/an/a96 MB
PC midvaries3 GB768 MB384 MB400 MB
PC highvaries6 GB1.5 GB768 MB400 MB

PerfGuard's "Memory Regression scenario" type captures baseline memreport, snapshots LLM CSV, and parses peak r.Streaming.PoolSize + Nanite pool + VT pool + RT pool + obj list class=Texture2D|StaticMesh|SoundWave totals into the report. CI gate fails when any tag exceeds per-platform budget by configured tolerance — same delta-vs-baseline + PASS/FAIL gate model as frame time, applied to memory.