123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Profiling;
-
- namespace UnityEngine.AdaptivePerformance
- {
- /// <summary>
- /// Describes what action is needed to stabilize.
- /// </summary>
- public enum StateAction
- {
- /// <summary>
- /// No action is required.
- /// </summary>
- Stale,
- /// <summary>
- /// Recommended to increase quality.
- /// </summary>
- Increase,
- /// <summary>
- /// Quality must be decreased.
- /// </summary>
- Decrease,
- /// <summary>
- /// Quality must be decreased as soon as possible.
- /// </summary>
- FastDecrease,
- }
-
- /// <summary>
- /// System used for tracking device thermal state.
- /// </summary>
- internal class ThermalStateTracker
- {
- private float warningTemp = 1.0f;
- private float throttlingTemp = 1.0f;
-
- public ThermalStateTracker()
- {
- }
-
- public StateAction Update()
- {
- float thermalTrend = Holder.Instance.ThermalStatus.ThermalMetrics.TemperatureTrend;
-
- var thermalLevel = Holder.Instance.ThermalStatus.ThermalMetrics.TemperatureLevel;
- var warning = Holder.Instance.ThermalStatus.ThermalMetrics.WarningLevel;
-
- if (warning == WarningLevel.ThrottlingImminent && warningTemp == 1.0f)
- warningTemp = thermalLevel; // remember throttling imminent level
-
- if (warning == WarningLevel.Throttling && throttlingTemp == 1.0f)
- throttlingTemp = thermalLevel; // remember throttling level
-
- // Throttling needs to cool down a lot before changing to no warning
- if (warning == WarningLevel.Throttling || thermalLevel >= throttlingTemp)
- return StateAction.FastDecrease;
-
- // warm device
- if (warning == WarningLevel.ThrottlingImminent || thermalLevel >= warningTemp)
- {
- // halfway to throttling?
- if (thermalLevel > (warningTemp + throttlingTemp) / 2)
- return StateAction.Decrease;
-
- if (thermalTrend <= 0)
- return StateAction.Stale;
- else if (thermalTrend > 0.5)
- return StateAction.FastDecrease;
- else
- return StateAction.Decrease;
- }
-
-
- // normal operating conditions
- if (warning == WarningLevel.NoWarning && thermalLevel < warningTemp)
- {
- if (thermalTrend <= 0)
- return StateAction.Increase;
- else if (thermalTrend > 0.5)
- return StateAction.FastDecrease;
- else if (thermalTrend > 0.1)
- return StateAction.Decrease;
- }
-
-
- return StateAction.Stale;
- }
- }
-
- /// <summary>
- /// System used for tracking device performance state.
- /// </summary>
- internal class PerformanceStateTracker
- {
- private Queue<float> m_Samples;
- private int m_SampleCapacity;
-
- public float Trend { get; set; }
-
- public PerformanceStateTracker(int sampleCapacity)
- {
- m_Samples = new Queue<float>(sampleCapacity);
- m_SampleCapacity = sampleCapacity;
- }
-
- public StateAction Update()
- {
- var frameMs = Holder.Instance.PerformanceStatus.FrameTiming.AverageFrameTime;
- if (frameMs > 0)
- {
- var targetMs = 1f / AdaptivePerformanceManager.EffectiveTargetFrameRate();
- var diffMs = (frameMs / targetMs) - 1;
-
- m_Samples.Enqueue(diffMs);
- if (m_Samples.Count > m_SampleCapacity)
- m_Samples.Dequeue();
- }
-
- var trend = 0.0f;
- foreach (var sample in m_Samples)
- trend += sample;
- trend /= m_Samples.Count;
- Trend = trend;
-
- // It is underperforming heavily, we need to increase performance
- if (Trend >= 0.30)
- return StateAction.FastDecrease;
-
- // It is underperforming, we need to increase performance
- if (Trend >= 0.15)
- return StateAction.Decrease;
-
- // TODO: we need to way identify overperforming as currently AverageFrameTime is returned with vsync
- // return StaterAction.Increase;
-
- return StateAction.Stale;
- }
- }
-
- /// <summary>
- /// System used for tracking impact of scaler on CPU and GPU counters.
- /// </summary>
- internal class AdaptivePerformanceScalerEfficiencyTracker
- {
- private AdaptivePerformanceScaler m_Scaler;
- private float m_LastAverageGpuFrameTime;
- private float m_LastAverageCpuFrameTime;
- private bool m_IsApplied;
-
- public bool IsRunning { get => m_Scaler != null; }
-
- public void Start(AdaptivePerformanceScaler scaler, bool isApply)
- {
- Debug.Assert(!IsRunning, "AdaptivePerformanceScalerEfficiencyTracker is already running");
- m_Scaler = scaler;
- m_LastAverageGpuFrameTime = Holder.Instance.PerformanceStatus.FrameTiming.AverageGpuFrameTime;
- m_LastAverageCpuFrameTime = Holder.Instance.PerformanceStatus.FrameTiming.AverageCpuFrameTime;
- m_IsApplied = true;
- }
-
- public void Stop()
- {
- var gpu = Holder.Instance.PerformanceStatus.FrameTiming.AverageGpuFrameTime - m_LastAverageGpuFrameTime;
- var cpu = Holder.Instance.PerformanceStatus.FrameTiming.AverageCpuFrameTime - m_LastAverageCpuFrameTime;
- var sign = m_IsApplied ? 1 : -1;
- m_Scaler.GpuImpact = sign * (int)(gpu * 1000);
- m_Scaler.CpuImpact = sign * (int)(cpu * 1000);
- m_Scaler = null;
- }
- }
-
- /// <summary>
- /// Higher level implementation of Adaptive performance that tracks performance and thermal states of the device and provides them to <see cref="AdaptivePerformanceScaler"/> which use the information to increase or decrease performance levels.
- /// System acts as <see cref="AdaptivePerformanceScaler"/> manager and handles the lifetime of the scalers in the scenes.
- /// </summary>
- public class AdaptivePerformanceIndexer
- {
- private List<AdaptivePerformanceScaler> m_UnappliedScalers;
- private List<AdaptivePerformanceScaler> m_AppliedScalers;
- private List<AdaptivePerformanceScaler> m_DisabledScalers;
- private ThermalStateTracker m_ThermalStateTracker;
- private PerformanceStateTracker m_PerformanceStateTracker;
- private AdaptivePerformanceScalerEfficiencyTracker m_ScalerEfficiencyTracker;
- private IAdaptivePerformanceSettings m_Settings;
- const string m_FeatureName = "Indexer";
-
- /// <summary>
- /// Time left until next action.
- /// </summary>
- public float TimeUntilNextAction { get; private set; }
-
- /// <summary>
- /// Current determined action needed from thermal state.
- /// Action <see cref="StateAction.Increase"/> will be ignored if <see cref="PerformanceAction"/> is decreasing.
- /// </summary>
- public StateAction ThermalAction { get; private set; }
-
- /// <summary>
- /// Current determined action needed from performance state.
- /// Action <see cref="StateAction.Increase"/> will be ignored if <see cref="ThermalAction"/> is decreasing.
- /// </summary>
- public StateAction PerformanceAction { get; private set; }
-
- /// <summary>
- /// Returns all currently applied scalers.
- /// </summary>
- /// <param name="scalers">Output where scalers will be written.</param>
- public void GetAppliedScalers(ref List<AdaptivePerformanceScaler> scalers)
- {
- scalers.Clear();
- scalers.AddRange(m_AppliedScalers);
- }
-
- /// <summary>
- /// Returns all currently unapplied scalers.
- /// </summary>
- /// <param name="scalers">Output where scalers will be written.</param>
- public void GetUnappliedScalers(ref List<AdaptivePerformanceScaler> scalers)
- {
- scalers.Clear();
- scalers.AddRange(m_UnappliedScalers);
- }
-
- /// <summary>
- /// Returns all currently disabled scalers.
- /// </summary>
- /// <param name="scalers">Output where scalers will be written.</param>
- public void GetDisabledScalers(ref List<AdaptivePerformanceScaler> scalers)
- {
- scalers.Clear();
- scalers.AddRange(m_DisabledScalers);
- }
-
- /// <summary>
- /// Returns all scalers independent of their state.
- /// </summary>
- /// <param name="scalers">Output where scalers will be written.</param>
- public void GetAllRegisteredScalers(ref List<AdaptivePerformanceScaler> scalers)
- {
- scalers.Clear();
- scalers.AddRange(m_DisabledScalers);
- scalers.AddRange(m_UnappliedScalers);
- scalers.AddRange(m_AppliedScalers);
- }
-
- /// <summary>
- /// Unapply all currently active scalers.
- /// </summary>
- public void UnapplyAllScalers()
- {
- TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
- while (m_AppliedScalers.Count != 0)
- {
- var scaler = m_AppliedScalers[0];
- UnapplyScaler(scaler);
- }
- }
-
- internal void UpdateOverrideLevel(AdaptivePerformanceScaler scaler)
- {
- if (scaler.OverrideLevel == -1)
- return;
- while (scaler.OverrideLevel > scaler.CurrentLevel)
- ApplyScaler(scaler);
- while (scaler.OverrideLevel < scaler.CurrentLevel)
- UnapplyScaler(scaler);
- }
-
- internal void AddScaler(AdaptivePerformanceScaler scaler)
- {
- if (m_UnappliedScalers.Contains(scaler) || m_AppliedScalers.Contains(scaler))
- return;
-
- m_UnappliedScalers.Add(scaler);
- }
-
- internal void RemoveScaler(AdaptivePerformanceScaler scaler)
- {
- if (m_UnappliedScalers.Contains(scaler))
- {
- m_UnappliedScalers.Remove(scaler);
- return;
- }
-
- if (m_AppliedScalers.Contains(scaler))
- {
- while (!scaler.NotLeveled)
- scaler.DecreaseLevel();
- m_AppliedScalers.Remove(scaler);
- }
- }
-
- internal AdaptivePerformanceIndexer(ref IAdaptivePerformanceSettings settings)
- {
- m_Settings = settings;
- TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
- m_ThermalStateTracker = new ThermalStateTracker();
- m_PerformanceStateTracker = new PerformanceStateTracker(120);
- m_UnappliedScalers = new List<AdaptivePerformanceScaler>();
- m_AppliedScalers = new List<AdaptivePerformanceScaler>();
- m_DisabledScalers = new List<AdaptivePerformanceScaler>();
- m_ScalerEfficiencyTracker = new AdaptivePerformanceScalerEfficiencyTracker();
-
- AdaptivePerformanceAnalytics.RegisterFeature(m_FeatureName, m_Settings.indexerSettings.active);
- }
-
- internal void Update()
- {
- if (Holder.Instance == null || !m_Settings.indexerSettings.active)
- return;
-
- DeactivateDisabledScalers();
- ActivateEnabledScalers();
-
- var thermalAction = m_ThermalStateTracker.Update();
- var performanceAction = m_PerformanceStateTracker.Update();
-
- ThermalAction = thermalAction;
- PerformanceAction = performanceAction;
-
- if (Profiler.enabled)
- CollectProfilerStats();
-
- // Enforce minimum wait time between any scaler changes
- TimeUntilNextAction = Mathf.Max(TimeUntilNextAction - Time.deltaTime, 0);
- if (TimeUntilNextAction != 0)
- return;
-
- if (m_ScalerEfficiencyTracker.IsRunning)
- m_ScalerEfficiencyTracker.Stop();
-
- if (thermalAction == StateAction.Increase && performanceAction == StateAction.Stale)
- {
- UnapplyHighestCostScaler();
- TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
- return;
- }
- if (thermalAction == StateAction.Stale && performanceAction == StateAction.Stale)
- {
- UnapplyHighestCostScaler();
- TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
- return;
- }
- if (thermalAction == StateAction.Decrease)
- {
- ApplyLowestCostScaler();
- TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
- return;
- }
- if (performanceAction == StateAction.Decrease)
- {
- ApplyLowestCostScaler();
- TimeUntilNextAction = m_Settings.indexerSettings.performanceActionDelay;
- return;
- }
- if (thermalAction == StateAction.FastDecrease)
- {
- ApplyLowestCostScaler();
- TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay / 2;
- return;
- }
- if (performanceAction == StateAction.FastDecrease)
- {
- ApplyLowestCostScaler();
- TimeUntilNextAction = m_Settings.indexerSettings.performanceActionDelay / 2;
- return;
- }
- }
-
- void CollectProfilerStats()
- {
- for (int i = m_UnappliedScalers.Count - 1; i >= 0; i--)
- {
- var scaler = m_UnappliedScalers[i];
- AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, false, scaler.MaxLevel);
- }
-
- for (int i = m_AppliedScalers.Count - 1; i >= 0; i--)
- {
- var scaler = m_AppliedScalers[i];
- AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, true, scaler.MaxLevel);
- }
- for (int i = m_DisabledScalers.Count - 1; i >= 0; i--)
- {
- var scaler = m_DisabledScalers[i];
- AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, false, scaler.MaxLevel);
- }
- AdaptivePerformanceProfilerStats.FlushScalerDataToProfilerStream();
- }
-
- void DeactivateDisabledScalers()
- {
- for (int i = m_UnappliedScalers.Count - 1; i >= 0; i--)
- {
- var scaler = m_UnappliedScalers[i];
- if (!scaler.Enabled && !m_DisabledScalers.Contains(scaler))
- {
- APLog.Debug($"[Indexer] Deactivated {scaler.Name} scaler.");
- scaler.Deactivate();
- m_DisabledScalers.Add(scaler);
- m_UnappliedScalers.RemoveAt(i);
- }
- }
-
- for (int i = m_AppliedScalers.Count - 1; i >= 0; i--)
- {
- var scaler = m_AppliedScalers[i];
- if (!scaler.Enabled && !m_DisabledScalers.Contains(scaler))
- {
- APLog.Debug($"[Indexer] Deactivated {scaler.Name} scaler.");
- scaler.Deactivate();
- m_DisabledScalers.Add(scaler);
- m_AppliedScalers.RemoveAt(i);
- }
- }
- }
-
- void ActivateEnabledScalers()
- {
- for (int i = m_DisabledScalers.Count - 1; i >= 0; i--)
- {
- var scaler = m_DisabledScalers[i];
- if (scaler.Enabled)
- {
- scaler.Activate();
- AddScaler(scaler);
- m_DisabledScalers.RemoveAt(i);
- APLog.Debug($"[Indexer] Activated {scaler.Name} scaler.");
- }
- }
- }
-
- private bool ApplyLowestCostScaler()
- {
- AdaptivePerformanceScaler result = null;
- var lowestCost = float.PositiveInfinity;
-
- foreach (var scaler in m_UnappliedScalers)
- {
- if (!scaler.Enabled)
- continue;
-
- if (scaler.OverrideLevel != -1)
- continue;
-
- var cost = scaler.CalculateCost();
-
- if (lowestCost > cost)
- {
- result = scaler;
- lowestCost = cost;
- }
- }
-
- foreach (var scaler in m_AppliedScalers)
- {
- if (!scaler.Enabled)
- continue;
-
- if (scaler.OverrideLevel != -1)
- continue;
-
- if (scaler.IsMaxLevel)
- continue;
-
- var cost = scaler.CalculateCost();
-
- if (lowestCost > cost)
- {
- result = scaler;
- lowestCost = cost;
- }
- }
-
- if (result != null)
- {
- m_ScalerEfficiencyTracker.Start(result, true);
-
- ApplyScaler(result);
- return true;
- }
-
- return false;
- }
-
- private void ApplyScaler(AdaptivePerformanceScaler scaler)
- {
- APLog.Debug($"[Indexer] Applying {scaler.Name} scaler at level {scaler.CurrentLevel} and try to increase level to {scaler.CurrentLevel+1}");
- if (scaler.NotLeveled)
- {
- m_UnappliedScalers.Remove(scaler);
- m_AppliedScalers.Add(scaler);
- }
- scaler.IncreaseLevel();
- }
-
- private bool UnapplyHighestCostScaler()
- {
- AdaptivePerformanceScaler result = null;
- var highestCost = float.NegativeInfinity;
-
- foreach (var scaler in m_AppliedScalers)
- {
- if (scaler.OverrideLevel != -1)
- continue;
-
- var cost = scaler.CalculateCost();
-
- if (highestCost < cost)
- {
- result = scaler;
- highestCost = cost;
- }
- }
-
- if (result != null)
- {
- m_ScalerEfficiencyTracker.Start(result, false);
-
- UnapplyScaler(result);
- return true;
- }
-
- return false;
- }
-
- private void UnapplyScaler(AdaptivePerformanceScaler scaler)
- {
- APLog.Debug($"[Indexer] Unapplying {scaler.Name} scaler at level {scaler.CurrentLevel} and try to decrease level to {scaler.CurrentLevel-1}");
- scaler.DecreaseLevel();
- if (scaler.NotLeveled)
- {
- m_AppliedScalers.Remove(scaler);
- m_UnappliedScalers.Add(scaler);
- }
- }
- }
- }
|