123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906 |
- // 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
|