Keine Beschreibung
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

AdaptivePerformanceIndexer.cs 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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 / GetEffectiveTargetFrameRate();
  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. protected virtual float GetEffectiveTargetFrameRate()
  116. {
  117. return AdaptivePerformanceManager.EffectiveTargetFrameRate();
  118. }
  119. }
  120. /// <summary>
  121. /// System used for tracking impact of scaler on CPU and GPU counters.
  122. /// </summary>
  123. internal class AdaptivePerformanceScalerEfficiencyTracker
  124. {
  125. private AdaptivePerformanceScaler m_Scaler;
  126. private float m_LastAverageGpuFrameTime;
  127. private float m_LastAverageCpuFrameTime;
  128. private bool m_IsApplied;
  129. public bool IsRunning { get => m_Scaler != null; }
  130. public void Start(AdaptivePerformanceScaler scaler, bool isApply)
  131. {
  132. Debug.Assert(!IsRunning, "AdaptivePerformanceScalerEfficiencyTracker is already running");
  133. m_Scaler = scaler;
  134. m_LastAverageGpuFrameTime = Holder.Instance.PerformanceStatus.FrameTiming.AverageGpuFrameTime;
  135. m_LastAverageCpuFrameTime = Holder.Instance.PerformanceStatus.FrameTiming.AverageCpuFrameTime;
  136. m_IsApplied = true;
  137. }
  138. public void Stop()
  139. {
  140. var gpu = Holder.Instance.PerformanceStatus.FrameTiming.AverageGpuFrameTime - m_LastAverageGpuFrameTime;
  141. var cpu = Holder.Instance.PerformanceStatus.FrameTiming.AverageCpuFrameTime - m_LastAverageCpuFrameTime;
  142. var sign = m_IsApplied ? 1 : -1;
  143. m_Scaler.GpuImpact = sign * (int)(gpu * 1000);
  144. m_Scaler.CpuImpact = sign * (int)(cpu * 1000);
  145. m_Scaler = null;
  146. }
  147. }
  148. /// <summary>
  149. /// 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.
  150. /// System acts as <see cref="AdaptivePerformanceScaler"/> manager and handles the lifetime of the scalers in the scenes.
  151. /// </summary>
  152. public class AdaptivePerformanceIndexer
  153. {
  154. private List<AdaptivePerformanceScaler> m_UnappliedScalers;
  155. private List<AdaptivePerformanceScaler> m_AppliedScalers;
  156. private List<AdaptivePerformanceScaler> m_DisabledScalers;
  157. private ThermalStateTracker m_ThermalStateTracker;
  158. private PerformanceStateTracker m_PerformanceStateTracker;
  159. private AdaptivePerformanceScalerEfficiencyTracker m_ScalerEfficiencyTracker;
  160. private IAdaptivePerformanceSettings m_Settings;
  161. const string m_FeatureName = "Indexer";
  162. /// <summary>
  163. /// Time left until next action.
  164. /// </summary>
  165. public float TimeUntilNextAction { get; private set; }
  166. /// <summary>
  167. /// Current determined action needed from thermal state.
  168. /// Action <see cref="StateAction.Increase"/> will be ignored if <see cref="PerformanceAction"/> is decreasing.
  169. /// </summary>
  170. public StateAction ThermalAction { get; private set; }
  171. /// <summary>
  172. /// Current determined action needed from performance state.
  173. /// Action <see cref="StateAction.Increase"/> will be ignored if <see cref="ThermalAction"/> is decreasing.
  174. /// </summary>
  175. public StateAction PerformanceAction { get; private set; }
  176. /// <summary>
  177. /// Returns all currently applied scalers.
  178. /// </summary>
  179. /// <param name="scalers">Output where scalers will be written.</param>
  180. public void GetAppliedScalers(ref List<AdaptivePerformanceScaler> scalers)
  181. {
  182. scalers.Clear();
  183. scalers.AddRange(m_AppliedScalers);
  184. }
  185. /// <summary>
  186. /// Returns all currently unapplied scalers.
  187. /// </summary>
  188. /// <param name="scalers">Output where scalers will be written.</param>
  189. public void GetUnappliedScalers(ref List<AdaptivePerformanceScaler> scalers)
  190. {
  191. scalers.Clear();
  192. scalers.AddRange(m_UnappliedScalers);
  193. }
  194. /// <summary>
  195. /// Returns all currently disabled scalers.
  196. /// </summary>
  197. /// <param name="scalers">Output where scalers will be written.</param>
  198. public void GetDisabledScalers(ref List<AdaptivePerformanceScaler> scalers)
  199. {
  200. scalers.Clear();
  201. scalers.AddRange(m_DisabledScalers);
  202. }
  203. /// <summary>
  204. /// Returns all scalers independent of their state.
  205. /// </summary>
  206. /// <param name="scalers">Output where scalers will be written.</param>
  207. public void GetAllRegisteredScalers(ref List<AdaptivePerformanceScaler> scalers)
  208. {
  209. scalers.Clear();
  210. scalers.AddRange(m_DisabledScalers);
  211. scalers.AddRange(m_UnappliedScalers);
  212. scalers.AddRange(m_AppliedScalers);
  213. }
  214. /// <summary>
  215. /// Unapply all currently active scalers.
  216. /// </summary>
  217. public void UnapplyAllScalers()
  218. {
  219. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  220. while (m_AppliedScalers.Count != 0)
  221. {
  222. var scaler = m_AppliedScalers[0];
  223. UnapplyScaler(scaler);
  224. }
  225. }
  226. internal void UpdateOverrideLevel(AdaptivePerformanceScaler scaler)
  227. {
  228. if (scaler.OverrideLevel == -1)
  229. return;
  230. while (scaler.OverrideLevel > scaler.CurrentLevel)
  231. ApplyScaler(scaler);
  232. while (scaler.OverrideLevel < scaler.CurrentLevel)
  233. UnapplyScaler(scaler);
  234. }
  235. internal void AddScaler(AdaptivePerformanceScaler scaler)
  236. {
  237. if (m_UnappliedScalers.Contains(scaler) || m_AppliedScalers.Contains(scaler))
  238. return;
  239. m_UnappliedScalers.Add(scaler);
  240. }
  241. internal void RemoveScaler(AdaptivePerformanceScaler scaler)
  242. {
  243. if (m_UnappliedScalers.Contains(scaler))
  244. {
  245. m_UnappliedScalers.Remove(scaler);
  246. return;
  247. }
  248. if (m_AppliedScalers.Contains(scaler))
  249. {
  250. while (!scaler.NotLeveled)
  251. scaler.DecreaseLevel();
  252. m_AppliedScalers.Remove(scaler);
  253. }
  254. }
  255. internal AdaptivePerformanceIndexer(ref IAdaptivePerformanceSettings settings, PerformanceStateTracker tracker)
  256. {
  257. m_Settings = settings;
  258. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  259. m_ThermalStateTracker = new ThermalStateTracker();
  260. m_PerformanceStateTracker = tracker;
  261. m_UnappliedScalers = new List<AdaptivePerformanceScaler>();
  262. m_AppliedScalers = new List<AdaptivePerformanceScaler>();
  263. m_DisabledScalers = new List<AdaptivePerformanceScaler>();
  264. m_ScalerEfficiencyTracker = new AdaptivePerformanceScalerEfficiencyTracker();
  265. AdaptivePerformanceAnalytics.RegisterFeature(m_FeatureName, m_Settings.indexerSettings.active);
  266. }
  267. internal void Update()
  268. {
  269. if (Holder.Instance == null || !m_Settings.indexerSettings.active)
  270. return;
  271. DeactivateDisabledScalers();
  272. ActivateEnabledScalers();
  273. var thermalAction = m_ThermalStateTracker.Update();
  274. var performanceAction = m_PerformanceStateTracker.Update();
  275. ThermalAction = thermalAction;
  276. PerformanceAction = performanceAction;
  277. if (Profiler.enabled)
  278. CollectProfilerStats();
  279. // Enforce minimum wait time between any scaler changes
  280. TimeUntilNextAction = Mathf.Max(TimeUntilNextAction - DeltaTime(), 0);
  281. if (TimeUntilNextAction != 0)
  282. return;
  283. if (m_ScalerEfficiencyTracker.IsRunning)
  284. m_ScalerEfficiencyTracker.Stop();
  285. if (thermalAction == StateAction.Increase && performanceAction == StateAction.Stale)
  286. {
  287. UnapplyHighestCostScaler();
  288. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  289. return;
  290. }
  291. if (thermalAction == StateAction.Stale && performanceAction == StateAction.Stale)
  292. {
  293. UnapplyHighestCostScaler();
  294. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  295. return;
  296. }
  297. if (thermalAction == StateAction.Decrease)
  298. {
  299. ApplyLowestCostScaler();
  300. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay;
  301. return;
  302. }
  303. if (performanceAction == StateAction.Decrease)
  304. {
  305. ApplyLowestCostScaler();
  306. TimeUntilNextAction = m_Settings.indexerSettings.performanceActionDelay;
  307. return;
  308. }
  309. if (thermalAction == StateAction.FastDecrease)
  310. {
  311. ApplyLowestCostScaler();
  312. TimeUntilNextAction = m_Settings.indexerSettings.thermalActionDelay / 2;
  313. return;
  314. }
  315. if (performanceAction == StateAction.FastDecrease)
  316. {
  317. ApplyLowestCostScaler();
  318. TimeUntilNextAction = m_Settings.indexerSettings.performanceActionDelay / 2;
  319. return;
  320. }
  321. }
  322. /// <summary>
  323. /// Returns <see cref="Time.deltaTime"/> only and is primarily encapsulated for tests.
  324. /// </summary>
  325. /// <returns>delta time</returns>
  326. protected virtual float DeltaTime()
  327. {
  328. return Time.deltaTime;
  329. }
  330. void CollectProfilerStats()
  331. {
  332. for (int i = m_UnappliedScalers.Count - 1; i >= 0; i--)
  333. {
  334. var scaler = m_UnappliedScalers[i];
  335. AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, false, scaler.MaxLevel);
  336. }
  337. for (int i = m_AppliedScalers.Count - 1; i >= 0; i--)
  338. {
  339. var scaler = m_AppliedScalers[i];
  340. AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, true, scaler.MaxLevel);
  341. }
  342. for (int i = m_DisabledScalers.Count - 1; i >= 0; i--)
  343. {
  344. var scaler = m_DisabledScalers[i];
  345. AdaptivePerformanceProfilerStats.EmitScalerDataToProfilerStream(scaler.Name, scaler.Enabled, scaler.OverrideLevel, scaler.CurrentLevel, scaler.Scale, false, scaler.MaxLevel);
  346. }
  347. AdaptivePerformanceProfilerStats.FlushScalerDataToProfilerStream();
  348. }
  349. void DeactivateDisabledScalers()
  350. {
  351. for (int i = m_UnappliedScalers.Count - 1; i >= 0; i--)
  352. {
  353. var scaler = m_UnappliedScalers[i];
  354. if (!scaler.Enabled && !m_DisabledScalers.Contains(scaler))
  355. {
  356. APLog.Debug($"[Indexer] Deactivated {scaler.Name} scaler.");
  357. scaler.Deactivate();
  358. m_DisabledScalers.Add(scaler);
  359. m_UnappliedScalers.RemoveAt(i);
  360. }
  361. }
  362. for (int i = m_AppliedScalers.Count - 1; i >= 0; i--)
  363. {
  364. var scaler = m_AppliedScalers[i];
  365. if (!scaler.Enabled && !m_DisabledScalers.Contains(scaler))
  366. {
  367. APLog.Debug($"[Indexer] Deactivated {scaler.Name} scaler.");
  368. scaler.Deactivate();
  369. m_DisabledScalers.Add(scaler);
  370. m_AppliedScalers.RemoveAt(i);
  371. }
  372. }
  373. }
  374. void ActivateEnabledScalers()
  375. {
  376. for (int i = m_DisabledScalers.Count - 1; i >= 0; i--)
  377. {
  378. var scaler = m_DisabledScalers[i];
  379. if (scaler.Enabled)
  380. {
  381. scaler.Activate();
  382. AddScaler(scaler);
  383. m_DisabledScalers.RemoveAt(i);
  384. APLog.Debug($"[Indexer] Activated {scaler.Name} scaler.");
  385. }
  386. }
  387. }
  388. private bool ApplyLowestCostScaler()
  389. {
  390. AdaptivePerformanceScaler result = null;
  391. var lowestCost = float.PositiveInfinity;
  392. foreach (var scaler in m_UnappliedScalers)
  393. {
  394. if (!scaler.Enabled)
  395. continue;
  396. if (scaler.OverrideLevel != -1)
  397. continue;
  398. var cost = scaler.CalculateCost();
  399. if (lowestCost > cost)
  400. {
  401. result = scaler;
  402. lowestCost = cost;
  403. }
  404. }
  405. foreach (var scaler in m_AppliedScalers)
  406. {
  407. if (!scaler.Enabled)
  408. continue;
  409. if (scaler.OverrideLevel != -1)
  410. continue;
  411. if (scaler.IsMaxLevel)
  412. continue;
  413. var cost = scaler.CalculateCost();
  414. if (lowestCost > cost)
  415. {
  416. result = scaler;
  417. lowestCost = cost;
  418. }
  419. }
  420. if (result != null)
  421. {
  422. m_ScalerEfficiencyTracker.Start(result, true);
  423. ApplyScaler(result);
  424. return true;
  425. }
  426. return false;
  427. }
  428. private void ApplyScaler(AdaptivePerformanceScaler scaler)
  429. {
  430. APLog.Debug($"[Indexer] Applying {scaler.Name} scaler at level {scaler.CurrentLevel} and try to increase level to {scaler.CurrentLevel+1}");
  431. if (scaler.NotLeveled)
  432. {
  433. m_UnappliedScalers.Remove(scaler);
  434. m_AppliedScalers.Add(scaler);
  435. }
  436. scaler.IncreaseLevel();
  437. }
  438. private bool UnapplyHighestCostScaler()
  439. {
  440. AdaptivePerformanceScaler result = null;
  441. var highestCost = float.NegativeInfinity;
  442. foreach (var scaler in m_AppliedScalers)
  443. {
  444. if (scaler.OverrideLevel != -1)
  445. continue;
  446. var cost = scaler.CalculateCost();
  447. if (highestCost < cost)
  448. {
  449. result = scaler;
  450. highestCost = cost;
  451. }
  452. }
  453. if (result != null)
  454. {
  455. m_ScalerEfficiencyTracker.Start(result, false);
  456. UnapplyScaler(result);
  457. return true;
  458. }
  459. return false;
  460. }
  461. private void UnapplyScaler(AdaptivePerformanceScaler scaler)
  462. {
  463. APLog.Debug($"[Indexer] Unapplying {scaler.Name} scaler at level {scaler.CurrentLevel} and try to decrease level to {scaler.CurrentLevel-1}");
  464. scaler.DecreaseLevel();
  465. if (scaler.NotLeveled)
  466. {
  467. m_AppliedScalers.Remove(scaler);
  468. m_UnappliedScalers.Add(scaler);
  469. }
  470. }
  471. }
  472. }