Tech Note: Unity Settings for Mobile VR
Oculus Developer Blog
Posted by Gabor Szauer
April 12, 2018

Unity provides a lot of checkboxes and drop-down menus to tweak the behavior and performance of your app. When starting a new project, it can be hard to know which settings are appropriate for the project. This post discusses the appropriate Unity settings for a Mobile VR app. This is general guidance and not a one-size-fits-all solution, we've provided reasoning behind each option to help you determine if these settings are the right call for your project.

Build Settings > Texture Compression

Set the Texture Compression in the Build Settings Window to ASTC. ASTC compression provides the best balance between quality and file size. Regardless of what is set here, it's possible to override the compression setting on individual textures. Textures used for UI might need to be set to uncompressed to keep them crisp.

Player Settings > Resolution & Presentation

Keep the default settings for Resolution & Presentation. These settings are:

  • Use 32-bit Display buffer should be checked
  • Disable Depth and Stencil should be unchecked

Player Settings > Other Settings

Color Space
A linear color space produces more realistic looking lighting. The option of linear or gamma is often stylistic. Linear color space on mobile only works with OpenGL ES 3 or above.

Auto Graphics APIM
Uncheck Auto Graphics API, set the Graphics API to OpenGLES 3. OpenGLES 3 is supported on all Mobile devices Oculus VR currently runs on. Other settings, such as linear color space require OpenGLES 3 as the graphics API.

Multithreaded Rendering
This should be checked, Multithreaded Rendering will move graphics API calls from the main thread to a separate worker thread. The effects of this will be most noticeable for applications with high CPU usage.

Static Batching
Static batching will combine meshes marked as static into one big mesh so long as they share the same material. When multiple meshes are batched together, they can be drawn with a single draw call. This comes at the cost of additional memory and storage space. Turning static batching on is recommended.

Dynamic Batching Similar to static batching, dynamic batching combines non-static meshes into one big mesh. For meshes to dynamically batch, they need to have the same material, contain less than 900 vertex attributes (this often translates to as little as 300 vertices), and not have multi-pass shaders.

There is some CPU overhead for every vertex that is dynamically batched. Because the criteria for dynamic batching is so strict and because there is a CPU cost associated with dynamic batching, dynamic batching might need to be turned off. However, in most cases turning this dynamic batching on is recommended.

GPU Skinning
This option should be checked. GPU skinning is supported for VR applications running with OpenGLES 3. GPU Skinning will move the skinning of an animated mesh to the GPU, this should free up CPU resources.

Graphics Jobs
Keep this option disabled. Currently, graphics jobs don't work with Dynamic batching.

Virtual Reality Supported
This of course needs to be checked, and Oculus needs to be added to the list of supported Virtual Reality SDKs. When an application is submitted to the Oculus store, Oculus should be the ONLY Virtual Reality SDK in this list.

Stereo Rendering Method
When Single Pass Stereo Rendering (also called Multiview) is enabled, instead of duplicating draw calls for each eye buffer, objects are rendered once to the left eye buffer, then duplicated to the right buffer automatically. When an object is duplicated to the right eye buffer, the appropriate modifications for vertex position and view-dependent variables such as reflection are applied.

This is one of the most important options and should be set to Single Pass (Preview). For more info, check out the Unity documentation for single pass stereo rendering.

Minimum API Level
All Android based HMD's that support Oculus should be running on API Level 21, Lollipop or above. Set the minimum API Level to at least 21. Be aware that API Level 23, Marshmallow changed the way requesting permissions works. The legacy method of requesting permissions at launch still works, but the new flow is to ask for permission when it is needed.

Stats on what percentage of users run which OS version are available here:

Scripting Backend
IL2CPP is going to run faster on device, but it will make build times longer. Mono2x on the other hand will allow for faster iteration at the cost of runtime performance. The IL2CPP backend is mature enough at this point to be a good option.

Prebake Collision Meshes
Unity computes physics meshes at load time. Checking this box will generate the meshes at build time instead. This will increase the project's build size but, decrease the load time for levels. If the apk size isn't an issue, it's a good idea to check this box.

Keep Loaded Shaders Alive
This option was called “Preload shaders” prior to Unity 5.6.0b3 and behaves the same way. Selecting this option will load all shaders on startup. It will increase the initial application load time, but any hitches due to on demand shader compilation should be resolved. If compiling shaders on demand is an issue, check this box.

Enable Internal Profiler
This option is useful when profiling a game, the information provided can be useful to determine what to optimize. Profiler information will be printed to the Android console. See the Unity Built In Profiler manual page for more details.

Optimize Mesh Data
Checking this box will remove any vertex data (tangents, normals, colors, UV, etc...) from a mesh that isn't referenced by materials using the mesh. Enabling this option can help reduce the final apk size.

Project Settings > Audio

For best immersion, we recommend all games should spatialize audio.

For spatialized audio you, at a minimum, should change the following settings in the Audio Manager:

  • Set DSP Buffer Size to Good latency
  • Set Spatializer Plugin to Oculus Spatializer

Each audio source has a Spatial Blend property; set this to 1.

For more control, download the Oculus Spatializer for Unity and read through the guide. The spatializer Unity package contains scripts to take full advantage of the Oculus Spatializer.

Project Settings > Quality

To make sure mobile builds always use a consistent quality setting, uncheck all the levels, except for Simple for the android build. Simple is a good one to start from, it should be the only level that is checked.

Pixel light count should be 1. If possible, try to use vertex lit shaders, in which case pixel light count could be set to 0.

Anisotropic Textures should be disabled; ARM Mali GPU's do not support Anisotropic filtering. Phones shipping with an Exynos chipset such as the international version of the S7 and S8 do not support this feature.

Anti Aliasing should generally be set to 4x Multi Sampling. If this has a large enough impact on performance, using 2x is also acceptable. The “Use Recommended MSAA Level” setting in OVRManager will override this setting when enabled.

Shadows should be set to Hard Shadows Only or Disable Shadows. With shadows disabled, blurry blobs can be used for character shadows, all other shadows should come from baked lightmaps.

Blend Weights should not be set to more than 2 bones.

V Sync Count should be set to Don't Sync. The oculus runtime handles it's own V Sync.

Project Settings > Graphics

Graphics on all tier levels should use Low for the standard shader quality. If possible, avoid using the standard shaders and use mobile shaders instead.

The rendering path for all tier levels should be set to Forward. The performance cost of deferred rendering is too high to make it a viable option on mobile.

Real Time GI CPU Usage should be set to Low for all tiers.

All the other settings should be good with their default values. Consider adding shaders to the Preloaded Shaders list located here. Adding shaders to this list will impact loading times but, will help avoid needing to load and compile these shaders on demand.