暫無描述
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.

BurstInspectorGUI.cs 66KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using System.Runtime.CompilerServices;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using Unity.Burst.LowLevel;
  8. using UnityEditor;
  9. using System.Text.RegularExpressions;
  10. using UnityEditor.IMGUI.Controls;
  11. using UnityEngine;
  12. [assembly: InternalsVisibleTo("Unity.Burst.Editor.Tests")]
  13. [assembly: InternalsVisibleTo("Unity.Burst.Tester.Editor.Tests")]
  14. namespace Unity.Burst.Editor
  15. {
  16. internal class BurstInspectorGUI : EditorWindow
  17. {
  18. private static bool Initialized;
  19. private static void EnsureInitialized()
  20. {
  21. if (Initialized)
  22. {
  23. return;
  24. }
  25. Initialized = true;
  26. BurstLoader.OnBurstShutdown += () =>
  27. {
  28. if (EditorWindow.HasOpenInstances<BurstInspectorGUI>())
  29. {
  30. var window = EditorWindow.GetWindow<BurstInspectorGUI>("Burst Inspector");
  31. window.Close();
  32. }
  33. };
  34. }
  35. private const string FontSizeIndexPref = "BurstInspectorFontSizeIndex";
  36. private static readonly string[] DisassemblyKindNames =
  37. {
  38. "Assembly",
  39. ".NET IL",
  40. "LLVM IR (Unoptimized)",
  41. "LLVM IR (Optimized)",
  42. "LLVM IR Optimisation Diagnostics"
  43. };
  44. internal enum AssemblyOptions
  45. {
  46. PlainWithoutDebugInformation = 0,
  47. PlainWithDebugInformation = 1,
  48. EnhancedWithMinimalDebugInformation = 2,
  49. EnhancedWithFullDebugInformation = 3,
  50. ColouredWithMinimalDebugInformation = 4,
  51. ColouredWithFullDebugInformation = 5
  52. }
  53. internal AssemblyOptions? _assemblyKind = null;
  54. private AssemblyOptions? _assemblyKindPrior = null;
  55. private AssemblyOptions _oldAssemblyKind;
  56. private bool SupportsEnhancedRendering => _disasmKind == DisassemblyKind.Asm || _disasmKind == DisassemblyKind.OptimizedIR || _disasmKind == DisassemblyKind.UnoptimizedIR;
  57. private static string[] DisasmOptions;
  58. internal static string[] GetDisasmOptions()
  59. {
  60. if (DisasmOptions == null)
  61. {
  62. // We can't initialize this in BurstInspectorGUI.cctor because BurstCompilerOptions may not yet
  63. // have been initialized by BurstLoader. So we initialize on-demand here. This method doesn't need to
  64. // be thread-safe because it's only called from the UI thread.
  65. DisasmOptions = new[]
  66. {
  67. "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.Asm),
  68. "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.IL),
  69. "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.IR),
  70. "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.IROptimized),
  71. "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.IRPassAnalysis)
  72. };
  73. }
  74. return DisasmOptions;
  75. }
  76. private static readonly SplitterState TreeViewSplitterState = new SplitterState(new float[] { 30, 70 }, new int[] { 128, 128 }, null);
  77. private static readonly string[] TargetCpuNames = Enum.GetNames(typeof(BurstTargetCpu));
  78. private static readonly string[] SIMDSmellTest = { "False", "True" };
  79. private static readonly int[] FontSizes =
  80. {
  81. 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20
  82. };
  83. private static string[] _fontSizesText;
  84. internal const int _scrollbarThickness = 14;
  85. internal float _buttonOverlapInspectorView = 0;
  86. /// <remarks>Used because it's not legal to change layout of GUI in a frame without the users input.</remarks>
  87. private float _buttonBarWidth = -1;
  88. [NonSerialized]
  89. internal readonly BurstDisassembler _burstDisassembler;
  90. private const string BurstSettingText = "Inspector Settings/";
  91. [SerializeField] private BurstTargetCpu _targetCpu = BurstTargetCpu.Auto;
  92. [SerializeField] private DisassemblyKind _disasmKind = DisassemblyKind.Asm;
  93. [SerializeField] private DisassemblyKind _oldDisasmKind = DisassemblyKind.Asm;
  94. [NonSerialized]
  95. internal GUIStyle fixedFontStyle;
  96. [NonSerialized]
  97. internal int fontSizeIndex = -1;
  98. [SerializeField] private int _previousTargetIndex = -1;
  99. [SerializeField] private bool _safetyChecks = false;
  100. [SerializeField] private bool _showBranchMarkers = true;
  101. [SerializeField] private bool _enhancedDisassembly = true;
  102. [SerializeField] private string _searchFilterJobs;
  103. [SerializeField] private bool _showUnityNamespaceJobs = false;
  104. [SerializeField] private bool _showDOTSGeneratedJobs = false;
  105. [SerializeField] private bool _focusTargetJob = true;
  106. [SerializeField] private string _searchFilterAssembly = String.Empty;
  107. [SerializeField] private bool _sameTargetButDifferentAssemblyKind = false;
  108. [SerializeField] internal Vector2 _scrollPos;
  109. internal SearchField _searchFieldJobs;
  110. internal SearchField _searchFieldAssembly;
  111. private bool saveSearchFieldFromEvent = false;
  112. [SerializeField] private bool _searchBarVisible = true;
  113. [SerializeField] private string _selectedItem;
  114. [NonSerialized]
  115. private BurstCompileTarget _target;
  116. [NonSerialized]
  117. private List<BurstCompileTarget> _targets;
  118. // Used as a serialized representation of _targets:
  119. [SerializeField] private List<string> targetNames;
  120. [NonSerialized]
  121. internal LongTextArea _textArea;
  122. internal Rect _inspectorView;
  123. [NonSerialized]
  124. internal Font _font;
  125. [NonSerialized]
  126. internal BurstMethodTreeView _treeView;
  127. // Serialized representation of _treeView:
  128. [SerializeField] private TreeViewState treeViewState;
  129. [NonSerialized]
  130. internal bool _initialized;
  131. [NonSerialized]
  132. private bool _requiresRepaint;
  133. private int FontSize => FontSizes[fontSizeIndex];
  134. private static readonly Regex _rx = new Regex(@"^.*\(\d+,\d+\):\sBurst\serror");
  135. private bool _leftClicked = false;
  136. [SerializeField] private bool _isCompileError = false;
  137. [SerializeField] private bool _prevWasCompileError;
  138. [SerializeField] private bool _smellTest = false;
  139. // Caching GUIContent and style options for button bar
  140. private readonly GUIContent _contentShowUnityNamespaceJobs = new GUIContent("Show Unity Namespace");
  141. private readonly GUIContent _contentShowDOTSGeneratedJobs = new GUIContent("Show \".Generated\"");
  142. private readonly GUIContent _contentDisasm = new GUIContent("Enhanced With Minimal Debug Information");
  143. private readonly GUIContent _contentCollapseToCode = new GUIContent("Focus on Code");
  144. private readonly GUIContent _contentExpandAll = new GUIContent("Expand All");
  145. private readonly GUIContent _contentBranchLines = new GUIContent("Show Branch Flow");
  146. private readonly GUIContent[] _contentsTarget;
  147. private readonly GUIContent[] _contentsFontSize;
  148. private readonly GUIContent[] _contentsSmellTest =
  149. {
  150. new GUIContent("Highlight SIMD Scalar vs Packed (False)"),
  151. new GUIContent("Highlight SIMD Scalar vs Packed (True)")
  152. };
  153. // content for button search bar
  154. private readonly GUIContent _ignoreCase = new GUIContent("Match Case");
  155. private readonly GUIContent _matchWord = new GUIContent("Whole words");
  156. private readonly GUIContent _regexSearch = new GUIContent("Regex");
  157. private readonly GUILayoutOption[] _toolbarStyleOptions = { GUILayout.ExpandWidth(true), GUILayout.MinWidth(5 * 10) };
  158. private readonly string[] _branchMarkerOptions = { "Hide Branch Flow", "Show Branch Flow" };
  159. private readonly string[] _safetyCheckOptions = { "Safety Check On", "Safety Check Off" };
  160. private enum KeyboardOperation
  161. {
  162. SelectAll,
  163. Copy,
  164. MoveLeft,
  165. MoveRight,
  166. MoveUp,
  167. MoveDown,
  168. Search,
  169. Escape,
  170. Enter,
  171. }
  172. private Dictionary<Event, KeyboardOperation> _keyboardEvents;
  173. private void FillKeyboardEvent()
  174. {
  175. if (_keyboardEvents != null)
  176. {
  177. return;
  178. }
  179. _keyboardEvents = new Dictionary<Event, KeyboardOperation>();
  180. _keyboardEvents.Add(Event.KeyboardEvent("#left"), KeyboardOperation.MoveLeft);
  181. _keyboardEvents.Add(Event.KeyboardEvent("#right"), KeyboardOperation.MoveRight);
  182. _keyboardEvents.Add(Event.KeyboardEvent("#down"), KeyboardOperation.MoveDown);
  183. _keyboardEvents.Add(Event.KeyboardEvent("#up"), KeyboardOperation.MoveUp);
  184. _keyboardEvents.Add(Event.KeyboardEvent("escape"), KeyboardOperation.Escape);
  185. _keyboardEvents.Add(Event.KeyboardEvent("return"), KeyboardOperation.Enter);
  186. _keyboardEvents.Add(Event.KeyboardEvent("#return"), KeyboardOperation.Enter);
  187. _keyboardEvents.Add(Event.KeyboardEvent("left"), KeyboardOperation.MoveLeft);
  188. _keyboardEvents.Add(Event.KeyboardEvent("right"), KeyboardOperation.MoveRight);
  189. _keyboardEvents.Add(Event.KeyboardEvent("up"), KeyboardOperation.MoveUp);
  190. _keyboardEvents.Add(Event.KeyboardEvent("down"), KeyboardOperation.MoveDown);
  191. if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX)
  192. {
  193. _keyboardEvents.Add(Event.KeyboardEvent("%a"), KeyboardOperation.SelectAll);
  194. _keyboardEvents.Add(Event.KeyboardEvent("%c"), KeyboardOperation.Copy);
  195. _keyboardEvents.Add(Event.KeyboardEvent("%f"), KeyboardOperation.Search);
  196. }
  197. else
  198. {
  199. // windows or linux bindings.
  200. _keyboardEvents.Add(Event.KeyboardEvent("^a"), KeyboardOperation.SelectAll);
  201. _keyboardEvents.Add(Event.KeyboardEvent("^c"), KeyboardOperation.Copy);
  202. _keyboardEvents.Add(Event.KeyboardEvent("^f"), KeyboardOperation.Search);
  203. }
  204. }
  205. public BurstInspectorGUI()
  206. {
  207. _burstDisassembler = new BurstDisassembler();
  208. string[] names = Enum.GetNames(typeof(BurstTargetCpu));
  209. int size = names.Length;
  210. _contentsTarget = new GUIContent[size];
  211. for (int i = 0; i < size; i++)
  212. {
  213. _contentsTarget[i] = new GUIContent($"Target ({names[i]})");
  214. }
  215. size = FontSizes.Length;
  216. _contentsFontSize = new GUIContent[size];
  217. for (int i = 0; i < size; i++)
  218. {
  219. _contentsFontSize[i] = new GUIContent($"Font Size ({FontSizes[i].ToString()})");
  220. }
  221. }
  222. private bool DisplayAssemblyKind(Enum assemblyKind)
  223. {
  224. var assemblyOption = (AssemblyOptions)assemblyKind;
  225. if (_disasmKind != DisassemblyKind.Asm || _isCompileError)
  226. {
  227. return assemblyOption == AssemblyOptions.PlainWithoutDebugInformation;
  228. }
  229. return true;
  230. }
  231. public void OnEnable()
  232. {
  233. EnsureInitialized();
  234. var newTreeState = false;
  235. if (treeViewState is null)
  236. {
  237. treeViewState = new TreeViewState();
  238. newTreeState = true;
  239. }
  240. _treeView ??= _treeView = new BurstMethodTreeView
  241. (
  242. treeViewState,
  243. () => _searchFilterJobs,
  244. () => (_showUnityNamespaceJobs, _showDOTSGeneratedJobs)
  245. );
  246. if (_keyboardEvents == null) FillKeyboardEvent();
  247. var assemblyList = BurstReflection.EditorAssembliesThatCanPossiblyContainJobs;
  248. Task.Run(
  249. () =>
  250. {
  251. // Do this stuff asynchronously.
  252. var result = BurstReflection.FindExecuteMethods(assemblyList, BurstReflectionAssemblyOptions.None);
  253. _targets = result.CompileTargets;
  254. _targets.Sort((left, right) => string.Compare(left.GetDisplayName(), right.GetDisplayName(), StringComparison.Ordinal));
  255. return result;
  256. })
  257. .ContinueWith(t =>
  258. {
  259. // Do this stuff on the main (UI) thread.
  260. if (t.Status == TaskStatus.RanToCompletion)
  261. {
  262. foreach (var logMessage in t.Result.LogMessages)
  263. {
  264. switch (logMessage.LogType)
  265. {
  266. case BurstReflection.LogType.Warning:
  267. Debug.LogWarning(logMessage.Message);
  268. break;
  269. case BurstReflection.LogType.Exception:
  270. Debug.LogException(logMessage.Exception);
  271. break;
  272. default:
  273. throw new InvalidOperationException();
  274. }
  275. }
  276. var newNames = new List<string>(_targets.Count);
  277. foreach (var target in _targets)
  278. {
  279. newNames.Add(target.GetDisplayName());
  280. }
  281. bool identical = !newTreeState && newNames.Count == targetNames.Count;
  282. int len = newNames.Count;
  283. int i = 0;
  284. while (identical && i < len)
  285. {
  286. identical = newNames[i] == targetNames[i];
  287. i++;
  288. }
  289. targetNames = newNames;
  290. _treeView.Initialize(_targets, identical);
  291. if (_selectedItem == null || !_treeView.TrySelectByDisplayName(_selectedItem))
  292. {
  293. _previousTargetIndex = -1;
  294. _scrollPos = Vector2.zero;
  295. }
  296. _requiresRepaint = true;
  297. _initialized = true;
  298. }
  299. else if (t.Exception != null)
  300. {
  301. Debug.LogError($"Could not load Inspector: {t.Exception}");
  302. }
  303. });
  304. }
  305. #if !UNITY_2023_1_OR_NEWER
  306. private void CleanupFont()
  307. {
  308. if (_font != null)
  309. {
  310. DestroyImmediate(_font, true);
  311. _font = null;
  312. }
  313. }
  314. public void OnDisable()
  315. {
  316. CleanupFont();
  317. }
  318. #endif
  319. public void Update()
  320. {
  321. // Need to do this because if we call Repaint from anywhere else,
  322. // it doesn't do anything if this window is not currently focused.
  323. if (_requiresRepaint)
  324. {
  325. Repaint();
  326. _requiresRepaint = false;
  327. }
  328. // Need this because pressing new target, and then not invoking new events,
  329. // will leave the assembly unrendered.
  330. // This is not included in above, to minimize needed calls.
  331. if (_target != null && _target.JustLoaded)
  332. {
  333. Repaint();
  334. }
  335. }
  336. /// <summary>
  337. /// Checks if there is space for given content withs style, and starts new horizontalgroup
  338. /// if there is no space on this line.
  339. /// </summary>
  340. private void FlowToNewLine(ref float remainingWidth, float width, Vector2 size)
  341. {
  342. float sizeX = size.x + _scrollbarThickness / 2;
  343. if (sizeX >= remainingWidth)
  344. {
  345. _buttonOverlapInspectorView += size.y + 2;
  346. remainingWidth = width - sizeX;
  347. GUILayout.EndHorizontal();
  348. GUILayout.BeginHorizontal();
  349. }
  350. else
  351. {
  352. remainingWidth -= sizeX;
  353. }
  354. }
  355. private bool IsRaw(AssemblyOptions kind)
  356. {
  357. return kind == AssemblyOptions.PlainWithoutDebugInformation || kind == AssemblyOptions.PlainWithDebugInformation;
  358. }
  359. private bool IsEnhanced(AssemblyOptions kind)
  360. {
  361. return !IsRaw(kind);
  362. }
  363. private bool IsColoured(AssemblyOptions kind)
  364. {
  365. return kind == AssemblyOptions.ColouredWithMinimalDebugInformation || kind == AssemblyOptions.ColouredWithFullDebugInformation;
  366. }
  367. /// <summary>
  368. /// Renders buttons bar, and handles saving/loading of _assemblyKind options when changing in inspector settings
  369. /// that disable/enables some options for _assemblyKind.
  370. /// </summary>
  371. private void HandleButtonBars(BurstCompileTarget target, bool targetChanged, out int fontIndex, out bool collapse, out bool focusCode)
  372. {
  373. // We can only make an educated guess for the correct width.
  374. if (_buttonBarWidth == -1)
  375. {
  376. _buttonBarWidth = (position.width * 2) / 3 - _scrollbarThickness;
  377. }
  378. RenderButtonBars(_buttonBarWidth, target, out fontIndex, out collapse, out focusCode);
  379. var disasmKindChanged = _oldDisasmKind != _disasmKind;
  380. // Handles saving and loading _assemblyKind option when going between settings, that disable/enable some options for it
  381. if ((disasmKindChanged && _oldDisasmKind == DisassemblyKind.Asm && !_isCompileError)
  382. || (targetChanged && !_prevWasCompileError && _isCompileError && _disasmKind == DisassemblyKind.Asm))
  383. {
  384. // save when _disasmKind changed from Asm WHEN we are not looking at a burst compile error,
  385. // or when target changed from non compile error to compile error and current _disasmKind is Asm.
  386. _oldAssemblyKind = (AssemblyOptions)_assemblyKind;
  387. }
  388. else if ((disasmKindChanged && _disasmKind == DisassemblyKind.Asm && !_isCompileError) ||
  389. (targetChanged && _prevWasCompileError && _disasmKind == DisassemblyKind.Asm))
  390. {
  391. // load when _diasmKind changed to Asm and we are not at burst compile error,
  392. // or when target changed from a burst compile error while _disasmKind is Asm.
  393. _assemblyKind = _oldAssemblyKind;
  394. }
  395. // if _assemblyKind is something that is not available, force it up to PlainWithoutDebugInformation.
  396. if ((_disasmKind != DisassemblyKind.Asm && _assemblyKind != AssemblyOptions.PlainWithoutDebugInformation)
  397. || _isCompileError)
  398. {
  399. _assemblyKind = AssemblyOptions.PlainWithoutDebugInformation;
  400. }
  401. }
  402. private void RenderButtonBars(float width, BurstCompileTarget target, out int fontIndex, out bool collapse, out bool focus)
  403. {
  404. var remainingWidth = width;
  405. GUILayout.BeginHorizontal();
  406. // First button should never call beginHorizontal().
  407. remainingWidth -= (EditorStyles.popup.CalcSize(_contentDisasm).x + _scrollbarThickness / 2f);
  408. EditorGUI.BeginDisabledGroup(target.DisassemblyKind == DisassemblyKind.IRPassAnalysis);
  409. _assemblyKind = (AssemblyOptions)EditorGUILayout.EnumPopup(GUIContent.none, _assemblyKind, DisplayAssemblyKind, true);
  410. EditorGUI.EndDisabledGroup();
  411. FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(_contentBranchLines));
  412. // Reversed "logic" to match the array of options, which has "positive" case on idx 0.
  413. _safetyChecks = EditorGUILayout.Popup(_safetyChecks ? 0 : 1, _safetyCheckOptions) == 0;
  414. EditorGUI.BeginDisabledGroup(!target.HasRequiredBurstCompileAttributes);
  415. GUIContent targetContent = _contentsTarget[(int)_targetCpu];
  416. FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(targetContent));
  417. _targetCpu = (BurstTargetCpu)LabeledPopup.Popup((int)_targetCpu, targetContent, TargetCpuNames);
  418. GUIContent fontSizeContent = _contentsFontSize[fontSizeIndex];
  419. FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(fontSizeContent));
  420. fontIndex = LabeledPopup.Popup(fontSizeIndex, fontSizeContent, _fontSizesText);
  421. EditorGUI.EndDisabledGroup();
  422. EditorGUI.BeginDisabledGroup(!IsEnhanced((AssemblyOptions)_assemblyKind) || !SupportsEnhancedRendering || _isCompileError);
  423. FlowToNewLine(ref remainingWidth, width, EditorStyles.miniButton.CalcSize(_contentCollapseToCode));
  424. focus = GUILayout.Button(_contentCollapseToCode, EditorStyles.miniButton);
  425. FlowToNewLine(ref remainingWidth, width, EditorStyles.miniButton.CalcSize(_contentExpandAll));
  426. collapse = GUILayout.Button(_contentExpandAll, EditorStyles.miniButton);
  427. FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(_contentBranchLines));
  428. _showBranchMarkers = EditorGUILayout.Popup(Convert.ToInt32(_showBranchMarkers), _branchMarkerOptions) == 1;
  429. int smellTestIdx = Convert.ToInt32(_smellTest);
  430. GUIContent smellTestContent = _contentsSmellTest[smellTestIdx];
  431. FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(smellTestContent));
  432. _smellTest = LabeledPopup.Popup(smellTestIdx, smellTestContent, SIMDSmellTest) == 1;
  433. EditorGUI.EndDisabledGroup();
  434. GUILayout.EndHorizontal();
  435. _oldDisasmKind = _disasmKind;
  436. _disasmKind = (DisassemblyKind)GUILayout.Toolbar((int)_disasmKind, DisassemblyKindNames, _toolbarStyleOptions);
  437. }
  438. /// <summary>
  439. /// Handles mouse events for selecting text.
  440. /// </summary>
  441. /// <remarks>
  442. /// Must be called after Render(...), as it uses the mouse events, and Render(...)
  443. /// need mouse events for buttons etc.
  444. /// </remarks>
  445. private void HandleMouseEventForSelection(Rect workingArea, int controlID, bool showBranchMarkers)
  446. {
  447. var evt = Event.current;
  448. var mousePos = evt.mousePosition;
  449. if (_textArea.MouseOutsideView(workingArea, mousePos, controlID))
  450. {
  451. return;
  452. }
  453. switch (evt.type)
  454. {
  455. case EventType.MouseDown:
  456. // button 0 is left and 1 is right
  457. if (evt.button == 0)
  458. {
  459. _textArea.MouseClicked(showBranchMarkers, evt.shift, mousePos, controlID);
  460. }
  461. else
  462. {
  463. _leftClicked = true;
  464. }
  465. evt.Use();
  466. break;
  467. case EventType.MouseDrag:
  468. _textArea.DragMouse(mousePos, showBranchMarkers);
  469. evt.Use();
  470. break;
  471. case EventType.MouseUp:
  472. _textArea.MouseReleased();
  473. evt.Use();
  474. break;
  475. case EventType.ScrollWheel:
  476. _textArea.DoScroll(workingArea, evt.delta.y);
  477. // we cannot Use() (consume) scrollWheel events, as they are still needed in EndScrollView.
  478. break;
  479. }
  480. }
  481. private bool AssemblyFocused() => !((_treeView != null && _treeView.HasFocus()) || (_searchFieldAssembly != null && _searchFieldAssembly.HasFocus()));
  482. private void HandleKeyboardEventAssemblyView(Rect workingArea, KeyboardOperation op, Event evt, bool showBranchMarkers)
  483. {
  484. switch (op)
  485. {
  486. case KeyboardOperation.SelectAll:
  487. _textArea.SelectAll();
  488. evt.Use();
  489. break;
  490. case KeyboardOperation.Copy:
  491. _textArea.DoSelectionCopy();
  492. evt.Use();
  493. break;
  494. case KeyboardOperation.MoveLeft:
  495. if (evt.shift)
  496. {
  497. if (_textArea.HasSelection) _textArea.MoveSelectionLeft(workingArea, showBranchMarkers);
  498. }
  499. else
  500. {
  501. _textArea.MoveView(LongTextArea.Direction.Left, workingArea);
  502. }
  503. evt.Use();
  504. break;
  505. case KeyboardOperation.MoveRight:
  506. if (evt.shift)
  507. {
  508. if (_textArea.HasSelection) _textArea.MoveSelectionRight(workingArea, showBranchMarkers);
  509. }
  510. else
  511. {
  512. _textArea.MoveView(LongTextArea.Direction.Right, workingArea);
  513. }
  514. evt.Use();
  515. break;
  516. case KeyboardOperation.MoveUp:
  517. if (evt.shift)
  518. {
  519. if (_textArea.HasSelection) _textArea.MoveSelectionUp(workingArea, showBranchMarkers);
  520. }
  521. else
  522. {
  523. _textArea.MoveView(LongTextArea.Direction.Up, workingArea);
  524. }
  525. evt.Use();
  526. break;
  527. case KeyboardOperation.MoveDown:
  528. if (evt.shift)
  529. {
  530. if (_textArea.HasSelection) _textArea.MoveSelectionDown(workingArea, showBranchMarkers);
  531. }
  532. else
  533. {
  534. _textArea.MoveView(LongTextArea.Direction.Down, workingArea);
  535. }
  536. evt.Use();
  537. break;
  538. case KeyboardOperation.Search:
  539. _searchBarVisible = true;
  540. _searchFieldAssembly?.SetFocus();
  541. evt.Use();
  542. break;
  543. }
  544. }
  545. /// <remarks>
  546. /// Must be called after Render(...) because of depenency on LongTextArea.finalAreaSize.
  547. /// </remarks>
  548. private void HandleKeyboardEventForSelection(Rect workingArea, bool showBranchMarkers)
  549. {
  550. var evt = Event.current;
  551. if (!_keyboardEvents.TryGetValue(evt, out var op))
  552. {
  553. return;
  554. }
  555. if (AssemblyFocused())
  556. {
  557. // Do input handling for assembly view.
  558. HandleKeyboardEventAssemblyView(workingArea, op, evt, showBranchMarkers);
  559. }
  560. else
  561. {
  562. // This amounts to logic for all else.
  563. switch (op)
  564. {
  565. case KeyboardOperation.Escape:
  566. if (_searchFieldAssembly != null && _searchFieldAssembly.HasFocus() && _searchFilterAssembly == "")
  567. {
  568. _searchBarVisible = false;
  569. evt.Use();
  570. }
  571. break;
  572. case KeyboardOperation.Enter:
  573. if (_searchFieldAssembly != null && _searchFieldAssembly.HasFocus())
  574. {
  575. _textArea.NextSearchHit(evt.shift, workingArea);
  576. saveSearchFieldFromEvent = true;
  577. evt.Use();
  578. }
  579. break;
  580. }
  581. }
  582. }
  583. private void RenderCompileTargetsFilters(float width)
  584. {
  585. GUILayout.BeginHorizontal();
  586. // Handle and render filtering toggles:
  587. var newShowUnityTests = GUILayout.Toggle(_showUnityNamespaceJobs, _contentShowUnityNamespaceJobs);
  588. FlowToNewLine(ref width, width, EditorStyles.toggle.CalcSize(_contentShowDOTSGeneratedJobs));
  589. var newShowDOTSGeneratedJobs = GUILayout.Toggle(_showDOTSGeneratedJobs, _contentShowDOTSGeneratedJobs);
  590. GUILayout.EndHorizontal();
  591. if (newShowUnityTests != _showUnityNamespaceJobs || newShowDOTSGeneratedJobs != _showDOTSGeneratedJobs)
  592. {
  593. _showDOTSGeneratedJobs = newShowDOTSGeneratedJobs;
  594. _showUnityNamespaceJobs = newShowUnityTests;
  595. _treeView.Reload();
  596. }
  597. // Handle and render search filter:
  598. var newFilter = _searchFieldJobs.OnGUI(_searchFilterJobs);
  599. if (newFilter != _searchFilterJobs)
  600. {
  601. _searchFilterJobs = newFilter;
  602. _treeView.Reload();
  603. }
  604. }
  605. private void CompileNewTarget(BurstCompileTarget target, BurstCompilerOptions targetOptions)
  606. {
  607. if (target.IsLoading) { return; }
  608. target.IsLoading = true;
  609. target.JustLoaded = false;
  610. // Setup target and it's compilation options.
  611. // This is done here as EditorGUIUtility.isProSkin must be on main thread.
  612. target.TargetCpu = _targetCpu;
  613. target.DisassemblyKind = _disasmKind;
  614. targetOptions.EnableBurstSafetyChecks = _safetyChecks;
  615. target.IsDarkMode = EditorGUIUtility.isProSkin;
  616. // Don't set debug mode, because it disables optimizations.
  617. // Instead we set debug level (None, Full, LineOnly) below.
  618. targetOptions.EnableBurstDebug = false;
  619. Task.Run(() =>
  620. {
  621. var options = new StringBuilder();
  622. if (targetOptions.TryGetOptions(target.IsStaticMethod ? (MemberInfo)target.Method : target.JobType, out var defaultOptions, isForCompilerClient: true))
  623. {
  624. options.AppendLine(defaultOptions);
  625. // Disables the 2 current warnings generated from code (since they clutter up the inspector display)
  626. // BC1370 - throw inside code not guarded with ConditionalSafetyCheck attribute
  627. // BC1322 - loop intrinsic on loop that has been optimized away
  628. options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDisableWarnings, "BC1370;BC1322")}");
  629. options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionTarget, TargetCpuNames[(int)_targetCpu])}");
  630. // For IRPassAnalysis, we always want full debug information.
  631. if (_disasmKind != DisassemblyKind.IRPassAnalysis)
  632. {
  633. switch (_assemblyKind)
  634. {
  635. case AssemblyOptions.EnhancedWithMinimalDebugInformation:
  636. case AssemblyOptions.ColouredWithMinimalDebugInformation:
  637. options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "2")}");
  638. break;
  639. case AssemblyOptions.ColouredWithFullDebugInformation:
  640. case AssemblyOptions.EnhancedWithFullDebugInformation:
  641. case AssemblyOptions.PlainWithDebugInformation:
  642. options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "1")}");
  643. break;
  644. default:
  645. case AssemblyOptions.PlainWithoutDebugInformation:
  646. options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "0")}");
  647. break;
  648. }
  649. }
  650. else
  651. {
  652. options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "1")}");
  653. }
  654. var baseOptions = options.ToString();
  655. target.RawDisassembly = GetDisassembly(target.Method, baseOptions + GetDisasmOptions()[(int)_disasmKind]);
  656. target.FormattedDisassembly = null;
  657. target.IsBurstError = IsBurstError(target.RawDisassembly);
  658. }
  659. target.IsLoading = false;
  660. target.JustLoaded = true;
  661. });
  662. }
  663. private void RenderBurstJobMenu()
  664. {
  665. float width = position.width / 3;
  666. GUILayout.BeginVertical(GUILayout.Width(width));
  667. // Render Treeview showing burst targets:
  668. GUILayout.Label("Compile Targets", EditorStyles.boldLabel);
  669. RenderCompileTargetsFilters(width);
  670. // Does not give proper rect during layout event.
  671. _inspectorView = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
  672. _treeView.OnGUI(_inspectorView);
  673. GUILayout.EndVertical();
  674. }
  675. private void HandleHorizontalFocus(float workingWidth, bool shouldSetupText, bool isTextFormatted)
  676. {
  677. if (!shouldSetupText || !isTextFormatted || !_burstDisassembler.IsInitialized) { return; }
  678. var branchFiller = _textArea.MaxLineDepth * 10;
  679. if (branchFiller < workingWidth / 2f) { return; }
  680. // Do horizontal padding:
  681. _scrollPos.x = _textArea.MaxLineDepth * 10;
  682. }
  683. private static void RenderLoading()
  684. {
  685. GUILayout.BeginHorizontal();
  686. GUILayout.FlexibleSpace();
  687. GUILayout.BeginVertical();
  688. GUILayout.FlexibleSpace();
  689. GUILayout.Label("Loading...");
  690. GUILayout.FlexibleSpace();
  691. GUILayout.EndVertical();
  692. GUILayout.FlexibleSpace();
  693. GUILayout.EndHorizontal();
  694. }
  695. public void OnGUI()
  696. {
  697. if (!_initialized)
  698. {
  699. RenderLoading();
  700. return;
  701. }
  702. // used to give hot control to inspector when a mouseDown event has happened.
  703. // This way we can register a mouseUp happening outside inspector.
  704. int controlID = GUIUtility.GetControlID(FocusType.Passive);
  705. // Make sure that editor options are synchronized
  706. BurstEditorOptions.EnsureSynchronized();
  707. if (_fontSizesText == null)
  708. {
  709. _fontSizesText = new string[FontSizes.Length];
  710. for (var i = 0; i < FontSizes.Length; ++i) _fontSizesText[i] = FontSizes[i].ToString();
  711. }
  712. if (fontSizeIndex == -1)
  713. {
  714. fontSizeIndex = EditorPrefs.GetInt(FontSizeIndexPref, 5);
  715. fontSizeIndex = Math.Max(0, fontSizeIndex);
  716. fontSizeIndex = Math.Min(fontSizeIndex, FontSizes.Length - 1);
  717. }
  718. if (fixedFontStyle == null || fixedFontStyle.font == null) // also check .font as it's reset somewhere when going out of play mode.
  719. {
  720. fixedFontStyle = new GUIStyle(GUI.skin.label);
  721. #if UNITY_2023_1_OR_NEWER
  722. _font = EditorGUIUtility.Load("Fonts/RobotoMono/RobotoMono-Regular.ttf") as Font;
  723. #else
  724. string fontName;
  725. if (Application.platform == RuntimePlatform.WindowsEditor)
  726. {
  727. fontName = "Consolas";
  728. }
  729. else
  730. {
  731. fontName = "Courier";
  732. }
  733. CleanupFont();
  734. _font = Font.CreateDynamicFontFromOSFont(fontName, FontSize);
  735. #endif
  736. fixedFontStyle.font = _font;
  737. fixedFontStyle.fontSize = FontSize;
  738. }
  739. if (_searchFieldJobs == null) _searchFieldJobs = new SearchField();
  740. if (_textArea == null) _textArea = new LongTextArea();
  741. GUILayout.BeginHorizontal();
  742. // SplitterGUILayout.BeginHorizontalSplit is internal in Unity but we don't have much choice
  743. SplitterGUILayout.BeginHorizontalSplit(TreeViewSplitterState);
  744. RenderBurstJobMenu();
  745. GUILayout.BeginVertical();
  746. var selection = _treeView.GetSelection();
  747. if (selection.Count == 1)
  748. {
  749. var targetIndex = selection[0];
  750. _target = _targets[targetIndex - 1];
  751. var targetOptions = _target.Options;
  752. var targetChanged = _previousTargetIndex != targetIndex;
  753. _previousTargetIndex = targetIndex;
  754. // Stash selected item name to handle domain reloads more gracefully
  755. _selectedItem = _target.GetDisplayName();
  756. if (_assemblyKind == null)
  757. {
  758. if (_enhancedDisassembly)
  759. {
  760. _assemblyKind = AssemblyOptions.ColouredWithMinimalDebugInformation;
  761. }
  762. else
  763. {
  764. _assemblyKind = AssemblyOptions.PlainWithoutDebugInformation;
  765. }
  766. _oldAssemblyKind = (AssemblyOptions)_assemblyKind;
  767. }
  768. // We are currently formatting only Asm output
  769. var isTextFormatted = IsEnhanced((AssemblyOptions)_assemblyKind) && SupportsEnhancedRendering;
  770. // Depending if we are formatted or not, we don't render the same text
  771. var textToRender = _target.RawDisassembly?.TrimStart('\n');
  772. // Only refresh if we are switching to a new selection that hasn't been disassembled yet
  773. // Or we are changing disassembly settings (safety checks / enhanced disassembly)
  774. var targetRefresh = textToRender == null
  775. || _target.DisassemblyKind != _disasmKind
  776. || targetOptions.EnableBurstSafetyChecks != _safetyChecks
  777. || _target.TargetCpu != _targetCpu
  778. || _target.IsDarkMode != EditorGUIUtility.isProSkin;
  779. if (_assemblyKindPrior != _assemblyKind)
  780. {
  781. targetRefresh = true;
  782. _assemblyKindPrior = _assemblyKind; // Needs to be refreshed, as we need to change disassembly options
  783. // If the target did not changed but our assembly kind did, we need to remember this.
  784. if (!targetChanged)
  785. {
  786. _sameTargetButDifferentAssemblyKind = true;
  787. }
  788. }
  789. // If the previous target changed the assembly kind and we have a target change, we need to
  790. // refresh the assembly because we'll have cached the previous assembly kinds output rather
  791. // than the one requested.
  792. if (_sameTargetButDifferentAssemblyKind && targetChanged)
  793. {
  794. targetRefresh = true;
  795. _sameTargetButDifferentAssemblyKind = false;
  796. }
  797. if (targetRefresh)
  798. {
  799. CompileNewTarget(_target, targetOptions);
  800. }
  801. _prevWasCompileError = _isCompileError;
  802. _isCompileError = _target.IsBurstError;
  803. _buttonOverlapInspectorView = 0;
  804. var oldSimdSmellTest = _smellTest;
  805. HandleButtonBars(_target, targetChanged, out var fontSize, out var expandAllBlocks, out var focusCode);
  806. var simdSmellTestChanged = oldSimdSmellTest != _smellTest;
  807. // Guard against _textArea being used, as the assembly isn't ready yet.
  808. // Have to test against event so it cannot finish between a Layout event and Repaint event;
  809. // this is necessary as we cannot alter GUI between these events.
  810. if (_target.HasRequiredBurstCompileAttributes && (_target.IsLoading || (_target.JustLoaded && Event.current.type != EventType.Layout)))
  811. {
  812. RenderLoading();
  813. // Need to close the splits we opened.
  814. GUILayout.EndVertical();
  815. SplitterGUILayout.EndHorizontalSplit();
  816. GUILayout.EndHorizontal();
  817. return;
  818. }
  819. var justLoaded = _target.JustLoaded;
  820. _target.JustLoaded = false;
  821. // If ´CompileNewTarget´ finishes before we enter loading screen above `textToRender` might not be set.
  822. textToRender ??= _target.RawDisassembly?.TrimStart('\n');
  823. if (!string.IsNullOrEmpty(textToRender))
  824. {
  825. // we should only call SetDisassembler(...) the first time assemblyKind is changed with same target.
  826. // Otherwise it will kep re-initializing fields such as _folded, meaning we can no longer fold/unfold.
  827. var shouldSetupText = !_textArea.IsTextSet(_selectedItem)
  828. || justLoaded
  829. || simdSmellTestChanged;
  830. if (shouldSetupText)
  831. {
  832. _textArea.SetText(
  833. _selectedItem,
  834. textToRender,
  835. _target.IsDarkMode,
  836. _burstDisassembler,
  837. isTextFormatted && _burstDisassembler.Initialize(
  838. textToRender,
  839. FetchAsmKind(_targetCpu, _disasmKind),
  840. _target.IsDarkMode,
  841. IsColoured((AssemblyOptions)_assemblyKind),
  842. _smellTest));
  843. }
  844. if (justLoaded)
  845. {
  846. _scrollPos = Vector2.zero;
  847. }
  848. HandleHorizontalFocus(
  849. _inspectorView.width == 1f ? _buttonBarWidth : _inspectorView.width,
  850. shouldSetupText,
  851. isTextFormatted
  852. );
  853. // Fixing lastRectSize to actually be size of scroll view
  854. _inspectorView.position = _scrollPos;
  855. _inspectorView.width = position.width - (_inspectorView.width + _scrollbarThickness);
  856. _inspectorView.height -= (_buttonOverlapInspectorView + 4); //+4 for alignment.
  857. if (_searchBarVisible) _inspectorView.height -= EditorStyles.searchField.CalcHeight(GUIContent.none, 2); // 2 is just arbitrary, as the width does not alter height
  858. // repaint indicate end of frame, so we can alter width for menu items to new correct.
  859. if (Event.current.type == EventType.Repaint)
  860. {
  861. _buttonBarWidth = _inspectorView.width - _scrollbarThickness;
  862. }
  863. // Do search if we did not try and find assembly and we were actually going to do a search.
  864. if (_focusTargetJob && TryFocusJobInAssemblyView(ref _inspectorView, shouldSetupText, _target))
  865. {
  866. _scrollPos.y = _inspectorView.y - _textArea.fontHeight*10;
  867. }
  868. _scrollPos = GUILayout.BeginScrollView(_scrollPos, true, true);
  869. if (Event.current.type != EventType.Layout) // we always want mouse position feedback
  870. {
  871. _textArea.Interact(_inspectorView, Event.current.type);
  872. }
  873. // Set up search information if it is happening.
  874. Regex regx = default;
  875. SearchCriteria sc = default;
  876. var doSearch = _searchBarVisible && _searchFilterAssembly != "";
  877. var wrongRegx = false;
  878. if (doSearch)
  879. {
  880. sc = new SearchCriteria(_searchFilterAssembly, _doIgnoreCase, _doWholeWordMatch, _doRegex);
  881. if (_doRegex)
  882. {
  883. try
  884. {
  885. var opt = RegexOptions.Compiled | RegexOptions.CultureInvariant;
  886. if (!_doIgnoreCase) opt |= RegexOptions.IgnoreCase;
  887. var filter = _searchFilterAssembly;
  888. if (_doWholeWordMatch) filter = @"\b" + filter + @"\b";
  889. regx = new Regex(filter, opt);
  890. }
  891. catch (Exception)
  892. {
  893. // Regex was invalid
  894. wrongRegx = true;
  895. doSearch = false;
  896. }
  897. }
  898. }
  899. var doRepaint = _textArea.Render(fixedFontStyle, _inspectorView, _showBranchMarkers, doSearch, sc, regx);
  900. // A change in the underlying textArea has happened, that requires the GUI to be repainted during this frame.
  901. if (doRepaint) Repaint();
  902. if (Event.current.type != EventType.Layout)
  903. {
  904. HandleMouseEventForSelection(_inspectorView, controlID, _showBranchMarkers);
  905. HandleKeyboardEventForSelection(_inspectorView, _showBranchMarkers);
  906. }
  907. if (_leftClicked)
  908. {
  909. GenericMenu menu = new GenericMenu();
  910. menu.AddItem(EditorGUIUtility.TrTextContent("Copy Selection"), false, _textArea.DoSelectionCopy);
  911. menu.AddItem(EditorGUIUtility.TrTextContent("Copy Color Tags"), _textArea.CopyColorTags, _textArea.ChangeCopyMode);
  912. menu.AddItem(EditorGUIUtility.TrTextContent("Select All"), false, _textArea.SelectAll);
  913. menu.AddItem(EditorGUIUtility.TrTextContent($"Find in {DisassemblyKindNames[(int)_disasmKind]}"), _searchBarVisible, EnableDisableSearchBar);
  914. menu.ShowAsContext();
  915. _leftClicked = false;
  916. }
  917. GUILayout.EndScrollView();
  918. if (_searchBarVisible)
  919. {
  920. if (_searchFieldAssembly == null)
  921. {
  922. _searchFieldAssembly = new SearchField();
  923. _searchFieldAssembly.autoSetFocusOnFindCommand = false;
  924. }
  925. int hitnumbers = _textArea.NrSearchHits > 0 ? _textArea.ActiveSearchNr + 1 : 0;
  926. var hitNumberContent = new GUIContent(" " + hitnumbers + " of " + _textArea.NrSearchHits + " hits");
  927. GUILayout.BeginHorizontal();
  928. // Makes sure that on "enter" keyboard event, the focus is not taken away from searchField.
  929. if (saveSearchFieldFromEvent) GUI.FocusControl("BurstInspectorGUI");
  930. string newFilterAssembly;
  931. if (wrongRegx)
  932. {
  933. var colb = GUI.contentColor;
  934. GUI.contentColor = Color.red;
  935. newFilterAssembly = _searchFieldAssembly.OnGUI(_searchFilterAssembly);
  936. GUI.contentColor = colb;
  937. }
  938. else
  939. {
  940. newFilterAssembly = _searchFieldAssembly.OnGUI(_searchFilterAssembly);
  941. }
  942. // Give back focus to the searchField, if we took it away.
  943. if (saveSearchFieldFromEvent)
  944. {
  945. _searchFieldAssembly.SetFocus();
  946. saveSearchFieldFromEvent = false;
  947. }
  948. if (newFilterAssembly != _searchFilterAssembly)
  949. {
  950. _searchFilterAssembly = newFilterAssembly;
  951. _textArea.StopSearching();
  952. }
  953. _doIgnoreCase = GUILayout.Toggle(_doIgnoreCase, _ignoreCase);
  954. _doWholeWordMatch = GUILayout.Toggle(_doWholeWordMatch, _matchWord);
  955. _doRegex = GUILayout.Toggle(_doRegex, _regexSearch);
  956. GUILayout.Label(hitNumberContent);
  957. if (GUILayout.Button(GUIContent.none, EditorStyles.searchFieldCancelButton))
  958. {
  959. _searchBarVisible = false;
  960. _textArea.StopSearching();
  961. }
  962. GUILayout.EndHorizontal();
  963. }
  964. }
  965. if (fontSize != fontSizeIndex)
  966. {
  967. _textArea.Invalidate();
  968. fontSizeIndex = fontSize;
  969. EditorPrefs.SetInt(FontSizeIndexPref, fontSize);
  970. fixedFontStyle = null;
  971. }
  972. if (expandAllBlocks)
  973. {
  974. _textArea.ExpandAllBlocks();
  975. }
  976. if (focusCode)
  977. {
  978. _textArea.FocusCodeBlocks();
  979. }
  980. }
  981. GUILayout.EndVertical();
  982. SplitterGUILayout.EndHorizontalSplit();
  983. GUILayout.EndHorizontal();
  984. }
  985. public static bool IsBurstError(string disassembly)
  986. {
  987. return _rx.IsMatch(disassembly ?? "");
  988. }
  989. /// <summary>
  990. /// Focuses the view on the active function if a jump is doable.
  991. /// </summary>
  992. /// <param name="workingArea">Current assembly view.</param>
  993. /// <param name="wasTextSetup">Whether text was set in <see cref="_textArea"/>.</param>
  994. /// <param name="target">Target job to find function in.</param>
  995. /// <returns>Whether a focus was attempted or not.</returns>
  996. private bool TryFocusJobInAssemblyView(ref Rect workingArea, bool wasTextSetup, BurstCompileTarget target)
  997. {
  998. bool TryFindByLabel(ref Rect workingArea)
  999. {
  1000. var regx = default(Regex);
  1001. var sb = new StringBuilder();
  1002. if (target.IsStaticMethod)
  1003. {
  1004. // Search for fullname as label
  1005. // Null reference not a danger, because of target being a static method
  1006. sb.Append(target.Method.DeclaringType.ToString().Replace("+", "."));
  1007. // Generic labels will be sorounded by "", while standard static methods won't
  1008. var genericArguments = target.JobType.GenericTypeArguments;
  1009. if (genericArguments.Length > 0)
  1010. {
  1011. // Need to alter the generic arguments from [] to <> form
  1012. // Removing [] form
  1013. var idx = sb.ToString().LastIndexOf('[');
  1014. sb.Remove(idx, sb.Length - idx);
  1015. // Adding <> form
  1016. sb.Append('<').Append(BurstCompileTarget.Pretty(genericArguments[0]));
  1017. for (var i = 1; i < genericArguments.Length; i++)
  1018. {
  1019. sb.Append(",").Append(BurstCompileTarget.Pretty(genericArguments[i]));
  1020. }
  1021. sb.Append('>').Append('.').Append(target.Method.Name);
  1022. }
  1023. else
  1024. {
  1025. sb.Append('.').Append(target.Method.Name);
  1026. }
  1027. const RegexOptions opt = RegexOptions.Compiled | RegexOptions.CultureInvariant;
  1028. regx = new Regex(@$"{Regex.Escape(sb.ToString())}[^"":]+"":", opt);
  1029. }
  1030. else
  1031. {
  1032. // Append full method name. Using display name for simpler access
  1033. var targetName = target.GetDisplayName();
  1034. // Remove part that tells about used interface
  1035. var idx = 0;
  1036. // If generic the argument part must also be removed, as they won't match
  1037. if ((idx = targetName.IndexOf('[')) == -1) idx = targetName.IndexOf('-') - 1;
  1038. targetName = targetName.Remove(idx);
  1039. sb.Append($@""".*<{Regex.Escape(targetName)}.*"":");
  1040. const RegexOptions opt = RegexOptions.Compiled | RegexOptions.CultureInvariant;
  1041. regx = new Regex(sb.ToString(), opt);
  1042. }
  1043. var sc = new SearchCriteria(sb.ToString(), false, false, true);
  1044. return _textArea.SearchText(sc, regx, ref workingArea, true, true);
  1045. }
  1046. var foundTarget = false;
  1047. // _isTextSetLastEvent used so we call this at the first scroll-able event after text was set.
  1048. // We cannot scroll during used or layout events, and the order of events are:
  1049. // 1. Used event: text is set in textArea
  1050. // 2. Layout event: Cannot do the jump yet
  1051. // 3. Repaint event: Now jump is doable
  1052. // Hence _isTextSetLastEvent is only set on layout events (during phase 2)
  1053. if (wasTextSetup)
  1054. {
  1055. // Need to call Layout to setup fontsize before searching
  1056. _textArea.Layout(fixedFontStyle, _textArea.horizontalPad);
  1057. foundTarget = TryFindByLabel(ref workingArea);
  1058. _textArea.StopSearching(); // Clear the internals of _textArea from this search; to avoid highlighting
  1059. // Clear other possible search, so it won't interfere with this.
  1060. _searchFilterAssembly = string.Empty;
  1061. // We need to do a Repaint() in order for the view to actually update immediately.
  1062. if (foundTarget) Repaint();
  1063. }
  1064. return foundTarget;
  1065. }
  1066. private void EnableDisableSearchBar()
  1067. {
  1068. _searchBarVisible = !_searchBarVisible;
  1069. if (_searchBarVisible && _searchFieldAssembly != null)
  1070. {
  1071. _searchFieldAssembly.SetFocus();
  1072. }
  1073. else if (!_searchBarVisible)
  1074. {
  1075. _textArea.StopSearching();
  1076. }
  1077. }
  1078. private bool _doIgnoreCase = false;
  1079. private bool _doWholeWordMatch = false;
  1080. private bool _doRegex = false;
  1081. internal static string GetDisassembly(MethodInfo method, string options)
  1082. {
  1083. try
  1084. {
  1085. var result = BurstCompilerService.GetDisassembly(method, options);
  1086. if (result.IndexOf('\t') >= 0)
  1087. {
  1088. result = result.Replace("\t", " ");
  1089. }
  1090. // Workaround to remove timings
  1091. if (result.Contains("Burst timings"))
  1092. {
  1093. var index = result.IndexOf("While compiling", StringComparison.Ordinal);
  1094. if (index > 0)
  1095. {
  1096. result = result.Substring(index);
  1097. }
  1098. }
  1099. return result;
  1100. }
  1101. catch (Exception e)
  1102. {
  1103. return "Failed to compile:\n" + e.Message;
  1104. }
  1105. }
  1106. internal static BurstDisassembler.AsmKind FetchAsmKind(BurstTargetCpu cpu, DisassemblyKind kind)
  1107. {
  1108. if (kind == DisassemblyKind.Asm)
  1109. {
  1110. switch (cpu)
  1111. {
  1112. case BurstTargetCpu.Auto:
  1113. string cpuType = BurstCompiler.GetTargetCpuFromHost();
  1114. if (cpuType.Contains("Arm"))
  1115. {
  1116. return BurstDisassembler.AsmKind.ARM;
  1117. }
  1118. return BurstDisassembler.AsmKind.Intel;
  1119. case BurstTargetCpu.ARMV7A_NEON32:
  1120. case BurstTargetCpu.ARMV8A_AARCH64:
  1121. case BurstTargetCpu.ARMV8A_AARCH64_HALFFP:
  1122. case BurstTargetCpu.THUMB2_NEON32:
  1123. case BurstTargetCpu.ARMV9A:
  1124. return BurstDisassembler.AsmKind.ARM;
  1125. case BurstTargetCpu.WASM32:
  1126. return BurstDisassembler.AsmKind.Wasm;
  1127. default:
  1128. return BurstDisassembler.AsmKind.Intel;
  1129. }
  1130. }
  1131. else
  1132. {
  1133. return BurstDisassembler.AsmKind.LLVMIR;
  1134. }
  1135. }
  1136. }
  1137. /// <summary>
  1138. /// Important: id for namespaces are negative, and ids for jobs are positive.
  1139. /// This lets us use the id for a job as an index directy into <see cref="_targets"/>.
  1140. /// Hence before going from <see cref="TreeViewItem"/> to <see cref="_targets"/> index,
  1141. /// One should check whether current item has any children (Only jobs are leafs).
  1142. /// </summary>
  1143. internal class BurstMethodTreeView : TreeView
  1144. {
  1145. private readonly Func<string> _getFilter;
  1146. private readonly Func<(bool,bool)> _getJobListFilterToggles;
  1147. private List<BurstCompileTarget> _targets;
  1148. public BurstMethodTreeView(TreeViewState state, Func<string> getFilter, Func<(bool,bool)> getJobListFilterToggles) : base(state)
  1149. {
  1150. _getFilter = getFilter;
  1151. _getJobListFilterToggles = getJobListFilterToggles;
  1152. showBorder = true;
  1153. }
  1154. public void Initialize(List<BurstCompileTarget> targets, bool identicalTargets)
  1155. {
  1156. _targets = targets;
  1157. Reload();
  1158. if (!identicalTargets) { ExpandAll(); }
  1159. }
  1160. /// <remarks>
  1161. /// Assumes that <see cref="str"/> is derived from <see cref="Type"/>.<see cref="Type.FullName"/>
  1162. /// i.e. types are separated by '+'.
  1163. /// </remarks>
  1164. /// <param name="str">Given type name string.</param>
  1165. /// <returns>(List of namespaces/types, index of method name in <see cref="str"/>)</returns>
  1166. internal static (List<StringSlice> ns, int nsEndIdx) ExtractNameSpaces(in string str)
  1167. {
  1168. if (str is null) { throw new ArgumentNullException(nameof(str)); }
  1169. var nameSpaces = new List<StringSlice>();
  1170. int len = str.Length;
  1171. int scope = 0;
  1172. int previdx = 0;
  1173. for (int i = 0; i < len; i++)
  1174. {
  1175. bool stop = false;
  1176. char c = str[i];
  1177. switch (c)
  1178. {
  1179. case '(':
  1180. // Jump out as we just found argument list!!!
  1181. stop = true;
  1182. break;
  1183. // We keep looking, as classes might have these in name:
  1184. case '{':
  1185. case '<':
  1186. case '[':
  1187. scope++;
  1188. break;
  1189. case '}':
  1190. case '>':
  1191. case ']':
  1192. scope--;
  1193. break;
  1194. case '+' when scope == 0:
  1195. nameSpaces.Add(new StringSlice(str, previdx, i - previdx));
  1196. previdx = i + 1;
  1197. break;
  1198. }
  1199. if (stop) { break; }
  1200. }
  1201. return (nameSpaces, previdx);
  1202. }
  1203. internal static (int idN, List<TreeViewItem> added, List<StringSlice> nameSpace)
  1204. ProcessNewItem(int idN, int idJ, BurstCompileTarget newTarget, List<StringSlice> oldNameSpace)
  1205. {
  1206. // Find all namespaces used for new target:
  1207. string fns = newTarget.JobType.FullName;
  1208. string dn = newTarget.GetDisplayName();
  1209. (List<StringSlice> newNameSpaces, int nameSpaceEndIdx) = ExtractNameSpaces(fns);
  1210. int methodNameIdx = nameSpaceEndIdx;
  1211. if (newTarget.IsStaticMethod)
  1212. {
  1213. // Static method does not have the function name in fns, so fix methodNameIdx.
  1214. methodNameIdx = dn.IndexOf('(', methodNameIdx) - newTarget.Method.Name.Length;
  1215. // Add the last namespace:
  1216. newNameSpaces.Add(new StringSlice(dn, nameSpaceEndIdx, methodNameIdx-1 - nameSpaceEndIdx));
  1217. }
  1218. string methodName = dn.Substring(methodNameIdx);
  1219. int iNewNs = 0;
  1220. int lNewNs = newNameSpaces.Count;
  1221. int iOldNs = 0;
  1222. int lOldNs = oldNameSpace.Count;
  1223. var added = new List<TreeViewItem>(lNewNs);
  1224. int depth = 0;
  1225. // Skip all namespaces shared by previous but increase depth accordingly:
  1226. for (; iNewNs < lNewNs && iOldNs < lOldNs && newNameSpaces[iNewNs] == oldNameSpace[iOldNs];
  1227. depth++, iNewNs++, iOldNs++) {}
  1228. // Handle all new namespaces:
  1229. for (; iNewNs < lNewNs;
  1230. depth++, iNewNs++)
  1231. {
  1232. added.Add(new TreeViewItem { id = --idN, depth = depth, displayName = newNameSpaces[iNewNs].ToString()});
  1233. }
  1234. // Add the function name:
  1235. added.Add(new TreeViewItem { id = idJ, depth = depth, displayName = methodName });
  1236. return (idN, added, newNameSpaces);
  1237. }
  1238. protected override TreeViewItem BuildRoot()
  1239. {
  1240. var root = new TreeViewItem {id = 0, depth = -1, displayName = "Root"};
  1241. var allItems = new List<TreeViewItem>();
  1242. if (_targets != null)
  1243. {
  1244. var filter = _getFilter();
  1245. var (showUnityNamespaceJobs, showDOTSGeneratedJobs) = _getJobListFilterToggles();
  1246. // Have two separate ids so "jobs ids == jobs index".
  1247. int idJ = 0;
  1248. int idN = 0;
  1249. var oldNameSpace = new List<StringSlice>();
  1250. foreach (BurstCompileTarget target in _targets)
  1251. {
  1252. // idJ used as index into _targets, which means it should also take hidden targets into account!
  1253. idJ++;
  1254. string displayName = target.GetDisplayName();
  1255. bool filtered =
  1256. (!string.IsNullOrEmpty(filter) &&
  1257. displayName.IndexOf(filter, 0, displayName.Length,
  1258. StringComparison.InvariantCultureIgnoreCase) < 0)
  1259. || (!showUnityNamespaceJobs &&
  1260. displayName.StartsWith("Unity.", StringComparison.InvariantCultureIgnoreCase))
  1261. || (!showDOTSGeneratedJobs &&
  1262. displayName.Contains(".Generated"));
  1263. if (filtered) { continue; }
  1264. try
  1265. {
  1266. var (newIdN, added, nameSpace) =
  1267. ProcessNewItem(idN, idJ, target, oldNameSpace);
  1268. allItems.AddRange(added);
  1269. idN = newIdN;
  1270. oldNameSpace = nameSpace;
  1271. }
  1272. catch (Exception ex)
  1273. {
  1274. Debug.Log($"Internal error: Could not add {displayName}\n Because: {ex.Message}");
  1275. }
  1276. }
  1277. }
  1278. SetupParentsAndChildrenFromDepths(root, allItems);
  1279. return root;
  1280. }
  1281. public new IList<int> GetSelection()
  1282. {
  1283. IList<int> selection = base.GetSelection();
  1284. // selection == non-leaf node => no job selected
  1285. if (selection.Count > 0 && selection[0] < 0) { return new List<int>(); }
  1286. return selection;
  1287. }
  1288. internal bool TrySelectByDisplayName(string name)
  1289. {
  1290. var id = 1;
  1291. foreach (var t in _targets)
  1292. {
  1293. if (t.GetDisplayName() == name)
  1294. {
  1295. try
  1296. {
  1297. SetSelection(new[] { id });
  1298. FrameItem(id);
  1299. return true;
  1300. }
  1301. catch (ArgumentException)
  1302. {
  1303. // When a search is made in the job list, such that the job we search for is filtered away
  1304. // FrameItem(id) will throw a dictionary error. So we catch this, and tell the caller that
  1305. // it cannot be selected.
  1306. return false;
  1307. }
  1308. }
  1309. else
  1310. {
  1311. ++id;
  1312. }
  1313. }
  1314. return false;
  1315. }
  1316. protected override void RowGUI(RowGUIArgs args)
  1317. {
  1318. if (!args.item.hasChildren)
  1319. {
  1320. var target = _targets[args.item.id - 1];
  1321. var wasEnabled = GUI.enabled;
  1322. GUI.enabled = target.HasRequiredBurstCompileAttributes;
  1323. base.RowGUI(args);
  1324. GUI.enabled = wasEnabled;
  1325. }
  1326. else
  1327. {
  1328. // Label GUI:
  1329. base.RowGUI(args);
  1330. }
  1331. }
  1332. protected override void SingleClickedItem(int id)
  1333. {
  1334. // If labeled click try and fold/expand:
  1335. if (id < 0)
  1336. {
  1337. SetExpanded(id, !IsExpanded(id));
  1338. SetSelection(new List<int>());
  1339. }
  1340. }
  1341. protected override bool CanMultiSelect(TreeViewItem item)
  1342. {
  1343. return false;
  1344. }
  1345. }
  1346. }