Repository navigation
Description
Unity version
6000.0.40
Where does the issue occur?
- In Unity Editor
- In Quest builds
Description
Summary
When running in Unity Editor over Quest Link, enabling EnvironmentDepthManager causes sustained PC GPU 3D usage around 90-100%, even in an almost empty scene with only Passthrough + EnvironmentDepthManager.
This happens even when OcclusionShadersMode is set to None, so the issue does not appear to be caused by occlusion shader rendering.
I tested a sample-and-hold workaround where the depth provider is only enabled when a new depth sample is needed, one valid depth texture is acquired, then the provider is disabled again while the last valid depth texture remains cached. With this approach, even at 90Hz sampling, sustained PC GPU 3D usage stays around 9-11%.
This suggests the abnormal GPU cost may be related to keeping the OpenXR environment depth provider continuously enabled in Quest Link Editor mode, rather than the cost of acquiring depth frames at high frequency.
Environment
- Unity version: 6000.0.40
- Meta XR Core SDK version: v85
- Also tested: v77
- Device: Quest 3
- Runtime: Unity Editor over Quest Link
- GPU: RTX 5090
- Render pipeline: URP
- Graphics API: Vulkan
- Passthrough enabled: yes
Steps to reproduce
Minimal Reproduction
- Create an empty Unity scene.
- Enable Passthrough.
- Add
EnvironmentDepthManager. - Set
OcclusionShadersModetoNone. - Run the scene in Unity Editor over Quest Link.
- Observe Windows Task Manager GPU 3D usage.
Observed Result
- Passthrough only: low PC GPU 3D usage.
- Passthrough +
EnvironmentDepthManagercontinuous mode: sustained 90-100% PC GPU 3D usage. - Meta XR Core SDK v77: around 80% GPU 3D usage.
- Meta XR Core SDK v85: around 90%+ GPU 3D usage.
- The high GPU usage happens even when
OcclusionShadersMode = None.
Sample-and-Hold Workaround Result
I tested a workaround that changes the manager behavior from continuous provider enabled to sample-and-hold:
- Enable the depth provider when a new sample is due.
- Acquire one updated depth texture.
- Keep the last valid depth texture bound/cached.
- Disable the depth provider until the next sample interval.
Results:
| Mode | Sample rate | Sustained PC GPU 3D usage |
|---|---|---|
| Passthrough only | N/A | low / baseline |
| EnvironmentDepthManager continuous | per-frame / continuous provider enabled | 90-100% |
| Sample-and-hold | 8Hz | ~9-11% |
| Sample-and-hold | 32Hz | ~9-11% |
| Sample-and-hold | 64Hz | ~9-11% |
| Sample-and-hold | 90Hz | ~9-11% |
There may be a short GPU spike when the provider is first enabled, but after that the usage drops and remains around 9-11%.
Expected Result
EnvironmentDepthManager should not cause sustained 90-100% PC GPU 3D usage in Quest Link Editor mode, especially when occlusion shader rendering is disabled.
If continuous provider mode is expected to be expensive in Quest Link, it would be helpful to expose an official configurable sampling mode or sample-and-hold mode.
Workaround / Hypothesis
The sample-and-hold test suggests that the cost is not primarily caused by the frequency of TryGetUpdatedDepthTexture() calls. Even at 90Hz sample-and-hold, GPU usage remains low.
The abnormal sustained GPU usage appears to be related to keeping the OpenXR environment depth provider continuously enabled in Quest Link Editor mode.
Enhancement Request
Please consider exposing an official API or inspector option for:
- sample-and-hold depth updates
- configurable environment depth sampling rate
- keeping the last valid depth texture cached while temporarily disabling provider acquisition
- safer provider lifecycle behavior in Unity Editor / Quest Link mode
This would allow applications that do not need fully continuous per-frame occlusion to use Environment Depth without saturating the PC GPU in Quest Link.
Logs
none
Additional info
//Simplified sample-and-hold workaround
private bool providerRunning;
private float nextSampleTime;
private float sampleHz = 90f;
private void OnBeforeRender()
{
float now = Time.realtimeSinceStartup;
if (!providerRunning && now >= nextSampleTime)
{
provider.SetDepthEnabled(true, removeHands: false);
providerRunning = true;
}
if (!providerRunning)
{
return;
}
if (!provider.TryGetUpdatedDepthTexture(out var depthTexture, frameDescriptors))
{
return;
}
if (depthTexture == null || !depthTexture.IsCreated())
{
return;
}
Shader.SetGlobalTexture(EnvironmentDepthTextureId, depthTexture);
provider.SetDepthEnabled(false, false);
providerRunning = false;
nextSampleTime = now + 1f / Mathf.Max(1f, sampleHz);
}
Activity
TudorJude commentedon May 18, 2026
Hello,
Thanks for the deep dive! This is quite interesting to read but it might just be new developments with Link. Back when we implemented Depth API, we never had this GPU load over Link so this sounds more like a regression in Link itself, the depth provider should not eat up this many resources.
This is still very useful for people to get Depth a little faster over Link but the actual root cause might be something else. I will relay this to the people working on Link.
I am pinning this, I think people can get some good value from this.
changruizhu96 commentedon May 18, 2026
Hello,
Just a quick update: I've tested with V201, and the above method will cause a Unity crash.
It happens because Meta Environment Depth gives Unity a native depth texture, but Unity fails to convert/register it as a normal RenderTexture.
The failing path is roughly:
EnvironmentDepthManager asks Meta’s depth provider for a depth frame.
Meta returns a native depth texture.
Unity tries to wrap it through XRDisplaySubsystem.GetRenderTexture().
That step repeatedly fails.
Each failed attempt still leaves texture create/destroy work inside Unity’s XR texture system.
When Play Mode exits, Unity tries to clean up those XR textures, the destroy queue overflows, and the Editor crashes.
But in v201, the GPU usage of environment depth manager dropped to around 60% .