DebugManager.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using UnityEngine.Assertions;
  5. using UnityEngine.Rendering.UI;
  6. namespace UnityEngine.Rendering
  7. {
  8. using UnityObject = UnityEngine.Object;
  9. /// <summary>
  10. /// IDebugData interface.
  11. /// </summary>
  12. public interface IDebugData
  13. {
  14. /// <summary>Get the reset callback for this DebugData</summary>
  15. /// <returns>The reset callback</returns>
  16. Action GetReset();
  17. //Action GetLoad();
  18. //Action GetSave();
  19. }
  20. /// <summary>
  21. /// Manager class for the Debug Window.
  22. /// </summary>
  23. public sealed partial class DebugManager
  24. {
  25. static readonly Lazy<DebugManager> s_Instance = new Lazy<DebugManager>(() => new DebugManager());
  26. /// <summary>
  27. /// Global instance of the DebugManager.
  28. /// </summary>
  29. public static DebugManager instance => s_Instance.Value;
  30. ReadOnlyCollection<DebugUI.Panel> m_ReadOnlyPanels;
  31. readonly List<DebugUI.Panel> m_Panels = new List<DebugUI.Panel>();
  32. void UpdateReadOnlyCollection()
  33. {
  34. m_Panels.Sort();
  35. m_ReadOnlyPanels = m_Panels.AsReadOnly();
  36. }
  37. /// <summary>
  38. /// List of currently registered debug panels.
  39. /// </summary>
  40. public ReadOnlyCollection<DebugUI.Panel> panels
  41. {
  42. get
  43. {
  44. if (m_ReadOnlyPanels == null)
  45. UpdateReadOnlyCollection();
  46. return m_ReadOnlyPanels;
  47. }
  48. }
  49. /// <summary>
  50. /// Callback called when the runtime UI changed.
  51. /// </summary>
  52. public event Action<bool> onDisplayRuntimeUIChanged = delegate {};
  53. /// <summary>
  54. /// Callback called when the debug window is dirty.
  55. /// </summary>
  56. public event Action onSetDirty = delegate {};
  57. event Action resetData;
  58. /// <summary>
  59. /// Force an editor request.
  60. /// </summary>
  61. public bool refreshEditorRequested;
  62. GameObject m_Root;
  63. DebugUIHandlerCanvas m_RootUICanvas;
  64. GameObject m_PersistentRoot;
  65. DebugUIHandlerPersistentCanvas m_RootUIPersistentCanvas;
  66. // Knowing if the DebugWindows is open, is done by event as it is in another assembly.
  67. // The DebugWindows is responsible to link its event to ToggleEditorUI.
  68. bool m_EditorOpen = false;
  69. /// <summary>
  70. /// Is the debug editor window open.
  71. /// </summary>
  72. public bool displayEditorUI => m_EditorOpen;
  73. /// <summary>
  74. /// Toggle the debug window.
  75. /// </summary>
  76. /// <param name="open">State of the debug window.</param>
  77. public void ToggleEditorUI(bool open) => m_EditorOpen = open;
  78. /// <summary>
  79. /// Displays the runtime version of the debug window.
  80. /// </summary>
  81. public bool displayRuntimeUI
  82. {
  83. get => m_Root != null && m_Root.activeInHierarchy;
  84. set
  85. {
  86. if (value)
  87. {
  88. m_Root = UnityObject.Instantiate(Resources.Load<Transform>("DebugUI Canvas")).gameObject;
  89. m_Root.name = "[Debug Canvas]";
  90. m_Root.transform.localPosition = Vector3.zero;
  91. m_RootUICanvas = m_Root.GetComponent<DebugUIHandlerCanvas>();
  92. m_Root.SetActive(true);
  93. }
  94. else
  95. {
  96. CoreUtils.Destroy(m_Root);
  97. m_Root = null;
  98. m_RootUICanvas = null;
  99. }
  100. onDisplayRuntimeUIChanged(value);
  101. }
  102. }
  103. /// <summary>
  104. /// Displays the persistent runtime debug window.
  105. /// </summary>
  106. public bool displayPersistentRuntimeUI
  107. {
  108. get => m_RootUIPersistentCanvas != null && m_PersistentRoot.activeInHierarchy;
  109. set
  110. {
  111. CheckPersistentCanvas();
  112. m_PersistentRoot.SetActive(value);
  113. }
  114. }
  115. DebugManager()
  116. {
  117. if (!Debug.isDebugBuild)
  118. return;
  119. RegisterInputs();
  120. RegisterActions();
  121. }
  122. /// <summary>
  123. /// Refresh the debug window.
  124. /// </summary>
  125. public void RefreshEditor()
  126. {
  127. refreshEditorRequested = true;
  128. }
  129. /// <summary>
  130. /// Reset the debug window.
  131. /// </summary>
  132. public void Reset()
  133. {
  134. resetData?.Invoke();
  135. ReDrawOnScreenDebug();
  136. }
  137. /// <summary>
  138. /// Redraw the runtime debug UI.
  139. /// </summary>
  140. public void ReDrawOnScreenDebug()
  141. {
  142. if (displayRuntimeUI)
  143. m_RootUICanvas?.ResetAllHierarchy();
  144. }
  145. /// <summary>
  146. /// Register debug data.
  147. /// </summary>
  148. /// <param name="data">Data to be registered.</param>
  149. public void RegisterData(IDebugData data) => resetData += data.GetReset();
  150. /// <summary>
  151. /// Register debug data.
  152. /// </summary>
  153. /// <param name="data">Data to be registered.</param>
  154. public void UnregisterData(IDebugData data) => resetData -= data.GetReset();
  155. /// <summary>
  156. /// Get hashcode state of the Debug Window.
  157. /// </summary>
  158. /// <returns></returns>
  159. public int GetState()
  160. {
  161. int hash = 17;
  162. foreach (var panel in m_Panels)
  163. hash = hash * 23 + panel.GetHashCode();
  164. return hash;
  165. }
  166. internal void RegisterRootCanvas(DebugUIHandlerCanvas root)
  167. {
  168. Assert.IsNotNull(root);
  169. m_Root = root.gameObject;
  170. m_RootUICanvas = root;
  171. }
  172. internal void ChangeSelection(DebugUIHandlerWidget widget, bool fromNext)
  173. {
  174. m_RootUICanvas.ChangeSelection(widget, fromNext);
  175. }
  176. void CheckPersistentCanvas()
  177. {
  178. if (m_RootUIPersistentCanvas == null)
  179. {
  180. var uiManager = UnityObject.FindObjectOfType<DebugUIHandlerPersistentCanvas>();
  181. if (uiManager == null)
  182. {
  183. m_PersistentRoot = UnityObject.Instantiate(Resources.Load<Transform>("DebugUI Persistent Canvas")).gameObject;
  184. m_PersistentRoot.name = "[Debug Canvas - Persistent]";
  185. m_PersistentRoot.transform.localPosition = Vector3.zero;
  186. }
  187. else
  188. {
  189. m_PersistentRoot = uiManager.gameObject;
  190. }
  191. m_RootUIPersistentCanvas = m_PersistentRoot.GetComponent<DebugUIHandlerPersistentCanvas>();
  192. }
  193. }
  194. internal void TogglePersistent(DebugUI.Widget widget)
  195. {
  196. if (widget == null)
  197. return;
  198. var valueWidget = widget as DebugUI.Value;
  199. if (valueWidget == null)
  200. {
  201. Debug.Log("Only DebugUI.Value items can be made persistent.");
  202. return;
  203. }
  204. CheckPersistentCanvas();
  205. m_RootUIPersistentCanvas.Toggle(valueWidget);
  206. }
  207. void OnPanelDirty(DebugUI.Panel panel)
  208. {
  209. onSetDirty();
  210. }
  211. // TODO: Optimally we should use a query path here instead of a display name
  212. /// <summary>
  213. /// Returns a debug panel.
  214. /// </summary>
  215. /// <param name="displayName">Name of the debug panel.</param>
  216. /// <param name="createIfNull">Create the panel if it does not exists.</param>
  217. /// <param name="groupIndex">Group index.</param>
  218. /// <param name="overrideIfExist">Replace an existing panel.</param>
  219. /// <returns></returns>
  220. public DebugUI.Panel GetPanel(string displayName, bool createIfNull = false, int groupIndex = 0, bool overrideIfExist = false)
  221. {
  222. DebugUI.Panel p = null;
  223. foreach (var panel in m_Panels)
  224. {
  225. if (panel.displayName == displayName)
  226. {
  227. p = panel;
  228. break;
  229. }
  230. }
  231. if (p != null)
  232. {
  233. if (overrideIfExist)
  234. {
  235. p.onSetDirty -= OnPanelDirty;
  236. RemovePanel(p);
  237. p = null;
  238. }
  239. else
  240. return p;
  241. }
  242. if (createIfNull)
  243. {
  244. p = new DebugUI.Panel { displayName = displayName, groupIndex = groupIndex };
  245. p.onSetDirty += OnPanelDirty;
  246. m_Panels.Add(p);
  247. UpdateReadOnlyCollection();
  248. }
  249. return p;
  250. }
  251. // TODO: Use a query path here as well instead of a display name
  252. /// <summary>
  253. /// Remove a debug panel.
  254. /// </summary>
  255. /// <param name="displayName">Name of the debug panel to remove.</param>
  256. public void RemovePanel(string displayName)
  257. {
  258. DebugUI.Panel panel = null;
  259. foreach (var p in m_Panels)
  260. {
  261. if (p.displayName == displayName)
  262. {
  263. p.onSetDirty -= OnPanelDirty;
  264. panel = p;
  265. break;
  266. }
  267. }
  268. RemovePanel(panel);
  269. }
  270. /// <summary>
  271. /// Remove a debug panel.
  272. /// </summary>
  273. /// <param name="panel">Reference to the debug panel to remove.</param>
  274. public void RemovePanel(DebugUI.Panel panel)
  275. {
  276. if (panel == null)
  277. return;
  278. m_Panels.Remove(panel);
  279. UpdateReadOnlyCollection();
  280. }
  281. /// <summary>
  282. /// Get a Debug Item.
  283. /// </summary>
  284. /// <param name="queryPath">Path of the debug item.</param>
  285. /// <returns>Reference to the requested debug item.</returns>
  286. public DebugUI.Widget GetItem(string queryPath)
  287. {
  288. foreach (var panel in m_Panels)
  289. {
  290. var w = GetItem(queryPath, panel);
  291. if (w != null)
  292. return w;
  293. }
  294. return null;
  295. }
  296. /// <summary>
  297. /// Get a debug item from a specific container.
  298. /// </summary>
  299. /// <param name="queryPath">Path of the debug item.</param>
  300. /// <param name="container">Container to query.</param>
  301. /// <returns>Reference to the requested debug item.</returns>
  302. DebugUI.Widget GetItem(string queryPath, DebugUI.IContainer container)
  303. {
  304. foreach (var child in container.children)
  305. {
  306. if (child.queryPath == queryPath)
  307. return child;
  308. var containerChild = child as DebugUI.IContainer;
  309. if (containerChild != null)
  310. {
  311. var w = GetItem(queryPath, containerChild);
  312. if (w != null)
  313. return w;
  314. }
  315. }
  316. return null;
  317. }
  318. }
  319. }