Last active
February 18, 2025 08:08
-
-
Save liamcary/fd25f2a4b35553e394ccb6357f054ba7 to your computer and use it in GitHub Desktop.
Dynamic Resolution for Oculus Quest with Unity 2021.3 LTS and URP 12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using UnityEngine; | |
using UnityEngine.Rendering; | |
using UnityEngine.Rendering.Universal; | |
using UnityEngine.XR; | |
public class DynamicResolutionScaler : MonoBehaviour | |
{ | |
[SerializeField, Range(0f, 1f)] float _minResolutionScale; | |
float _lastStepTime; | |
float _minCooldown; | |
float _normalizedMinScale; | |
float _maxResolutionScale; | |
Vector2Int _recommendedResolution; | |
const int _pixelStepDown = 256; | |
const int _pixelStepUp = 128; | |
const float _stepDownCooldown = 0.25f; | |
const float _stepUpCooldown = 1f; | |
void Awake() | |
{ | |
_maxResolutionScale = (GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset).renderScale; | |
_normalizedMinScale = _minResolutionScale / _maxResolutionScale; | |
_minCooldown = Mathf.Min(_stepDownCooldown, _stepUpCooldown); | |
} | |
void LateUpdate() | |
{ | |
float timeSinceLastStep = Time.time - _lastStepTime; | |
if (timeSinceLastStep < _minCooldown || !TryGetRecommendedResolution(out _recommendedResolution)) { | |
return; | |
} | |
float currentScale = XRSettings.renderViewportScale; | |
int recommendedWidth = Mathf.Clamp( | |
_recommendedResolution.x, | |
(int) (XRSettings.eyeTextureWidth * _normalizedMinScale) - _pixelStepDown, | |
(int) (XRSettings.eyeTextureWidth * currentScale) + _pixelStepUp | |
); | |
float newScale = Mathf.Clamp(recommendedWidth / (float) XRSettings.eyeTextureWidth, _normalizedMinScale, 1f); | |
if (Mathf.Approximately(currentScale, newScale)) { | |
return; | |
} | |
if ((newScale > currentScale && timeSinceLastStep > _stepUpCooldown) || (newScale < currentScale && timeSinceLastStep > _stepDownCooldown)) { | |
XRSettings.renderViewportScale = newScale; | |
_lastStepTime = Time.time; | |
} | |
} | |
bool TryGetRecommendedResolution(out Vector2Int resolution) | |
{ | |
if (OVRPlugin.GetEyeLayerRecommendedResolution(out var scale)) { | |
resolution = new Vector2Int(scale.w, scale.h); | |
return true; | |
} | |
resolution = new Vector2Int(XRSettings.eyeTextureWidth, XRSettings.eyeTextureHeight); | |
return false; | |
} | |
} |
I found this a couple days ago and ended up using it because at least it seems to work, thanks a lot. I'm on Unity 2022.3.49 and Dynamic Resolution still doesn't work well with URP, in my case it always sets the maximum resolution possible and never goes down, same behavior both with OpenGL and Vulkan. I opened a ticket with Meta and a thread on their developer forums, but I don't have solutions yet.
I'm wondering if you are still using this as it is, or do you have an updated version? Asking just because in my project it seems to catch framerate drops a bit too late than it would be necessary.
Thanks! Works for me too (vulkan, urp 12, unity 2022.3)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is my own implementation of Dynamic Resolution for an Oculus Quest game. Oculus introduced dynamic resolution in the OVRManager, but it doesn't work with URP and changing the viewport scale every frame can completely destroy performance and cause a crash within seconds.
This script works great for my specific project configuration, but different configurations may need changes. My project configuration is Unity 2021.3.28, Android (min API 29, target API 32), IL2CPP, ARM64, the Oculus Graphics fork of URP, Vulkan, graphics jobs enabled, Oculus XR Plugin, Multiview, Application Spacewarp enabled, Dynamic foveated rendering, etc.
To fix the lag and eventual crash, this implementation introduces a cooldown between stepping the resolution up and down. The stepping down cooldown is more aggressive, because its more important to drop down resolution and maintain frame rate if you have a sudden increase in GPU usage.
It also moves the viewport scaling to LateUpdate. I was having some lag spikes and from the profiler timeline it seemed to be caused by the previous frame not submitting until after the current frame's update. Moving Frame N's viewport scale change to LateUpdate, after Frame N-1 has submitted the frame fixed the issue.
This uses the resolution scale you have configured in the URP asset as the maximum resolution and you only have to set the minimum resolution on this script. The script will then scale the render viewport between a normalized minimum scale and 1.0. E.g, if you want to scale between 0.9 and 1.2 you would set _minimumResolution scale to 0.9 on this script, set resolution scale to 1.2 in the URP asset, then your eye texture will be allocated at 1.2 and this script will scale your render viewport between 0.75 (minRes / maxRes) and 1.0.
My game was launched at a fixed resolution scale of 1.0, but we updated it to scale dynamically between 1.0 and 1.3 with this script. It now renders at 1.3 most of the time and its a huge visual improvement.