Mirror of Svelto.ECS because we're a fan of it
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.

EngineProfilerInspector.cs 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEditor;
  4. using UnityEngine;
  5. //This profiler is based on the Entitas Visual Debugging tool
  6. //https://github.com/sschmid/Entitas-CSharp
  7. namespace Svelto.ECS.Profiler
  8. {
  9. [CustomEditor(typeof (EngineProfilerBehaviour))]
  10. public class EngineProfilerInspector : Editor
  11. {
  12. enum SORTING_OPTIONS
  13. {
  14. AVERAGE,
  15. MIN,
  16. MAX,
  17. NAME,
  18. NONE
  19. }
  20. static bool _hideEmptyEngines = true;
  21. static bool _showTickEngines;
  22. static bool _showAddEngines;
  23. static bool _showRemoveEngines;
  24. static string _systemNameSearchTerm = string.Empty;
  25. float _axisUpperBounds = 2f;
  26. string updateTitle = "Update".PadRight(15, ' ');
  27. string lateUpdateTitle = "Late".PadRight(13, ' ');
  28. string fixedupdateTitle = "Fixed".PadRight(15, ' ');
  29. string minTitle = "Min".PadRight(15, ' ');
  30. string maxTitle = "Max".PadRight(15, ' ');
  31. string avgTitle = "Avg".PadRight(15, ' ');
  32. EnginesMonitor _enginesMonitor;
  33. Queue<float> _engineMonitorData;
  34. const int SYSTEM_MONITOR_DATA_LENGTH = 300;
  35. SORTING_OPTIONS _sortingOption = SORTING_OPTIONS.AVERAGE;
  36. public override void OnInspectorGUI()
  37. {
  38. var engineProfilerBehaviour = (EngineProfilerBehaviour) target;
  39. EngineInfo[] engines = new EngineInfo[engineProfilerBehaviour.engines.Count];
  40. engineProfilerBehaviour.engines.CopyTo(engines, 0);
  41. DrawEnginesMonitor(engines);
  42. DrawEngineList(engineProfilerBehaviour, engines);
  43. EditorUtility.SetDirty(target);
  44. }
  45. void DrawEngineList(EngineProfilerBehaviour engineProfilerBehaviour, EngineInfo[] engines)
  46. {
  47. ProfilerEditorLayout.BeginVerticalBox();
  48. {
  49. ProfilerEditorLayout.BeginHorizontal();
  50. {
  51. if (GUILayout.Button("Reset Durations", GUILayout.Width(120), GUILayout.Height(14)))
  52. {
  53. engineProfilerBehaviour.ResetDurations();
  54. }
  55. }
  56. ProfilerEditorLayout.EndHorizontal();
  57. _sortingOption = (SORTING_OPTIONS) EditorGUILayout.EnumPopup("Sort By:", _sortingOption);
  58. _hideEmptyEngines = EditorGUILayout.Toggle("Hide empty systems", _hideEmptyEngines);
  59. EditorGUILayout.Space();
  60. ProfilerEditorLayout.BeginHorizontal();
  61. {
  62. _systemNameSearchTerm = EditorGUILayout.TextField("Search", _systemNameSearchTerm);
  63. const string clearButtonControlName = "Clear Button";
  64. GUI.SetNextControlName(clearButtonControlName);
  65. if (GUILayout.Button("x", GUILayout.Width(19), GUILayout.Height(14)))
  66. {
  67. _systemNameSearchTerm = string.Empty;
  68. GUI.FocusControl(clearButtonControlName);
  69. }
  70. }
  71. ProfilerEditorLayout.EndHorizontal();
  72. _showTickEngines = EditorGUILayout.Foldout(_showTickEngines, "Engines Ticks");
  73. if (_showTickEngines && ShouldShowSystems(engines))
  74. {
  75. ProfilerEditorLayout.BeginVerticalBox();
  76. {
  77. var systemsDrawn = DrawUpdateEngineInfos(engines);
  78. if (systemsDrawn == 0)
  79. {
  80. EditorGUILayout.LabelField(string.Empty);
  81. }
  82. }
  83. ProfilerEditorLayout.EndVertical();
  84. }
  85. _showAddEngines = EditorGUILayout.Foldout(_showAddEngines, "Engines Add");
  86. if (_showAddEngines && ShouldShowSystems(engines))
  87. {
  88. ProfilerEditorLayout.BeginVerticalBox();
  89. {
  90. var systemsDrawn = DrawAddEngineInfos(engines);
  91. if (systemsDrawn == 0)
  92. {
  93. EditorGUILayout.LabelField(string.Empty);
  94. }
  95. }
  96. ProfilerEditorLayout.EndVertical();
  97. }
  98. _showRemoveEngines = EditorGUILayout.Foldout(_showRemoveEngines, "Engines Remove");
  99. if (_showRemoveEngines && ShouldShowSystems(engines))
  100. {
  101. ProfilerEditorLayout.BeginVerticalBox();
  102. {
  103. var systemsDrawn = DrawRemoveEngineInfos(engines);
  104. if (systemsDrawn == 0)
  105. {
  106. EditorGUILayout.LabelField(string.Empty);
  107. }
  108. }
  109. ProfilerEditorLayout.EndVertical();
  110. }
  111. }
  112. ProfilerEditorLayout.EndVertical();
  113. }
  114. void DrawEnginesMonitor(EngineInfo[] engines)
  115. {
  116. if (_enginesMonitor == null)
  117. {
  118. _enginesMonitor = new EnginesMonitor(SYSTEM_MONITOR_DATA_LENGTH);
  119. _engineMonitorData = new Queue<float>(new float[SYSTEM_MONITOR_DATA_LENGTH]);
  120. if (EditorApplication.update != Repaint)
  121. {
  122. EditorApplication.update += Repaint;
  123. }
  124. }
  125. double totalDuration = 0;
  126. for (int i = 0; i < engines.Length; i++)
  127. {
  128. totalDuration += engines[i].lastUpdateDuration;
  129. }
  130. ProfilerEditorLayout.BeginVerticalBox();
  131. {
  132. EditorGUILayout.LabelField("Execution duration", EditorStyles.boldLabel);
  133. ProfilerEditorLayout.BeginHorizontal();
  134. {
  135. EditorGUILayout.LabelField("Total", totalDuration.ToString());
  136. }
  137. ProfilerEditorLayout.EndHorizontal();
  138. ProfilerEditorLayout.BeginHorizontal();
  139. {
  140. _axisUpperBounds = EditorGUILayout.FloatField("Axis Upper Bounds", _axisUpperBounds);
  141. }
  142. ProfilerEditorLayout.EndHorizontal();
  143. if (!EditorApplication.isPaused)
  144. {
  145. if (_engineMonitorData.Count >= SYSTEM_MONITOR_DATA_LENGTH)
  146. {
  147. _engineMonitorData.Dequeue();
  148. }
  149. _engineMonitorData.Enqueue((float) totalDuration);
  150. }
  151. _enginesMonitor.Draw(_engineMonitorData.ToArray(), 80f, _axisUpperBounds);
  152. }
  153. ProfilerEditorLayout.EndVertical();
  154. }
  155. int DrawUpdateEngineInfos(EngineInfo[] engines)
  156. {
  157. if (_sortingOption != SORTING_OPTIONS.NONE)
  158. {
  159. SortUpdateEngines(engines);
  160. }
  161. string title =
  162. updateTitle.FastConcat(lateUpdateTitle)
  163. .FastConcat(fixedupdateTitle)
  164. .FastConcat(minTitle)
  165. .FastConcat(maxTitle);
  166. EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel);
  167. int enginesDrawn = 0;
  168. for (int i = 0; i < engines.Length; i++)
  169. {
  170. EngineInfo engineInfo = engines[i];
  171. if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower()))
  172. {
  173. ProfilerEditorLayout.BeginHorizontal();
  174. {
  175. var avg = string.Format("{0:0.000}", engineInfo.averageUpdateDuration).PadRight(15);
  176. var avgLate = string.Format("{0:0.000}", engineInfo.averageLateUpdateDuration).PadRight(15);
  177. var avgFixed = string.Format("{0:0.000}", engineInfo.averageFixedUpdateDuration).PadRight(15);
  178. var min = string.Format("{0:0.000}", engineInfo.minUpdateDuration).PadRight(15);
  179. var max = string.Format("{0:0.000}", engineInfo.maxUpdateDuration);
  180. string output = avg.FastConcat(avgLate).FastConcat(avgFixed).FastConcat(min).FastConcat(max);
  181. EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle());
  182. }
  183. ProfilerEditorLayout.EndHorizontal();
  184. enginesDrawn += 1;
  185. }
  186. }
  187. return enginesDrawn;
  188. }
  189. int DrawAddEngineInfos(EngineInfo[] engines)
  190. {
  191. if (_sortingOption != SORTING_OPTIONS.NONE)
  192. {
  193. SortAddEngines(engines);
  194. }
  195. string title = avgTitle.FastConcat(minTitle).FastConcat(maxTitle);
  196. EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel);
  197. int enginesDrawn = 0;
  198. for (int i = 0; i < engines.Length; i++)
  199. {
  200. EngineInfo engineInfo = engines[i];
  201. if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower()) &&
  202. !engineInfo.minAddDuration.Equals(0) && !engineInfo.maxAddDuration.Equals(0))
  203. {
  204. ProfilerEditorLayout.BeginHorizontal();
  205. {
  206. var avg = string.Format("{0:0.000}", engineInfo.averageAddDuration).PadRight(15);
  207. var min = string.Format("{0:0.000}", engineInfo.minAddDuration).PadRight(15);
  208. var max = string.Format("{0:0.000}", engineInfo.maxAddDuration);
  209. string output = avg.FastConcat(min).FastConcat(max);
  210. EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle());
  211. }
  212. ProfilerEditorLayout.EndHorizontal();
  213. enginesDrawn += 1;
  214. }
  215. }
  216. return enginesDrawn;
  217. }
  218. int DrawRemoveEngineInfos(EngineInfo[] engines)
  219. {
  220. if (_sortingOption != SORTING_OPTIONS.NONE)
  221. {
  222. SortRemoveEngines(engines);
  223. }
  224. string title = avgTitle.FastConcat(minTitle).FastConcat(maxTitle);
  225. EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel);
  226. int enginesDrawn = 0;
  227. for (int i = 0; i < engines.Length; i++)
  228. {
  229. EngineInfo engineInfo = engines[i];
  230. if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower()) &&
  231. !engineInfo.minRemoveDuration.Equals(0) && !engineInfo.maxRemoveDuration.Equals(0))
  232. {
  233. ProfilerEditorLayout.BeginHorizontal();
  234. {
  235. var avg = string.Format("{0:0.000}", engineInfo.averageRemoveDuration).PadRight(15);
  236. var min = string.Format("{0:0.000}", engineInfo.minRemoveDuration).PadRight(15);
  237. var max = string.Format("{0:0.000}", engineInfo.maxRemoveDuration);
  238. string output = avg.FastConcat(min).FastConcat(max);
  239. EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle());
  240. }
  241. ProfilerEditorLayout.EndHorizontal();
  242. enginesDrawn += 1;
  243. }
  244. }
  245. return enginesDrawn;
  246. }
  247. static GUIStyle GetEngineStyle()
  248. {
  249. var style = new GUIStyle(GUI.skin.label);
  250. var color = EditorGUIUtility.isProSkin ? Color.white : style.normal.textColor;
  251. style.normal.textColor = color;
  252. return style;
  253. }
  254. static bool ShouldShowSystems(EngineInfo[] engines)
  255. {
  256. return engines.Length > 0;
  257. }
  258. #region Sorting Engines
  259. void SortUpdateEngines(EngineInfo[] engines)
  260. {
  261. switch (_sortingOption)
  262. {
  263. case SORTING_OPTIONS.AVERAGE:
  264. Array.Sort(engines,
  265. (engine1, engine2) => engine2.averageUpdateDuration.CompareTo(engine1.averageUpdateDuration));
  266. break;
  267. case SORTING_OPTIONS.MIN:
  268. Array.Sort(engines,
  269. (engine1, engine2) => engine2.minUpdateDuration.CompareTo(engine1.minUpdateDuration));
  270. break;
  271. case SORTING_OPTIONS.MAX:
  272. Array.Sort(engines,
  273. (engine1, engine2) => engine2.maxUpdateDuration.CompareTo(engine1.maxUpdateDuration));
  274. break;
  275. case SORTING_OPTIONS.NAME:
  276. Array.Sort(engines, StringComparer.InvariantCulture);
  277. break;
  278. }
  279. }
  280. void SortAddEngines(EngineInfo[] engines)
  281. {
  282. switch (_sortingOption)
  283. {
  284. case SORTING_OPTIONS.AVERAGE:
  285. Array.Sort(engines,
  286. (engine1, engine2) => engine2.averageAddDuration.CompareTo(engine1.averageAddDuration));
  287. break;
  288. case SORTING_OPTIONS.MIN:
  289. Array.Sort(engines,
  290. (engine1, engine2) => engine2.minAddDuration.CompareTo(engine1.minAddDuration));
  291. break;
  292. case SORTING_OPTIONS.MAX:
  293. Array.Sort(engines,
  294. (engine1, engine2) => engine2.maxAddDuration.CompareTo(engine1.maxAddDuration));
  295. break;
  296. case SORTING_OPTIONS.NAME:
  297. Array.Sort(engines, StringComparer.InvariantCulture);
  298. break;
  299. }
  300. }
  301. void SortRemoveEngines(EngineInfo[] engines)
  302. {
  303. switch (_sortingOption)
  304. {
  305. case SORTING_OPTIONS.AVERAGE:
  306. Array.Sort(engines,
  307. (engine1, engine2) => engine2.averageRemoveDuration.CompareTo(engine1.averageRemoveDuration));
  308. break;
  309. case SORTING_OPTIONS.MIN:
  310. Array.Sort(engines,
  311. (engine1, engine2) => engine2.minRemoveDuration.CompareTo(engine1.minRemoveDuration));
  312. break;
  313. case SORTING_OPTIONS.MAX:
  314. Array.Sort(engines,
  315. (engine1, engine2) => engine2.maxRemoveDuration.CompareTo(engine1.maxRemoveDuration));
  316. break;
  317. case SORTING_OPTIONS.NAME:
  318. Array.Sort(engines, StringComparer.InvariantCulture);
  319. break;
  320. }
  321. }
  322. }
  323. #endregion
  324. }