FPSCounterData.cs 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. #region copyright
  2. //-------------------------------------------------------
  3. // Copyright (C) Dmitriy Yukhanov [https://codestage.net]
  4. //-------------------------------------------------------
  5. #endregion
  6. namespace CodeStage.AdvancedFPSCounter.CountersData
  7. {
  8. using System;
  9. using System.Collections;
  10. using Utils;
  11. using UnityEngine;
  12. using Object = UnityEngine.Object;
  13. /// <summary>
  14. /// Shows frames per second counter.
  15. /// </summary>
  16. [Serializable]
  17. public class FPSCounterData : UpdatableCounterData
  18. {
  19. // ----------------------------------------------------------------------------
  20. // constants
  21. // ----------------------------------------------------------------------------
  22. private const string ColorTextStart = "<color=#{0}>";
  23. private const string ColorTextEnd = "</color>";
  24. private const string FPSTextStart = ColorTextStart + "FPS: ";
  25. private const string MsTextStart = " " + ColorTextStart + "[";
  26. private const string MsTextEnd = " MS]" + ColorTextEnd;
  27. private const string MinTextStart = ColorTextStart + "MIN: ";
  28. private const string MaxTextStart = ColorTextStart + "MAX: ";
  29. private const string AvgTextStart = ColorTextStart + "AVG: ";
  30. private const string RenderTextStart = ColorTextStart + "REN: ";
  31. // ----------------------------------------------------------------------------
  32. // public fields exposed to the inspector
  33. // ----------------------------------------------------------------------------
  34. /// <summary>
  35. /// If FPS will drop below this value, #ColorWarning will be used for counter text.
  36. /// </summary>
  37. public int warningLevelValue = 50;
  38. /// <summary>
  39. /// If FPS will be equal or less this value, #ColorCritical will be used for counter text.
  40. /// </summary>
  41. public int criticalLevelValue = 20;
  42. /// <summary>
  43. /// Average FPS counter accumulative data will be reset on new scene load if enabled.
  44. /// \sa AverageSamples, LastAverageValue
  45. /// </summary>
  46. [Tooltip("Average FPS counter accumulative data will be reset on new scene load if enabled.")]
  47. public bool resetAverageOnNewScene;
  48. /// <summary>
  49. /// Minimum and maximum FPS readings will be reset on new scene load if enabled.
  50. /// \sa LastMinimumValue, LastMaximumValue
  51. /// </summary>
  52. [Tooltip("Minimum and maximum FPS readouts will be reset on new scene load if enabled")]
  53. public bool resetMinMaxOnNewScene;
  54. /// <summary>
  55. /// Amount of update intervals to skip before recording minimum and maximum FPS. Use it to skip initialization performance spikes and drops.
  56. /// \sa LastMinimumValue, LastMaximumValue
  57. /// </summary>
  58. [Tooltip("Amount of update intervals to skip before recording minimum and maximum FPS.\n" +
  59. "Use it to skip initialization performance spikes and drops.")]
  60. [Range(0, 10)]
  61. public int minMaxIntervalsToSkip = 3;
  62. // ----------------------------------------------------------------------------
  63. // events
  64. // ----------------------------------------------------------------------------
  65. /// <summary>
  66. /// Event to let you react on FPS level change.
  67. /// \sa FPSLevel, CurrentFpsLevel
  68. /// </summary>
  69. public event Action<FPSLevel> OnFPSLevelChange;
  70. // ----------------------------------------------------------------------------
  71. // internal fields
  72. // ----------------------------------------------------------------------------
  73. internal float newValue = -1;
  74. // ----------------------------------------------------------------------------
  75. // private fields
  76. // ----------------------------------------------------------------------------
  77. private string colorCachedMs;
  78. private string colorCachedMin;
  79. private string colorCachedMax;
  80. private string colorCachedAvg;
  81. private string colorCachedRender;
  82. private string colorWarningCached;
  83. private string colorWarningCachedMs;
  84. private string colorWarningCachedMin;
  85. private string colorWarningCachedMax;
  86. private string colorWarningCachedAvg;
  87. private string colorCriticalCached;
  88. private string colorCriticalCachedMs;
  89. private string colorCriticalCachedMin;
  90. private string colorCriticalCachedMax;
  91. private string colorCriticalCachedAvg;
  92. private int currentAverageSamples;
  93. private float currentAverageRaw;
  94. private int minMaxIntervalsSkipped;
  95. private float renderTimeBank;
  96. private int previousFrameCount;
  97. // ----------------------------------------------------------------------------
  98. // properties exposed to the inspector
  99. // ----------------------------------------------------------------------------
  100. #region RealtimeFPS
  101. #if UNITY_EDITOR
  102. [SerializeField]
  103. private bool realtimeFPSFoldout;
  104. #endif
  105. [Tooltip("Shows realtime FPS at the moment of the counter update scene start. Allows to hide FPS readout if necessary.")]
  106. [SerializeField]
  107. private bool realtimeFPS;
  108. /// <summary>
  109. /// Shows realtime FPS at the moment of the counter update scene start. Allows to hide FPS readout if necessary.
  110. /// \sa LastValue
  111. /// </summary>
  112. public bool RealtimeFPS
  113. {
  114. get { return realtimeFPS; }
  115. set
  116. {
  117. if (realtimeFPS == value || !Application.isPlaying) return;
  118. realtimeFPS = value;
  119. if (!realtimeFPS) LastValue = 0;
  120. if (!enabled) return;
  121. Refresh();
  122. }
  123. }
  124. #endregion
  125. #region Milliseconds
  126. [Tooltip("Shows time in milliseconds spent to render 1 frame.")]
  127. [SerializeField]
  128. private bool milliseconds;
  129. /// <summary>
  130. /// Shows time in milliseconds spent to render 1 frame.
  131. /// \sa LastMillisecondsValue
  132. /// </summary>
  133. public bool Milliseconds
  134. {
  135. get { return milliseconds; }
  136. set
  137. {
  138. if (milliseconds == value || !Application.isPlaying) return;
  139. milliseconds = value;
  140. if (!milliseconds) LastMillisecondsValue = 0f;
  141. if (!enabled) return;
  142. Refresh();
  143. }
  144. }
  145. #endregion
  146. #region Average
  147. #if UNITY_EDITOR
  148. [SerializeField]
  149. private bool averageFoldout;
  150. #endif
  151. [Tooltip("Shows Average FPS calculated from specified Samples amount or since game / scene start, " +
  152. "depending on Samples value and 'Reset On Load' toggle.")]
  153. [SerializeField]
  154. private bool average;
  155. /// <summary>
  156. /// Shows Average FPS calculated from specified #AverageSamples amount or since game / scene start, depending on #AverageSamples value and #resetAverageOnNewScene toggle.
  157. /// \sa LastAverageValue
  158. /// </summary>
  159. public bool Average
  160. {
  161. get { return average; }
  162. set
  163. {
  164. if (average == value || !Application.isPlaying) return;
  165. average = value;
  166. if (!average) ResetAverage();
  167. if (!enabled) return;
  168. Refresh();
  169. }
  170. }
  171. #endregion
  172. #region AverageMilliseconds
  173. [Tooltip("Shows time in milliseconds for the average FPS.")]
  174. [SerializeField]
  175. private bool averageMilliseconds;
  176. /// <summary>
  177. /// Shows time in milliseconds for the average FPS.
  178. /// </summary>
  179. public bool AverageMilliseconds
  180. {
  181. get { return averageMilliseconds; }
  182. set
  183. {
  184. if (averageMilliseconds == value || !Application.isPlaying) return;
  185. averageMilliseconds = value;
  186. if (!averageMilliseconds) LastAverageMillisecondsValue = 0f;
  187. if (!enabled) return;
  188. Refresh();
  189. }
  190. }
  191. #endregion
  192. #region AverageNewLine
  193. [Tooltip("Controls placing Average on the new line.")]
  194. [SerializeField]
  195. private bool averageNewLine;
  196. /// <summary>
  197. /// Controls placing Average on the new line.
  198. /// \sa Average
  199. /// </summary>
  200. public bool AverageNewLine
  201. {
  202. get { return averageNewLine; }
  203. set
  204. {
  205. if (averageNewLine == value || !Application.isPlaying) return;
  206. averageNewLine = value;
  207. if (!enabled) return;
  208. Refresh();
  209. }
  210. }
  211. #endregion
  212. #region AverageSamples
  213. [Tooltip("Amount of last samples to get average from. Set 0 to get average from all samples since startup or level load.\n" +
  214. "One Sample recorded per one Interval.")]
  215. [Range(0, 100)]
  216. [SerializeField]
  217. private int averageSamples = 50;
  218. /// <summary>
  219. /// Amount of last samples to get average from. Set 0 to get average from all samples since startup or level load. One Sample recorded per one #UpdateInterval.
  220. /// \sa resetAverageOnNewScene
  221. /// </summary>
  222. public int AverageSamples
  223. {
  224. get { return averageSamples; }
  225. set
  226. {
  227. if (averageSamples == value || !Application.isPlaying) return;
  228. averageSamples = value;
  229. if (!enabled) return;
  230. ResetAverage();
  231. Refresh();
  232. }
  233. }
  234. #endregion
  235. #region MinMax
  236. #if UNITY_EDITOR
  237. [SerializeField]
  238. private bool minMaxFoldout;
  239. #endif
  240. [Tooltip("Shows minimum and maximum FPS readouts since game / scene start, depending on 'Reset On Load' toggle.")]
  241. [SerializeField]
  242. private bool minMax;
  243. /// <summary>
  244. /// Shows minimum and maximum FPS readouts since game / scene start, depending on #resetMinMaxOnNewScene toggle.
  245. /// </summary>
  246. public bool MinMax
  247. {
  248. get { return minMax; }
  249. set
  250. {
  251. if (minMax == value || !Application.isPlaying) return;
  252. minMax = value;
  253. if (!minMax) ResetMinMax();
  254. if (!enabled) return;
  255. Refresh();
  256. }
  257. }
  258. #endregion
  259. #region MinMaxMilliseconds
  260. [Tooltip("Shows time in milliseconds for the Min Max FPS.")]
  261. [SerializeField]
  262. private bool minMaxMilliseconds;
  263. /// <summary>
  264. /// Shows time in milliseconds for the Min Max FPS.
  265. /// </summary>
  266. public bool MinMaxMilliseconds
  267. {
  268. get { return minMaxMilliseconds; }
  269. set
  270. {
  271. if (minMaxMilliseconds == value || !Application.isPlaying) return;
  272. minMaxMilliseconds = value;
  273. if (!minMaxMilliseconds)
  274. {
  275. LastMinMillisecondsValue = 0f;
  276. LastMaxMillisecondsValue = 0f;
  277. }
  278. else
  279. {
  280. LastMinMillisecondsValue = 1000f / LastMinimumValue;
  281. LastMaxMillisecondsValue = 1000f / LastMaximumValue;
  282. }
  283. if (!enabled) return;
  284. Refresh();
  285. }
  286. }
  287. #endregion
  288. #region MinMaxNewLine
  289. [Tooltip("Controls placing Min Max on the new line.")]
  290. [SerializeField]
  291. private bool minMaxNewLine;
  292. /// <summary>
  293. /// Controls placing Min Max on the new line.
  294. /// \sa MinMax
  295. /// </summary>
  296. public bool MinMaxNewLine
  297. {
  298. get { return minMaxNewLine; }
  299. set
  300. {
  301. if (minMaxNewLine == value || !Application.isPlaying) return;
  302. minMaxNewLine = value;
  303. if (!enabled) return;
  304. Refresh();
  305. }
  306. }
  307. #endregion
  308. #region MinMaxTwoLines
  309. [Tooltip("Check to place Min Max on two separate lines. Otherwise they will be placed on a single line.")]
  310. [SerializeField]
  311. private bool minMaxTwoLines;
  312. /// <summary>
  313. /// Check to place Min Max on two separate lines. Otherwise they will be placed on a single line.
  314. /// \sa MinMax
  315. /// </summary>
  316. public bool MinMaxTwoLines
  317. {
  318. get { return minMaxTwoLines; }
  319. set
  320. {
  321. if (minMaxTwoLines == value || !Application.isPlaying) return;
  322. minMaxTwoLines = value;
  323. if (!enabled) return;
  324. Refresh();
  325. }
  326. }
  327. #endregion
  328. #region Render
  329. #if UNITY_EDITOR
  330. [SerializeField]
  331. private bool renderFoldout;
  332. #endif
  333. [Tooltip("Shows time spent on Camera.Render excluding Image Effects. Add AFPSRenderRecorder to the cameras you wish to count.")]
  334. [SerializeField]
  335. private bool render;
  336. /// <summary>
  337. /// Shows approximate time in ms spent on Camera.Render excluding Image Effects and IMGUI.
  338. /// Requires \link CodeStage.AdvancedFPSCounter.Utils.AFPSRenderRecorder AFPSRenderRecorder \endlink added to the cameras you wish to count.
  339. /// </summary>
  340. /// <strong>\htmlonly<font color="7030A0">NOTE:</font>\endhtmlonly It doesn't take into account Image Effects and IMGUI!</strong>
  341. /// \sa LastRenderValue
  342. /// \sa \link CodeStage.AdvancedFPSCounter.Utils.AFPSRenderRecorder AFPSRenderRecorder \endlink
  343. public bool Render
  344. {
  345. get { return render; }
  346. set
  347. {
  348. if (render == value || !Application.isPlaying) return;
  349. render = value;
  350. if (!render)
  351. {
  352. if (renderAutoAdd) TryToRemoveRenderRecorder();
  353. return;
  354. }
  355. previousFrameCount = Time.frameCount;
  356. if (renderAutoAdd) TryToAddRenderRecorder();
  357. Refresh();
  358. }
  359. }
  360. #endregion
  361. #region RenderNewLine
  362. [Tooltip("Controls placing Render on the new line.")]
  363. [SerializeField]
  364. private bool renderNewLine;
  365. /// <summary>
  366. /// Controls placing Render on the new line.
  367. /// \sa Render
  368. /// </summary>
  369. public bool RenderNewLine
  370. {
  371. get { return renderNewLine; }
  372. set
  373. {
  374. if (renderNewLine == value || !Application.isPlaying) return;
  375. renderNewLine = value;
  376. if (!enabled) return;
  377. Refresh();
  378. }
  379. }
  380. #endregion
  381. #region RenderAutoAdd
  382. [Tooltip("Check to automatically add AFPSRenderRecorder to the Main Camera if present.")]
  383. [SerializeField]
  384. private bool renderAutoAdd = true;
  385. /// <summary>
  386. /// Check to let FPS Counter try automatically add AFPSRenderRecorder to the Camera with MainCamera tag.
  387. /// <br/>You're free to add AFPSRenderRecorder to any cameras you wish to count.
  388. /// \sa Render
  389. /// </summary>
  390. public bool RenderAutoAdd
  391. {
  392. get { return renderAutoAdd; }
  393. set
  394. {
  395. if (renderAutoAdd == value || !Application.isPlaying) return;
  396. renderAutoAdd = value;
  397. if (!enabled) return;
  398. TryToAddRenderRecorder();
  399. Refresh();
  400. }
  401. }
  402. #endregion
  403. #region ColorWarning
  404. [Tooltip("Color of the FPS counter while FPS is between Critical and Warning levels.")]
  405. [SerializeField]
  406. private Color colorWarning = new Color32(236, 224, 88, 255);
  407. /// <summary>
  408. /// Color of the FPS counter while FPS is between #criticalLevelValue and #warningLevelValue levels.
  409. /// </summary>
  410. public Color ColorWarning
  411. {
  412. get { return colorWarning; }
  413. set
  414. {
  415. if (colorWarning == value || !Application.isPlaying) return;
  416. colorWarning = value;
  417. if (!enabled) return;
  418. CacheWarningColor();
  419. Refresh();
  420. }
  421. }
  422. #endregion
  423. #region ColorCritical
  424. [Tooltip("Color of the FPS counter while FPS is below Critical level.")]
  425. [SerializeField]
  426. private Color colorCritical = new Color32(249, 91, 91, 255);
  427. /// <summary>
  428. /// Color of the FPS counter while FPS is below #criticalLevelValue.
  429. /// </summary>
  430. public Color ColorCritical
  431. {
  432. get { return colorCritical; }
  433. set
  434. {
  435. if (colorCritical == value || !Application.isPlaying) return;
  436. colorCritical = value;
  437. if (!enabled) return;
  438. CacheCriticalColor();
  439. Refresh();
  440. }
  441. }
  442. #endregion
  443. #region ColorRender
  444. [Tooltip("Color of the Render Time output.")]
  445. [SerializeField]
  446. protected Color colorRender;
  447. /// <summary>
  448. /// Color of the Render Time output.
  449. /// </summary>
  450. public Color ColorRender
  451. {
  452. get { return colorRender; }
  453. set
  454. {
  455. if (colorRender == value || !Application.isPlaying) return;
  456. colorRender = value;
  457. if (!enabled) return;
  458. CacheCurrentColor();
  459. Refresh();
  460. }
  461. }
  462. #endregion
  463. // ----------------------------------------------------------------------------
  464. // properties only accessible from code
  465. // ----------------------------------------------------------------------------
  466. /// <summary>
  467. /// Last realtime FPS value.
  468. /// </summary>
  469. public int LastValue { get; private set; }
  470. /// <summary>
  471. /// Last calculated Milliseconds value.
  472. /// </summary>
  473. public float LastMillisecondsValue { get; private set; }
  474. /// <summary>
  475. /// Last calculated Render Time value.
  476. /// \sa Render
  477. /// </summary>
  478. public float LastRenderValue { get; private set; }
  479. /// <summary>
  480. /// Last calculated Average FPS value.
  481. /// \sa AverageSamples, resetAverageOnNewScene
  482. /// </summary>
  483. public int LastAverageValue { get; private set; }
  484. /// <summary>
  485. /// Last calculated Milliseconds value for Average FPS.
  486. /// </summary>
  487. public float LastAverageMillisecondsValue { get; private set; }
  488. /// <summary>
  489. /// Last minimum FPS value.
  490. /// \sa resetMinMaxOnNewScene
  491. /// </summary>
  492. public int LastMinimumValue { get; private set; }
  493. /// <summary>
  494. /// Last maximum FPS value.
  495. /// \sa resetMinMaxOnNewScene
  496. /// </summary>
  497. public int LastMaximumValue { get; private set; }
  498. /// <summary>
  499. /// Last calculated Milliseconds value for Minimum FPS.
  500. /// \sa resetMinMaxOnNewScene
  501. /// </summary>
  502. public float LastMinMillisecondsValue { get; private set; }
  503. /// <summary>
  504. /// Last calculated Milliseconds value for Maximum FPS.
  505. /// \sa resetMinMaxOnNewScene
  506. /// </summary>
  507. public float LastMaxMillisecondsValue { get; private set; }
  508. /// <summary>
  509. /// Current FPS level.
  510. /// \sa FPSLevel, OnFPSLevelChange
  511. /// </summary>
  512. public FPSLevel CurrentFpsLevel { get; private set; }
  513. // ----------------------------------------------------------------------------
  514. // constructor
  515. // ----------------------------------------------------------------------------
  516. internal FPSCounterData()
  517. {
  518. color = new Color32(85, 218, 102, 255);
  519. colorRender = new Color32(167, 110, 209, 255);
  520. style = FontStyle.Bold;
  521. realtimeFPS = true;
  522. milliseconds = true;
  523. render = false;
  524. renderNewLine = true;
  525. average = true;
  526. averageMilliseconds = true;
  527. averageNewLine = true;
  528. resetAverageOnNewScene = true;
  529. minMax = true;
  530. minMaxNewLine = true;
  531. resetMinMaxOnNewScene = true;
  532. }
  533. // ----------------------------------------------------------------------------
  534. // public methods
  535. // ----------------------------------------------------------------------------
  536. /// <summary>
  537. /// Resets Average FPS counter accumulative data.
  538. /// </summary>
  539. public void ResetAverage()
  540. {
  541. if (!Application.isPlaying) return;
  542. LastAverageValue = LastValue;
  543. LastAverageMillisecondsValue = LastMillisecondsValue;
  544. currentAverageSamples = 0;
  545. currentAverageRaw = LastValue;
  546. }
  547. /// <summary>
  548. /// Resets minimum and maximum FPS readings.
  549. /// </summary>
  550. public void ResetMinMax()
  551. {
  552. if (!Application.isPlaying) return;
  553. LastMinimumValue = -1;
  554. LastMaximumValue = -1;
  555. minMaxIntervalsSkipped = 0;
  556. UpdateValue(true);
  557. dirty = true;
  558. }
  559. // ----------------------------------------------------------------------------
  560. // internal methods
  561. // ----------------------------------------------------------------------------
  562. internal void OnLevelLoadedCallback()
  563. {
  564. if (minMax && resetMinMaxOnNewScene) ResetMinMax();
  565. if (average && resetAverageOnNewScene) ResetAverage();
  566. if (render && renderAutoAdd) TryToAddRenderRecorder();
  567. }
  568. internal void AddRenderTime(float time)
  569. {
  570. if (!enabled || !inited) return;
  571. renderTimeBank += time;
  572. }
  573. internal override void UpdateValue(bool force)
  574. {
  575. if (!enabled) return;
  576. if (!realtimeFPS && !average && !minMax && !render)
  577. {
  578. if (text.Length > 0)
  579. {
  580. text.Length = 0;
  581. }
  582. return;
  583. }
  584. var roundedValue = (int)newValue;
  585. if (LastValue != roundedValue || force)
  586. {
  587. LastValue = roundedValue;
  588. dirty = true;
  589. }
  590. if (LastValue <= criticalLevelValue)
  591. {
  592. if (LastValue != 0 && CurrentFpsLevel != FPSLevel.Critical)
  593. {
  594. CurrentFpsLevel = FPSLevel.Critical;
  595. if (OnFPSLevelChange != null) OnFPSLevelChange(CurrentFpsLevel);
  596. }
  597. }
  598. else if (LastValue < warningLevelValue)
  599. {
  600. if (LastValue != 0 && CurrentFpsLevel != FPSLevel.Warning)
  601. {
  602. CurrentFpsLevel = FPSLevel.Warning;
  603. if (OnFPSLevelChange != null) OnFPSLevelChange(CurrentFpsLevel);
  604. }
  605. }
  606. else
  607. {
  608. if (LastValue != 0 && CurrentFpsLevel != FPSLevel.Normal)
  609. {
  610. CurrentFpsLevel = FPSLevel.Normal;
  611. if (OnFPSLevelChange != null) OnFPSLevelChange(CurrentFpsLevel);
  612. }
  613. }
  614. // since ms calculates from fps we can calculate it when fps changed
  615. if (dirty && milliseconds)
  616. {
  617. LastMillisecondsValue = 1000f / newValue;
  618. }
  619. if (render)
  620. {
  621. if (renderTimeBank > 0)
  622. {
  623. var frameCount = Time.frameCount;
  624. var framesSinceLastUpdate = frameCount - previousFrameCount;
  625. if (framesSinceLastUpdate == 0) framesSinceLastUpdate = 1;
  626. var renderTime = renderTimeBank / framesSinceLastUpdate;
  627. if (Math.Abs(renderTime - LastRenderValue) > 0.0001f || force)
  628. {
  629. LastRenderValue = renderTime;
  630. dirty = true;
  631. }
  632. previousFrameCount = frameCount;
  633. renderTimeBank = 0;
  634. }
  635. }
  636. var currentAverageRounded = -1;
  637. if (average && LastValue > -1)
  638. {
  639. currentAverageSamples++;
  640. if (currentAverageSamples > averageSamples && averageSamples != 0)
  641. {
  642. currentAverageRaw += (LastValue - currentAverageRaw) / (averageSamples + 1);
  643. }
  644. else
  645. {
  646. currentAverageRaw += (LastValue - currentAverageRaw) / currentAverageSamples;
  647. }
  648. currentAverageRounded = Mathf.RoundToInt(currentAverageRaw);
  649. if (LastAverageValue != currentAverageRounded || force)
  650. {
  651. LastAverageValue = currentAverageRounded;
  652. dirty = true;
  653. if (averageMilliseconds)
  654. {
  655. LastAverageMillisecondsValue = 1000f / LastAverageValue;
  656. }
  657. }
  658. }
  659. if (minMax)
  660. {
  661. if (minMaxIntervalsSkipped <= minMaxIntervalsToSkip)
  662. {
  663. if (!force) minMaxIntervalsSkipped++;
  664. }
  665. else if (LastMinimumValue == -1)
  666. {
  667. dirty = true;
  668. }
  669. if (minMaxIntervalsSkipped > minMaxIntervalsToSkip && dirty)
  670. {
  671. if (LastMinimumValue == -1)
  672. {
  673. LastMinimumValue = LastValue;
  674. if (minMaxMilliseconds)
  675. {
  676. LastMinMillisecondsValue = 1000f / LastMinimumValue;
  677. }
  678. }
  679. else if (LastValue < LastMinimumValue)
  680. {
  681. LastMinimumValue = LastValue;
  682. if (minMaxMilliseconds)
  683. {
  684. LastMinMillisecondsValue = 1000f / LastMinimumValue;
  685. }
  686. }
  687. if (LastMaximumValue == -1)
  688. {
  689. LastMaximumValue = LastValue;
  690. if (minMaxMilliseconds)
  691. {
  692. LastMaxMillisecondsValue = 1000f / LastMaximumValue;
  693. }
  694. }
  695. else if (LastValue > LastMaximumValue)
  696. {
  697. LastMaximumValue = LastValue;
  698. if (minMaxMilliseconds)
  699. {
  700. LastMaxMillisecondsValue = 1000f / LastMaximumValue;
  701. }
  702. }
  703. }
  704. }
  705. if (dirty && main.OperationMode == OperationMode.Normal)
  706. {
  707. string coloredStartText;
  708. text.Length = 0;
  709. if (realtimeFPS)
  710. {
  711. if (LastValue >= warningLevelValue || LastValue == -1)
  712. coloredStartText = colorCached;
  713. else if (LastValue <= criticalLevelValue)
  714. coloredStartText = colorCriticalCached;
  715. else
  716. coloredStartText = colorWarningCached;
  717. text.Append(coloredStartText).AppendLookup(LastValue).Append(ColorTextEnd);
  718. if (milliseconds)
  719. {
  720. if (LastValue >= warningLevelValue || LastValue == -1)
  721. coloredStartText = colorCachedMs;
  722. else if (LastValue <= criticalLevelValue)
  723. coloredStartText = colorCriticalCachedMs;
  724. else
  725. coloredStartText = colorWarningCachedMs;
  726. text.Append(coloredStartText).AppendLookup(LastMillisecondsValue).Append(MsTextEnd);
  727. }
  728. }
  729. if (average)
  730. {
  731. if (realtimeFPS)
  732. {
  733. text.Append(averageNewLine ? AFPSCounter.NewLine : AFPSCounter.Space);
  734. }
  735. if (currentAverageRounded >= warningLevelValue || currentAverageRounded == -1)
  736. coloredStartText = colorCachedAvg;
  737. else if (currentAverageRounded <= criticalLevelValue)
  738. coloredStartText = colorCriticalCachedAvg;
  739. else
  740. coloredStartText = colorWarningCachedAvg;
  741. text.Append(coloredStartText).AppendLookup(currentAverageRounded);
  742. if (averageMilliseconds)
  743. {
  744. text.Append(" [").AppendLookup(LastAverageMillisecondsValue).Append(" MS]");
  745. }
  746. text.Append(ColorTextEnd);
  747. }
  748. if (minMax)
  749. {
  750. if (realtimeFPS || average)
  751. {
  752. text.Append(minMaxNewLine ? AFPSCounter.NewLine : AFPSCounter.Space);
  753. }
  754. if (LastMinimumValue >= warningLevelValue || LastMinimumValue == -1)
  755. coloredStartText = colorCachedMin;
  756. else if (LastMinimumValue <= criticalLevelValue)
  757. coloredStartText = colorCriticalCachedMin;
  758. else
  759. coloredStartText = colorWarningCachedMin;
  760. text.Append(coloredStartText).AppendLookup(LastMinimumValue);
  761. if (minMaxMilliseconds)
  762. {
  763. text.Append(" [").AppendLookup(LastMinMillisecondsValue).Append(" MS]");
  764. }
  765. text.Append(ColorTextEnd);
  766. text.Append(minMaxTwoLines ? AFPSCounter.NewLine : AFPSCounter.Space);
  767. if (LastMaximumValue >= warningLevelValue || LastMaximumValue == -1)
  768. coloredStartText = colorCachedMax;
  769. else if (LastMaximumValue <= criticalLevelValue)
  770. coloredStartText = colorCriticalCachedMax;
  771. else
  772. coloredStartText = colorWarningCachedMax;
  773. text.Append(coloredStartText).AppendLookup(LastMaximumValue);
  774. if (minMaxMilliseconds)
  775. {
  776. text.Append(" [").AppendLookup(LastMaxMillisecondsValue).Append(" MS]");
  777. }
  778. text.Append(ColorTextEnd);
  779. }
  780. if (render)
  781. {
  782. if (realtimeFPS || average || minMax)
  783. {
  784. text.Append(renderNewLine ? AFPSCounter.NewLine : AFPSCounter.Space);
  785. }
  786. text.Append(colorCachedRender).
  787. AppendLookup(LastRenderValue).Append(" MS").
  788. Append(ColorTextEnd);
  789. }
  790. ApplyTextStyles();
  791. }
  792. }
  793. // ----------------------------------------------------------------------------
  794. // protected methods
  795. // ----------------------------------------------------------------------------
  796. protected override void PerformActivationActions()
  797. {
  798. base.PerformActivationActions();
  799. LastValue = -1;
  800. LastMinimumValue = -1;
  801. LastMaximumValue = -1;
  802. LastAverageValue = -1;
  803. LastAverageMillisecondsValue = -1;
  804. if (render)
  805. {
  806. previousFrameCount = Time.frameCount;
  807. if (renderAutoAdd)
  808. {
  809. TryToAddRenderRecorder();
  810. }
  811. }
  812. if (main.OperationMode == OperationMode.Normal)
  813. {
  814. if (colorWarningCached == null)
  815. {
  816. CacheWarningColor();
  817. }
  818. if (colorCriticalCached == null)
  819. {
  820. CacheCriticalColor();
  821. }
  822. dirty = true;
  823. UpdateValue();
  824. }
  825. }
  826. protected override void PerformDeActivationActions()
  827. {
  828. base.PerformDeActivationActions();
  829. ResetMinMax();
  830. ResetAverage();
  831. LastValue = 0;
  832. CurrentFpsLevel = FPSLevel.Normal;
  833. }
  834. protected override IEnumerator UpdateCounter()
  835. {
  836. while (true)
  837. {
  838. var previousUpdateTime = Time.unscaledTime;
  839. var previousUpdateFrames = Time.frameCount;
  840. while (Time.unscaledTime < previousUpdateTime + updateInterval)
  841. {
  842. yield return null;
  843. }
  844. var timeElapsed = Time.unscaledTime - previousUpdateTime;
  845. var framesChanged = Time.frameCount - previousUpdateFrames;
  846. newValue = framesChanged / timeElapsed;
  847. UpdateValue(false);
  848. main.UpdateTexts();
  849. }
  850. }
  851. protected override bool HasData()
  852. {
  853. return true;
  854. }
  855. protected override void CacheCurrentColor()
  856. {
  857. var colorString = AFPSCounter.Color32ToHex(color);
  858. colorCached = string.Format(FPSTextStart, colorString);
  859. colorCachedMs = string.Format(MsTextStart, colorString);
  860. colorCachedMin = string.Format(MinTextStart, colorString);
  861. colorCachedMax = string.Format(MaxTextStart, colorString);
  862. colorCachedAvg = string.Format(AvgTextStart, colorString);
  863. var colorRenderString = AFPSCounter.Color32ToHex(colorRender);
  864. colorCachedRender = string.Format(RenderTextStart, colorRenderString);
  865. }
  866. protected void CacheWarningColor()
  867. {
  868. var colorString = AFPSCounter.Color32ToHex(colorWarning);
  869. colorWarningCached = string.Format(FPSTextStart, colorString);
  870. colorWarningCachedMs = string.Format(MsTextStart, colorString);
  871. colorWarningCachedMin = string.Format(MinTextStart, colorString);
  872. colorWarningCachedMax = string.Format(MaxTextStart, colorString);
  873. colorWarningCachedAvg = string.Format(AvgTextStart, colorString);
  874. }
  875. protected void CacheCriticalColor()
  876. {
  877. var colorString = AFPSCounter.Color32ToHex(colorCritical);
  878. colorCriticalCached = string.Format(FPSTextStart, colorString);
  879. colorCriticalCachedMs = string.Format(MsTextStart, colorString);
  880. colorCriticalCachedMin = string.Format(MinTextStart, colorString);
  881. colorCriticalCachedMax = string.Format(MaxTextStart, colorString);
  882. colorCriticalCachedAvg = string.Format(AvgTextStart, colorString);
  883. }
  884. // ----------------------------------------------------------------------------
  885. // private methods
  886. // ----------------------------------------------------------------------------
  887. private static void TryToAddRenderRecorder()
  888. {
  889. var mainCamera = Camera.main;
  890. if (mainCamera == null) return;
  891. if (mainCamera.GetComponent<AFPSRenderRecorder>() == null)
  892. {
  893. mainCamera.gameObject.AddComponent<AFPSRenderRecorder>();
  894. }
  895. }
  896. private static void TryToRemoveRenderRecorder()
  897. {
  898. var mainCamera = Camera.main;
  899. if (mainCamera == null) return;
  900. var recorder = mainCamera.GetComponent<AFPSRenderRecorder>();
  901. if (recorder != null)
  902. {
  903. Object.Destroy(recorder);
  904. }
  905. }
  906. }
  907. }