예제 #1
0
        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);
        }