|
|
- // Adaptive Quality|Utilities|90100
-
- // Adapted from The Lab Renderer's ValveCamera.cs, available at
- // https://github.com/ValveSoftware/the_lab_renderer/blob/ae64c48a8ccbe5406aba1e39b160d4f2f7156c2c/Assets/TheLabRenderer/Scripts/ValveCamera.cs
- // For The Lab Renderer's license see THIRD_PARTY_NOTICES.
- // **Only Compatible With Unity 5.4 and above**
-
- #if (UNITY_5_4_OR_NEWER)
- namespace VRTK
- {
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Linq;
- using System.Text;
- using UnityEngine;
- #if UNITY_2017_2_OR_NEWER
- using UnityEngine.XR;
- #else
- using XRSettings = UnityEngine.VR.VRSettings;
- using XRDevice = UnityEngine.VR.VRDevice;
- #endif
-
- /// <summary>
- /// Adaptive Quality dynamically changes rendering settings to maintain VR framerate while maximizing GPU utilization.
- /// </summary>
- /// <remarks>
- /// > **Only Compatible With Unity 5.4 and above**
- ///
- /// There are two goals:
- /// <list type="bullet">
- /// <item> <description>Reduce the chances of dropping frames and reprojecting</description> </item>
- /// <item> <description>Increase quality when there are idle GPU cycles</description> </item>
- /// </list>
- /// <para />
- /// This script currently changes the following to reach these goals:
- /// <list type="bullet">
- /// <item> <description>Rendering resolution and viewport size (aka Dynamic Resolution)</description> </item>
- /// </list>
- /// <para />
- /// In the future it could be changed to also change the following:
- /// <list type="bullet">
- /// <item> <description>MSAA level</description> </item>
- /// <item> <description>Fixed Foveated Rendering</description> </item>
- /// <item> <description>Radial Density Masking</description> </item>
- /// <item> <description>(Non-fixed) Foveated Rendering (once HMDs support eye tracking)</description> </item>
- /// </list>
- /// <para />
- /// Some shaders, especially Image Effects, need to be modified to work with the changed render scale. To fix them
- /// pass `1.0f / VRSettings.renderViewportScale` into the shader and scale all incoming UV values with it in the vertex
- /// program. Do this by using `Material.SetFloat` to set the value in the script that configures the shader.
- /// <para />
- /// In more detail:
- /// <list type="bullet">
- /// <item> <description>In the `.shader` file: Add a new runtime-set property value `float _InverseOfRenderViewportScale`
- /// and add `vertexInput.texcoord *= _InverseOfRenderViewportScale` to the start of the vertex program</description> </item>
- /// <item> <description>In the `.cs` file: Before using the material (eg. `Graphics.Blit`) add
- /// `material.SetFloat("_InverseOfRenderViewportScale", 1.0f / VRSettings.renderViewportScale)`</description> </item>
- /// </list>
- /// </remarks>
- /// <example>
- /// `VRTK/Examples/039_CameraRig_AdaptiveQuality` displays the frames per second in the centre of the headset view.
- /// The debug visualization of this script is displayed near the top edge of the headset view.
- /// Pressing the trigger generates a new sphere and pressing the touchpad generates ten new spheres.
- /// Eventually when lots of spheres are present the FPS will drop and demonstrate the script.
- /// </example>
- [AddComponentMenu("VRTK/Scripts/Utilities/VRTK_AdaptiveQuality")]
- public sealed class VRTK_AdaptiveQuality : MonoBehaviour
- {
- #region Public fields
-
- [Tooltip("Toggles whether to show the debug overlay.\n\n"
- + "Each square represents a different level on the quality scale. Levels increase from left to right,"
- + " the first green box that is lit above represents the recommended render target resolution provided by the"
- + " current `VRDevice`, the box that is lit below in cyan represents the current resolution and the filled box"
- + " represents the current viewport scale. The yellow boxes represent resolutions below the recommended render target resolution.\n"
- + "The currently lit box becomes red whenever the user is likely seeing reprojection in the HMD since the"
- + " application isn't maintaining VR framerate. If lit, the box all the way on the left is almost always lit"
- + " red because it represents the lowest render scale with reprojection on.")]
- public bool drawDebugVisualization;
-
- [Tooltip("Toggles whether to allow keyboard shortcuts to control this script.\n\n"
- + " * The supported shortcuts are:\n"
- + " * `Shift+F1`: Toggle debug visualization on/off\n"
- + " * `Shift+F2`: Toggle usage of override render scale on/off\n"
- + " * `Shift+F3`: Decrease override render scale level\n"
- + " * `Shift+F4`: Increase override render scale level")]
- public bool allowKeyboardShortcuts = true;
-
- [Tooltip("Toggles whether to allow command line arguments to control this script at startup of the standalone build.\n\n"
- + " * The supported command line arguments all begin with '-' and are:\n"
- + " * `-noaq`: Disable adaptive quality\n"
- + " * `-aqminscale X`: Set minimum render scale to X\n"
- + " * `-aqmaxscale X`: Set maximum render scale to X\n"
- + " * `-aqmaxres X`: Set maximum render target dimension to X\n"
- + " * `-aqfillratestep X`: Set render scale fill rate step size in percent to X (X from 1 to 100)\n"
- + " * `-aqoverride X`: Set override render scale level to X\n"
- + " * `-vrdebug`: Enable debug visualization\n"
- + " * `-msaa X`: Set MSAA level to X")]
- public bool allowCommandLineArguments = true;
-
- [Tooltip("The MSAA level to use.")]
- [Header("Quality")]
- [Range(0, 8)]
- public int msaaLevel = 4;
-
- [Tooltip("Toggles whether the render viewport scale is dynamically adjusted to maintain VR framerate.\n\n"
- + "If unchecked, the renderer will render at the recommended resolution provided by the current `VRDevice`.")]
- public bool scaleRenderViewport = true;
- [Tooltip("The minimum and maximum allowed render scale.")]
- [MinMaxRange(0.01f, 5f)]
- public Limits2D renderScaleLimits = new Limits2D(0.8f, 1.4f);
-
- [Obsolete("`VRTK_AdaptiveQuality.minimumRenderScale` has been replaced with the `VRTK_AdaptiveQuality.renderScaleLimits`. This parameter will be removed in a future version of VRTK.")]
- [ObsoleteInspector]
- public float minimumRenderScale = 0.8f;
- [Obsolete("`VRTK_AdaptiveQuality.maximumRenderScale` has been replaced with the `VRTK_AdaptiveQuality.renderScaleLimits`. This parameter will be removed in a future version of VRTK.")]
- [ObsoleteInspector]
- public float maximumRenderScale = 1.4f;
-
- [Tooltip("The maximum allowed render target dimension.\n\n"
- + "This puts an upper limit on the size of the render target regardless of the maximum render scale.")]
- public int maximumRenderTargetDimension = 4096;
- [Tooltip("The fill rate step size in percent by which the render scale levels will be calculated.")]
- [Range(1, 100)]
- public int renderScaleFillRateStepSizeInPercent = 15;
- [Tooltip("Toggles whether the render target resolution is dynamically adjusted to maintain VR framerate.\n\n"
- + "If unchecked, the renderer will use the maximum target resolution specified by `maximumRenderScale`.")]
- public bool scaleRenderTargetResolution = false;
-
- [Tooltip("Toggles whether to override the used render viewport scale level.")]
- [Header("Override")]
- [NonSerialized]
- public bool overrideRenderViewportScale;
- [Tooltip("The render viewport scale level to override the current one with.")]
- [NonSerialized]
- public int overrideRenderViewportScaleLevel;
- #endregion
-
- #region Public readonly fields & properties
-
- /// <summary>
- /// All the calculated render scales.
- /// </summary>
- /// <remarks>
- /// The elements of this collection are to be interpreted as modifiers to the recommended render target
- /// resolution provided by the current `VRDevice`.
- /// </remarks>
- public readonly ReadOnlyCollection<float> renderScales;
-
- /// <summary>
- /// The current render scale.
- /// </summary>
- /// <remarks>
- /// A render scale of `1.0` represents the recommended render target resolution provided by the current `VRDevice`.
- /// </remarks>
- public static float CurrentRenderScale
- {
- get { return VRTK_SharedMethods.GetEyeTextureResolutionScale() * XRSettings.renderViewportScale; }
- }
-
- /// <summary>
- /// The recommended render target resolution provided by the current `VRDevice`.
- /// </summary>
- public Vector2 defaultRenderTargetResolution
- {
- get { return RenderTargetResolutionForRenderScale(allRenderScales[defaultRenderViewportScaleLevel]); }
- }
-
- /// <summary>
- /// The current render target resolution.
- /// </summary>
- public Vector2 currentRenderTargetResolution
- {
- get { return RenderTargetResolutionForRenderScale(CurrentRenderScale); }
- }
-
- #endregion
-
- #region Private fields
-
- /// <summary>
- /// The frame duration in milliseconds to fallback to if the current `VRDevice` specifies no refresh rate.
- /// </summary>
- private const float DefaultFrameDurationInMilliseconds = 1000f / 90f;
-
- private readonly AdaptiveSetting<int> renderViewportScaleSetting = new AdaptiveSetting<int>(0, 30, 10);
- private readonly AdaptiveSetting<int> renderScaleSetting = new AdaptiveSetting<int>(0, 180, 90);
-
- private readonly List<float> allRenderScales = new List<float>();
- private int defaultRenderViewportScaleLevel;
- private float previousMinimumRenderScale;
- private float previousMaximumRenderScale;
- private float previousRenderScaleFillRateStepSizeInPercent;
-
- private readonly Timing timing = new Timing();
- private int lastRenderViewportScaleLevelBelowRenderScaleLevelFrameCount;
-
- private bool interleavedReprojectionEnabled;
- private bool hmdDisplayIsOnDesktop;
- private float singleFrameDurationInMilliseconds;
-
- private GameObject debugVisualizationQuad;
- private Material debugVisualizationQuadMaterial;
-
- #endregion
-
- public VRTK_AdaptiveQuality()
- {
- renderScales = allRenderScales.AsReadOnly();
- }
-
- /// <summary>
- /// Calculates and returns the render target resolution for a given render scale.
- /// </summary>
- /// <param name="renderScale">
- /// The render scale to calculate the render target resolution with.
- /// </param>
- /// <returns>
- /// The render target resolution for `renderScale`.
- /// </returns>
- public static Vector2 RenderTargetResolutionForRenderScale(float renderScale)
- {
- return new Vector2((int)(XRSettings.eyeTextureWidth / VRTK_SharedMethods.GetEyeTextureResolutionScale() * renderScale),
- (int)(XRSettings.eyeTextureHeight / VRTK_SharedMethods.GetEyeTextureResolutionScale() * renderScale));
- }
-
- /// <summary>
- /// Calculates and returns the biggest allowed maximum render scale to be used for `maximumRenderScale`
- /// given the current `maximumRenderTargetDimension`.
- /// </summary>
- /// <returns>
- /// The biggest allowed maximum render scale.
- /// </returns>
- public float BiggestAllowedMaximumRenderScale()
- {
- if (XRSettings.eyeTextureWidth == 0 || XRSettings.eyeTextureHeight == 0)
- {
- return renderScaleLimits.maximum;
- }
-
- float maximumHorizontalRenderScale = maximumRenderTargetDimension * VRTK_SharedMethods.GetEyeTextureResolutionScale()
- / XRSettings.eyeTextureWidth;
- float maximumVerticalRenderScale = maximumRenderTargetDimension * VRTK_SharedMethods.GetEyeTextureResolutionScale()
- / XRSettings.eyeTextureHeight;
- return Mathf.Min(maximumHorizontalRenderScale, maximumVerticalRenderScale);
- }
-
- /// <summary>
- /// A summary of this script by listing all the calculated render scales with their
- /// corresponding render target resolution.
- /// </summary>
- /// <returns>
- /// The summary.
- /// </returns>
- public override string ToString()
- {
- StringBuilder stringBuilder = new StringBuilder("Adaptive Quality\n");
- stringBuilder.AppendLine("Render Scale:");
- stringBuilder.AppendLine("Level - Resolution - Multiplier");
-
- for (int index = 0; index < allRenderScales.Count; index++)
- {
- float renderScale = allRenderScales[index];
- Vector2 renderTargetResolution = RenderTargetResolutionForRenderScale(renderScale);
-
- stringBuilder.AppendFormat(
- "{0, 3} {1, 5}x{2, -5} {3, -8}",
- index,
- (int)renderTargetResolution.x,
- (int)renderTargetResolution.y,
- renderScale);
-
- if (index == 0)
- {
- stringBuilder.Append(" (Interleaved reprojection hint)");
- }
- else if (index == defaultRenderViewportScaleLevel)
- {
- stringBuilder.Append(" (Default)");
- }
-
- if (index == renderViewportScaleSetting.currentValue)
- {
- stringBuilder.Append(" (Current Viewport)");
- }
-
- if (index == renderScaleSetting.currentValue)
- {
- stringBuilder.Append(" (Current Target Resolution)");
- }
-
- if (index != allRenderScales.Count - 1)
- {
- stringBuilder.AppendLine();
- }
- }
-
- return stringBuilder.ToString();
- }
-
- #region MonoBehaviour methods
-
- private void Awake()
- {
- VRTK_SDKManager.AttemptAddBehaviourToToggleOnLoadedSetupChange(this);
- }
-
- private void OnEnable()
- {
- Camera.onPreCull += OnCameraPreCull;
-
- hmdDisplayIsOnDesktop = VRTK_SDK_Bridge.IsDisplayOnDesktop();
- singleFrameDurationInMilliseconds = XRDevice.refreshRate > 0.0f
- ? 1000.0f / XRDevice.refreshRate
- : DefaultFrameDurationInMilliseconds;
-
- HandleCommandLineArguments();
-
- if (!Application.isEditor)
- {
- OnValidate();
- }
- }
-
- private void OnDisable()
- {
- Camera.onPreCull -= OnCameraPreCull;
- SetRenderScale(1.0f, 1.0f);
- }
-
- private void OnDestroy()
- {
- VRTK_SDKManager.AttemptRemoveBehaviourToToggleOnLoadedSetupChange(this);
- }
-
- private void OnValidate()
- {
- renderScaleLimits.minimum = Mathf.Max(0.01f, renderScaleLimits.minimum);
- renderScaleLimits.maximum = Mathf.Max(renderScaleLimits.minimum, renderScaleLimits.maximum);
- maximumRenderTargetDimension = Mathf.Max(2, maximumRenderTargetDimension);
- renderScaleFillRateStepSizeInPercent = Mathf.Max(1, renderScaleFillRateStepSizeInPercent);
- msaaLevel = msaaLevel == 1 ? 0 : Mathf.Clamp(Mathf.ClosestPowerOfTwo(msaaLevel), 0, 8);
- }
-
- private void Update()
- {
- HandleKeyPresses();
- UpdateRenderScaleLevels();
- CreateOrDestroyDebugVisualization();
- UpdateDebugVisualization();
-
- timing.SaveCurrentFrameTiming();
- }
-
- #if UNITY_5_4_1 || UNITY_5_4_2 || UNITY_5_4_3 || UNITY_5_5_OR_NEWER
- private void LateUpdate()
- {
- UpdateRenderScale();
- }
- #endif
-
- private void OnCameraPreCull(Camera camera)
- {
- if (camera.transform != VRTK_SDK_Bridge.GetHeadsetCamera())
- {
- return;
- }
-
- #if !(UNITY_5_4_1 || UNITY_5_4_2 || UNITY_5_4_3 || UNITY_5_5_OR_NEWER)
- UpdateRenderScale();
- #endif
- UpdateMSAALevel();
- }
-
- #endregion
-
- private void HandleCommandLineArguments()
- {
- if (!allowCommandLineArguments)
- {
- return;
- }
-
- string[] commandLineArguments = VRTK_SharedMethods.GetCommandLineArguements();
-
- for (int index = 0; index < commandLineArguments.Length; index++)
- {
- string argument = commandLineArguments[index];
- string nextArgument = index + 1 < commandLineArguments.Length ? commandLineArguments[index + 1] : "";
-
- switch (argument)
- {
- case CommandLineArguments.Disable:
- scaleRenderViewport = false;
- break;
- case CommandLineArguments.MinimumRenderScale:
- renderScaleLimits.minimum = float.Parse(nextArgument);
- break;
- case CommandLineArguments.MaximumRenderScale:
- renderScaleLimits.maximum = float.Parse(nextArgument);
- break;
- case CommandLineArguments.MaximumRenderTargetDimension:
- maximumRenderTargetDimension = int.Parse(nextArgument);
- break;
- case CommandLineArguments.RenderScaleFillRateStepSizeInPercent:
- renderScaleFillRateStepSizeInPercent = int.Parse(nextArgument);
- break;
- case CommandLineArguments.OverrideRenderScaleLevel:
- overrideRenderViewportScale = true;
- overrideRenderViewportScaleLevel = int.Parse(nextArgument);
- break;
- case CommandLineArguments.DrawDebugVisualization:
- drawDebugVisualization = true;
- break;
- case CommandLineArguments.MSAALevel:
- msaaLevel = int.Parse(nextArgument);
- break;
- }
- }
- }
-
- private void HandleKeyPresses()
- {
- if (!allowKeyboardShortcuts || !KeyboardShortcuts.Modifiers.Any(Input.GetKey))
- {
- return;
- }
-
- if (Input.GetKeyDown(KeyboardShortcuts.ToggleDrawDebugVisualization))
- {
- drawDebugVisualization = !drawDebugVisualization;
- }
- else if (Input.GetKeyDown(KeyboardShortcuts.ToggleOverrideRenderScale))
- {
- overrideRenderViewportScale = !overrideRenderViewportScale;
- }
- else if (Input.GetKeyDown(KeyboardShortcuts.DecreaseOverrideRenderScaleLevel))
- {
- overrideRenderViewportScaleLevel = ClampRenderScaleLevel(overrideRenderViewportScaleLevel - 1);
- }
- else if (Input.GetKeyDown(KeyboardShortcuts.IncreaseOverrideRenderScaleLevel))
- {
- overrideRenderViewportScaleLevel = ClampRenderScaleLevel(overrideRenderViewportScaleLevel + 1);
- }
- }
-
- private void UpdateMSAALevel()
- {
- if (QualitySettings.antiAliasing != msaaLevel)
- {
- QualitySettings.antiAliasing = msaaLevel;
- }
- }
-
- #region Render scale methods
-
- private void UpdateRenderScaleLevels()
- {
- if (Mathf.Abs(previousMinimumRenderScale - renderScaleLimits.minimum) <= float.Epsilon
- && Mathf.Abs(previousMaximumRenderScale - renderScaleLimits.maximum) <= float.Epsilon
- && Mathf.Abs(previousRenderScaleFillRateStepSizeInPercent - renderScaleFillRateStepSizeInPercent) <= float.Epsilon)
- {
- return;
- }
-
- previousMinimumRenderScale = renderScaleLimits.minimum;
- previousMaximumRenderScale = renderScaleLimits.maximum;
- previousRenderScaleFillRateStepSizeInPercent = renderScaleFillRateStepSizeInPercent;
-
- allRenderScales.Clear();
-
- // Respect maximumRenderTargetDimension
- float allowedMaximumRenderScale = BiggestAllowedMaximumRenderScale();
- float renderScaleToAdd = Mathf.Min(renderScaleLimits.minimum, allowedMaximumRenderScale);
-
- // Add min scale as the reprojection scale
- allRenderScales.Add(renderScaleToAdd);
-
- // Add all entries
- while (renderScaleToAdd <= renderScaleLimits.maximum)
- {
- allRenderScales.Add(renderScaleToAdd);
- renderScaleToAdd =
- Mathf.Sqrt((renderScaleFillRateStepSizeInPercent + 100) / 100.0f * renderScaleToAdd * renderScaleToAdd);
-
- if (renderScaleToAdd > allowedMaximumRenderScale)
- {
- // Too large
- break;
- }
- }
-
- // Figure out default render viewport scale level for debug visualization
- defaultRenderViewportScaleLevel = Mathf.Clamp(
- allRenderScales.FindIndex(renderScale => renderScale >= 1.0f),
- 1,
- allRenderScales.Count - 1);
- renderViewportScaleSetting.currentValue = defaultRenderViewportScaleLevel;
- renderScaleSetting.currentValue = defaultRenderViewportScaleLevel;
- overrideRenderViewportScaleLevel = ClampRenderScaleLevel(overrideRenderViewportScaleLevel);
- }
-
- private void UpdateRenderScale()
- {
- if (allRenderScales.Count == 0)
- {
- return;
- }
-
- if (!scaleRenderViewport)
- {
- renderViewportScaleSetting.currentValue = defaultRenderViewportScaleLevel;
- renderScaleSetting.currentValue = defaultRenderViewportScaleLevel;
- SetRenderScale(1.0f, 1.0f);
-
- return;
- }
-
- // 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 allowedSingleFrameDurationInMilliseconds = renderInLowResolution
- ? singleFrameDurationInMilliseconds * 0.75f
- : singleFrameDurationInMilliseconds;
- float lowThresholdInMilliseconds = 0.7f * allowedSingleFrameDurationInMilliseconds;
- float extrapolationThresholdInMilliseconds = 0.85f * allowedSingleFrameDurationInMilliseconds;
- float highThresholdInMilliseconds = 0.9f * allowedSingleFrameDurationInMilliseconds;
-
- int newRenderViewportScaleLevel = renderViewportScaleSetting.currentValue;
-
- // Rapidly reduce render viewport scale level if cost of last 1 or 3 frames, or the predicted next frame's cost are expensive
- if (timing.WasFrameTimingBad(
- 1,
- highThresholdInMilliseconds,
- renderViewportScaleSetting.lastChangeFrameCount,
- renderViewportScaleSetting.decreaseFrameCost)
- || timing.WasFrameTimingBad(
- 3,
- highThresholdInMilliseconds,
- renderViewportScaleSetting.lastChangeFrameCount,
- renderViewportScaleSetting.decreaseFrameCost)
- || timing.WillFrameTimingBeBad(
- extrapolationThresholdInMilliseconds,
- highThresholdInMilliseconds,
- renderViewportScaleSetting.lastChangeFrameCount,
- renderViewportScaleSetting.decreaseFrameCost))
- {
- // Always drop 2 levels except when dropping from level 2 (level 0 is for reprojection)
- newRenderViewportScaleLevel = ClampRenderScaleLevel(renderViewportScaleSetting.currentValue == 2
- ? 1
- : renderViewportScaleSetting.currentValue - 2);
- }
- // Rapidly increase render viewport scale level if last 12 frames are cheap
- else if (timing.WasFrameTimingGood(
- 12,
- lowThresholdInMilliseconds,
- renderViewportScaleSetting.lastChangeFrameCount - renderViewportScaleSetting.increaseFrameCost,
- renderViewportScaleSetting.increaseFrameCost))
- {
- newRenderViewportScaleLevel = ClampRenderScaleLevel(renderViewportScaleSetting.currentValue + 2);
- }
- // Slowly increase render viewport scale level if last 6 frames are cheap
- else if (timing.WasFrameTimingGood(
- 6,
- lowThresholdInMilliseconds,
- renderViewportScaleSetting.lastChangeFrameCount,
- renderViewportScaleSetting.increaseFrameCost))
- {
- // Only increase by 1 level to prevent frame drops caused by adjusting
- newRenderViewportScaleLevel = ClampRenderScaleLevel(renderViewportScaleSetting.currentValue + 1);
- }
-
- // Apply and remember when render viewport scale level changed
- if (newRenderViewportScaleLevel != renderViewportScaleSetting.currentValue)
- {
- if (renderViewportScaleSetting.currentValue >= renderScaleSetting.currentValue
- && newRenderViewportScaleLevel < renderScaleSetting.currentValue)
- {
- lastRenderViewportScaleLevelBelowRenderScaleLevelFrameCount = Time.frameCount;
- }
-
- renderViewportScaleSetting.currentValue = newRenderViewportScaleLevel;
- }
-
- // Ignore frame timings if overriding
- if (overrideRenderViewportScale)
- {
- renderViewportScaleSetting.currentValue = overrideRenderViewportScaleLevel;
- }
-
- // 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 (renderViewportScaleSetting.currentValue == 0)
- {
- if (interleavedReprojectionEnabled && timing.GetFrameTiming(0) < singleFrameDurationInMilliseconds * 0.85f)
- {
- interleavedReprojectionEnabled = false;
- }
- else if (timing.GetFrameTiming(0) > 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 some more
- else if (renderViewportScaleSetting.currentValue == 0)
- {
- additionalViewportScale = 0.8f;
- }
-
- int newRenderScaleLevel = renderScaleSetting.currentValue;
- int levelInBetween = (renderViewportScaleSetting.currentValue - renderScaleSetting.currentValue) / 2;
-
- // Increase render scale level to the level in between
- if (renderScaleSetting.currentValue < renderViewportScaleSetting.currentValue
- && Time.frameCount >= renderScaleSetting.lastChangeFrameCount + renderScaleSetting.increaseFrameCost)
- {
- newRenderScaleLevel = ClampRenderScaleLevel(renderScaleSetting.currentValue + Mathf.Max(1, levelInBetween));
- }
- // Decrease render scale level
- else if (renderScaleSetting.currentValue > renderViewportScaleSetting.currentValue
- && Time.frameCount >= renderScaleSetting.lastChangeFrameCount + renderScaleSetting.decreaseFrameCost
- && Time.frameCount >= lastRenderViewportScaleLevelBelowRenderScaleLevelFrameCount + renderViewportScaleSetting.increaseFrameCost)
- {
- // Slowly decrease render scale level to level in between if last 6 frames are cheap, otherwise rapidly
- newRenderScaleLevel = timing.WasFrameTimingGood(6, lowThresholdInMilliseconds, 0, 0)
- ? ClampRenderScaleLevel(renderScaleSetting.currentValue + Mathf.Min(-1, levelInBetween))
- : renderViewportScaleSetting.currentValue;
- }
-
- // Apply and remember when render scale level changed
- renderScaleSetting.currentValue = newRenderScaleLevel;
-
- if (!scaleRenderTargetResolution)
- {
- renderScaleSetting.currentValue = allRenderScales.Count - 1;
- }
-
- float newRenderScale = allRenderScales[renderScaleSetting.currentValue];
- float newRenderViewportScale = allRenderScales[Mathf.Min(renderViewportScaleSetting.currentValue, renderScaleSetting.currentValue)]
- / newRenderScale * additionalViewportScale;
-
- SetRenderScale(newRenderScale, newRenderViewportScale);
- }
-
- private static void SetRenderScale(float renderScale, float renderViewportScale)
- {
- if (Mathf.Abs(VRTK_SharedMethods.GetEyeTextureResolutionScale() - renderScale) > float.Epsilon)
- {
- VRTK_SharedMethods.SetEyeTextureResolutionScale(renderScale);
- }
- if (Mathf.Abs(XRSettings.renderViewportScale - renderViewportScale) > float.Epsilon)
- {
- XRSettings.renderViewportScale = renderViewportScale;
- }
- }
-
- private int ClampRenderScaleLevel(int renderScaleLevel)
- {
- return Mathf.Clamp(renderScaleLevel, 0, allRenderScales.Count - 1);
- }
-
- #endregion
-
- #region Debug visualization methods
-
- private void CreateOrDestroyDebugVisualization()
- {
- if (!Application.isPlaying)
- {
- return;
- }
-
- if (enabled && drawDebugVisualization && debugVisualizationQuad == null)
- {
- Mesh mesh = new Mesh
- {
- vertices =
- new[]
- {
- new Vector3(-0.5f, 0.9f, 1.0f),
- new Vector3(-0.5f, 1.0f, 1.0f),
- new Vector3(0.5f, 1.0f, 1.0f),
- new Vector3(0.5f, 0.9f, 1.0f)
- },
- uv =
- new[]
- {
- new Vector2(0.0f, 0.0f),
- new Vector2(0.0f, 1.0f),
- new Vector2(1.0f, 1.0f),
- new Vector2(1.0f, 0.0f)
- },
- triangles = new[] { 0, 1, 2, 0, 2, 3 }
- };
- #if !UNITY_5_5_OR_NEWER
- mesh.Optimize();
- #endif
- mesh.UploadMeshData(true);
-
- debugVisualizationQuad = new GameObject(VRTK_SharedMethods.GenerateVRTKObjectName(true, "AdaptiveQualityDebugVisualizationQuad"));
- debugVisualizationQuad.transform.parent = VRTK_DeviceFinder.HeadsetTransform();
- debugVisualizationQuad.transform.localPosition = Vector3.forward;
- debugVisualizationQuad.transform.localRotation = Quaternion.identity;
- debugVisualizationQuad.AddComponent<MeshFilter>().mesh = mesh;
-
- debugVisualizationQuadMaterial = Resources.Load<Material>("AdaptiveQualityDebugVisualization");
- debugVisualizationQuad.AddComponent<MeshRenderer>().material = debugVisualizationQuadMaterial;
- }
- else if ((!enabled || !drawDebugVisualization) && debugVisualizationQuad != null)
- {
- Destroy(debugVisualizationQuad);
-
- debugVisualizationQuad = null;
- debugVisualizationQuadMaterial = null;
- }
- }
-
- private void UpdateDebugVisualization()
- {
- if (!drawDebugVisualization || debugVisualizationQuadMaterial == null)
- {
- return;
- }
-
- int lastFrameIsInBudget = (interleavedReprojectionEnabled || VRTK_SharedMethods.GetGPUTimeLastFrame() > singleFrameDurationInMilliseconds ? 0 : 1);
-
- debugVisualizationQuadMaterial.SetInt(ShaderPropertyIDs.RenderScaleLevelsCount, allRenderScales.Count);
- debugVisualizationQuadMaterial.SetInt(ShaderPropertyIDs.DefaultRenderViewportScaleLevel, defaultRenderViewportScaleLevel);
- debugVisualizationQuadMaterial.SetInt(ShaderPropertyIDs.CurrentRenderViewportScaleLevel, renderViewportScaleSetting.currentValue);
- debugVisualizationQuadMaterial.SetInt(ShaderPropertyIDs.CurrentRenderScaleLevel, renderScaleSetting.currentValue);
- debugVisualizationQuadMaterial.SetInt(ShaderPropertyIDs.LastFrameIsInBudget, lastFrameIsInBudget);
- }
-
- #endregion
-
- #region Private helper classes
-
- private sealed class AdaptiveSetting<T>
- {
- public T currentValue
- {
- get { return _currentValue; }
- set
- {
- if (!EqualityComparer<T>.Default.Equals(value, _currentValue))
- {
- lastChangeFrameCount = Time.frameCount;
- }
-
- previousValue = _currentValue;
- _currentValue = value;
- }
- }
- public T previousValue { get; private set; }
- public int lastChangeFrameCount { get; private set; }
-
- public readonly int increaseFrameCost;
- public readonly int decreaseFrameCost;
-
- private T _currentValue;
-
- public AdaptiveSetting(T currentValue, int increaseFrameCost, int decreaseFrameCost)
- {
- previousValue = currentValue;
- this.currentValue = currentValue;
-
- this.increaseFrameCost = increaseFrameCost;
- this.decreaseFrameCost = decreaseFrameCost;
- }
- }
-
- private static class CommandLineArguments
- {
- public const string Disable = "-noaq";
-
- public const string MinimumRenderScale = "-aqminscale";
- public const string MaximumRenderScale = "-aqmaxscale";
- public const string MaximumRenderTargetDimension = "-aqmaxres";
- public const string RenderScaleFillRateStepSizeInPercent = "-aqfillratestep";
-
- public const string OverrideRenderScaleLevel = "-aqoverride";
-
- public const string DrawDebugVisualization = "-vrdebug";
-
- public const string MSAALevel = "-msaa";
- }
-
- private static class KeyboardShortcuts
- {
- public static readonly KeyCode[] Modifiers = { KeyCode.LeftShift, KeyCode.RightShift };
- public const KeyCode ToggleDrawDebugVisualization = KeyCode.F1;
- public const KeyCode ToggleOverrideRenderScale = KeyCode.F2;
- public const KeyCode DecreaseOverrideRenderScaleLevel = KeyCode.F3;
- public const KeyCode IncreaseOverrideRenderScaleLevel = KeyCode.F4;
- }
-
- private static class ShaderPropertyIDs
- {
- public static readonly int RenderScaleLevelsCount = Shader.PropertyToID("_RenderScaleLevelsCount");
- public static readonly int DefaultRenderViewportScaleLevel = Shader.PropertyToID("_DefaultRenderViewportScaleLevel");
- public static readonly int CurrentRenderViewportScaleLevel = Shader.PropertyToID("_CurrentRenderViewportScaleLevel");
- public static readonly int CurrentRenderScaleLevel = Shader.PropertyToID("_CurrentRenderScaleLevel");
- public static readonly int LastFrameIsInBudget = Shader.PropertyToID("_LastFrameIsInBudget");
- }
-
- private sealed class Timing
- {
- private readonly float[] buffer = new float[12];
- private int bufferIndex;
-
- public void SaveCurrentFrameTiming()
- {
- bufferIndex = (bufferIndex + 1) % buffer.Length;
- buffer[bufferIndex] = VRTK_SharedMethods.GetGPUTimeLastFrame();
- }
-
- public float GetFrameTiming(int framesAgo)
- {
- return buffer[(bufferIndex - framesAgo + buffer.Length) % buffer.Length];
- }
-
- public bool WasFrameTimingBad(int framesAgo, float thresholdInMilliseconds, int lastChangeFrameCount, int changeFrameCost)
- {
- if (!AreFramesAvailable(framesAgo, lastChangeFrameCount, changeFrameCost))
- {
- // Too early to know
- return false;
- }
-
- for (int frame = 0; frame < framesAgo; frame++)
- {
- if (GetFrameTiming(frame) <= thresholdInMilliseconds)
- {
- return false;
- }
- }
-
- return true;
- }
-
- public bool WasFrameTimingGood(int framesAgo, float thresholdInMilliseconds, int lastChangeFrameCount, int changeFrameCost)
- {
- if (!AreFramesAvailable(framesAgo, lastChangeFrameCount, changeFrameCost))
- {
- // Too early to know
- return false;
- }
-
- for (int frame = 0; frame < framesAgo; frame++)
- {
- if (GetFrameTiming(frame) > thresholdInMilliseconds)
- {
- return false;
- }
- }
-
- return true;
- }
-
- public bool WillFrameTimingBeBad(float extrapolationThresholdInMilliseconds, float thresholdInMilliseconds,
- int lastChangeFrameCount, int changeFrameCost)
- {
- if (!AreFramesAvailable(2, lastChangeFrameCount, changeFrameCost))
- {
- // Too early to know
- return false;
- }
-
- // Predict next frame's cost using linear extrapolation: max(frame-1 to frame+1, frame-2 to frame+1)
- float frameMinus0Timing = GetFrameTiming(0);
- if (frameMinus0Timing <= extrapolationThresholdInMilliseconds)
- {
- return false;
- }
-
- float delta = frameMinus0Timing - GetFrameTiming(1);
-
- if (!AreFramesAvailable(3, lastChangeFrameCount, changeFrameCost))
- {
- delta = Mathf.Max(delta, (frameMinus0Timing - GetFrameTiming(2)) / 2f);
- }
-
- return frameMinus0Timing + delta > thresholdInMilliseconds;
- }
-
- private static bool AreFramesAvailable(int framesAgo, int lastChangeFrameCount, int changeFrameCost)
- {
- return Time.frameCount >= framesAgo + lastChangeFrameCount + changeFrameCost;
- }
- }
-
- #endregion
- }
- }
- #endif
|