Optimizing graphics for 72/90/120 Hz refresh rates in VR
Dropped frame in VR – not just image stutter. This vestibular conflict: visual system says "you stand," vestibular apparatus says "you move." On regular drops, nausea appears. At 72 fps budget per frame – 13.9 ms. At 90 – 11.1 ms. At 120 – 8.3 ms. Every millisecond counts, standard optimization approaches "like regular game" don't work here.
Meta Quest 3 runs at 72 Hz by default – developer must explicitly request 90 or 120 via OVRManager.display.displayFrequency. But requesting insufficient: must ensure render-budget allows not dropping frames at chosen frequency, else system auto-reverts.
Where milliseconds really disappear
Most common drop cause – overdraw on transparent materials. VR renders two eyes, each semi-transparent object draws twice. Effects particles with ten transparency layers, costing 1–2 ms in regular game, in VR easily become 4–5 ms. RenderDoc shows precise overdraw: Overdraw Heatmap mode shows pixels repainted 8–12 times.
Second problem – draw calls. On Quest 3 (Snapdragon XR2 Gen 2) GPU less scary than CPU-overhead from large call count. 300 draw calls with inefficient batching – 3–4 ms CPU-side only. GPU Instancing works for identical meshes, Static Batching – for static. Problem: Dynamic Batching transforms geometry on CPU, costlier than separate draw. Disable Dynamic Batching in VR, replace with manual DrawMeshInstanced for dynamic objects.
Third – shadows. Real-time shadows in VR – luxury. Even one cascaded Directional Light with Shadow Distance 30m on Quest costs 3–5 ms. Standard approach: bake shadows in lightmap for static, use Blob Shadow Projector or pseudo-shadows for dynamic. If shadows necessary – single cascade only, Shadow Distance max 10–15 m, PCF filtering instead of VSM.
Fixed Foveated Rendering and Variable Rate Shading
FFR – main optimization tool, unused through ignorance. Human peripheral vision much less sensitive to detail than central. FFR renders screen edges reduced resolution – on Quest configurable via OVRManager.fixedFoveatedRenderingLevel.
FFR levels from Low to HighTop: at High, edges render at 1/4 resolution. For most scenes imperceptible – saves up to 20–30% GPU time. Exceptions: thin text on periphery (UI-elements at view edges), thin lines (wires, grids). For these objects, disable FFR via OVROverlay – render above main layer without FFR-degradation.
Variable Rate Shading – more flexible, shading rate varies by screen tiles. In Unity 2022+ supported via ShadingRateImage API, only platforms with hardware VRS support. Meta Quest 3 supports.
Optimization practice
Without data – no optimization. First step always: profiling via OVR Metrics Tool (Quest) or Unity Profiler with device connection. Interested in GPU time per frame, not FPS – FPS can seem stable due to Asynchronous SpaceWarp (ASW), synthesizing frames on drops. ASW masks problem, doesn't solve.
After profiling – Frame Debugger for draw call sequence analysis and RenderDoc for detailed GPU capture. Usually 80% GPU-time occupy 20% objects. Optimize them, not everything.
Typical real project result: Quest 3, action-game, start – 68 fps at 72 Hz with periodic drops. After analysis: overdraw from particle systems (6-layer burn effects) – minus 3.2 ms after replacing with atlas sprites 2 layers. Shadows from three lights – minus 4.1 ms after replacing with baked lightmaps. Dynamic Batching – disabled, GPU Instancing added for trees and stones – minus 1.8 ms CPU. Result: stable 90 fps with 2–3 ms margin.
Timeline
| Optimization scope | Timeline |
|---|---|
| Profiling + recommendations | 2–3 days |
| Render optimization (shadows, batching, FFR) | 1–2 weeks |
| Full optimization for target frequency | 3–6 weeks |
Cost calculated after project audit and determining target platform and refresh rate.





