This sample demonstrates runtime performance optimization controls available for Meta Quest applications built with Unity. It provides an interactive testing environment where you can adjust CPU/GPU performance levels, rendering features, and stress test parameters while observing immediate effects on frame rate and resource utilization.
Control CPU and GPU performance levels dynamically using OVRPlugin APIs
Implement Fixed Foveated Rendering (FFR) with static and dynamic modes
Adjust resolution scaling using the two-factor system combining eyeTextureResolutionScale and renderViewportScale
Configure dynamic resolution, MSAA, and Application SpaceWarp at runtime
Create stress tests using Unity’s Job System and dynamic mesh instantiation
Build compositor layer UI that remains unaffected by render scale changes
Requirements
Device: Quest 2, Quest 3, or Quest 3S
Development environment: Unity 6000.0.59f2 or newer with Android build support
For complete platform setup instructions, see the Meta Quest development documentation.
Get started
Clone the repository from Unity-PerformanceSettings and open the project in Unity 6. The editor window at Meta > Unity Performance Settings Sample Intro Window provides build-time configuration options. Open Assets/PerformanceSettings/Scenes/MainScene.unity and build to your Quest device. For detailed build instructions and dependency versions, see the README.
Explore the sample
The sample contains two interactive screens accessible via controller pointing and trigger press. Both screens are rendered as compositor layers for consistent clarity regardless of rendering settings.
File / Scene
What it demonstrates
Key concepts
MainScene.unity
Main environment with two interactive panels
Compositor layer UI, OVR SDK integration
RenderingAndPerf.cs
All runtime performance controls on the main panel
The Performance, CPU & GPU Levels panel provides real-time controls for framerate targets, FFR modes, MSAA quality, resolution scaling, CPU/GPU performance levels, passthrough, and Application SpaceWarp.
Toggle buttons are generated dynamically from a prefab based on device capabilities. When you change a setting, the sample applies the new value via OVR or URP APIs, waits 0.1 seconds for the platform to stabilize, then rebuilds the UI to reflect the current state.
The Logs panel displays Unity debug logs, warnings, and errors alongside OVRManager lifecycle events in an in-game console, providing immediate visibility into performance warnings and state changes without removing your headset.
Key concepts
Fixed Foveated Rendering control
The sample provides separate controls for static and dynamic FFR. Static FFR applies a fixed foveation level from the OVRPlugin.FoveatedRenderingLevel enum, while dynamic FFR lets the runtime adjust foveation automatically. The FFR effect is visible on the environment but not on the menu panels, since those are rendered as compositor layers.
// From RenderingAndPerf.cs
OVRPlugin.foveatedRenderingLevel = ffrLevel;
OVRPlugin.useDynamicFoveatedRendering = isDynamicFfr;
Two-factor resolution scaling
The sample exposes both XRSettings.eyeTextureResolutionScale and XRSettings.renderViewportScale with separate sliders. The final render resolution is the product of these two values. Lowering either scale reduces GPU load and increases frame rate.
// From RenderingAndPerf.cs
XRSettings.renderViewportScale = viewportScale;
XRSettings.eyeTextureResolutionScale = eyeTextureScale;
Delayed recalculation pattern
After applying performance setting changes, RenderingAndPerf.cs uses WaitAndRecalculate() to pause 0.1 seconds before reading state back from the platform. This delay ensures the OVR runtime has completed its internal updates before the UI queries current values.
Compositor layer UI independence
Both interactive panels are rendered as compositor layers, meaning they bypass the main render pipeline. When you lower resolution scale or increase FFR level, the environment becomes blurrier but the UI panels remain sharp. This pattern is used for critical XR UI elements that must remain readable regardless of rendering quality.
CPU stress testing with the Job System
The CPU utilization controls include a “Use all cores” toggle. When enabled, PushCPUJob distributes 10,000 jobs across worker threads using IJobParallelFor with a batch size of 32. When disabled, the stress test runs a single-threaded loop on the main thread. This contrast highlights the performance advantage of parallel execution for CPU-bound workloads.
Thread-safe logging
LogSection.cs subscribes to Application.logMessageReceivedThreaded, which fires on arbitrary threads. Log entries are buffered in a thread-safe collection and applied to UI TextMeshPro elements during Update() on the main thread, preventing race conditions while maintaining real-time visibility into runtime events.
Extend the sample
Add custom performance profiles that save and restore groups of settings with a single button press
Integrate OVR Metrics Tool data overlays to display thermal state and sustained frame rate alongside the existing controls
Create additional stress tests for physics workloads, shader complexity, or particle systems to identify performance bottlenecks in specific subsystems