Last active
April 7, 2026 17:21
-
-
Save maluoi/4ea99fc6a4b32b69369a7e12acdf203b to your computer and use it in GitHub Desktop.
Scaling resolution dynamically in a StereoKit app based on GPU timing
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
| ////////////////////// | |
| // Dynamic Performance Scaling | |
| static float perfGpuSmoothed = 0; | |
| static float perfCurrentScale = 0; | |
| /// <summary>Dynamically adjusts Renderer.ViewportScaling based on GPU | |
| /// performance relative to the display's refresh rate budget. Call this | |
| /// each frame to maintain smooth performance.</summary> | |
| /// <param name="targetUtilization">Fraction of the GPU frame budget to | |
| /// target, default 0.85 (85%).</param> | |
| /// <param name="minScale">Minimum viewport scale, default 0.5.</param> | |
| /// <param name="maxScale">Maximum viewport scale, default 1.0.</param> | |
| /// <param name="smoothing">EMA smoothing factor for GPU time, lower is | |
| /// smoother. Default 0.05.</param> | |
| /// <param name="deadZone">Ratio dead zone where no adjustment is made, | |
| /// default 0.05 (5%).</param> | |
| /// <param name="adjustRateUp">How fast the scale increases when under | |
| /// budget, default 0.02 (2% per frame).</param> | |
| /// <param name="adjustRateDown">How fast the scale decreases when over | |
| /// budget, default 0.1 (10% per frame).</param> | |
| static void AutoScalePerformance( | |
| float targetUtilization = 0.9f, | |
| float minScale = 0.5f, | |
| float maxScale = 1.0f, | |
| float smoothing = 0.05f, | |
| float deadZone = 0.05f, | |
| float adjustRateUp = 0.02f, | |
| float adjustRateDown = 0.8f) | |
| { | |
| float gpuUs = Time.PerfGPUus; | |
| if (gpuUs <= 0 || Device.DisplayRefreshRate <= 0) return; | |
| // Initialize from current state on first call | |
| if (perfCurrentScale <= 0) | |
| perfCurrentScale = Renderer.ViewportScaling; | |
| float budgetUs = 1_000_000f / Device.DisplayRefreshRate; | |
| float targetUs = budgetUs * targetUtilization; | |
| // Smooth GPU time with exponential moving average | |
| perfGpuSmoothed = perfGpuSmoothed <= 0 | |
| ? gpuUs | |
| : perfGpuSmoothed + (gpuUs - perfGpuSmoothed) * smoothing; | |
| // If we're close to our target, don't adjust | |
| float ratio = targetUs / perfGpuSmoothed; | |
| if (Math.Abs(ratio - 1.0f) < deadZone) return; | |
| // Calculate the ideal scale and move toward it gradually, | |
| // scaling down faster than up to avoid dropped frames. | |
| float idealScale = Math.Clamp(perfCurrentScale * ratio, minScale, maxScale); | |
| float rate = idealScale < perfCurrentScale | |
| ? adjustRateDown | |
| : adjustRateUp; | |
| perfCurrentScale += (idealScale - perfCurrentScale) * rate; | |
| perfCurrentScale = Math.Clamp(perfCurrentScale, minScale, maxScale); | |
| Renderer.ViewportScaling = perfCurrentScale; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment