暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AdaptivePerformanceIndexer.cs 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.Profiling;
  4. namespace UnityEngine.AdaptivePerformance
  5. {
  6. /// <summary>
  7. /// Describes what action is needed to stabilize.
  8. /// </summary>
  9. public enum StateAction
  10. {
  11. /// <summary>
  12. /// No action is required.
  13. /// </summary>
  14. Stale,
  15. /// <summary>
  16. /// Recommended to increase quality.
  17. /// </summary>
  18. Increase,
  19. /// <summary>
  20. /// Quality must be decreased.
  21. /// </summary>
  22. Decrease,
  23. /// <summary>
  24. /// Quality must be decreased as soon as possible.
  25. /// </summary>
  26. FastDecrease,
  27. }
  28. /// <summary>
  29. /// System used for tracking device thermal state.
  30. /// </summary>
  31. internal class ThermalStateTracker
  32. {
  33. private float warningTemp = 1.0f;
  34. private float throttlingTemp = 1.0f;
  35. public ThermalStateTracker()
  36. {
  37. }
  38. public StateAction Update()
  39. {
  40. float thermalTrend = Holder.Instance.ThermalStatus.ThermalMetrics.TemperatureTrend;
  41. var thermalLevel = Holder.Instance.ThermalStatus.ThermalMetrics.TemperatureLevel;
  42. var warning = Holder.Instance.ThermalStatus.ThermalMetrics.WarningLevel;
  43. if (warning == WarningLevel.ThrottlingImminent && warningTemp == 1.0f)
  44. warningTemp = thermalLevel; // remember throttling imminent level
  45. if (warning == WarningLevel.Throttling && throttlingTemp == 1.0f)
  46. throttlingTemp = thermalLevel; // remember throttling level
  47. // Throttling needs to cool down a lot before changing to no warning
  48. if (warning == WarningLevel.Throttling || thermalLevel >= throttlingTemp)
  49. return StateAction.FastDecrease;
  50. // warm device
  51. if (warning == WarningLevel.ThrottlingImminent || thermalLevel >= warningTemp)
  52. {
  53. // halfway to throttling?
  54. if (thermalLevel > (warningTemp + throttlingTemp) / 2)
  55. return StateAction.Decrease;
  56. if (thermalTrend <= 0)
  57. return StateAction.Stale;
  58. else if (thermalTrend > 0.5)
  59. return StateAction.FastDecrease;
  60. else
  61. return StateAction.Decrease;
  62. }
  63. // normal operating conditions
  64. if (warning == WarningLevel.NoWarning && thermalLevel < warningTemp)
  65. {
  66. if (thermalTrend <= 0)
  67. return StateAction.Increase;
  68. else if (thermalTrend > 0.5)
  69. return StateAction.FastDecrease;
  70. else if (thermalTrend > 0.1)
  71. return StateAction.Decrease;
  72. }
  73. return StateAction.Stale;
  74. }
  75. }
  76. /// <summary>
  77. /// System used for tracking device performance state.
  78. /// </summary>
  79. internal class PerformanceStateTracker
  80. {
  81. private Queue<float> m_Samples;
  82. private int m_SampleCapacity;
  83. public float Trend { get; set; }
  84. public PerformanceStateTracker(int sampleCapacity)
  85. {
  86. m_Samples = new Queue<float>(sampleCapacity);
  87. m_SampleCapacity = sampleCapacity;
  88. }
  89. public StateAction Update()
  90. {
  91. var frameMs = Holder.Instance.PerformanceStatus.FrameTiming.AverageFrameTime;
  92. if (frameMs > 0)
  93. {
  94. var targetMs = 1f / AdaptivePerformanceManager.EffectiveTargetFrameRate();
  95. var diffMs = (frameMs / targetMs) - 1;
  96. m_Samples.Enqueue(diffMs);
  97. if (m_Samples.Count > m_SampleCapacity)
  98. m_Samples.Dequeue();
  99. }
  100. var trend = 0.0f;
  101. foreach (var sample in m_Samples)
  102. trend += sample;
  103. trend /= m_Samples.Count;
  104. Trend = trend;
  105. // It is underperforming heavily, we need to increase performance
  106. if (Trend >= 0.30)
  107. return StateAction.FastDecrease;
  108. // It is underperforming, we need to increase performance
  109. if (Trend >= 0.15)
  110. return StateAction.Decrease;
  111. // TODO: we need to way identify overperforming as currently AverageFrameTime is returned with vsync
  112. // return StaterAction.Increase;
  113. return StateAction.Stale;
  114. }
  115. }
  116. /// <summary>
  117. /// System used for tracking impact of scaler on CPU and GPU counters.
  118. /// </summary>
  119. internal class AdaptivePerformanceScalerEfficiencyTracker
  120. {
  121. private AdaptivePerformanceScaler m_Scaler;
  122. private float m_LastAverageGpuFrameTime;
  123. private float m_LastAverageCpuFrameTime;
  124. private bool m_IsApplied;
  125. public bool IsRunning { get => m_Scaler != null; }
  126. public void Start(AdaptivePerformanceScaler scaler, bool isApply)
  127. {
  128. Debug.Assert(!IsRunning, "AdaptivePerformanceScalerEfficiencyTracker is already running");
  129. m_Scaler = scaler;
  130. m_LastAverageGpuFrameTime = Holder.Instance.PerformanceStatus.FrameTiming.AverageGpuFrameTime;
  131. m_LastAverageCpuFrameTime = Holder.Instance.PerformanceStatus.FrameTiming.AverageCpuFrameTime;
  132. m_IsApplied = true;
  133. }
  134. public void Stop()
  135. {
  136. var gpu = Holder.Instance.PerformanceStatus.FrameTiming.AverageGpuFrameTime - m_LastAverageGpuFrameTime;
  137. var cpu = Holder.Instance.PerformanceStatus.FrameTiming.AverageCpuFrameTime - m_LastAverageCpuFrameTime;
  138. var sign = m_IsApplied ? 1 : -1;
  139. m_Scaler.GpuImpact = sign * (int)(gpu * 1000);
  140. m_Scaler.CpuImpact = sign * (int)(cpu * 1000);
  141. m_Scaler = null;
  142. }
  143. }
  144. /// <summary>
  145. /// 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.
  146. /// System acts as <see cref="AdaptivePerformanceScaler"/> manager and handles the lifetime of the scalers in the scenes.
  147. /// </summary>
  148. public class AdaptivePerformanceIndexer
  149. {
  150. private List<AdaptivePerformanceScaler> m_UnappliedScalers;
  151. private List<AdaptivePerformanceScaler> m_AppliedScalers;
  152. private List<AdaptivePerformanceScaler> m_DisabledScalers;
  153. private ThermalStateTracker m_ThermalStateTracker;
  154. private PerformanceStateTracker m_PerformanceStateTracker;
  155. private AdaptivePerformanceScalerEfficiencyTracker m_ScalerEfficiencyTracker;
  156. private IAdaptivePerformanceSettings m_Settings;
  157. const string m_FeatureName = "Indexer";
  158. /// <summary>
  159. /// Time left until next action.
  160. /// </summary>
  161. public float TimeUntilNextAction { get; private set; }
  162. /// <summary>
  163. /// Current determined action needed from thermal state.
  164. /// Action <see cref="StateAction.Increase"/> will be ignored if <see cref="PerformanceAction"/> is decreasing.
  165. /// </summary>
  166. public StateAction ThermalAction { get; private set; }
  167. /// <summary>
  168. /// Current determined action needed from performance state.
  169. /// Action <see cref="StateAction.Increase"/> will be ignored if <see cref="ThermalAction"/> is decreasing.
  170. /// </summary>
  171. public StateAction PerformanceAction { get; private set; }
  172. /// <summary>
  173. /// Returns all currently applied scalers.
  174. /// </summary>
  175. /// <param name="scalers">Output where scalers will be written.</param>
  176. public void GetAppliedScalers(ref List<AdaptivePerformanceScaler> scalers)
  177. {
  178. scalers.Clear();
  179. scalers.AddRange(m_AppliedScalers);
  180. }
  181. /// <summary>
  182. /// Returns all currently unapplied scalers.
  183. /// </summary>
  184. /// <param name="scalers">Output where scalers will be written.</param>
  185. public void GetUnappliedScalers(ref List<AdaptivePerformanceScaler> scalers)
  186. {
  187. scalers.Clear();
  188. scalers.AddRange(m_UnappliedScalers);
  189. }
  190. /// <summary>
  191. /// Returns all currently disabled scalers.
  192. /// </summary>
  193. /// <param name="scalers">Output where scalers will be written.</param>
  194. public void GetDisabledScalers(ref List<AdaptivePerformanceScaler> scalers)
  195. {
  196. scalers.Clear();
  197. scalers.AddRange(m_DisabledScalers);
  198. }
  199. /// <summary>
  200. /// Returns all scalers independent of their state.
  201. /// </summary>
  202. /// <param name="scalers">Output where scalers will be written.</param>
  203. public void GetAllRegisteredScalers(ref List<AdaptivePerformanceScaler> scalers)
  204. {
  205. scalers.Clear();
  206. scalers.AddRange(m_DisabledScalers);
  207. scalers.AddRange(m_UnappliedScalers);
  208. scalers.AddRange(m_AppliedScalers);
  209. }
  210. /// <summary>
  211. /// Unapply all currently active scalers.
  212. /// </summary>
  213. public void UnapplyAllScalers()
  214. {
  215. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  216. while (m_AppliedScalers.Count != 0)
  217. {
  218. var scaler = m_AppliedScalers[0];
  219. UnapplyScaler(scaler);
  220. }
  221. }
  222. internal void UpdateOverrideLevel(AdaptivePerformanceScaler scaler)
  223. {
  224. if (scaler.OverrideLevel == -1)
  225. return;
  226. while (scaler.OverrideLevel > scaler.CurrentLevel)
  227. ApplyScaler(scaler);
  228. while (scaler.OverrideLevel < scaler.CurrentLevel)
  229. UnapplyScaler(scaler);
  230. }
  231. internal void AddScaler(AdaptivePerformanceScaler scaler)
  232. {
  233. if (m_UnappliedScalers.Contains(scaler) || m_AppliedScalers.Contains(scaler))
  234. return;
  235. m_UnappliedScalers.Add(scaler);
  236. }
  237. internal void RemoveScaler(AdaptivePerformanceScaler scaler)
  238. {
  239. if (m_UnappliedScalers.Contains(scaler))
  240. {
  241. m_UnappliedScalers.Remove(scaler);
  242. return;
  243. }
  244. if (m_AppliedScalers.Contains(scaler))
  245. {
  246. while (!scaler.NotLeveled)
  247. scaler.DecreaseLevel();
  248. m_AppliedScalers.Remove(scaler);
  249. }
  250. }
  251. internal AdaptivePerformanceIndexer(ref IAdaptivePerformanceSettings settings)
  252. {
  253. m_Settings = settings;
  254. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  255. m_ThermalStateTracker = new ThermalStateTracker();
  256. m_PerformanceStateTracker = new PerformanceStateTracker(120);
  257. m_UnappliedScalers = new List<AdaptivePerformanceScaler>();
  258. m_AppliedScalers = new List<AdaptivePerformanceScaler>();
  259. m_DisabledScalers = new List<AdaptivePerformanceScaler>();
  260. m_ScalerEfficiencyTracker = new AdaptivePerformanceScalerEfficiencyTracker();
  261. AdaptivePerformanceAnalytics.RegisterFeature(m_FeatureName, m_Settings.indexerSettings.active);
  262. }
  263. internal void Update()
  264. {
  265. if (Holder.Instance == null || !m_Settings.indexerSettings.active)
  266. return;
  267. DeactivateDisabledScalers();
  268. ActivateEnabledScalers();
  269. var thermalAction = m_ThermalStateTracker.Update();
  270. var performanceAction = m_PerformanceStateTracker.Update();
  271. ThermalAction = thermalAction;
  272. PerformanceAction = performanceAction;
  273. if (Profiler.enabled)
  274. CollectProfilerStats();
  275. // Enforce minimum wait time between any scaler changes
  276. TimeUntilNextAction = Mathf.Max(TimeUntilNextAction - Time.deltaTime, 0);
  277. if (TimeUntilNextAction != 0)
  278. return;
  279. if (m_ScalerEfficiencyTracker.IsRunning)
  280. m_ScalerEfficiencyTracker.Stop();
  281. if (thermalAction == StateAction.Increase && performanceAction == StateAction.Stale)
  282. {
  283. UnapplyHighestCostScaler();
  284. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  285. return;
  286. }
  287. if (thermalAction == StateAction.Stale && performanceAction == StateAction.Stale)
  288. {
  289. UnapplyHighestCostScaler();
  290. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  291. return;
  292. }
  293. if (thermalAction == StateAction.Decrease)
  294. {
  295. ApplyLowestCostScaler();
  296. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  297. return;
  298. }
  299. if (performanceAction == StateAction.Decrease)
  300. {
  301. ApplyLowestCostScaler();
  302. TimeUntilNextAction = m_Settings.indexerSettings.performanceActionDelay;
  303. return;
  304. }
  305. if (thermalAction == StateAction.FastDecrease)
  306. {
  307. ApplyLowestCostScaler();
  308. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay / 2;
  309. return;
  310. }
  311. if (performanceAction == StateAction.FastDecrease)
  312. {
  313. ApplyLowestCostScaler();
  314. TimeUntilNextAction = m_Settings.indexerSettings.performanceActionDelay / 2;
  315. return;
  316. }
  317. }
  318. void CollectProfilerStats()
  319. {
  320. for (int i = m_UnappliedScalers.Count - 1; i >= 0; i--)
  321. {
  322. var scaler = m_UnappliedScalers[i];
  323. AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, false, scaler.MaxLevel);
  324. }
  325. for (int i = m_AppliedScalers.Count - 1; i >= 0; i--)
  326. {
  327. var scaler = m_AppliedScalers[i];
  328. AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, true, scaler.MaxLevel);
  329. }
  330. for (int i = m_DisabledScalers.Count - 1; i >= 0; i--)
  331. {
  332. var scaler = m_DisabledScalers[i];
  333. AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, false, scaler.MaxLevel);
  334. }
  335. AdaptivePerformanceProfilerStats.FlushScalerDataToProfilerStream();
  336. }
  337. void DeactivateDisabledScalers()
  338. {
  339. for (int i = m_UnappliedScalers.Count - 1; i >= 0; i--)
  340. {
  341. var scaler = m_UnappliedScalers[i];
  342. if (!scaler.Enabled && !m_DisabledScalers.Contains(scaler))
  343. {
  344. APLog.Debug($"[Indexer] Deactivated {scaler.Name} scaler.");
  345. scaler.Deactivate();
  346. m_DisabledScalers.Add(scaler);
  347. m_UnappliedScalers.RemoveAt(i);
  348. }
  349. }
  350. for (int i = m_AppliedScalers.Count - 1; i >= 0; i--)
  351. {
  352. var scaler = m_AppliedScalers[i];
  353. if (!scaler.Enabled && !m_DisabledScalers.Contains(scaler))
  354. {
  355. APLog.Debug($"[Indexer] Deactivated {scaler.Name} scaler.");
  356. scaler.Deactivate();
  357. m_DisabledScalers.Add(scaler);
  358. m_AppliedScalers.RemoveAt(i);
  359. }
  360. }
  361. }
  362. void ActivateEnabledScalers()
  363. {
  364. for (int i = m_DisabledScalers.Count - 1; i >= 0; i--)
  365. {
  366. var scaler = m_DisabledScalers[i];
  367. if (scaler.Enabled)
  368. {
  369. scaler.Activate();
  370. AddScaler(scaler);
  371. m_DisabledScalers.RemoveAt(i);
  372. APLog.Debug($"[Indexer] Activated {scaler.Name} scaler.");
  373. }
  374. }
  375. }
  376. private bool ApplyLowestCostScaler()
  377. {
  378. AdaptivePerformanceScaler result = null;
  379. var lowestCost = float.PositiveInfinity;
  380. foreach (var scaler in m_UnappliedScalers)
  381. {
  382. if (!scaler.Enabled)
  383. continue;
  384. if (scaler.OverrideLevel != -1)
  385. continue;
  386. var cost = scaler.CalculateCost();
  387. if (lowestCost > cost)
  388. {
  389. result = scaler;
  390. lowestCost = cost;
  391. }
  392. }
  393. foreach (var scaler in m_AppliedScalers)
  394. {
  395. if (!scaler.Enabled)
  396. continue;
  397. if (scaler.OverrideLevel != -1)
  398. continue;
  399. if (scaler.IsMaxLevel)
  400. continue;
  401. var cost = scaler.CalculateCost();
  402. if (lowestCost > cost)
  403. {
  404. result = scaler;
  405. lowestCost = cost;
  406. }
  407. }
  408. if (result != null)
  409. {
  410. m_ScalerEfficiencyTracker.Start(result, true);
  411. ApplyScaler(result);
  412. return true;
  413. }
  414. return false;
  415. }
  416. private void ApplyScaler(AdaptivePerformanceScaler scaler)
  417. {
  418. APLog.Debug($"[Indexer] Applying {scaler.Name} scaler at level {scaler.CurrentLevel} and try to increase level to {scaler.CurrentLevel+1}");
  419. if (scaler.NotLeveled)
  420. {
  421. m_UnappliedScalers.Remove(scaler);
  422. m_AppliedScalers.Add(scaler);
  423. }
  424. scaler.IncreaseLevel();
  425. }
  426. private bool UnapplyHighestCostScaler()
  427. {
  428. AdaptivePerformanceScaler result = null;
  429. var highestCost = float.NegativeInfinity;
  430. foreach (var scaler in m_AppliedScalers)
  431. {
  432. if (scaler.OverrideLevel != -1)
  433. continue;
  434. var cost = scaler.CalculateCost();
  435. if (highestCost < cost)
  436. {
  437. result = scaler;
  438. highestCost = cost;
  439. }
  440. }
  441. if (result != null)
  442. {
  443. m_ScalerEfficiencyTracker.Start(result, false);
  444. UnapplyScaler(result);
  445. return true;
  446. }
  447. return false;
  448. }
  449. private void UnapplyScaler(AdaptivePerformanceScaler scaler)
  450. {
  451. APLog.Debug($"[Indexer] Unapplying {scaler.Name} scaler at level {scaler.CurrentLevel} and try to decrease level to {scaler.CurrentLevel-1}");
  452. scaler.DecreaseLevel();
  453. if (scaler.NotLeveled)
  454. {
  455. m_AppliedScalers.Remove(scaler);
  456. m_UnappliedScalers.Add(scaler);
  457. }
  458. }
  459. }
  460. }