AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
AdaptiveQuality.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 using System.Collections.Generic;
4 using UnityEngine;
5 
6 namespace HoloToolkit.Unity
7 {
14 
15  public class AdaptiveQuality : MonoBehaviour
16  {
17  [SerializeField]
18  [Tooltip("The minimum frame time percentage threshold used to increase render quality.")]
19  private float MinFrameTimeThreshold = 0.75f;
20 
21  [SerializeField]
22  [Tooltip("The maximum frame time percentage threshold used to decrease render quality.")]
23  private float MaxFrameTimeThreshold = 0.95f;
24 
25  [SerializeField]
26  private int MinQualityLevel = -5;
27  [SerializeField]
28  private int MaxQualityLevel = 5;
29  [SerializeField]
30  private int StartQualityLevel = 5;
31 
32  public delegate void QualityChangedEvent(int newQuality, int previousQuality);
33  public event QualityChangedEvent QualityChanged;
34 
35  public int QualityLevel { get; private set; }
36  public int RefreshRate { get; private set; }
37 
38  private float frameTimeQuota;
39 
43  private const int maxLastFrames = 7;
44  private Queue<float> lastFrames = new Queue<float>();
45 
46  private const int minFrameCountBeforeQualityChange = 5;
47  private int frameCountSinceLastLevelUpdate;
48 
49  [SerializeField]
50  private Camera adaptiveCamera;
51 
52  public const string TimingTag = "Frame";
53 
54  private void OnEnable()
55  {
56  QualityLevel = StartQualityLevel;
57 
58  // Store our refresh rate
59 
60 #if UNITY_2017_2_OR_NEWER
61  RefreshRate = (int)UnityEngine.XR.XRDevice.refreshRate;
62 #else
63  RefreshRate = (int)UnityEngine.VR.VRDevice.refreshRate;
64 #endif
65  if (RefreshRate == 0)
66  {
67  RefreshRate = 60;
68  if (!Application.isEditor)
69  {
70  Debug.LogWarning("Could not retrieve the HMD's native refresh rate. Assuming " + RefreshRate + " Hz.");
71  }
72  }
73  frameTimeQuota = 1.0f / RefreshRate;
74 
75  // Assume main camera if no camera was setup
76  if (adaptiveCamera == null)
77  {
78  adaptiveCamera = Camera.main;
79  }
80 
81  // Make sure we have the GpuTimingCamera component attached to our camera with the correct timing tag
82  GpuTimingCamera gpuCamera = adaptiveCamera.GetComponent<GpuTimingCamera>();
83  if (gpuCamera == null || gpuCamera.TimingTag.CompareTo(TimingTag) != 0)
84  {
85  adaptiveCamera.gameObject.AddComponent<GpuTimingCamera>();
86  }
87  }
88 
89  protected void Update()
90  {
91  UpdateAdaptiveQuality();
92  }
93 
94  private bool LastFramesBelowThreshold(int frameCount)
95  {
96  // Make sure we have enough new frames since last change
97  if (lastFrames.Count < frameCount || frameCountSinceLastLevelUpdate < frameCount)
98  {
99  return false;
100  }
101 
102  float maxTime = frameTimeQuota * MinFrameTimeThreshold;
103  // See if all our frames are below the threshold
104  foreach (var frameTime in lastFrames)
105  {
106  if (frameTime >= maxTime)
107  {
108  return false;
109  }
110  }
111 
112  return true;
113  }
114 
115  private void UpdateQualityLevel(int delta)
116  {
117  // Change and clamp the new quality level
118  int prevQualityLevel = QualityLevel;
119  QualityLevel = Mathf.Clamp(QualityLevel + delta, MinQualityLevel, MaxQualityLevel);
120 
121  //Trigger the event if we changed quality
122  if (QualityLevel != prevQualityLevel)
123  {
124  if (QualityChanged != null)
125  {
126  QualityChanged(QualityLevel, prevQualityLevel);
127  }
128  frameCountSinceLastLevelUpdate = 0;
129  }
130  }
131 
132  private void UpdateAdaptiveQuality()
133  {
134  float lastAppFrameTime = (float)GpuTiming.GetTime("Frame");
135 
136  if (lastAppFrameTime <= 0)
137  {
138  return;
139  }
140 
141  //Store a list of the frame samples
142  lastFrames.Enqueue(lastAppFrameTime);
143  if (lastFrames.Count > maxLastFrames)
144  {
145  lastFrames.Dequeue();
146  }
147 
148  //Wait for a few frames between changes
149  frameCountSinceLastLevelUpdate++;
150  if (frameCountSinceLastLevelUpdate < minFrameCountBeforeQualityChange)
151  {
152  return;
153  }
154 
155  // If the last frame is over budget, decrease quality level by 2 slots.
156  if (lastAppFrameTime > MaxFrameTimeThreshold * frameTimeQuota)
157  {
158  UpdateQualityLevel(-2);
159  }
160  else if (lastAppFrameTime < MinFrameTimeThreshold * frameTimeQuota)
161  {
162  // If the last 5 frames are below the GPU usage threshold, increase quality level by one.
163  if (LastFramesBelowThreshold(maxLastFrames))
164  {
165  UpdateQualityLevel(1);
166  }
167  }
168  }
169  }
170 }
Main components for controlling the quality of the system to maintain a steady frame rate...
Tracks the GPU time spent rendering a camera. For stereo rendering sampling is made from the beginnin...
Encapsulates access to GPU timing methods.
Definition: GpuTiming.cs:14
static double GetTime(string eventId)
Gets the latest available sample time for the given event.
Definition: GpuTiming.cs:33
QualityChangedEvent QualityChanged