AstarDebugger.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. //#define ProfileAstar
  2. using UnityEngine;
  3. using System.Text;
  4. namespace Pathfinding {
  5. [AddComponentMenu("Pathfinding/Pathfinding Debugger")]
  6. [ExecuteInEditMode]
  7. /// <summary>
  8. /// Debugger for the A* Pathfinding Project.
  9. /// This class can be used to profile different parts of the pathfinding system
  10. /// and the whole game as well to some extent.
  11. ///
  12. /// Clarification of the labels shown when enabled.
  13. /// All memory related things profiles <b>the whole game</b> not just the A* Pathfinding System.
  14. /// - Currently allocated: memory the GC (garbage collector) says the application has allocated right now.
  15. /// - Peak allocated: maximum measured value of the above.
  16. /// - Last collect peak: the last peak of 'currently allocated'.
  17. /// - Allocation rate: how much the 'currently allocated' value increases per second. This value is not as reliable as you can think
  18. /// it is often very random probably depending on how the GC thinks this application is using memory.
  19. /// - Collection frequency: how often the GC is called. Again, the GC might decide it is better with many small collections
  20. /// or with a few large collections. So you cannot really trust this variable much.
  21. /// - Last collect fps: FPS during the last garbage collection, the GC will lower the fps a lot.
  22. ///
  23. /// - FPS: current FPS (not updated every frame for readability)
  24. /// - Lowest FPS (last x): As the label says, the lowest fps of the last x frames.
  25. ///
  26. /// - Size: Size of the path pool.
  27. /// - Total created: Number of paths of that type which has been created. Pooled paths are not counted twice.
  28. /// If this value just keeps on growing and growing without an apparent stop, you are are either not pooling any paths
  29. /// or you have missed to pool some path somewhere in your code.
  30. ///
  31. /// See: pooling
  32. ///
  33. /// TODO: Add field showing how many graph updates are being done right now
  34. /// </summary>
  35. [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_astar_debugger.php")]
  36. public class AstarDebugger : VersionedMonoBehaviour {
  37. public int yOffset = 5;
  38. public bool show = true;
  39. public bool showInEditor = false;
  40. public bool showFPS = false;
  41. public bool showPathProfile = false;
  42. public bool showMemProfile = false;
  43. public bool showGraph = false;
  44. public int graphBufferSize = 200;
  45. /// <summary>
  46. /// Font to use.
  47. /// A monospaced font is the best
  48. /// </summary>
  49. public Font font = null;
  50. public int fontSize = 12;
  51. StringBuilder text = new StringBuilder();
  52. string cachedText;
  53. float lastUpdate = -999;
  54. private GraphPoint[] graph;
  55. struct GraphPoint {
  56. public float fps, memory;
  57. public bool collectEvent;
  58. }
  59. private float delayedDeltaTime = 1;
  60. private float lastCollect = 0;
  61. private float lastCollectNum = 0;
  62. private float delta = 0;
  63. private float lastDeltaTime = 0;
  64. private int allocRate = 0;
  65. private int lastAllocMemory = 0;
  66. private float lastAllocSet = -9999;
  67. private int allocMem = 0;
  68. private int collectAlloc = 0;
  69. private int peakAlloc = 0;
  70. private int fpsDropCounterSize = 200;
  71. private float[] fpsDrops;
  72. private Rect boxRect;
  73. private GUIStyle style;
  74. private Camera cam;
  75. float graphWidth = 100;
  76. float graphHeight = 100;
  77. float graphOffset = 50;
  78. public void Start () {
  79. useGUILayout = false;
  80. fpsDrops = new float[fpsDropCounterSize];
  81. cam = GetComponent<Camera>();
  82. if (cam == null) {
  83. cam = Camera.main;
  84. }
  85. graph = new GraphPoint[graphBufferSize];
  86. if (Time.unscaledDeltaTime > 0) {
  87. for (int i = 0; i < fpsDrops.Length; i++) {
  88. fpsDrops[i] = 1F / Time.unscaledDeltaTime;
  89. }
  90. }
  91. }
  92. int maxVecPool = 0;
  93. int maxNodePool = 0;
  94. PathTypeDebug[] debugTypes = new PathTypeDebug[] {
  95. new PathTypeDebug("ABPath", () => PathPool.GetSize(typeof(ABPath)), () => PathPool.GetTotalCreated(typeof(ABPath)))
  96. ,
  97. new PathTypeDebug("MultiTargetPath", () => PathPool.GetSize(typeof(MultiTargetPath)), () => PathPool.GetTotalCreated(typeof(MultiTargetPath))),
  98. new PathTypeDebug("RandomPath", () => PathPool.GetSize(typeof(RandomPath)), () => PathPool.GetTotalCreated(typeof(RandomPath))),
  99. new PathTypeDebug("FleePath", () => PathPool.GetSize(typeof(FleePath)), () => PathPool.GetTotalCreated(typeof(FleePath))),
  100. new PathTypeDebug("ConstantPath", () => PathPool.GetSize(typeof(ConstantPath)), () => PathPool.GetTotalCreated(typeof(ConstantPath))),
  101. new PathTypeDebug("FloodPath", () => PathPool.GetSize(typeof(FloodPath)), () => PathPool.GetTotalCreated(typeof(FloodPath))),
  102. new PathTypeDebug("FloodPathTracer", () => PathPool.GetSize(typeof(FloodPathTracer)), () => PathPool.GetTotalCreated(typeof(FloodPathTracer)))
  103. };
  104. struct PathTypeDebug {
  105. string name;
  106. System.Func<int> getSize;
  107. System.Func<int> getTotalCreated;
  108. public PathTypeDebug (string name, System.Func<int> getSize, System.Func<int> getTotalCreated) {
  109. this.name = name;
  110. this.getSize = getSize;
  111. this.getTotalCreated = getTotalCreated;
  112. }
  113. public void Print (StringBuilder text) {
  114. int totCreated = getTotalCreated();
  115. if (totCreated > 0) {
  116. text.Append("\n").Append((" " + name).PadRight(25)).Append(getSize()).Append("/").Append(totCreated);
  117. }
  118. }
  119. }
  120. public void LateUpdate () {
  121. if (!show || (!Application.isPlaying && !showInEditor)) return;
  122. if (Time.unscaledDeltaTime <= 0.0001f)
  123. return;
  124. int collCount = System.GC.CollectionCount(0);
  125. if (lastCollectNum != collCount) {
  126. lastCollectNum = collCount;
  127. delta = Time.realtimeSinceStartup-lastCollect;
  128. lastCollect = Time.realtimeSinceStartup;
  129. lastDeltaTime = Time.unscaledDeltaTime;
  130. collectAlloc = allocMem;
  131. }
  132. allocMem = (int)System.GC.GetTotalMemory(false);
  133. bool collectEvent = allocMem < peakAlloc;
  134. peakAlloc = !collectEvent ? allocMem : peakAlloc;
  135. if (Time.realtimeSinceStartup - lastAllocSet > 0.3F || !Application.isPlaying) {
  136. int diff = allocMem - lastAllocMemory;
  137. lastAllocMemory = allocMem;
  138. lastAllocSet = Time.realtimeSinceStartup;
  139. delayedDeltaTime = Time.unscaledDeltaTime;
  140. if (diff >= 0) {
  141. allocRate = diff;
  142. }
  143. }
  144. if (Application.isPlaying) {
  145. fpsDrops[Time.frameCount % fpsDrops.Length] = Time.unscaledDeltaTime > 0.00001f ? 1F / Time.unscaledDeltaTime : 0;
  146. int graphIndex = Time.frameCount % graph.Length;
  147. graph[graphIndex].fps = Time.unscaledDeltaTime < 0.00001f ? 1F / Time.unscaledDeltaTime : 0;
  148. graph[graphIndex].collectEvent = collectEvent;
  149. graph[graphIndex].memory = allocMem;
  150. }
  151. if (Application.isPlaying && cam != null && showGraph) {
  152. graphWidth = cam.pixelWidth*0.8f;
  153. float minMem = float.PositiveInfinity, maxMem = 0, minFPS = float.PositiveInfinity, maxFPS = 0;
  154. for (int i = 0; i < graph.Length; i++) {
  155. minMem = Mathf.Min(graph[i].memory, minMem);
  156. maxMem = Mathf.Max(graph[i].memory, maxMem);
  157. minFPS = Mathf.Min(graph[i].fps, minFPS);
  158. maxFPS = Mathf.Max(graph[i].fps, maxFPS);
  159. }
  160. int currentGraphIndex = Time.frameCount % graph.Length;
  161. Matrix4x4 m = Matrix4x4.TRS(new Vector3((cam.pixelWidth - graphWidth)/2f, graphOffset, 1), Quaternion.identity, new Vector3(graphWidth, graphHeight, 1));
  162. for (int i = 0; i < graph.Length-1; i++) {
  163. if (i == currentGraphIndex) continue;
  164. DrawGraphLine(i, m, i/(float)graph.Length, (i+1)/(float)graph.Length, Mathf.InverseLerp(minMem, maxMem, graph[i].memory), Mathf.InverseLerp(minMem, maxMem, graph[i+1].memory), Color.blue);
  165. DrawGraphLine(i, m, i/(float)graph.Length, (i+1)/(float)graph.Length, Mathf.InverseLerp(minFPS, maxFPS, graph[i].fps), Mathf.InverseLerp(minFPS, maxFPS, graph[i+1].fps), Color.green);
  166. }
  167. }
  168. }
  169. void DrawGraphLine (int index, Matrix4x4 m, float x1, float x2, float y1, float y2, Color color) {
  170. Debug.DrawLine(cam.ScreenToWorldPoint(m.MultiplyPoint3x4(new Vector3(x1, y1))), cam.ScreenToWorldPoint(m.MultiplyPoint3x4(new Vector3(x2, y2))), color);
  171. }
  172. public void OnGUI () {
  173. if (!show || (!Application.isPlaying && !showInEditor)) return;
  174. if (style == null) {
  175. style = new GUIStyle();
  176. style.normal.textColor = Color.white;
  177. style.padding = new RectOffset(5, 5, 5, 5);
  178. }
  179. if (Time.realtimeSinceStartup - lastUpdate > 0.5f || cachedText == null || !Application.isPlaying) {
  180. lastUpdate = Time.realtimeSinceStartup;
  181. boxRect = new Rect(5, yOffset, 310, 40);
  182. text.Length = 0;
  183. text.AppendLine("A* Pathfinding Project Debugger");
  184. text.Append("A* Version: ").Append(AstarPath.Version.ToString());
  185. if (showMemProfile) {
  186. boxRect.height += 200;
  187. text.AppendLine();
  188. text.AppendLine();
  189. text.Append("Currently allocated".PadRight(25));
  190. text.Append((allocMem/1000000F).ToString("0.0 MB"));
  191. text.AppendLine();
  192. text.Append("Peak allocated".PadRight(25));
  193. text.Append((peakAlloc/1000000F).ToString("0.0 MB")).AppendLine();
  194. text.Append("Last collect peak".PadRight(25));
  195. text.Append((collectAlloc/1000000F).ToString("0.0 MB")).AppendLine();
  196. text.Append("Allocation rate".PadRight(25));
  197. text.Append((allocRate/1000000F).ToString("0.0 MB")).AppendLine();
  198. text.Append("Collection frequency".PadRight(25));
  199. text.Append(delta.ToString("0.00"));
  200. text.Append("s\n");
  201. text.Append("Last collect fps".PadRight(25));
  202. text.Append((1F/lastDeltaTime).ToString("0.0 fps"));
  203. text.Append(" (");
  204. text.Append(lastDeltaTime.ToString("0.000 s"));
  205. text.Append(")");
  206. }
  207. if (showFPS) {
  208. text.AppendLine();
  209. text.AppendLine();
  210. var delayedFPS = delayedDeltaTime > 0.00001f ? 1F/delayedDeltaTime : 0;
  211. text.Append("FPS".PadRight(25)).Append(delayedFPS.ToString("0.0 fps"));
  212. float minFps = Mathf.Infinity;
  213. for (int i = 0; i < fpsDrops.Length; i++) if (fpsDrops[i] < minFps) minFps = fpsDrops[i];
  214. text.AppendLine();
  215. text.Append(("Lowest fps (last " + fpsDrops.Length + ")").PadRight(25)).Append(minFps.ToString("0.0"));
  216. }
  217. if (showPathProfile) {
  218. AstarPath astar = AstarPath.active;
  219. text.AppendLine();
  220. if (astar == null) {
  221. text.Append("\nNo AstarPath Object In The Scene");
  222. } else {
  223. #if ProfileAstar
  224. double searchSpeed = (double)AstarPath.TotalSearchedNodes*10000 / (double)AstarPath.TotalSearchTime;
  225. text.Append("\nSearch Speed (nodes/ms) ").Append(searchSpeed.ToString("0")).Append(" ("+AstarPath.TotalSearchedNodes+" / ").Append(((double)AstarPath.TotalSearchTime/10000F).ToString("0")+")");
  226. #endif
  227. if (Pathfinding.Util.ListPool<Vector3>.GetSize() > maxVecPool) maxVecPool = Pathfinding.Util.ListPool<Vector3>.GetSize();
  228. if (Pathfinding.Util.ListPool<Pathfinding.GraphNode>.GetSize() > maxNodePool) maxNodePool = Pathfinding.Util.ListPool<Pathfinding.GraphNode>.GetSize();
  229. text.Append("\nPool Sizes (size/total created)");
  230. for (int i = 0; i < debugTypes.Length; i++) {
  231. debugTypes[i].Print(text);
  232. }
  233. }
  234. }
  235. cachedText = text.ToString();
  236. }
  237. if (font != null) {
  238. style.font = font;
  239. style.fontSize = fontSize;
  240. }
  241. boxRect.height = style.CalcHeight(new GUIContent(cachedText), boxRect.width);
  242. GUI.Box(boxRect, "");
  243. GUI.Label(boxRect, cachedText, style);
  244. if (showGraph) {
  245. float minMem = float.PositiveInfinity, maxMem = 0, minFPS = float.PositiveInfinity, maxFPS = 0;
  246. for (int i = 0; i < graph.Length; i++) {
  247. minMem = Mathf.Min(graph[i].memory, minMem);
  248. maxMem = Mathf.Max(graph[i].memory, maxMem);
  249. minFPS = Mathf.Min(graph[i].fps, minFPS);
  250. maxFPS = Mathf.Max(graph[i].fps, maxFPS);
  251. }
  252. float line;
  253. GUI.color = Color.blue;
  254. // Round to nearest x.x MB
  255. line = Mathf.RoundToInt(maxMem/(100.0f*1000));
  256. GUI.Label(new Rect(5, Screen.height - AstarMath.MapTo(minMem, maxMem, 0 + graphOffset, graphHeight + graphOffset, line*1000*100) - 10, 100, 20), (line/10.0f).ToString("0.0 MB"));
  257. line = Mathf.Round(minMem/(100.0f*1000));
  258. GUI.Label(new Rect(5, Screen.height - AstarMath.MapTo(minMem, maxMem, 0 + graphOffset, graphHeight + graphOffset, line*1000*100) - 10, 100, 20), (line/10.0f).ToString("0.0 MB"));
  259. GUI.color = Color.green;
  260. // Round to nearest x.x MB
  261. line = Mathf.Round(maxFPS);
  262. GUI.Label(new Rect(55, Screen.height - AstarMath.MapTo(minFPS, maxFPS, 0 + graphOffset, graphHeight + graphOffset, line) - 10, 100, 20), line.ToString("0 FPS"));
  263. line = Mathf.Round(minFPS);
  264. GUI.Label(new Rect(55, Screen.height - AstarMath.MapTo(minFPS, maxFPS, 0 + graphOffset, graphHeight + graphOffset, line) - 10, 100, 20), line.ToString("0 FPS"));
  265. }
  266. }
  267. }
  268. }