private void UpdateRenderScale() { if (!active) { SetRenderScale(1.0f, 1.0f); return; } // Add latest timing to ring buffer frameTimeRingBufferIndex = (frameTimeRingBufferIndex + 1) % frameTimeRingBuffer.Length; frameTimeRingBuffer[frameTimeRingBufferIndex] = VRStats.gpuTimeLastFrame; // Rendering in low resolution means adaptive quality needs to scale back the render scale target to free up gpu cycles bool renderInLowResolution = VRTK_SDK_Bridge.ShouldAppRenderWithLowResources(); // Thresholds float thresholdModifier = renderInLowResolution ? singleFrameDurationInMilliseconds * 0.75f : singleFrameDurationInMilliseconds; float lowThresholdInMilliseconds = 0.7f * thresholdModifier; float extrapolationThresholdInMilliseconds = 0.85f * thresholdModifier; float highThresholdInMilliseconds = 0.9f * thresholdModifier; // Get latest 3 frames float frameMinus0 = frameTimeRingBuffer[(frameTimeRingBufferIndex - 0 + frameTimeRingBuffer.Length) % frameTimeRingBuffer.Length]; float frameMinus1 = frameTimeRingBuffer[(frameTimeRingBufferIndex - 1 + frameTimeRingBuffer.Length) % frameTimeRingBuffer.Length]; float frameMinus2 = frameTimeRingBuffer[(frameTimeRingBufferIndex - 2 + frameTimeRingBuffer.Length) % frameTimeRingBuffer.Length]; int previousLevel = currentRenderScaleLevel; // Always drop 2 levels except when dropping from level 2 int dropTargetLevel = previousLevel == 2 ? 1 : previousLevel - 2; int newLevel = Mathf.Clamp(dropTargetLevel, 0, allRenderScales.Count - 1); // Ignore frame timings if overriding if (overrideRenderScale) { currentRenderScaleLevel = overrideRenderScaleLevel; } // Rapidly reduce quality 2 levels if last frame was critical else if (Time.frameCount >= lastRenderScaleChangeFrameCount + 2 + 1 && frameMinus0 > highThresholdInMilliseconds && newLevel != previousLevel) { currentRenderScaleLevel = newLevel; lastRenderScaleChangeFrameCount = Time.frameCount; } // Rapidly reduce quality 2 levels if last 3 frames are expensive else if (Time.frameCount >= lastRenderScaleChangeFrameCount + 2 + 3 && frameMinus0 > highThresholdInMilliseconds && frameMinus1 > highThresholdInMilliseconds && frameMinus2 > highThresholdInMilliseconds && newLevel != previousLevel) { currentRenderScaleLevel = newLevel; lastRenderScaleChangeFrameCount = Time.frameCount; } // Predict next frame's cost using linear extrapolation: max(frame-1 to frame+1, frame-2 to frame+1) else if (Time.frameCount >= lastRenderScaleChangeFrameCount + 2 + 2 && frameMinus0 > extrapolationThresholdInMilliseconds) { float frameDelta = frameMinus0 - frameMinus1; // Use frame-2 if it's available if (Time.frameCount >= lastRenderScaleChangeFrameCount + 2 + 3) { float frameDelta2 = (frameMinus0 - frameMinus2) * 0.5f; frameDelta = Mathf.Max(frameDelta, frameDelta2); } if (frameMinus0 + frameDelta > highThresholdInMilliseconds && newLevel != previousLevel) { currentRenderScaleLevel = newLevel; lastRenderScaleChangeFrameCount = Time.frameCount; } } else { // Increase quality 1 level if last 3 frames are cheap newLevel = Mathf.Clamp(previousLevel + 1, 0, allRenderScales.Count - 1); if (Time.frameCount >= lastRenderScaleChangeFrameCount + 2 + 3 && frameMinus0 < lowThresholdInMilliseconds && frameMinus1 < lowThresholdInMilliseconds && frameMinus2 < lowThresholdInMilliseconds && newLevel != previousLevel) { currentRenderScaleLevel = newLevel; lastRenderScaleChangeFrameCount = Time.frameCount; } } // Force on interleaved reprojection for level 0 which is just a replica of level 1 with reprojection enabled float additionalViewportScale = 1.0f; if (!hmdDisplayIsOnDesktop) { if (currentRenderScaleLevel == 0) { if (interleavedReprojectionEnabled && frameMinus0 < singleFrameDurationInMilliseconds * 0.85f) { interleavedReprojectionEnabled = false; } else if (frameMinus0 > singleFrameDurationInMilliseconds * 0.925f) { interleavedReprojectionEnabled = true; } } else { interleavedReprojectionEnabled = false; } VRTK_SDK_Bridge.ForceInterleavedReprojectionOn(interleavedReprojectionEnabled); } // Not running in direct mode! Interleaved reprojection not supported, so scale down the viewport else if (currentRenderScaleLevel == 0) { additionalViewportScale = 0.8f; } float newRenderScale = allRenderScales[allRenderScales.Count - 1]; float newRenderViewportScale = allRenderScales[currentRenderScaleLevel] / newRenderScale * additionalViewportScale; SetRenderScale(newRenderScale, newRenderViewportScale); }