1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603 |
- 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 System.Text.RegularExpressions;
- using UnityEditor.IMGUI.Controls;
- using UnityEngine;
-
- [assembly: InternalsVisibleTo("Unity.Burst.Editor.Tests")]
- [assembly: InternalsVisibleTo("Unity.Burst.Tester.Editor.Tests")]
-
- namespace Unity.Burst.Editor
- {
- internal class BurstInspectorGUI : EditorWindow
- {
- private static bool Initialized;
-
- private static void EnsureInitialized()
- {
- if (Initialized)
- {
- return;
- }
-
- Initialized = true;
-
- BurstLoader.OnBurstShutdown += () =>
- {
- if (EditorWindow.HasOpenInstances<BurstInspectorGUI>())
- {
- var window = EditorWindow.GetWindow<BurstInspectorGUI>("Burst Inspector");
- window.Close();
- }
- };
- }
-
- private const string FontSizeIndexPref = "BurstInspectorFontSizeIndex";
-
- private static readonly string[] DisassemblyKindNames =
- {
- "Assembly",
- ".NET IL",
- "LLVM IR (Unoptimized)",
- "LLVM IR (Optimized)",
- "LLVM IR Optimisation Diagnostics"
- };
-
- internal enum AssemblyOptions
- {
- PlainWithoutDebugInformation = 0,
- PlainWithDebugInformation = 1,
- EnhancedWithMinimalDebugInformation = 2,
- EnhancedWithFullDebugInformation = 3,
- ColouredWithMinimalDebugInformation = 4,
- ColouredWithFullDebugInformation = 5
- }
- internal AssemblyOptions? _assemblyKind = null;
- private AssemblyOptions? _assemblyKindPrior = null;
- private AssemblyOptions _oldAssemblyKind;
-
- private bool SupportsEnhancedRendering => _disasmKind == DisassemblyKind.Asm || _disasmKind == DisassemblyKind.OptimizedIR || _disasmKind == DisassemblyKind.UnoptimizedIR;
-
- private static string[] DisasmOptions;
-
- internal 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 string[] SIMDSmellTest = { "False", "True" };
-
- private static readonly int[] FontSizes =
- {
- 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20
- };
-
- private static string[] _fontSizesText;
- internal const int _scrollbarThickness = 14;
-
- internal float _buttonOverlapInspectorView = 0;
-
- /// <remarks>Used because it's not legal to change layout of GUI in a frame without the users input.</remarks>
- private float _buttonBarWidth = -1;
-
- [NonSerialized]
- internal readonly BurstDisassembler _burstDisassembler;
-
- private const string BurstSettingText = "Inspector Settings/";
-
- [SerializeField] private BurstTargetCpu _targetCpu = BurstTargetCpu.Auto;
-
- [SerializeField] private DisassemblyKind _disasmKind = DisassemblyKind.Asm;
- [SerializeField] private DisassemblyKind _oldDisasmKind = DisassemblyKind.Asm;
-
- [NonSerialized]
- internal GUIStyle fixedFontStyle;
-
- [NonSerialized]
- internal int fontSizeIndex = -1;
-
- [SerializeField] private int _previousTargetIndex = -1;
-
- [SerializeField] private bool _safetyChecks = false;
- [SerializeField] private bool _showBranchMarkers = true;
- [SerializeField] private bool _enhancedDisassembly = true;
- [SerializeField] private string _searchFilterJobs;
- [SerializeField] private bool _showUnityNamespaceJobs = false;
- [SerializeField] private bool _showDOTSGeneratedJobs = false;
- [SerializeField] private bool _focusTargetJob = true;
- [SerializeField] private string _searchFilterAssembly = String.Empty;
-
- [SerializeField] private bool _sameTargetButDifferentAssemblyKind = false;
- [SerializeField] internal Vector2 _scrollPos;
- internal SearchField _searchFieldJobs;
- internal SearchField _searchFieldAssembly;
- private bool saveSearchFieldFromEvent = false;
-
- [SerializeField] private bool _searchBarVisible = true;
-
- [SerializeField] private string _selectedItem;
-
- [NonSerialized]
- private BurstCompileTarget _target;
- [NonSerialized]
- private List<BurstCompileTarget> _targets;
- // Used as a serialized representation of _targets:
- [SerializeField] private List<string> targetNames;
-
- [NonSerialized]
- internal LongTextArea _textArea;
-
- internal Rect _inspectorView;
-
- [NonSerialized]
- internal Font _font;
-
- [NonSerialized]
- internal BurstMethodTreeView _treeView;
- // Serialized representation of _treeView:
- [SerializeField] private TreeViewState treeViewState;
-
- [NonSerialized]
- internal bool _initialized;
-
- [NonSerialized]
- private bool _requiresRepaint;
-
- private int FontSize => FontSizes[fontSizeIndex];
-
- private static readonly Regex _rx = new Regex(@"^.*\(\d+,\d+\):\sBurst\serror");
-
- private bool _leftClicked = false;
-
- [SerializeField] private bool _isCompileError = false;
- [SerializeField] private bool _prevWasCompileError;
-
- [SerializeField] private bool _smellTest = false;
-
- // Caching GUIContent and style options for button bar
- private readonly GUIContent _contentShowUnityNamespaceJobs = new GUIContent("Show Unity Namespace");
- private readonly GUIContent _contentShowDOTSGeneratedJobs = new GUIContent("Show \".Generated\"");
- private readonly GUIContent _contentDisasm = new GUIContent("Enhanced With Minimal Debug Information");
- private readonly GUIContent _contentCollapseToCode = new GUIContent("Focus on Code");
- private readonly GUIContent _contentExpandAll = new GUIContent("Expand All");
- private readonly GUIContent _contentBranchLines = new GUIContent("Show Branch Flow");
- private readonly GUIContent[] _contentsTarget;
- private readonly GUIContent[] _contentsFontSize;
- private readonly GUIContent[] _contentsSmellTest =
- {
- new GUIContent("Highlight SIMD Scalar vs Packed (False)"),
- new GUIContent("Highlight SIMD Scalar vs Packed (True)")
- };
-
- // content for button search bar
- private readonly GUIContent _ignoreCase = new GUIContent("Match Case");
- private readonly GUIContent _matchWord = new GUIContent("Whole words");
- private readonly GUIContent _regexSearch = new GUIContent("Regex");
-
- private readonly GUILayoutOption[] _toolbarStyleOptions = { GUILayout.ExpandWidth(true), GUILayout.MinWidth(5 * 10) };
-
- private readonly string[] _branchMarkerOptions = { "Hide Branch Flow", "Show Branch Flow" };
- private readonly string[] _safetyCheckOptions = { "Safety Check On", "Safety Check Off" };
-
-
- private enum KeyboardOperation
- {
- SelectAll,
- Copy,
- MoveLeft,
- MoveRight,
- MoveUp,
- MoveDown,
- Search,
- Escape,
- Enter,
- }
-
- private Dictionary<Event, KeyboardOperation> _keyboardEvents;
-
- private void FillKeyboardEvent()
- {
- if (_keyboardEvents != null)
- {
- return;
- }
-
- _keyboardEvents = new Dictionary<Event, KeyboardOperation>();
-
- _keyboardEvents.Add(Event.KeyboardEvent("#left"), KeyboardOperation.MoveLeft);
- _keyboardEvents.Add(Event.KeyboardEvent("#right"), KeyboardOperation.MoveRight);
- _keyboardEvents.Add(Event.KeyboardEvent("#down"), KeyboardOperation.MoveDown);
- _keyboardEvents.Add(Event.KeyboardEvent("#up"), KeyboardOperation.MoveUp);
- _keyboardEvents.Add(Event.KeyboardEvent("escape"), KeyboardOperation.Escape);
- _keyboardEvents.Add(Event.KeyboardEvent("return"), KeyboardOperation.Enter);
- _keyboardEvents.Add(Event.KeyboardEvent("#return"), KeyboardOperation.Enter);
-
- _keyboardEvents.Add(Event.KeyboardEvent("left"), KeyboardOperation.MoveLeft);
- _keyboardEvents.Add(Event.KeyboardEvent("right"), KeyboardOperation.MoveRight);
- _keyboardEvents.Add(Event.KeyboardEvent("up"), KeyboardOperation.MoveUp);
- _keyboardEvents.Add(Event.KeyboardEvent("down"), KeyboardOperation.MoveDown);
-
- if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX)
- {
- _keyboardEvents.Add(Event.KeyboardEvent("%a"), KeyboardOperation.SelectAll);
- _keyboardEvents.Add(Event.KeyboardEvent("%c"), KeyboardOperation.Copy);
- _keyboardEvents.Add(Event.KeyboardEvent("%f"), KeyboardOperation.Search);
- }
- else
- {
- // windows or linux bindings.
- _keyboardEvents.Add(Event.KeyboardEvent("^a"), KeyboardOperation.SelectAll);
- _keyboardEvents.Add(Event.KeyboardEvent("^c"), KeyboardOperation.Copy);
- _keyboardEvents.Add(Event.KeyboardEvent("^f"), KeyboardOperation.Search);
- }
- }
-
- public BurstInspectorGUI()
- {
- _burstDisassembler = new BurstDisassembler();
-
- string[] names = Enum.GetNames(typeof(BurstTargetCpu));
- int size = names.Length;
- _contentsTarget = new GUIContent[size];
- for (int i = 0; i < size; i++)
- {
- _contentsTarget[i] = new GUIContent($"Target ({names[i]})");
- }
-
- size = FontSizes.Length;
- _contentsFontSize = new GUIContent[size];
- for (int i = 0; i < size; i++)
- {
- _contentsFontSize[i] = new GUIContent($"Font Size ({FontSizes[i].ToString()})");
- }
- }
-
- private bool DisplayAssemblyKind(Enum assemblyKind)
- {
- var assemblyOption = (AssemblyOptions)assemblyKind;
- if (_disasmKind != DisassemblyKind.Asm || _isCompileError)
- {
- return assemblyOption == AssemblyOptions.PlainWithoutDebugInformation;
- }
- return true;
- }
-
- public void OnEnable()
- {
- EnsureInitialized();
-
- var newTreeState = false;
- if (treeViewState is null)
- {
- treeViewState = new TreeViewState();
- newTreeState = true;
- }
- _treeView ??= _treeView = new BurstMethodTreeView
- (
- treeViewState,
- () => _searchFilterJobs,
- () => (_showUnityNamespaceJobs, _showDOTSGeneratedJobs)
- );
-
- if (_keyboardEvents == null) FillKeyboardEvent();
-
- 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();
- }
- }
-
- var newNames = new List<string>(_targets.Count);
- foreach (var target in _targets)
- {
- newNames.Add(target.GetDisplayName());
- }
-
- bool identical = !newTreeState && newNames.Count == targetNames.Count;
- int len = newNames.Count;
- int i = 0;
- while (identical && i < len)
- {
- identical = newNames[i] == targetNames[i];
- i++;
- }
- targetNames = newNames;
- _treeView.Initialize(_targets, identical);
-
- 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}");
- }
- });
- }
-
- #if !UNITY_2023_1_OR_NEWER
- private void CleanupFont()
- {
- if (_font != null)
- {
- DestroyImmediate(_font, true);
- _font = null;
- }
- }
-
- public void OnDisable()
- {
- CleanupFont();
- }
- #endif
-
- 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;
- }
-
- // Need this because pressing new target, and then not invoking new events,
- // will leave the assembly unrendered.
- // This is not included in above, to minimize needed calls.
- if (_target != null && _target.JustLoaded)
- {
- Repaint();
- }
- }
-
- /// <summary>
- /// Checks if there is space for given content withs style, and starts new horizontalgroup
- /// if there is no space on this line.
- /// </summary>
- private void FlowToNewLine(ref float remainingWidth, float width, Vector2 size)
- {
- float sizeX = size.x + _scrollbarThickness / 2;
- if (sizeX >= remainingWidth)
- {
- _buttonOverlapInspectorView += size.y + 2;
- remainingWidth = width - sizeX;
- GUILayout.EndHorizontal();
- GUILayout.BeginHorizontal();
- }
- else
- {
- remainingWidth -= sizeX;
- }
- }
-
- private bool IsRaw(AssemblyOptions kind)
- {
- return kind == AssemblyOptions.PlainWithoutDebugInformation || kind == AssemblyOptions.PlainWithDebugInformation;
- }
-
- private bool IsEnhanced(AssemblyOptions kind)
- {
- return !IsRaw(kind);
- }
-
- private bool IsColoured(AssemblyOptions kind)
- {
- return kind == AssemblyOptions.ColouredWithMinimalDebugInformation || kind == AssemblyOptions.ColouredWithFullDebugInformation;
- }
-
- /// <summary>
- /// Renders buttons bar, and handles saving/loading of _assemblyKind options when changing in inspector settings
- /// that disable/enables some options for _assemblyKind.
- /// </summary>
- private void HandleButtonBars(BurstCompileTarget target, bool targetChanged, out int fontIndex, out bool collapse, out bool focusCode)
- {
- // We can only make an educated guess for the correct width.
- if (_buttonBarWidth == -1)
- {
- _buttonBarWidth = (position.width * 2) / 3 - _scrollbarThickness;
- }
-
- RenderButtonBars(_buttonBarWidth, target, out fontIndex, out collapse, out focusCode);
-
- var disasmKindChanged = _oldDisasmKind != _disasmKind;
-
- // Handles saving and loading _assemblyKind option when going between settings, that disable/enable some options for it
- if ((disasmKindChanged && _oldDisasmKind == DisassemblyKind.Asm && !_isCompileError)
- || (targetChanged && !_prevWasCompileError && _isCompileError && _disasmKind == DisassemblyKind.Asm))
- {
- // save when _disasmKind changed from Asm WHEN we are not looking at a burst compile error,
- // or when target changed from non compile error to compile error and current _disasmKind is Asm.
- _oldAssemblyKind = (AssemblyOptions)_assemblyKind;
- }
- else if ((disasmKindChanged && _disasmKind == DisassemblyKind.Asm && !_isCompileError) ||
- (targetChanged && _prevWasCompileError && _disasmKind == DisassemblyKind.Asm))
- {
- // load when _diasmKind changed to Asm and we are not at burst compile error,
- // or when target changed from a burst compile error while _disasmKind is Asm.
- _assemblyKind = _oldAssemblyKind;
- }
-
- // if _assemblyKind is something that is not available, force it up to PlainWithoutDebugInformation.
- if ((_disasmKind != DisassemblyKind.Asm && _assemblyKind != AssemblyOptions.PlainWithoutDebugInformation)
- || _isCompileError)
- {
- _assemblyKind = AssemblyOptions.PlainWithoutDebugInformation;
- }
- }
-
- private void RenderButtonBars(float width, BurstCompileTarget target, out int fontIndex, out bool collapse, out bool focus)
- {
- var remainingWidth = width;
- GUILayout.BeginHorizontal();
-
- // First button should never call beginHorizontal().
- remainingWidth -= (EditorStyles.popup.CalcSize(_contentDisasm).x + _scrollbarThickness / 2f);
-
- EditorGUI.BeginDisabledGroup(target.DisassemblyKind == DisassemblyKind.IRPassAnalysis);
-
- _assemblyKind = (AssemblyOptions)EditorGUILayout.EnumPopup(GUIContent.none, _assemblyKind, DisplayAssemblyKind, true);
-
- EditorGUI.EndDisabledGroup();
-
- FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(_contentBranchLines));
- // Reversed "logic" to match the array of options, which has "positive" case on idx 0.
- _safetyChecks = EditorGUILayout.Popup(_safetyChecks ? 0 : 1, _safetyCheckOptions) == 0;
-
- EditorGUI.BeginDisabledGroup(!target.HasRequiredBurstCompileAttributes);
-
- GUIContent targetContent = _contentsTarget[(int)_targetCpu];
- FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(targetContent));
- _targetCpu = (BurstTargetCpu)LabeledPopup.Popup((int)_targetCpu, targetContent, TargetCpuNames);
-
- GUIContent fontSizeContent = _contentsFontSize[fontSizeIndex];
- FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(fontSizeContent));
- fontIndex = LabeledPopup.Popup(fontSizeIndex, fontSizeContent, _fontSizesText);
-
- EditorGUI.EndDisabledGroup();
-
- EditorGUI.BeginDisabledGroup(!IsEnhanced((AssemblyOptions)_assemblyKind) || !SupportsEnhancedRendering || _isCompileError);
-
- FlowToNewLine(ref remainingWidth, width, EditorStyles.miniButton.CalcSize(_contentCollapseToCode));
- focus = GUILayout.Button(_contentCollapseToCode, EditorStyles.miniButton);
-
- FlowToNewLine(ref remainingWidth, width, EditorStyles.miniButton.CalcSize(_contentExpandAll));
- collapse = GUILayout.Button(_contentExpandAll, EditorStyles.miniButton);
-
- FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(_contentBranchLines));
- _showBranchMarkers = EditorGUILayout.Popup(Convert.ToInt32(_showBranchMarkers), _branchMarkerOptions) == 1;
-
- int smellTestIdx = Convert.ToInt32(_smellTest);
- GUIContent smellTestContent = _contentsSmellTest[smellTestIdx];
- FlowToNewLine(ref remainingWidth, width, EditorStyles.popup.CalcSize(smellTestContent));
- _smellTest = LabeledPopup.Popup(smellTestIdx, smellTestContent, SIMDSmellTest) == 1;
-
- EditorGUI.EndDisabledGroup();
-
- GUILayout.EndHorizontal();
-
- _oldDisasmKind = _disasmKind;
- _disasmKind = (DisassemblyKind)GUILayout.Toolbar((int)_disasmKind, DisassemblyKindNames, _toolbarStyleOptions);
- }
-
- /// <summary>
- /// Handles mouse events for selecting text.
- /// </summary>
- /// <remarks>
- /// Must be called after Render(...), as it uses the mouse events, and Render(...)
- /// need mouse events for buttons etc.
- /// </remarks>
- private void HandleMouseEventForSelection(Rect workingArea, int controlID, bool showBranchMarkers)
- {
- var evt = Event.current;
- var mousePos = evt.mousePosition;
-
- if (_textArea.MouseOutsideView(workingArea, mousePos, controlID))
- {
- return;
- }
-
- switch (evt.type)
- {
- case EventType.MouseDown:
- // button 0 is left and 1 is right
- if (evt.button == 0)
- {
- _textArea.MouseClicked(showBranchMarkers, evt.shift, mousePos, controlID);
- }
- else
- {
- _leftClicked = true;
- }
- evt.Use();
- break;
- case EventType.MouseDrag:
- _textArea.DragMouse(mousePos, showBranchMarkers);
- evt.Use();
- break;
- case EventType.MouseUp:
- _textArea.MouseReleased();
- evt.Use();
- break;
- case EventType.ScrollWheel:
- _textArea.DoScroll(workingArea, evt.delta.y);
- // we cannot Use() (consume) scrollWheel events, as they are still needed in EndScrollView.
- break;
- }
- }
-
- private bool AssemblyFocused() => !((_treeView != null && _treeView.HasFocus()) || (_searchFieldAssembly != null && _searchFieldAssembly.HasFocus()));
-
- private void HandleKeyboardEventAssemblyView(Rect workingArea, KeyboardOperation op, Event evt, bool showBranchMarkers)
- {
- switch (op)
- {
- case KeyboardOperation.SelectAll:
- _textArea.SelectAll();
- evt.Use();
- break;
-
- case KeyboardOperation.Copy:
- _textArea.DoSelectionCopy();
- evt.Use();
- break;
-
- case KeyboardOperation.MoveLeft:
- if (evt.shift)
- {
- if (_textArea.HasSelection) _textArea.MoveSelectionLeft(workingArea, showBranchMarkers);
- }
- else
- {
- _textArea.MoveView(LongTextArea.Direction.Left, workingArea);
- }
-
- evt.Use();
- break;
-
- case KeyboardOperation.MoveRight:
- if (evt.shift)
- {
- if (_textArea.HasSelection) _textArea.MoveSelectionRight(workingArea, showBranchMarkers);
- }
- else
- {
- _textArea.MoveView(LongTextArea.Direction.Right, workingArea);
- }
-
- evt.Use();
- break;
-
- case KeyboardOperation.MoveUp:
- if (evt.shift)
- {
- if (_textArea.HasSelection) _textArea.MoveSelectionUp(workingArea, showBranchMarkers);
- }
- else
- {
- _textArea.MoveView(LongTextArea.Direction.Up, workingArea);
- }
-
- evt.Use();
- break;
-
- case KeyboardOperation.MoveDown:
- if (evt.shift)
- {
- if (_textArea.HasSelection) _textArea.MoveSelectionDown(workingArea, showBranchMarkers);
- }
- else
- {
- _textArea.MoveView(LongTextArea.Direction.Down, workingArea);
- }
-
- evt.Use();
- break;
- case KeyboardOperation.Search:
- _searchBarVisible = true;
- _searchFieldAssembly?.SetFocus();
- evt.Use();
- break;
- }
- }
-
- /// <remarks>
- /// Must be called after Render(...) because of depenency on LongTextArea.finalAreaSize.
- /// </remarks>
- private void HandleKeyboardEventForSelection(Rect workingArea, bool showBranchMarkers)
- {
- var evt = Event.current;
-
- if (!_keyboardEvents.TryGetValue(evt, out var op))
- {
- return;
- }
-
- if (AssemblyFocused())
- {
- // Do input handling for assembly view.
- HandleKeyboardEventAssemblyView(workingArea, op, evt, showBranchMarkers);
- }
- else
- {
- // This amounts to logic for all else.
- switch (op)
- {
- case KeyboardOperation.Escape:
- if (_searchFieldAssembly != null && _searchFieldAssembly.HasFocus() && _searchFilterAssembly == "")
- {
- _searchBarVisible = false;
- evt.Use();
- }
- break;
- case KeyboardOperation.Enter:
- if (_searchFieldAssembly != null && _searchFieldAssembly.HasFocus())
- {
- _textArea.NextSearchHit(evt.shift, workingArea);
- saveSearchFieldFromEvent = true;
- evt.Use();
- }
- break;
- }
- }
- }
-
- private void RenderCompileTargetsFilters(float width)
- {
- GUILayout.BeginHorizontal();
- // Handle and render filtering toggles:
- var newShowUnityTests = GUILayout.Toggle(_showUnityNamespaceJobs, _contentShowUnityNamespaceJobs);
-
- FlowToNewLine(ref width, width, EditorStyles.toggle.CalcSize(_contentShowDOTSGeneratedJobs));
- var newShowDOTSGeneratedJobs = GUILayout.Toggle(_showDOTSGeneratedJobs, _contentShowDOTSGeneratedJobs);
- GUILayout.EndHorizontal();
-
- if (newShowUnityTests != _showUnityNamespaceJobs || newShowDOTSGeneratedJobs != _showDOTSGeneratedJobs)
- {
- _showDOTSGeneratedJobs = newShowDOTSGeneratedJobs;
- _showUnityNamespaceJobs = newShowUnityTests;
- _treeView.Reload();
- }
-
- // Handle and render search filter:
- var newFilter = _searchFieldJobs.OnGUI(_searchFilterJobs);
- if (newFilter != _searchFilterJobs)
- {
- _searchFilterJobs = newFilter;
- _treeView.Reload();
- }
- }
-
-
- private void CompileNewTarget(BurstCompileTarget target, BurstCompilerOptions targetOptions)
- {
- if (target.IsLoading) { return; }
-
- target.IsLoading = true;
- target.JustLoaded = false;
-
- // Setup target and it's compilation options.
- // This is done here as EditorGUIUtility.isProSkin must be on main thread.
- target.TargetCpu = _targetCpu;
- target.DisassemblyKind = _disasmKind;
- targetOptions.EnableBurstSafetyChecks = _safetyChecks;
- target.IsDarkMode = EditorGUIUtility.isProSkin;
-
- // Don't set debug mode, because it disables optimizations.
- // Instead we set debug level (None, Full, LineOnly) below.
- targetOptions.EnableBurstDebug = false;
-
- Task.Run(() =>
- {
- var options = new StringBuilder();
-
- if (targetOptions.TryGetOptions(target.IsStaticMethod ? (MemberInfo)target.Method : target.JobType, out var defaultOptions, isForCompilerClient: true))
- {
- options.AppendLine(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 optimized away
- options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDisableWarnings, "BC1370;BC1322")}");
-
- options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionTarget, TargetCpuNames[(int)_targetCpu])}");
-
- // For IRPassAnalysis, we always want full debug information.
- if (_disasmKind != DisassemblyKind.IRPassAnalysis)
- {
- switch (_assemblyKind)
- {
- case AssemblyOptions.EnhancedWithMinimalDebugInformation:
- case AssemblyOptions.ColouredWithMinimalDebugInformation:
- options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "2")}");
- break;
- case AssemblyOptions.ColouredWithFullDebugInformation:
- case AssemblyOptions.EnhancedWithFullDebugInformation:
- case AssemblyOptions.PlainWithDebugInformation:
- options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "1")}");
- break;
- default:
- case AssemblyOptions.PlainWithoutDebugInformation:
- options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "0")}");
- break;
- }
- }
- else
- {
- options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "1")}");
- }
-
- var baseOptions = options.ToString();
-
- target.RawDisassembly = GetDisassembly(target.Method, baseOptions + GetDisasmOptions()[(int)_disasmKind]);
-
- target.FormattedDisassembly = null;
-
- target.IsBurstError = IsBurstError(target.RawDisassembly);
- }
-
- target.IsLoading = false;
- target.JustLoaded = true;
- });
- }
-
- private void RenderBurstJobMenu()
- {
- float width = position.width / 3;
- GUILayout.BeginVertical(GUILayout.Width(width));
-
- // Render Treeview showing burst targets:
- GUILayout.Label("Compile Targets", EditorStyles.boldLabel);
- RenderCompileTargetsFilters(width);
-
- // Does not give proper rect during layout event.
- _inspectorView = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
-
- _treeView.OnGUI(_inspectorView);
-
- GUILayout.EndVertical();
- }
-
- private void HandleHorizontalFocus(float workingWidth, bool shouldSetupText, bool isTextFormatted)
- {
- if (!shouldSetupText || !isTextFormatted || !_burstDisassembler.IsInitialized) { return; }
-
- var branchFiller = _textArea.MaxLineDepth * 10;
-
- if (branchFiller < workingWidth / 2f) { return; }
-
- // Do horizontal padding:
- _scrollPos.x = _textArea.MaxLineDepth * 10;
- }
-
-
- private static void RenderLoading()
- {
- GUILayout.BeginHorizontal();
- GUILayout.FlexibleSpace();
- GUILayout.BeginVertical();
- GUILayout.FlexibleSpace();
- GUILayout.Label("Loading...");
- GUILayout.FlexibleSpace();
- GUILayout.EndVertical();
- GUILayout.FlexibleSpace();
- GUILayout.EndHorizontal();
- }
-
- public void OnGUI()
- {
- if (!_initialized)
- {
- RenderLoading();
- return;
- }
- // used to give hot control to inspector when a mouseDown event has happened.
- // This way we can register a mouseUp happening outside inspector.
- int controlID = GUIUtility.GetControlID(FocusType.Passive);
-
- // 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.font == null) // also check .font as it's reset somewhere when going out of play mode.
- {
- fixedFontStyle = new GUIStyle(GUI.skin.label);
-
- #if UNITY_2023_1_OR_NEWER
- _font = EditorGUIUtility.Load("Fonts/RobotoMono/RobotoMono-Regular.ttf") as Font;
- #else
- string fontName;
- if (Application.platform == RuntimePlatform.WindowsEditor)
- {
- fontName = "Consolas";
- }
- else
- {
- fontName = "Courier";
- }
-
- CleanupFont();
-
- _font = Font.CreateDynamicFontFromOSFont(fontName, FontSize);
- #endif
-
- fixedFontStyle.font = _font;
- fixedFontStyle.fontSize = FontSize;
- }
-
- if (_searchFieldJobs == null) _searchFieldJobs = 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);
-
- RenderBurstJobMenu();
-
- GUILayout.BeginVertical();
-
- var selection = _treeView.GetSelection();
- if (selection.Count == 1)
- {
- var targetIndex = selection[0];
- _target = _targets[targetIndex - 1];
- var targetOptions = _target.Options;
-
- var targetChanged = _previousTargetIndex != targetIndex;
-
- _previousTargetIndex = targetIndex;
-
- // Stash selected item name to handle domain reloads more gracefully
- _selectedItem = _target.GetDisplayName();
-
- if (_assemblyKind == null)
- {
- if (_enhancedDisassembly)
- {
- _assemblyKind = AssemblyOptions.ColouredWithMinimalDebugInformation;
- }
- else
- {
- _assemblyKind = AssemblyOptions.PlainWithoutDebugInformation;
- }
- _oldAssemblyKind = (AssemblyOptions)_assemblyKind;
- }
-
- // We are currently formatting only Asm output
- var isTextFormatted = IsEnhanced((AssemblyOptions)_assemblyKind) && SupportsEnhancedRendering;
-
- // Depending if we are formatted or not, we don't render the same text
- var textToRender = _target.RawDisassembly?.TrimStart('\n');
-
- // 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;
-
- 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)
- {
- CompileNewTarget(_target, targetOptions);
- }
-
- _prevWasCompileError = _isCompileError;
- _isCompileError = _target.IsBurstError;
-
- _buttonOverlapInspectorView = 0;
- var oldSimdSmellTest = _smellTest;
- HandleButtonBars(_target, targetChanged, out var fontSize, out var expandAllBlocks, out var focusCode);
- var simdSmellTestChanged = oldSimdSmellTest != _smellTest;
-
- // Guard against _textArea being used, as the assembly isn't ready yet.
- // Have to test against event so it cannot finish between a Layout event and Repaint event;
- // this is necessary as we cannot alter GUI between these events.
- if (_target.HasRequiredBurstCompileAttributes && (_target.IsLoading || (_target.JustLoaded && Event.current.type != EventType.Layout)))
- {
- RenderLoading();
-
- // Need to close the splits we opened.
- GUILayout.EndVertical();
- SplitterGUILayout.EndHorizontalSplit();
- GUILayout.EndHorizontal();
- return;
- }
-
- var justLoaded = _target.JustLoaded;
- _target.JustLoaded = false;
-
- // If ´CompileNewTarget´ finishes before we enter loading screen above `textToRender` might not be set.
- textToRender ??= _target.RawDisassembly?.TrimStart('\n');
-
- if (!string.IsNullOrEmpty(textToRender))
- {
- // we should only call SetDisassembler(...) the first time assemblyKind is changed with same target.
- // Otherwise it will kep re-initializing fields such as _folded, meaning we can no longer fold/unfold.
- var shouldSetupText = !_textArea.IsTextSet(_selectedItem)
- || justLoaded
- || simdSmellTestChanged;
-
- if (shouldSetupText)
- {
- _textArea.SetText(
- _selectedItem,
- textToRender,
- _target.IsDarkMode,
- _burstDisassembler,
- isTextFormatted && _burstDisassembler.Initialize(
- textToRender,
- FetchAsmKind(_targetCpu, _disasmKind),
- _target.IsDarkMode,
- IsColoured((AssemblyOptions)_assemblyKind),
- _smellTest));
- }
- if (justLoaded)
- {
- _scrollPos = Vector2.zero;
- }
-
- HandleHorizontalFocus(
- _inspectorView.width == 1f ? _buttonBarWidth : _inspectorView.width,
- shouldSetupText,
- isTextFormatted
- );
-
- // Fixing lastRectSize to actually be size of scroll view
- _inspectorView.position = _scrollPos;
- _inspectorView.width = position.width - (_inspectorView.width + _scrollbarThickness);
- _inspectorView.height -= (_buttonOverlapInspectorView + 4); //+4 for alignment.
- if (_searchBarVisible) _inspectorView.height -= EditorStyles.searchField.CalcHeight(GUIContent.none, 2); // 2 is just arbitrary, as the width does not alter height
-
- // repaint indicate end of frame, so we can alter width for menu items to new correct.
- if (Event.current.type == EventType.Repaint)
- {
- _buttonBarWidth = _inspectorView.width - _scrollbarThickness;
- }
-
- // Do search if we did not try and find assembly and we were actually going to do a search.
- if (_focusTargetJob && TryFocusJobInAssemblyView(ref _inspectorView, shouldSetupText, _target))
- {
- _scrollPos.y = _inspectorView.y - _textArea.fontHeight*10;
- }
-
- _scrollPos = GUILayout.BeginScrollView(_scrollPos, true, true);
-
- if (Event.current.type != EventType.Layout) // we always want mouse position feedback
- {
- _textArea.Interact(_inspectorView, Event.current.type);
- }
-
- // Set up search information if it is happening.
- Regex regx = default;
- SearchCriteria sc = default;
- var doSearch = _searchBarVisible && _searchFilterAssembly != "";
- var wrongRegx = false;
- if (doSearch)
- {
- sc = new SearchCriteria(_searchFilterAssembly, _doIgnoreCase, _doWholeWordMatch, _doRegex);
- if (_doRegex)
- {
- try
- {
- var opt = RegexOptions.Compiled | RegexOptions.CultureInvariant;
- if (!_doIgnoreCase) opt |= RegexOptions.IgnoreCase;
-
- var filter = _searchFilterAssembly;
- if (_doWholeWordMatch) filter = @"\b" + filter + @"\b";
-
- regx = new Regex(filter, opt);
- }
- catch (Exception)
- {
- // Regex was invalid
- wrongRegx = true;
- doSearch = false;
- }
- }
- }
-
- var doRepaint = _textArea.Render(fixedFontStyle, _inspectorView, _showBranchMarkers, doSearch, sc, regx);
-
- // A change in the underlying textArea has happened, that requires the GUI to be repainted during this frame.
- if (doRepaint) Repaint();
-
- if (Event.current.type != EventType.Layout)
- {
- HandleMouseEventForSelection(_inspectorView, controlID, _showBranchMarkers);
- HandleKeyboardEventForSelection(_inspectorView, _showBranchMarkers);
- }
-
- if (_leftClicked)
- {
- GenericMenu menu = new GenericMenu();
-
- menu.AddItem(EditorGUIUtility.TrTextContent("Copy Selection"), false, _textArea.DoSelectionCopy);
- menu.AddItem(EditorGUIUtility.TrTextContent("Copy Color Tags"), _textArea.CopyColorTags, _textArea.ChangeCopyMode);
- menu.AddItem(EditorGUIUtility.TrTextContent("Select All"), false, _textArea.SelectAll);
- menu.AddItem(EditorGUIUtility.TrTextContent($"Find in {DisassemblyKindNames[(int)_disasmKind]}"), _searchBarVisible, EnableDisableSearchBar);
- menu.ShowAsContext();
-
- _leftClicked = false;
- }
-
- GUILayout.EndScrollView();
-
- if (_searchBarVisible)
- {
- if (_searchFieldAssembly == null)
- {
- _searchFieldAssembly = new SearchField();
- _searchFieldAssembly.autoSetFocusOnFindCommand = false;
- }
-
- int hitnumbers = _textArea.NrSearchHits > 0 ? _textArea.ActiveSearchNr + 1 : 0;
- var hitNumberContent = new GUIContent(" " + hitnumbers + " of " + _textArea.NrSearchHits + " hits");
-
- GUILayout.BeginHorizontal();
-
- // Makes sure that on "enter" keyboard event, the focus is not taken away from searchField.
- if (saveSearchFieldFromEvent) GUI.FocusControl("BurstInspectorGUI");
-
- string newFilterAssembly;
- if (wrongRegx)
- {
- var colb = GUI.contentColor;
- GUI.contentColor = Color.red;
- newFilterAssembly = _searchFieldAssembly.OnGUI(_searchFilterAssembly);
- GUI.contentColor = colb;
- }
- else
- {
- newFilterAssembly = _searchFieldAssembly.OnGUI(_searchFilterAssembly);
- }
- // Give back focus to the searchField, if we took it away.
- if (saveSearchFieldFromEvent)
- {
- _searchFieldAssembly.SetFocus();
- saveSearchFieldFromEvent = false;
- }
-
-
- if (newFilterAssembly != _searchFilterAssembly)
- {
- _searchFilterAssembly = newFilterAssembly;
- _textArea.StopSearching();
- }
-
- _doIgnoreCase = GUILayout.Toggle(_doIgnoreCase, _ignoreCase);
- _doWholeWordMatch = GUILayout.Toggle(_doWholeWordMatch, _matchWord);
- _doRegex = GUILayout.Toggle(_doRegex, _regexSearch);
- GUILayout.Label(hitNumberContent);
- if (GUILayout.Button(GUIContent.none, EditorStyles.searchFieldCancelButton))
- {
- _searchBarVisible = false;
- _textArea.StopSearching();
- }
-
- GUILayout.EndHorizontal();
- }
- }
-
- if (fontSize != fontSizeIndex)
- {
- _textArea.Invalidate();
- fontSizeIndex = fontSize;
- EditorPrefs.SetInt(FontSizeIndexPref, fontSize);
- fixedFontStyle = null;
- }
-
- if (expandAllBlocks)
- {
- _textArea.ExpandAllBlocks();
- }
-
- if (focusCode)
- {
- _textArea.FocusCodeBlocks();
- }
- }
-
- GUILayout.EndVertical();
-
- SplitterGUILayout.EndHorizontalSplit();
-
- GUILayout.EndHorizontal();
- }
-
- public static bool IsBurstError(string disassembly)
- {
- return _rx.IsMatch(disassembly ?? "");
- }
-
- /// <summary>
- /// Focuses the view on the active function if a jump is doable.
- /// </summary>
- /// <param name="workingArea">Current assembly view.</param>
- /// <param name="wasTextSetup">Whether text was set in <see cref="_textArea"/>.</param>
- /// <param name="target">Target job to find function in.</param>
- /// <returns>Whether a focus was attempted or not.</returns>
- private bool TryFocusJobInAssemblyView(ref Rect workingArea, bool wasTextSetup, BurstCompileTarget target)
- {
- bool TryFindByLabel(ref Rect workingArea)
- {
- var regx = default(Regex);
- var sb = new StringBuilder();
- if (target.IsStaticMethod)
- {
- // Search for fullname as label
- // Null reference not a danger, because of target being a static method
- sb.Append(target.Method.DeclaringType.ToString().Replace("+", "."));
-
- // Generic labels will be sorounded by "", while standard static methods won't
- var genericArguments = target.JobType.GenericTypeArguments;
- if (genericArguments.Length > 0)
- {
- // Need to alter the generic arguments from [] to <> form
- // Removing [] form
- var idx = sb.ToString().LastIndexOf('[');
- sb.Remove(idx, sb.Length - idx);
-
- // Adding <> form
- sb.Append('<').Append(BurstCompileTarget.Pretty(genericArguments[0]));
- for (var i = 1; i < genericArguments.Length; i++)
- {
- sb.Append(",").Append(BurstCompileTarget.Pretty(genericArguments[i]));
- }
- sb.Append('>').Append('.').Append(target.Method.Name);
- }
- else
- {
- sb.Append('.').Append(target.Method.Name);
- }
-
- const RegexOptions opt = RegexOptions.Compiled | RegexOptions.CultureInvariant;
- regx = new Regex(@$"{Regex.Escape(sb.ToString())}[^"":]+"":", opt);
- }
- else
- {
- // Append full method name. Using display name for simpler access
- var targetName = target.GetDisplayName();
- // Remove part that tells about used interface
- var idx = 0;
- // If generic the argument part must also be removed, as they won't match
- if ((idx = targetName.IndexOf('[')) == -1) idx = targetName.IndexOf('-') - 1;
- targetName = targetName.Remove(idx);
-
- sb.Append($@""".*<{Regex.Escape(targetName)}.*"":");
-
- const RegexOptions opt = RegexOptions.Compiled | RegexOptions.CultureInvariant;
- regx = new Regex(sb.ToString(), opt);
- }
-
- var sc = new SearchCriteria(sb.ToString(), false, false, true);
-
- return _textArea.SearchText(sc, regx, ref workingArea, true, true);
- }
-
- var foundTarget = false;
- // _isTextSetLastEvent used so we call this at the first scroll-able event after text was set.
- // We cannot scroll during used or layout events, and the order of events are:
- // 1. Used event: text is set in textArea
- // 2. Layout event: Cannot do the jump yet
- // 3. Repaint event: Now jump is doable
- // Hence _isTextSetLastEvent is only set on layout events (during phase 2)
- if (wasTextSetup)
- {
- // Need to call Layout to setup fontsize before searching
- _textArea.Layout(fixedFontStyle, _textArea.horizontalPad);
-
- foundTarget = TryFindByLabel(ref workingArea);
- _textArea.StopSearching(); // Clear the internals of _textArea from this search; to avoid highlighting
-
- // Clear other possible search, so it won't interfere with this.
- _searchFilterAssembly = string.Empty;
-
- // We need to do a Repaint() in order for the view to actually update immediately.
- if (foundTarget) Repaint();
- }
-
- return foundTarget;
- }
-
- private void EnableDisableSearchBar()
- {
- _searchBarVisible = !_searchBarVisible;
-
- if (_searchBarVisible && _searchFieldAssembly != null)
- {
- _searchFieldAssembly.SetFocus();
- }
- else if (!_searchBarVisible)
- {
- _textArea.StopSearching();
- }
- }
- private bool _doIgnoreCase = false;
- private bool _doWholeWordMatch = false;
- private bool _doRegex = false;
-
- internal 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;
- }
- }
-
- internal static BurstDisassembler.AsmKind FetchAsmKind(BurstTargetCpu cpu, DisassemblyKind kind)
- {
- if (kind == DisassemblyKind.Asm)
- {
- switch (cpu)
- {
- case BurstTargetCpu.Auto:
- string cpuType = BurstCompiler.GetTargetCpuFromHost();
- if (cpuType.Contains("Arm"))
- {
- return BurstDisassembler.AsmKind.ARM;
- }
- return BurstDisassembler.AsmKind.Intel;
- case BurstTargetCpu.ARMV7A_NEON32:
- case BurstTargetCpu.ARMV8A_AARCH64:
- case BurstTargetCpu.ARMV8A_AARCH64_HALFFP:
- case BurstTargetCpu.THUMB2_NEON32:
- case BurstTargetCpu.ARMV9A:
- return BurstDisassembler.AsmKind.ARM;
- case BurstTargetCpu.WASM32:
- return BurstDisassembler.AsmKind.Wasm;
- default:
- return BurstDisassembler.AsmKind.Intel;
- }
- }
- else
- {
- return BurstDisassembler.AsmKind.LLVMIR;
- }
- }
- }
-
- /// <summary>
- /// Important: id for namespaces are negative, and ids for jobs are positive.
- /// This lets us use the id for a job as an index directy into <see cref="_targets"/>.
- /// Hence before going from <see cref="TreeViewItem"/> to <see cref="_targets"/> index,
- /// One should check whether current item has any children (Only jobs are leafs).
- /// </summary>
- internal class BurstMethodTreeView : TreeView
- {
- private readonly Func<string> _getFilter;
- private readonly Func<(bool,bool)> _getJobListFilterToggles;
-
- private List<BurstCompileTarget> _targets;
-
- public BurstMethodTreeView(TreeViewState state, Func<string> getFilter, Func<(bool,bool)> getJobListFilterToggles) : base(state)
- {
- _getFilter = getFilter;
- _getJobListFilterToggles = getJobListFilterToggles;
- showBorder = true;
- }
-
- public void Initialize(List<BurstCompileTarget> targets, bool identicalTargets)
- {
- _targets = targets;
- Reload();
- if (!identicalTargets) { ExpandAll(); }
- }
-
- /// <remarks>
- /// Assumes that <see cref="str"/> is derived from <see cref="Type"/>.<see cref="Type.FullName"/>
- /// i.e. types are separated by '+'.
- /// </remarks>
- /// <param name="str">Given type name string.</param>
- /// <returns>(List of namespaces/types, index of method name in <see cref="str"/>)</returns>
- internal static (List<StringSlice> ns, int nsEndIdx) ExtractNameSpaces(in string str)
- {
- if (str is null) { throw new ArgumentNullException(nameof(str)); }
-
- var nameSpaces = new List<StringSlice>();
- int len = str.Length;
- int scope = 0;
- int previdx = 0;
- for (int i = 0; i < len; i++)
- {
- bool stop = false;
- char c = str[i];
- switch (c)
- {
- case '(':
- // Jump out as we just found argument list!!!
- stop = true;
- break;
- // We keep looking, as classes might have these in name:
- case '{':
- case '<':
- case '[':
- scope++;
- break;
- case '}':
- case '>':
- case ']':
- scope--;
- break;
- case '+' when scope == 0:
- nameSpaces.Add(new StringSlice(str, previdx, i - previdx));
- previdx = i + 1;
- break;
- }
-
- if (stop) { break; }
- }
- return (nameSpaces, previdx);
- }
-
- internal static (int idN, List<TreeViewItem> added, List<StringSlice> nameSpace)
- ProcessNewItem(int idN, int idJ, BurstCompileTarget newTarget, List<StringSlice> oldNameSpace)
- {
- // Find all namespaces used for new target:
- string fns = newTarget.JobType.FullName;
- string dn = newTarget.GetDisplayName();
-
- (List<StringSlice> newNameSpaces, int nameSpaceEndIdx) = ExtractNameSpaces(fns);
-
- int methodNameIdx = nameSpaceEndIdx;
- if (newTarget.IsStaticMethod)
- {
- // Static method does not have the function name in fns, so fix methodNameIdx.
- methodNameIdx = dn.IndexOf('(', methodNameIdx) - newTarget.Method.Name.Length;
- // Add the last namespace:
- newNameSpaces.Add(new StringSlice(dn, nameSpaceEndIdx, methodNameIdx-1 - nameSpaceEndIdx));
- }
- string methodName = dn.Substring(methodNameIdx);
-
- int iNewNs = 0;
- int lNewNs = newNameSpaces.Count;
- int iOldNs = 0;
- int lOldNs = oldNameSpace.Count;
-
- var added = new List<TreeViewItem>(lNewNs);
- int depth = 0;
-
- // Skip all namespaces shared by previous but increase depth accordingly:
- for (; iNewNs < lNewNs && iOldNs < lOldNs && newNameSpaces[iNewNs] == oldNameSpace[iOldNs];
- depth++, iNewNs++, iOldNs++) {}
-
- // Handle all new namespaces:
- for (; iNewNs < lNewNs;
- depth++, iNewNs++)
- {
- added.Add(new TreeViewItem { id = --idN, depth = depth, displayName = newNameSpaces[iNewNs].ToString()});
- }
-
- // Add the function name:
- added.Add(new TreeViewItem { id = idJ, depth = depth, displayName = methodName });
-
- return (idN, added, newNameSpaces);
- }
-
- protected override TreeViewItem BuildRoot()
- {
- var root = new TreeViewItem {id = 0, depth = -1, displayName = "Root"};
- var allItems = new List<TreeViewItem>();
-
- if (_targets != null)
- {
- var filter = _getFilter();
- var (showUnityNamespaceJobs, showDOTSGeneratedJobs) = _getJobListFilterToggles();
- // Have two separate ids so "jobs ids == jobs index".
- int idJ = 0;
- int idN = 0;
- var oldNameSpace = new List<StringSlice>();
- foreach (BurstCompileTarget target in _targets)
- {
- // idJ used as index into _targets, which means it should also take hidden targets into account!
- idJ++;
-
- string displayName = target.GetDisplayName();
-
- bool filtered =
- (!string.IsNullOrEmpty(filter) &&
- displayName.IndexOf(filter, 0, displayName.Length,
- StringComparison.InvariantCultureIgnoreCase) < 0)
- || (!showUnityNamespaceJobs &&
- displayName.StartsWith("Unity.", StringComparison.InvariantCultureIgnoreCase))
- || (!showDOTSGeneratedJobs &&
- displayName.Contains(".Generated"));
-
- if (filtered) { continue; }
-
- try
- {
- var (newIdN, added, nameSpace) =
- ProcessNewItem(idN, idJ, target, oldNameSpace);
-
- allItems.AddRange(added);
- idN = newIdN;
- oldNameSpace = nameSpace;
- }
- catch (Exception ex)
- {
- Debug.Log($"Internal error: Could not add {displayName}\n Because: {ex.Message}");
- }
- }
- }
- SetupParentsAndChildrenFromDepths(root, allItems);
- return root;
- }
-
- public new IList<int> GetSelection()
- {
- IList<int> selection = base.GetSelection();
- // selection == non-leaf node => no job selected
- if (selection.Count > 0 && selection[0] < 0) { return new List<int>(); }
- return selection;
- }
-
- internal bool TrySelectByDisplayName(string name)
- {
- var id = 1;
- foreach (var t in _targets)
- {
- if (t.GetDisplayName() == name)
- {
- try
- {
- SetSelection(new[] { id });
- FrameItem(id);
- return true;
- }
- catch (ArgumentException)
- {
- // When a search is made in the job list, such that the job we search for is filtered away
- // FrameItem(id) will throw a dictionary error. So we catch this, and tell the caller that
- // it cannot be selected.
- return false;
- }
- }
- else
- {
- ++id;
- }
- }
- return false;
- }
-
- protected override void RowGUI(RowGUIArgs args)
- {
- if (!args.item.hasChildren)
- {
- var target = _targets[args.item.id - 1];
- var wasEnabled = GUI.enabled;
- GUI.enabled = target.HasRequiredBurstCompileAttributes;
- base.RowGUI(args);
- GUI.enabled = wasEnabled;
- }
- else
- {
- // Label GUI:
- base.RowGUI(args);
- }
- }
-
- protected override void SingleClickedItem(int id)
- {
- // If labeled click try and fold/expand:
- if (id < 0)
- {
- SetExpanded(id, !IsExpanded(id));
- SetSelection(new List<int>());
- }
- }
-
- protected override bool CanMultiSelect(TreeViewItem item)
- {
- return false;
- }
- }
- }
|