説明なし
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

DebugManager.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Linq;
  6. using UnityEngine.Assertions;
  7. using UnityEngine.Rendering.UI;
  8. namespace UnityEngine.Rendering
  9. {
  10. using UnityObject = UnityEngine.Object;
  11. /// <summary>
  12. /// IDebugData interface.
  13. /// </summary>
  14. public interface IDebugData
  15. {
  16. /// <summary>Get the reset callback for this DebugData</summary>
  17. /// <returns>The reset callback</returns>
  18. Action GetReset();
  19. //Action GetLoad();
  20. //Action GetSave();
  21. }
  22. /// <summary>
  23. /// Manager class for the Debug Window.
  24. /// </summary>
  25. public sealed partial class DebugManager
  26. {
  27. static readonly Lazy<DebugManager> s_Instance = new Lazy<DebugManager>(() => new DebugManager());
  28. /// <summary>
  29. /// Global instance of the DebugManager.
  30. /// </summary>
  31. public static DebugManager instance => s_Instance.Value;
  32. ReadOnlyCollection<DebugUI.Panel> m_ReadOnlyPanels;
  33. readonly List<DebugUI.Panel> m_Panels = new List<DebugUI.Panel>();
  34. void UpdateReadOnlyCollection()
  35. {
  36. m_Panels.Sort();
  37. m_ReadOnlyPanels = m_Panels.AsReadOnly();
  38. }
  39. /// <summary>
  40. /// List of currently registered debug panels.
  41. /// </summary>
  42. public ReadOnlyCollection<DebugUI.Panel> panels
  43. {
  44. get
  45. {
  46. if (m_ReadOnlyPanels == null)
  47. UpdateReadOnlyCollection();
  48. return m_ReadOnlyPanels;
  49. }
  50. }
  51. /// <summary>
  52. /// Callback called when the runtime UI changed.
  53. /// </summary>
  54. public event Action<bool> onDisplayRuntimeUIChanged = delegate { };
  55. /// <summary>
  56. /// Callback called when the debug window is dirty.
  57. /// </summary>
  58. public event Action onSetDirty = delegate { };
  59. event Action resetData;
  60. /// <summary>
  61. /// Force an editor request.
  62. /// </summary>
  63. public bool refreshEditorRequested;
  64. int? m_RequestedPanelIndex;
  65. GameObject m_Root;
  66. DebugUIHandlerCanvas m_RootUICanvas;
  67. GameObject m_PersistentRoot;
  68. DebugUIHandlerPersistentCanvas m_RootUIPersistentCanvas;
  69. /// <summary>
  70. /// Is any debug window or UI currently active.
  71. /// </summary>
  72. public bool isAnyDebugUIActive
  73. {
  74. get
  75. {
  76. return
  77. displayRuntimeUI || displayPersistentRuntimeUI
  78. #if UNITY_EDITOR
  79. || displayEditorUI
  80. #endif
  81. ;
  82. }
  83. }
  84. DebugManager()
  85. {
  86. #if DEVELOPMENT_BUILD || UNITY_EDITOR
  87. RegisterInputs();
  88. RegisterActions();
  89. #endif
  90. }
  91. /// <summary>
  92. /// Refresh the debug window.
  93. /// </summary>
  94. public void RefreshEditor()
  95. {
  96. refreshEditorRequested = true;
  97. }
  98. /// <summary>
  99. /// Reset the debug window.
  100. /// </summary>
  101. public void Reset()
  102. {
  103. resetData?.Invoke();
  104. ReDrawOnScreenDebug();
  105. }
  106. /// <summary>
  107. /// Request the runtime debug UI be redrawn on the next update.
  108. /// </summary>
  109. public void ReDrawOnScreenDebug()
  110. {
  111. if (displayRuntimeUI)
  112. m_RootUICanvas?.RequestHierarchyReset();
  113. }
  114. /// <summary>
  115. /// Register debug data.
  116. /// </summary>
  117. /// <param name="data">Data to be registered.</param>
  118. public void RegisterData(IDebugData data) => resetData += data.GetReset();
  119. /// <summary>
  120. /// Register debug data.
  121. /// </summary>
  122. /// <param name="data">Data to be registered.</param>
  123. public void UnregisterData(IDebugData data) => resetData -= data.GetReset();
  124. /// <summary>
  125. /// Get hashcode state of the Debug Window.
  126. /// </summary>
  127. /// <returns>The calculated hashcode for the current state of the Debug Window.</returns>
  128. public int GetState()
  129. {
  130. int hash = 17;
  131. foreach (var panel in m_Panels)
  132. hash = hash * 23 + panel.GetHashCode();
  133. return hash;
  134. }
  135. internal void RegisterRootCanvas(DebugUIHandlerCanvas root)
  136. {
  137. Assert.IsNotNull(root);
  138. m_Root = root.gameObject;
  139. m_RootUICanvas = root;
  140. }
  141. internal void ChangeSelection(DebugUIHandlerWidget widget, bool fromNext)
  142. {
  143. m_RootUICanvas.ChangeSelection(widget, fromNext);
  144. }
  145. internal void SetScrollTarget(DebugUIHandlerWidget widget)
  146. {
  147. if (m_RootUICanvas != null)
  148. m_RootUICanvas.SetScrollTarget(widget);
  149. }
  150. void EnsurePersistentCanvas()
  151. {
  152. if (m_RootUIPersistentCanvas == null)
  153. {
  154. var uiManager = UnityObject.FindFirstObjectByType<DebugUIHandlerPersistentCanvas>();
  155. if (uiManager == null)
  156. {
  157. m_PersistentRoot = UnityObject.Instantiate(Resources.Load<Transform>("DebugUIPersistentCanvas")).gameObject;
  158. m_PersistentRoot.name = "[Debug Canvas - Persistent]";
  159. m_PersistentRoot.transform.localPosition = Vector3.zero;
  160. }
  161. else
  162. {
  163. m_PersistentRoot = uiManager.gameObject;
  164. }
  165. m_RootUIPersistentCanvas = m_PersistentRoot.GetComponent<DebugUIHandlerPersistentCanvas>();
  166. }
  167. }
  168. internal void TogglePersistent(DebugUI.Widget widget, int? forceTupleIndex = null)
  169. {
  170. if (widget == null)
  171. return;
  172. EnsurePersistentCanvas();
  173. switch (widget)
  174. {
  175. case DebugUI.Value value:
  176. m_RootUIPersistentCanvas.Toggle(value);
  177. break;
  178. case DebugUI.ValueTuple valueTuple:
  179. m_RootUIPersistentCanvas.Toggle(valueTuple, forceTupleIndex);
  180. break;
  181. case DebugUI.Container container:
  182. // When container is toggled, we make sure that if there are ValueTuples, they all get the same element index.
  183. int pinnedIndex = container.children.Max(w => (w as DebugUI.ValueTuple)?.pinnedElementIndex ?? -1);
  184. foreach (var child in container.children)
  185. {
  186. if (child is DebugUI.Value || child is DebugUI.ValueTuple)
  187. TogglePersistent(child, pinnedIndex);
  188. }
  189. break;
  190. default:
  191. Debug.Log("Only readonly items can be made persistent.");
  192. break;
  193. }
  194. }
  195. void OnPanelDirty(DebugUI.Panel panel)
  196. {
  197. onSetDirty();
  198. }
  199. /// <summary>
  200. /// Returns the panel index
  201. /// </summary>
  202. /// <param name="displayName">The displayname for the panel</param>
  203. /// <returns>The index for the panel or -1 if not found.</returns>
  204. public int PanelIndex([DisallowNull] string displayName)
  205. {
  206. displayName ??= string.Empty;
  207. for (int i = 0; i < m_Panels.Count; ++i)
  208. {
  209. if (displayName.Equals(m_Panels[i].displayName, StringComparison.InvariantCultureIgnoreCase))
  210. return i;
  211. }
  212. return -1;
  213. }
  214. /// <summary>
  215. /// Returns the panel display name
  216. /// </summary>
  217. /// <param name="panelIndex">The panelIndex for the panel to get the name</param>
  218. /// <returns>The display name of the panel, or empty string otherwise</returns>
  219. public string PanelDiplayName([DisallowNull] int panelIndex)
  220. {
  221. if (panelIndex < 0 || panelIndex > m_Panels.Count - 1)
  222. return string.Empty;
  223. return m_Panels[panelIndex].displayName;
  224. }
  225. /// <summary>
  226. /// Request DebugWindow to open the specified panel.
  227. /// </summary>
  228. /// <param name="index">Index of the debug window panel to activate.</param>
  229. public void RequestEditorWindowPanelIndex(int index)
  230. {
  231. // Similar to RefreshEditor(), this function is required to bypass a dependency problem where DebugWindow
  232. // cannot be accessed from the Core.Runtime assembly. Should there be a better way to allow editor-dependent
  233. // features in DebugUI?
  234. m_RequestedPanelIndex = index;
  235. }
  236. internal int? GetRequestedEditorWindowPanelIndex()
  237. {
  238. int? requestedIndex = m_RequestedPanelIndex;
  239. m_RequestedPanelIndex = null;
  240. return requestedIndex;
  241. }
  242. // TODO: Optimally we should use a query path here instead of a display name
  243. /// <summary>
  244. /// Returns a debug panel.
  245. /// </summary>
  246. /// <param name="displayName">Name of the debug panel.</param>
  247. /// <param name="createIfNull">Create the panel if it does not exists.</param>
  248. /// <param name="groupIndex">Group index.</param>
  249. /// <param name="overrideIfExist">Replace an existing panel.</param>
  250. /// <returns>The requested debug panel or null if it does not exist and createIfNull is set to false</returns>
  251. public DebugUI.Panel GetPanel(string displayName, bool createIfNull = false, int groupIndex = 0, bool overrideIfExist = false)
  252. {
  253. int panelIndex = PanelIndex(displayName);
  254. DebugUI.Panel p = panelIndex >= 0 ? m_Panels[panelIndex] : null;
  255. if (p != null)
  256. {
  257. if (overrideIfExist)
  258. {
  259. p.onSetDirty -= OnPanelDirty;
  260. RemovePanel(p);
  261. p = null;
  262. }
  263. else
  264. return p;
  265. }
  266. if (createIfNull)
  267. {
  268. p = new DebugUI.Panel { displayName = displayName, groupIndex = groupIndex };
  269. p.onSetDirty += OnPanelDirty;
  270. m_Panels.Add(p);
  271. UpdateReadOnlyCollection();
  272. }
  273. return p;
  274. }
  275. /// <summary>
  276. /// Find the index of the panel from it's display name.
  277. /// </summary>
  278. /// <param name="displayName">The display name of the panel to find.</param>
  279. /// <returns>The index of the panel in the list. -1 if not found.</returns>
  280. public int FindPanelIndex(string displayName)
  281. => m_Panels.FindIndex(p => p.displayName == displayName);
  282. // TODO: Use a query path here as well instead of a display name
  283. /// <summary>
  284. /// Remove a debug panel.
  285. /// </summary>
  286. /// <param name="displayName">Name of the debug panel to remove.</param>
  287. public void RemovePanel(string displayName)
  288. {
  289. DebugUI.Panel panel = null;
  290. foreach (var p in m_Panels)
  291. {
  292. if (p.displayName == displayName)
  293. {
  294. p.onSetDirty -= OnPanelDirty;
  295. panel = p;
  296. break;
  297. }
  298. }
  299. RemovePanel(panel);
  300. }
  301. /// <summary>
  302. /// Remove a debug panel.
  303. /// </summary>
  304. /// <param name="panel">Reference to the debug panel to remove.</param>
  305. public void RemovePanel(DebugUI.Panel panel)
  306. {
  307. if (panel == null)
  308. return;
  309. m_Panels.Remove(panel);
  310. UpdateReadOnlyCollection();
  311. }
  312. /// <summary>
  313. /// Gets an <see cref="DebugUI.Widget[]"/> matching the given <see cref="DebugUI.Flags"/>
  314. /// </summary>
  315. /// <param name="flags">The flags of the widget</param>
  316. /// <returns>Reference to the requested debug item.</returns>
  317. public DebugUI.Widget[] GetItems(DebugUI.Flags flags)
  318. {
  319. using (ListPool<DebugUI.Widget>.Get(out var temp))
  320. {
  321. foreach (var panel in m_Panels)
  322. {
  323. var widgets = GetItemsFromContainer(flags, panel);
  324. temp.AddRange(widgets);
  325. }
  326. return temp.ToArray();
  327. }
  328. }
  329. internal DebugUI.Widget[] GetItemsFromContainer(DebugUI.Flags flags, DebugUI.IContainer container)
  330. {
  331. using (ListPool<DebugUI.Widget>.Get(out var temp))
  332. {
  333. foreach (var child in container.children)
  334. {
  335. if (child.flags.HasFlag(flags))
  336. {
  337. temp.Add(child);
  338. continue;
  339. }
  340. if (child is DebugUI.IContainer containerChild)
  341. {
  342. temp.AddRange(GetItemsFromContainer(flags, containerChild));
  343. }
  344. }
  345. return temp.ToArray();
  346. }
  347. }
  348. /// <summary>
  349. /// Get a Debug Item.
  350. /// </summary>
  351. /// <param name="queryPath">Path of the debug item.</param>
  352. /// <returns>Reference to the requested debug item.</returns>
  353. public DebugUI.Widget GetItem(string queryPath)
  354. {
  355. foreach (var panel in m_Panels)
  356. {
  357. var w = GetItem(queryPath, panel);
  358. if (w != null)
  359. return w;
  360. }
  361. return null;
  362. }
  363. /// <summary>
  364. /// Get a debug item from a specific container.
  365. /// </summary>
  366. /// <param name="queryPath">Path of the debug item.</param>
  367. /// <param name="container">Container to query.</param>
  368. /// <returns>Reference to the requested debug item.</returns>
  369. DebugUI.Widget GetItem(string queryPath, DebugUI.IContainer container)
  370. {
  371. foreach (var child in container.children)
  372. {
  373. if (child.queryPath == queryPath)
  374. return child;
  375. if (child is DebugUI.IContainer containerChild)
  376. {
  377. var w = GetItem(queryPath, containerChild);
  378. if (w != null)
  379. return w;
  380. }
  381. }
  382. return null;
  383. }
  384. }
  385. }