Search Unity

Olly’s seven stages of optimizations for mobile VR

Mobile VR has to work within a number of constraints. You have to render everything twice, and target a really high frame-rate. Here are the seven stages covering everything that takes processing time in your game–from the game loop to the render loop–and how to optimize along the way.

The tl;dr: quick optimizations for mobile VR

Things to do early on:

  • Enable static and dynamic batching.
  • Disable shadow mapping.
  • Disable procedural skybox and all skybox lighting.
  • Disable runtime global illumination.
  • Change all lights to baked.
  • Change all emissive shader properties to baked.
  • Size up your lightmap to 4096.

...And things to do later:

  • Use the Profiler!
  • Use CPU or GPU skinning.
  • Inspect overdraw (scene view overdraw mode or replacement shader).
  • Use the Frame Debugger to watch rendering order.
  • Change render queues accordingly.
CPU post-processing

Where/what to look at when considering performance:

  • Gameplay/physics/AI
  • Processing transforms
  • LOD and allocating different mesh resolutions for different objects in your game
  • Occlusion culling: look at which objects are rendered in front of other objects (and therefore occluding; look at objects we don’t need to render at all


From Occlusion culling docs: Notice in the Overdraw scene view, a high density of overdraw as many rooms beyond the visible walls are rendered. These aren’t visible in the game view, but nonetheless time is being taken to render them.


With occlusion culling applied, the distant rooms are not rendered, the overdraw is much less dense, and the number of triangles and batches being rendered has dropped dramatically, without any change to how the game view looks.

  • Dynamic batching: if you need to batch then use dynamic batching; find similar, dynamic objects in your scene and batch them together to save on costs
  • CPU skinning
  • Frustrum culling: identify what is being rendered in the frustrum at any given moment
  • Set up the sorting, i.e. the order that things are rendered in

How to optimize:

  • Use the CullingGroup API to help you decide what objects to cull.
  • Optimize your character hierarchy: there’s actually a check box for optimizing the full skeletal mesh down to only the required nodes that are exposed at that point for you to add transform, scripts, and so on.
  • Implement LOD groups: Load in meshes of different resolutions, different texture sets and materials, and control the distance at which they load into your scene.
  • Disable sorting, if you know it’s not going to benefit you at this stage.
  • Finally, if you’re not bound by draw calls, then don’t batch.
Render loop

Where/what to look at when considering performance:

  • You’ll have to render any object twice, once for each eye.
  • How expensive is your Shadow map computation.

How to optimize:

  • Enable "Single Pass Stereo" rendering; Instead of rendering each eye separately, this uses a wider render target and alternating draw calls to render both eyes with a single scene traversal.


To enable Single-Pass Stereo Rendering: open PlayerSettings (menu: Edit > Project Settings > Player). In PlayerSettings, navigate to Other Settings, ensure the Virtual Reality Supported checkbox is ticked, then tick the Single-Pass Stereo Rendering checkbox underneath it.

  • Reduce the expense of your shadows: reduce shadow casters and receivers; limit shadow distance and cascades, and turn off shadows where you can.
  • Avoid shadow mapping: use blob shadows or baked shadows. Use decals that can be parented to their respective objects.

Where/what to look at when considering performance:

  • Avoid the state changing inside the GPU so that it leaves the loop.
  • Think about reducing material and lightmap changes.
  • Get an overview of how objects in your scene are grouped together.

How to optimize:

  • Batch stuff: for example, batch static objects so that you render one big mesh instead of multiple smaller ones.
  • Atlas your textures: combine as many of your textures and materials into texture atlases as possible, to reduce the number of rendering draw calls.
  • Implement fewer large lightmaps, instead of many smaller ones, for example, one per room or location.
  • Use Material property blocks: change an element of a Material instead of switching it out entirely, and thereby, get all the great benefits of instancing.


To enable GPU Instancing on Materials, select your Material in the Project window, and in the Inspector, tick the Enable Instancing checkbox.

  • Use multithreaded rendering.
  • Work directly with the Mesh API and draw straight to the screen. It’s complicated to do, but if you know how to program what you want to see on the screen, then this is the low-level API you want to to use to do that.
Vertex processing

Where/what to look at when considering performance:

  • Rasterization
  • Spherical-Harmonic lights
  • Per vertex lights
  • GPU skinning

How to optimize:

  • Use fewer triangles.
  • Use CPU skinning if you are GPU bound. In other words if the GPU is overloaded make use of the CPU for tasks that are transferable and stuck in the GPU bottleneck.
Fragment processing

Where/what to look at when considering performance:

  • Shadow map computation
  • Deferred lighting pre-pass
  • Lightmapping
  • Physically-based rendering

How to optimize:

  • Limit the overdraw to avoid a drop in performance. This means thinking about any and all elements in your content that are overlaid: semi-transparent smoke textures and other particle effects, or foliage: the more you employ overlay, the bigger the tax on performance.
  • Control the rendering order with Material.renderQueue, so you know what is drawn and in which order.
  • Use the Frame Debugger to run through your render order and pinpoint at which stage each part of your scene is rendered. Then, you can look at reordering your rendering and find the things that you don’t even need to render.


Unity’s Frame debugger.

  • Avoid transparency where you can, with objects such as foliage; simplify their geometry
  • Ask yourself if you can need to use the Standard Shader, which can be quite expensive on mobile. Opt to use the mobile specular shader instead, or some other optimized mobile shader.

Where/what to look at when considering performance:

  • Shadow mapping
  • Deferred lighting
  • Per vertex lights
  • Forward lighting light pass

How to optimize:

  • Look at how your scene is lit; how the lights overlap with different objects in your scene. Space out your lights and avoid overlapping where you can, and you’ll save on performance.
  • Decrease per pixel light range.
  • Set a Maximum number of per pixel lights.
Screen space processing

Where/what to look at when considering performance:

  • This is the stage at which you apply effects to make your experience look stunning, making it potentially expensive.. This list of the most-used post-processing effects is roughly in order of cost, from cheapest to most expensive:

    • Color grading
    • Tone mapping
    • Bloom
    • AA
    • SSRR: Screen space reflections
    • SSAO: Screen space ambient occlusions

How to optimize:

  • Use Unity’s optimized post-processing stack, available for free, to keep expense down. There’s a nice quick start guide for the stack here. You can also check out Corey Johnson’s talk, which takes you through the workflow for each effect.


Unity’s new post-processing stack in action.

  • Think carefully about the way you layer effects, each type comes with a different cost. As little processing per pixel is always ideal, but you’ll have to find a balance.
More resources

We just gotta know! Did you like this content?

Yes. Keep it coming Meh. Could be better