Hi,
I’m hitting a frustrating issue with unstable frame rates in my Unity 6.2 project targeting visionOS (Metal Rendering with Compositor Services) on Apple Vision Pro. My target is a stable 90 Hz, which I achieved consistently in Unity 6.1. After upgrading to 6.2, the FPS fluctuates between 70-45 Hz and often locks at 45 Hz (seemingly as if the compositor repeats frames to avoid judder). Oddly, if I exit the app during gameplay and quickly reopen it, it snaps back to a stable 90 Hz, even before loading the second scene. Reverting to Unity 6.1 doesn’t fix it, suggesting a project or visionOS issue.
Details:
Unity: 6.2
visionOS: 2.5 or 26.0
Render Pipeline: URP
Target Frame Rate: 90 Hz (enforced via Application.targetFrameRate = 90; QualitySettings.vSyncCount = 0;)
Tested Fixes:
-Cleaned Library/Temp/Obj, full reimport, fresh builds.
-Disabled HDR, post-processing, depth textures.
-Added script to pause/resume AR Session.
-Tried to keep only one AR Session and XR Origin for the entire project instead of having them on each scene.
-Tried async scene loading and stripped scenes to isolate, but issue persists in main project.
Thanks
Same issue here — adding an independent repro with hard data, including GPU captures.
Setup: Unity 6000.3.10f1, URP 17.3.0, visionOS Metal (Compositor Services), com.unity.xr.visionos 3.1.5 and 3.2.2 (both affected), Apple Vision Pro (M5), Foveated Rendering enabled, Set Target Frame Rate On Startup = on, Initial Minimum Frame Repeat Count = 0.
Symptom: On a normal launch, a scene section with ~2000 batches locks to 45 Hz and never recovers while that content is visible. The SAME content renders at a stable 90 Hz after ANY scene deactivate/reactivate cycle:
- minimizing the app to the home view and returning, OR
- the first-install permission dialog (hand tracking) appearing at launch — first launch after install runs at 90 Hz everywhere, every subsequent launch locks at 45 Hz in the heavy section.
Once “fixed” by a reactivation cycle, it stays at 90 Hz for the rest of the process lifetime, including re-entering the heavy section.
Measurements (FrameTimingManager, averaged over 16 frames):
- Before reactivation (45 Hz): cpuMainThreadFrameTime ~6-7 ms, present wait ~12 ms, cpuRenderThreadFrameTime ~11-14 ms, gpuFrameTime ~19-20 ms
- After reactivation (90 Hz): main ~3.3 ms, wait ~1.4 ms, render thread ~0 ms reported
Xcode GPU captures of both states show IDENTICAL GPU work: Effective GPU Time 4.60 ms vs 4.69 ms, same 9 encoders, same ~4890 GPU commands, same render target size, foveation and stereo mode (single-pass instanced). So the GPU is not the bottleneck in either state — the difference is entirely CPU-side. It looks like the render thread (draw call encoding) runs several times slower in the initial compositor session, consistent with threads being scheduled on efficiency cores / low QoS until a scene reactivation happens.
Workarounds we tried that did NOT help:
- Forcing XRDisplaySubsystem.foveatedRenderingLevel = 1 every frame (minor gain only)
- Promoting the render thread to QOS_CLASS_USER_INTERACTIVE via pthread_set_qos_class_self_np called from an IssuePluginEvent callback (no effect — does the engine set an explicit scheduling policy on the render thread?)
- Stopping/starting the XRDisplaySubsystem (closes the immersive space; visionOS then terminates the app)
- Upgrading com.unity.xr.visionos 3.1.5 → 3.2.2 (no change)
Reducing draw calls (combining meshes, 4839 → 2081 batches) raises the locked framerate from 45 to ~50-60, which supports the “render thread is CPU-bound only in the first session” theory.
Questions for the Unity team:
- Does the visionOS render loop / render thread get created with default QoS, and could it be created with (or promoted to) user-interactive QoS like Apple’s Compositor Services samples do?
- Why does a scene deactivate/reactivate cycle permanently fix the scheduling for the process?
Note that vsync, which is OS forced by XR devices, will half the FPS. So as soon as you cannot sustain 90, it will drop to 45 (even if you app could do 85fps).
For optimization, make sure you’re on the latest LTS (6.3.21f1 atm) and the latest XR packages. Trying a pre release also doesn’t hurt.
Then check all the XR settings (including gear icons for OpenXR) and the Unity player and URP settings. Maybe a new setting has been added which hurts performance.
But that still doesn’t answer why the frame rate suddenly jumps to the maximum after minimizing and then maximizing the game. These are headsets powered by the Apple M5 chip, so a scene like this shouldn’t be a problem for them at all. You can see that after maximizing the game, nothing else has changed, yet suddenly everything runs at over 90 FPS. It’s impossible for a scene with just 300 low-poly objects to simply cripple this processor like that—especially since everything runs fine once the game is maximized. What’s more, the same scene runs without issues at 76 FPS on the Meta Quest; and when comparing the Snapdragon to the M5, they are worlds apart in terms of performance, even accounting for the Vision Pro’s higher resolution. So, that explanation doesn’t address why performance suddenly rises to the level expected of this class of hardware after minimizing and maximizing the game.
If it’s reproducible in a blank or small project, file a bug report (if you’ve updated everything, maybe it has already been fixed).