123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Runtime.CompilerServices;
- using System.Text;
- using System.Threading.Tasks;
- using Unity.Burst.LowLevel;
- using UnityEditor;
- using UnityEditor.Compilation;
- using UnityEditor.IMGUI.Controls;
- using UnityEngine;
-
- [assembly: InternalsVisibleTo("Unity.Burst.Editor.Tests")]
-
- namespace Unity.Burst.Editor
- {
- internal enum AssemblyKind
- {
- RawNoDebugInformation,
- RawWithDebugInformation,
- EnhancedMinimalDebugInformation,
- EnhancedFullDebugInformation,
- ColouredMinimalDebugInformation,
- ColouredFullDebugInformation,
- }
-
- internal class BurstInspectorGUI : EditorWindow
- {
- private static bool Initialized;
-
- private static void EnsureInitialized()
- {
- if (Initialized)
- {
- return;
- }
-
- Initialized = true;
-
- #if UNITY_2020_2_OR_NEWER
- BurstLoader.OnBurstShutdown += () =>
- {
- if (EditorWindow.HasOpenInstances<BurstInspectorGUI>())
- {
- var window = EditorWindow.GetWindow<BurstInspectorGUI>("Burst Inspector");
- window.Close();
- }
- };
- #endif
- }
-
- private const string FontSizeIndexPref = "BurstInspectorFontSizeIndex";
-
- private static readonly string[] DisassemblyKindNames =
- {
- "Assembly",
- ".NET IL",
- "LLVM IR (Unoptimized)",
- "LLVM IR (Optimized)",
- "LLVM IR Optimisation Diagnostics"
- };
-
- private static readonly string[] AssemblyOptions =
- {
- "Plain (No debug information)",
- "Plain (With debug information)",
- "Enhanced (Minimal debug information)",
- "Enhanced (Full debug information)",
- "Coloured (Minimal debug information)",
- "Coloured (Full debug information)"
- };
-
- private static string[] DisasmOptions;
-
- private static string[] GetDisasmOptions()
- {
- if (DisasmOptions == null)
- {
- // We can't initialize this in BurstInspectorGUI.cctor because BurstCompilerOptions may not yet
- // have been initialized by BurstLoader. So we initialize on-demand here. This method doesn't need to
- // be thread-safe because it's only called from the UI thread.
- DisasmOptions = new[]
- {
- "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.Asm),
- "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.IL),
- "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.IR),
- "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.IROptimized),
- "\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDump, NativeDumpFlags.IRPassAnalysis)
- };
- }
- return DisasmOptions;
- }
-
- private static readonly SplitterState TreeViewSplitterState = new SplitterState(new float[] { 30, 70 }, new int[] { 128, 128 }, null);
-
- private static readonly string[] TargetCpuNames = Enum.GetNames(typeof(BurstTargetCpu));
-
- private static readonly int[] FontSizes =
- {
- 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20
- };
-
- private static string[] _fontSizesText;
-
- [NonSerialized]
- private readonly BurstDisassembler _burstDisassembler;
-
- [SerializeField] private BurstTargetCpu _targetCpu = BurstTargetCpu.Auto;
-
- [SerializeField] private DisassemblyKind _disasmKind = DisassemblyKind.Asm;
-
- [NonSerialized]
- private GUIStyle _fixedFontStyle;
-
- [NonSerialized]
- private int _fontSizeIndex = -1;
-
- [SerializeField] private int _previousTargetIndex = -1;
-
- [SerializeField] private bool _safetyChecks = false;
- [SerializeField] private bool _enhancedDisassembly = true;
- [SerializeField] private int _assemblyKind = -1;
- [SerializeField] private string _searchFilter;
-
- private int _assemblyKindPrior = -1;
- private bool _sameTargetButDifferentAssemblyKind = false;
- [SerializeField] private Vector2 _scrollPos;
- private SearchField _searchField;
-
- [SerializeField] private string _selectedItem;
-
- [NonSerialized]
- private List<BurstCompileTarget> _targets;
-
- [NonSerialized]
- private LongTextArea _textArea;
-
- [NonSerialized]
- private Font _font;
-
- [NonSerialized]
- private BurstMethodTreeView _treeView;
-
- [NonSerialized]
- private bool _initialized;
-
- [NonSerialized]
- private bool _requiresRepaint;
-
- private int FontSize => FontSizes[_fontSizeIndex];
-
- public BurstInspectorGUI()
- {
- _burstDisassembler = new BurstDisassembler();
- _assemblyKindPrior = _assemblyKind;
- }
-
- public void OnEnable()
- {
- EnsureInitialized();
-
- if (_treeView == null) _treeView = new BurstMethodTreeView(new TreeViewState(), () => _searchFilter);
-
- var assemblyList = BurstReflection.EditorAssembliesThatCanPossiblyContainJobs;
-
- Task.Run(
- () =>
- {
- // Do this stuff asynchronously.
- var result = BurstReflection.FindExecuteMethods(assemblyList, BurstReflectionAssemblyOptions.None);
- _targets = result.CompileTargets;
- _targets.Sort((left, right) => string.Compare(left.GetDisplayName(), right.GetDisplayName(), StringComparison.Ordinal));
- return result;
- })
- .ContinueWith(t =>
- {
- // Do this stuff on the main (UI) thread.
- if (t.Status == TaskStatus.RanToCompletion)
- {
- foreach (var logMessage in t.Result.LogMessages)
- {
- switch (logMessage.LogType)
- {
- case BurstReflection.LogType.Warning:
- Debug.LogWarning(logMessage.Message);
- break;
- case BurstReflection.LogType.Exception:
- Debug.LogException(logMessage.Exception);
- break;
- default:
- throw new InvalidOperationException();
- }
- }
-
- _treeView.Targets = _targets;
- _treeView.Reload();
-
- if (_selectedItem == null || !_treeView.TrySelectByDisplayName(_selectedItem))
- {
- _previousTargetIndex = -1;
- _scrollPos = Vector2.zero;
- }
-
- _requiresRepaint = true;
- _initialized = true;
- }
- else if (t.Exception != null)
- {
- Debug.LogError($"Could not load Inspector: {t.Exception}");
- }
- });
- }
-
- private void CleanupFont()
- {
- if (_font != null)
- {
- DestroyImmediate(_font, true);
- _font = null;
- }
- }
-
- public void OnDisable()
- {
- CleanupFont();
- }
-
- public void Update()
- {
- // Need to do this because if we call Repaint from anywhere else,
- // it doesn't do anything if this window is not currently focused.
- if (_requiresRepaint)
- {
- Repaint();
- _requiresRepaint = false;
- }
- }
-
- private void FlowToNewLine(ref float remainingWidth, float resetWidth, GUIStyle style, GUIContent content)
- {
- float spaceRemainingBeforeNewLine = EditorStyles.toggle.CalcSize(new GUIContent("WWWW")).x;
-
- remainingWidth -= style.CalcSize(content).x;
- if (remainingWidth <= spaceRemainingBeforeNewLine)
- {
- remainingWidth = resetWidth;
- GUILayout.EndHorizontal();
- GUILayout.BeginHorizontal();
- }
- }
-
- private bool IsRaw(AssemblyKind kind)
- {
- return kind == AssemblyKind.RawNoDebugInformation || kind == AssemblyKind.RawWithDebugInformation;
- }
-
- private bool IsEnhanced(AssemblyKind kind)
- {
- return !IsRaw(kind);
- }
-
- private bool IsColoured(AssemblyKind kind)
- {
- return kind == AssemblyKind.ColouredMinimalDebugInformation || kind == AssemblyKind.ColouredFullDebugInformation;
- }
-
- private void RenderButtonBars(float width, BurstCompileTarget target, out bool doCopy, out int fontIndex)
- {
- float remainingWidth = width;
-
- var contentDisasm = new GUIContent("Enhanced Disassembly");
- var contentSafety = new GUIContent("Safety Checks");
- var contentCodeGenOptions = new GUIContent("Auto");
- var contentLabelFontSize = new GUIContent("Font Size");
- var contentFontSize = new GUIContent("99");
- var contentCopyToClip = new GUIContent("Copy to Clipboard");
-
- GUILayout.BeginHorizontal();
-
- _assemblyKind = EditorGUILayout.Popup(_assemblyKind, AssemblyOptions, EditorStyles.popup);
- FlowToNewLine(ref remainingWidth, width, EditorStyles.popup, contentDisasm);
-
- _safetyChecks = GUILayout.Toggle(_safetyChecks, contentSafety, EditorStyles.toggle);
- FlowToNewLine(ref remainingWidth, width, EditorStyles.toggle, contentSafety);
-
- EditorGUI.BeginDisabledGroup(!target.HasRequiredBurstCompileAttributes);
-
- _targetCpu = (BurstTargetCpu)EditorGUILayout.Popup((int)_targetCpu, TargetCpuNames, EditorStyles.popup);
- FlowToNewLine(ref remainingWidth, width, EditorStyles.popup, contentCodeGenOptions);
-
- GUILayout.Label("Font Size", EditorStyles.label);
- fontIndex = EditorGUILayout.Popup(_fontSizeIndex, _fontSizesText, EditorStyles.popup);
- FlowToNewLine(ref remainingWidth, width, EditorStyles.label,contentLabelFontSize);
- FlowToNewLine(ref remainingWidth, width, EditorStyles.popup,contentFontSize);
-
- doCopy = GUILayout.Button(contentCopyToClip, EditorStyles.miniButton);
- FlowToNewLine(ref remainingWidth, width, EditorStyles.miniButton,contentCopyToClip);
- EditorGUI.EndDisabledGroup();
-
- GUILayout.EndHorizontal();
-
- _disasmKind = (DisassemblyKind) GUILayout.Toolbar((int) _disasmKind, DisassemblyKindNames, GUILayout.ExpandWidth(true), GUILayout.MinWidth(5*10));
- }
-
- public void OnGUI()
- {
- if (!_initialized)
- {
- GUILayout.BeginHorizontal();
- GUILayout.FlexibleSpace();
- GUILayout.BeginVertical();
- GUILayout.FlexibleSpace();
- GUILayout.Label("Loading...");
- GUILayout.FlexibleSpace();
- GUILayout.EndVertical();
- GUILayout.FlexibleSpace();
- GUILayout.EndHorizontal();
- return;
- }
-
- // Make sure that editor options are synchronized
- BurstEditorOptions.EnsureSynchronized();
-
- if (_fontSizesText == null)
- {
- _fontSizesText = new string[FontSizes.Length];
- for (var i = 0; i < FontSizes.Length; ++i) _fontSizesText[i] = FontSizes[i].ToString();
- }
-
- if (_fontSizeIndex == -1)
- {
- _fontSizeIndex = EditorPrefs.GetInt(FontSizeIndexPref, 5);
- _fontSizeIndex = Math.Max(0, _fontSizeIndex);
- _fontSizeIndex = Math.Min(_fontSizeIndex, FontSizes.Length - 1);
- }
-
- if (_fixedFontStyle == null)
- {
- _fixedFontStyle = new GUIStyle(GUI.skin.label);
- string fontName;
- if (Application.platform == RuntimePlatform.WindowsEditor)
- fontName = "Consolas";
- else
- fontName = "Courier";
-
- CleanupFont();
-
- _font = Font.CreateDynamicFontFromOSFont(fontName, FontSize);
- _fixedFontStyle.font = _font;
- _fixedFontStyle.fontSize = FontSize;
- }
-
- if (_searchField == null) _searchField = new SearchField();
-
- if (_textArea == null) _textArea = new LongTextArea();
-
- GUILayout.BeginHorizontal();
-
- // SplitterGUILayout.BeginHorizontalSplit is internal in Unity but we don't have much choice
- SplitterGUILayout.BeginHorizontalSplit(TreeViewSplitterState);
-
- GUILayout.BeginVertical(GUILayout.Width(position.width / 3));
-
- GUILayout.Label("Compile Targets", EditorStyles.boldLabel);
-
- var newFilter = _searchField.OnGUI(_searchFilter);
-
- if (newFilter != _searchFilter)
- {
- _searchFilter = newFilter;
- _treeView.Reload();
- }
-
- _treeView.OnGUI(GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true)));
-
- var lastRectSize = GUILayoutUtility.GetLastRect();
-
- GUILayout.EndVertical();
-
- GUILayout.BeginVertical();
-
- var selection = _treeView.GetSelection();
- if (selection.Count == 1)
- {
- var targetIndex = selection[0];
- var target = _targets[targetIndex - 1];
- var targetOptions = target.Options;
-
- // Stash selected item name to handle domain reloads more gracefully
- _selectedItem = target.GetDisplayName();
-
- if (_assemblyKind == -1)
- {
- if (_enhancedDisassembly)
- {
- _assemblyKind = (int)AssemblyKind.ColouredMinimalDebugInformation;
- }
- else
- {
- _assemblyKind = (int)AssemblyKind.RawNoDebugInformation;
- }
- }
-
- // Refresh if any options are changed
- bool doCopy;
- int fontSize;
- // -14 to add a little bit of space for the vertical scrollbar to display correctly
- RenderButtonBars((position.width*2)/3 - 14, target, out doCopy, out fontSize);
-
- var supportsEnhancedRendering = _disasmKind == DisassemblyKind.Asm || _disasmKind == DisassemblyKind.OptimizedIR || _disasmKind == DisassemblyKind.UnoptimizedIR;
-
- // We are currently formatting only Asm output
- var isTextFormatted = IsEnhanced((AssemblyKind)_assemblyKind) && supportsEnhancedRendering;
-
- // Depending if we are formatted or not, we don't render the same text
- var textToRender = isTextFormatted ? target.FormattedDisassembly : target.RawDisassembly;
-
- // Only refresh if we are switching to a new selection that hasn't been disassembled yet
- // Or we are changing disassembly settings (safety checks / enhanced disassembly)
- var targetRefresh = textToRender == null
- || target.DisassemblyKind != _disasmKind
- || targetOptions.EnableBurstSafetyChecks != _safetyChecks
- || target.TargetCpu != _targetCpu
- || target.IsDarkMode != EditorGUIUtility.isProSkin;
-
- bool targetChanged = _previousTargetIndex != targetIndex;
-
- _previousTargetIndex = targetIndex;
-
- if (_assemblyKindPrior != _assemblyKind)
- {
- targetRefresh = true;
- _assemblyKindPrior = _assemblyKind; // Needs to be refreshed, as we need to change disassembly options
-
- // If the target did not changed but our assembly kind did, we need to remember this.
- if (!targetChanged)
- {
- _sameTargetButDifferentAssemblyKind = true;
- }
- }
-
- // If the previous target changed the assembly kind and we have a target change, we need to
- // refresh the assembly because we'll have cached the previous assembly kinds output rather
- // than the one requested.
- if (_sameTargetButDifferentAssemblyKind && targetChanged)
- {
- targetRefresh = true;
- _sameTargetButDifferentAssemblyKind = false;
- }
-
- if (targetRefresh)
- {
- // TODO: refactor this code with a proper AppendOption to avoid these "\n"
- var options = new StringBuilder();
-
- target.TargetCpu = _targetCpu;
- target.DisassemblyKind = _disasmKind;
- targetOptions.EnableBurstSafetyChecks = _safetyChecks;
- target.IsDarkMode = EditorGUIUtility.isProSkin;
- targetOptions.EnableBurstCompileSynchronously = true;
-
- string defaultOptions;
- if (targetOptions.TryGetOptions(target.IsStaticMethod ? (MemberInfo)target.Method : target.JobType, true, out defaultOptions))
- {
- options.Append(defaultOptions);
-
- // Disables the 2 current warnings generated from code (since they clutter up the inspector display)
- // BC1370 - throw inside code not guarded with ConditionalSafetyCheck attribute
- // BC1322 - loop intrinsic on loop that has been optimised away
- options.Append($"\n{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDisableWarnings, "BC1370;BC1322")}");
-
- options.AppendFormat("\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionTarget, TargetCpuNames[(int)_targetCpu]));
-
- switch ((AssemblyKind)_assemblyKind)
- {
- case AssemblyKind.EnhancedMinimalDebugInformation:
- case AssemblyKind.ColouredMinimalDebugInformation:
- options.AppendFormat("\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "2"));
- break;
- case AssemblyKind.ColouredFullDebugInformation:
- case AssemblyKind.EnhancedFullDebugInformation:
- case AssemblyKind.RawWithDebugInformation:
- options.AppendFormat("\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "1"));
- break;
- default:
- case AssemblyKind.RawNoDebugInformation:
- break;
- }
-
- options.AppendFormat("\n" + BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionJitSkipBurstInitialize));
-
- var baseOptions = options.ToString().Trim('\n', ' ');
-
- target.RawDisassembly = GetDisassembly(target.Method, baseOptions + GetDisasmOptions()[(int)_disasmKind]);
-
- if (isTextFormatted)
- {
- target.FormattedDisassembly = _burstDisassembler.Process(target.RawDisassembly, FetchAsmKind(_targetCpu, _disasmKind), target.IsDarkMode, IsColoured((AssemblyKind)_assemblyKind));
- textToRender = target.FormattedDisassembly;
- }
- else
- {
- target.FormattedDisassembly = null;
- textToRender = target.RawDisassembly;
- }
- }
- }
-
- if (textToRender != null)
- {
- _textArea.Text = textToRender;
- if (targetChanged) _scrollPos = Vector2.zero;
- _scrollPos = GUILayout.BeginScrollView(_scrollPos, true, true);
- _textArea.Render(_fixedFontStyle, _scrollPos, lastRectSize);
- GUILayout.EndScrollView();
- }
-
- if (doCopy)
- {
- // When copying to the clipboard, we copy the version the user sees
- EditorGUIUtility.systemCopyBuffer = textToRender ?? string.Empty;
- }
-
- if (fontSize != _fontSizeIndex)
- {
- _textArea.Invalidate();
- _fontSizeIndex = fontSize;
- EditorPrefs.SetInt(FontSizeIndexPref, fontSize);
- _fixedFontStyle = null;
- }
- }
-
- GUILayout.EndVertical();
-
- SplitterGUILayout.EndHorizontalSplit();
-
- GUILayout.EndHorizontal();
- }
-
- private static string GetDisassembly(MethodInfo method, string options)
- {
- try
- {
- var result = BurstCompilerService.GetDisassembly(method, options);
- if (result.IndexOf('\t') >= 0)
- {
- result = result.Replace("\t", " ");
- }
-
- // Workaround to remove timings
- if (result.Contains("Burst timings"))
- {
- var index = result.IndexOf("While compiling", StringComparison.Ordinal);
- if (index > 0)
- {
- result = result.Substring(index);
- }
- }
-
- return result;
- }
- catch (Exception e)
- {
- return "Failed to compile:\n" + e.Message;
- }
- }
-
- private static BurstDisassembler.AsmKind FetchAsmKind(BurstTargetCpu cpu, DisassemblyKind kind)
- {
- if (kind == DisassemblyKind.Asm)
- {
- switch (cpu)
- {
- case BurstTargetCpu.ARMV7A_NEON32:
- case BurstTargetCpu.ARMV8A_AARCH64:
- case BurstTargetCpu.ARMV8A_AARCH64_HALFFP:
- case BurstTargetCpu.THUMB2_NEON32:
- return BurstDisassembler.AsmKind.ARM;
- case BurstTargetCpu.WASM32:
- return BurstDisassembler.AsmKind.Wasm;
- }
- return BurstDisassembler.AsmKind.Intel;
- }
- else
- {
- return BurstDisassembler.AsmKind.LLVMIR;
- }
- }
- }
-
- internal class BurstMethodTreeView : TreeView
- {
- private readonly Func<string> _getFilter;
-
- public BurstMethodTreeView(TreeViewState state, Func<string> getFilter) : base(state)
- {
- _getFilter = getFilter;
- showBorder = true;
- }
-
- public List<BurstCompileTarget> Targets { get; set; }
-
- protected override TreeViewItem BuildRoot()
- {
- var root = new TreeViewItem {id = 0, depth = -1, displayName = "Root"};
- var allItems = new List<TreeViewItem>();
-
- if (Targets != null)
- {
- allItems.Capacity = Targets.Count;
- var id = 1;
- var filter = _getFilter();
- foreach (var t in Targets)
- {
- var displayName = t.GetDisplayName();
- if (string.IsNullOrEmpty(filter) || displayName.IndexOf(filter, 0, displayName.Length, StringComparison.InvariantCultureIgnoreCase) >= 0)
- allItems.Add(new TreeViewItem {id = id, depth = 0, displayName = displayName});
-
- ++id;
- }
- }
-
- SetupParentsAndChildrenFromDepths(root, allItems);
-
- return root;
- }
-
- internal bool TrySelectByDisplayName(string name)
- {
- var id = 1;
- foreach (var t in Targets)
- {
- if (t.GetDisplayName() == name)
- {
- SetSelection(new[] { id });
- FrameItem(id);
- return true;
- }
- else
- {
- ++id;
- }
- }
- return false;
- }
-
- protected override void RowGUI(RowGUIArgs args)
- {
- var target = Targets[args.item.id - 1];
- var wasEnabled = GUI.enabled;
- GUI.enabled = target.HasRequiredBurstCompileAttributes;
- base.RowGUI(args);
- GUI.enabled = wasEnabled;
- }
-
- protected override bool CanMultiSelect(TreeViewItem item)
- {
- return false;
- }
- }
- }
|