← Back to Tutorials
Advanced ~22 min read UE 5.5 / 5.6

VR/XR Performance Deep Dive

VR is not "flat 3D with two cameras." Stereo doubles GPU work, the frame deadline is 11 ms or 8 ms instead of 16 ms, missed frames produce visible reprojection artifacts and nausea, and the platforms (Quest mobile, Index PC, PSVR2 console) each have their own perf shape. This tutorial covers stereo rendering paths, MSAA on tiled GPUs, foveated rendering, Late Latching, Application SpaceWarp, and the per-platform settings actually shipped UE5 VR titles use.

1

Why VR is a different perf problem

The frame budget is half what a flat-3D engineer is used to. At 90 Hz the deadline is 11.1 ms; at 120 Hz it's 8.3 ms; at 72 Hz (Quest default) it's 13.7 ms. Per Meta's PC VR Performance Guidelines, PC-VR Rift target is 90 fps recommended-spec, 45 fps minimum-spec.

And the GPU work doubles. Both eyes have to be rendered, with sufficient overlap that single-pass instanced stereo can amortize work but never eliminate it. Distortion and TimeWarp also reserve roughly 2 ms per frame from the headset compositor — that's gone before your renderer touches anything.

And missed frames are not stutters — they're nausea. When you miss a frame in flat-3D, the image freezes for 16 ms and the player notices. When you miss a frame in VR, the runtime reprojects the previous frame to the new head pose, the player sees ghosting and judder, and they take the headset off. Reprojection can mask geometry costs, but never input-latency costs — and that's the design constraint that shapes everything.

2

Picking a renderer per platform

Renderer choice maps to platform tightly:

  • Quest (Quest 2/3/Pro): mobile forward + Vulkan. r.MobileHDR=False mandatory for Multi-View.
  • PC-VR (Rift, Index, Vive): forward shading for most VR; deferred runs but at significantly higher cost. Lumen/Nanite stereo support exists on UE5 but is experimental, deferred-only, PC-only.
  • PSVR2: PS5 deferred renderer with eye-tracked foveated rendering as the headline perf feature.
DefaultEngine.ini (Quest forward path)
[/Script/Engine.RendererSettings]
r.MobileHDR=False                      ; required for MultiView
r.Mobile.AntiAliasing=3                ; MSAA
r.MobileMSAA=4                         ; 4x sample count
vr.MobileMultiView=1                   ; tiled GPU stereo
r.Mobile.PackLightGridLightDataToUBO.Enable=1
r.Forward.NoLightGridIfNoLightOpt=1    ; saves ~0.14ms when no dynamic lights

Both vr.InstancedStereo and vr.MobileMultiView are ECVF_ReadOnly — they must live in DefaultEngine.ini and be set before shader compilation. Setting them at runtime silently no-ops.

3

Instanced stereo vs Mobile Multi-View

Both reduce per-eye CPU cost by submitting one draw call that produces two views. The difference is implementation:

  • Instanced Stereo Rendering (ISR): vr.InstancedStereo=1. Single instanced draw, vertex shader writes both eye positions via SV_RenderTargetArrayIndex. Runs on desktop GPUs.
  • Mobile Multi-View (MMV): vr.MobileMultiView=1. The same conceptual savings, but uses the tile-based GPU's hardware-accelerated multi-view extension. Runs on Quest/mobile-XR Vulkan only.

UE 5.2 tuned the ISR/MMV pass and reported ~5% GPU win on Quest 2 (per UE 5.2 release notes). They don't combine — one or the other.

4

MSAA on tiled renderers

MSAA is the right answer for VR on tile-based mobile GPUs. The tile buffer natively supports multiple samples per pixel, so 4× MSAA on Quest's Adreno is roughly free in bandwidth and trivially cheap in fragment cost. Avoid TAA and FXAA in VR — both produce visible smear under head motion and sub-pixel aliasing is amplified by the lens-warp distortion.

For PC-VR with a forward shading path, r.MSAACount=4 (or 8 on flagship GPUs) is the canonical setting. Deferred-on-PC-VR cannot use MSAA the same way, which is part of why forward shading is preferred for VR even on hardware where deferred is normally faster.

5

Foveated rendering (FFR + ETFR)

Foveated rendering reduces shading cost in the periphery, where the lens optics blur detail anyway. Two flavors:

Fixed Foveated Rendering (FFR). Fixed-position quality reduction toward edges. Per Meta's FFR docs: pixel-bound app savings of ~6.5% (Low), ~11.5% (Medium), ~21% (High) on Quest. Available on all Quest and Quest-class mobile XR.

Eye-Tracked Foveated Rendering (ETFR). Quality is centered on the user's actual gaze point. ETFR has higher GPU savings on average than FFR across all levels (per Meta). Quest Pro supports ETFR; Quest 3/3S do not. PSVR2 has eye tracking and supports an equivalent feature: PSVR2 documentation cites GPU frame time up to 3.6× faster with eye-tracked foveated, 2.5× faster without (Sony GDC).

Foveated levels are runtime-toggleable on Quest:

Blueprint / C++
// 0=Off 1=Low 2=Medium 3=High 4=HighTop
UOculusXRFunctionLibrary::SetFoveatedRenderingLevel(level, isDynamic);

// ADB runtime override (debug only):
adb shell setprop debug.oculus.foveation.level 3
adb shell setprop debug.oculus.foveation.dynamic 1

Dynamic FFR adapts level based on GPU pressure. On Quest, prefer Dynamic FFR over Dynamic Resolution — FFR keeps central pixels sharp; DR scales the whole image down.

6

Latency: Late Latching & motion-to-photon

VR cares about motion-to-photon (MTP) latency — how long between the headset moving and the player seeing the new frame. Long MTP = swimminess and discomfort. UE's primary MTP-saving tool on Quest is Late Latching, which substitutes a fresher head/controller pose into the rendered frame just before submission, removing up to one frame of input latency.

Per Meta's Late Latching docs: example reductions of 45 ms predicted-render-delay (PRD) down to 35 ms.

Late Latching has hard incompatibilities:

  • Multi-View + Vulkan mandatory — not available on OpenGL ES or non-MMV builds.
  • UE4 conflict: occlusion culling must be off.
  • UE5 conflict: r.Vulkan.UseEmulatedUBs must be off (mutually exclusive optimizations).

Phase Sync was deprecated in UE5; UE4-era advice citing it no longer applies. Late Latching is the supported successor.

7

Async TimeWarp / Application SpaceWarp

Async TimeWarp (ATW) is the runtime's safety net — if your app drops a frame, the runtime reprojects the previous frame to the new head pose. Automatic. Free.

Application SpaceWarp (ASW) is a different beast: the app renders at half the headset refresh rate (e.g., 36 fps on a 72 Hz Quest), and the runtime synthesizes the intermediate frames using motion vectors. Per Meta's ASW docs, ASW provides "up to 70% additional compute" headroom — effectively doubling your GPU budget at the cost of motion-vector authoring requirements.

DefaultEngine.ini
r.Mobile.Oculus.SpaceWarp.Enable=True
ASW requires motion vectors on every custom material Materials that don't emit correct motion vectors produce ghosting/wobble under ASW reprojection. Foliage WPO, Niagara CPU sims, custom passes, and procedurally-animated materials all need verification. The Has Pixel Animation material flag (5.4+) is the friend; many ASW-ghosting reports are missing it.
8

CPU/GPU budgeting + Boost rules

Quest has a CPU/GPU level system — the runtime selects performance levels based on thermal headroom. Apps can request boost levels short-term, but boost is rate-limited to 45 second windows, totaling no more than 20% of runtime (per Meta's Quest Boost docs). Using boost as a steady-state crutch produces a regression in the live build.

Boost gains by device:

DeviceCPU BoostGPU Boost
Quest 2+25% (+45% dual-core)+18%
Quest 3+7%+10%
Quest Pro+25% (+45% dual-core)+10%

Per Meta's PC and Quest performance guides: game-thread time should stay under 2–3 ms per system, and any single function pinning over 2 ms is flag-worthy. Draw-call budgets: Quest 3 100–500 per frame, PC-VR 500–1000. Triangle budgets post-cull: Quest 3 100k–250k, PC-VR 1–2 M.

9

Dynamic Resolution & pixel density

UE 5.5 renamed vr.PixelDensity to xr.SecondaryScreenPercentage.HMDRenderTarget. Code referencing the old CVar silently no-ops on 5.5+ — this is one of the most common gotchas after upgrading.

Oculus Dynamic Resolution clamps the eye-buffer scale to 0.7×–1.3× of default (per Meta's DynRes docs). The defaults:

DefaultEngine.ini
r.Oculus.DynamicResolution.PixelDensity=0       ; 0 = runtime control
r.Oculus.DynamicResolution.PixelDensityMin=0.7
r.Oculus.DynamicResolution.PixelDensityMax=1.3

GPU level 5 on Quest 3 requires Dynamic Resolution enabled — otherwise thermal throttle drops you mid-session with no recovery path.

10

Lumen / Nanite / VSM in VR — what's workable

Per Epic's Nanite and Lumen for XR page: stereo support is experimental, PC-only, deferred renderer only, DX12 SM6 required. Not viable on Quest.

VSM works on PC-VR with reduced page pool / shadow quality, but cost scales with view count — instanced stereo halves the cost vs naive multi-pass. Most shipped UE5 PC-VR titles ship with conventional CSM rather than VSM.

For Quest, the realistic toolset is identical to mobile — one stationary directional + CSM + baked GI + DFAO sky. Translucency is the #1 fragment-bound culprit on tiled GPUs; particle systems with overlapping translucent quads are the easiest perf trap to fall into.

Per-platform budget table

The summary that should live in your project's design doc:

Platform Refresh Frame budget Draw calls FFR Renderer
Quest 272 / 9013.7 / 11.1 ms~300Medium-HighForward + MMV + 4×MSAA
Quest 3 / 3S90 / 12011.1 / 8.3 ms~500Low-Medium + Adreno occlusionForward + MMV + 4×MSAA
Quest Pro9011.1 ms~500ETFR (eye-tracked)Forward + MMV + 4×MSAA
Valve Index90 / 120 / 14411.1 / 8.3 / 6.9 ms500–1000NonePC Forward + MSAA
Rift / Quest-Link9011.1 ms500–1000NonePC Forward + MSAA
PSVR290 / 12011.1 / 8.3 ms~700Eye-tracked (~3.6×)PS5 deferred + foveated

PerfGuard can lock these as platform-aware regression budgets — per-eye GPU time with a 9 ms ceiling at 90 Hz, draw-call gates per platform, FFR/ETFR config detection (so a "now-FFR-Off" regression surfaces as a config drift not a perf regression), and a frame-pacing histogram with p99.9 > 1.0×refresh-period as a "reprojection imminent" warning.