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.
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.
Memory budget by platform tier
Set hard caps per-platform on day one. Reasonable splits:
| Pool | % of game-accessible memory |
|---|---|
| Texture pool | 40–60% |
| Render targets | 20–25% |
| Nanite stream pool | 8–12% |
| VT pool | 8–12% |
| Headroom | 10% |
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.
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 -alphasort—NumKBytes(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.
Texture pool tuning
[/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.
Mesh streaming & Nanite pool
r.Streaming.PoolSizeForMeshes— default-1means 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 acrossProcessAsyncLoadingandUpdateLevelStreaming; donates unused budget to whichever needs it.
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).
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.
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.
Allocator-side tuning & UE 5.6 wins
- VLPA hoarding —
VeryLargePageAllocator.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
| Platform | Game RAM | Texture pool | Nanite pool | VT pool | RT pool |
|---|---|---|---|---|---|
| PS5 | ~12.5 GB | 6 GB | 1.2 GB | 512 MB | 400 MB |
| XSX | ~13.5 GB | 6 GB | 1.2 GB | 512 MB | 400 MB |
| XSS | ~7.5 GB | 3.5 GB | 512 MB | 256 MB | 256 MB |
| Steam Deck | ~8 GB shared | 3 GB | 512 MB | 256 MB | 256 MB |
| Quest 3 | ~7 GB | 2.5 GB | n/a | n/a | 192 MB |
| Switch 1 | ~3.2 GB | 512 MB | n/a | n/a | 96 MB |
| PC mid | varies | 3 GB | 768 MB | 384 MB | 400 MB |
| PC high | varies | 6 GB | 1.5 GB | 768 MB | 400 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.
- Memory Insights & LLM — the profiling workflow that produces the data this tutorial budgets.
- Loading Time Optimization — soft references and async loading that affect memory.
- Cook & Packaging — IoStore vs pak affects on-disk + load-time memory.