Maze / Library /PackageCache /com.unity.performance.profile-analyzer@1.2.2 /Editor /ProfileAnalyzerWindow.cs
| using System; | |
| using System.Collections.Generic; | |
| using System.Diagnostics; | |
| using System.IO; | |
| using System.Linq; | |
| using System.Text.RegularExpressions; | |
| using System.Threading; | |
| using UnityEditor.IMGUI.Controls; | |
| using UnityEditorInternal; | |
| using UnityEngine; | |
| using UnityEngine.Profiling; | |
| using Debug = UnityEngine.Debug; | |
| using ProfilerCommon = UnityEditorInternal.ProfilerDriver; | |
| using ProfilerMarkerAbstracted = Unity.Profiling.ProfilerMarker; | |
| namespace UnityEditor.Performance.ProfileAnalyzer | |
| { | |
| enum ThreadRange | |
| { | |
| Median, | |
| UpperQuartile, | |
| Max | |
| }; | |
| enum ActiveTab | |
| { | |
| Summary, | |
| Compare, | |
| }; | |
| enum ActiveView | |
| { | |
| Single, | |
| Left, | |
| Right | |
| } | |
| enum ThreadActivity | |
| { | |
| None, | |
| Analyze, | |
| AnalyzeDone, | |
| Compare, | |
| CompareDone, | |
| Load, | |
| LoadDone | |
| }; | |
| enum TopTenDisplay | |
| { | |
| Normalized, | |
| LongestTime, | |
| }; | |
| enum NameFilterOperation | |
| { | |
| All, // AND | |
| Any, // OR | |
| }; | |
| enum RemoveMarkerOperation | |
| { | |
| ShowAll, | |
| HideWaitForFPS, | |
| HideWaitForPresent, | |
| Custom, | |
| }; | |
| /// <summary> | |
| /// Main profile Analyzer UI window | |
| /// </summary> | |
| public class ProfileAnalyzerWindow : EditorWindow | |
| { | |
| internal static class Styles | |
| { | |
| public static readonly GUIContent emptyString = new GUIContent("", ""); | |
| public static readonly GUIContent dash = new GUIContent("-", ""); | |
| public static readonly GUIContent thread = new GUIContent("Thread", ""); | |
| public static readonly GUIContent noThread = new GUIContent("", "Thread not present on this data set"); | |
| public static readonly GUIContent max = new GUIContent("Max", "The peak value in the data set"); | |
| public static readonly GUIContent upperQuartile = new GUIContent("Upper Quartile", "The middle value between the median and the highest value of the data set. I.e. at 75% of the ordered data."); | |
| public static readonly GUIContent mean = new GUIContent("Mean", "The average value in the data set"); | |
| public static readonly GUIContent median = new GUIContent("Median", "The central value in the data set"); | |
| public static readonly GUIContent lowerQuartile = new GUIContent("Lower Quartile", "The middle number between the smallest number and the median of the data set. I.e. at 25% of the ordered data."); | |
| public static readonly GUIContent min = new GUIContent("Min", "The minimum value in the data set"); | |
| public static readonly GUIContent individualMin = new GUIContent("Individual Min", "The minimum value in the data set for an individual marker instance (not the total in the frame)"); | |
| public static readonly GUIContent individualMax = new GUIContent("Individual Max", "The maximum value in the data set for an individual marker instance (not the total in the frame)"); | |
| public static readonly GUIContent export = new GUIContent("Export", "Export profiler data as CSV files"); | |
| public static readonly GUIContent pullOpen = new GUIContent("Pull Data", "Pull data from Unity profiler.\nFirst you must open Unity profiler to pull data from it"); | |
| public static readonly GUIContent pullRange = new GUIContent("Pull Data", "Pull data from Unity profiler.\nFirst you must use the Unity profiler to capture data from application"); | |
| public static readonly GUIContent pullRecording = new GUIContent("Pull Data", "Pull data from Unity profiler.\nStop Unity profiler recording to enable pulling data"); | |
| public static readonly GUIContent pull = new GUIContent("Pull Data", "Pull data from Unity profiler"); | |
| public static readonly GUIContent nameFilter = new GUIContent("Name Filter : ", "Only show markers containing the strings\n\n(Effects the marker table below)"); | |
| public static readonly GUIContent nameExclude = new GUIContent("Exclude Names : ", "Excludes markers containing the strings\n\n(Effects the marker table below)"); | |
| public static readonly GUIContent threadFilter = new GUIContent("Thread : ", "Select threads to focus on\n\n(Effects the marker table below)"); | |
| public static readonly GUIContent threadFilterSelect = new GUIContent("Select", "Select threads to focus on\n\n(Effects the marker table below)"); | |
| public static readonly GUIContent unitFilter = new GUIContent("Units : ", "Units to show in UI"); | |
| public static readonly GUIContent timingFilter = new GUIContent("Analysis Type : ", TimingOptions.Tooltip); | |
| public static readonly GUIContent markerColumns = new GUIContent("Marker Columns : ", "Set of Columns to show in the table"); | |
| public static readonly GUIContent graphPairing = new GUIContent("Pair Graph Selection", "Selections on one graph will affect the other"); | |
| public static readonly GUIContent removeMarker = new GUIContent("Remove : ", "Remove a specific marker from time analysis\n\n(Effects all views)"); | |
| public static readonly GUIContent hideRemoveMarkers = new GUIContent("Hide Removed Markers", "Hide removed markers from the marker table"); | |
| public static readonly GUIContent frameSummary = new GUIContent("Frame Summary", ""); | |
| public static readonly GUIContent frameCount = new GUIContent("Frame Count", "Frame Count"); | |
| public static readonly GUIContent frameStart = new GUIContent("Start", "Frame Start"); | |
| public static readonly GUIContent frameEnd = new GUIContent("End", "Frame End"); | |
| public static readonly GUIContent threadSummary = new GUIContent("Thread Summary", ""); | |
| public static readonly GUIContent threadGraphScale = new GUIContent("Graph Scale : ", ""); | |
| public static readonly GUIContent[] threadRanges = | |
| { | |
| new GUIContent("Median", "Median frame time"), | |
| new GUIContent("Upper quartile", "Upper quartile of frame time"), | |
| new GUIContent("Max", "Max frame time") | |
| }; | |
| public static readonly GUIContent markerSummary = new GUIContent("Marker Summary", ""); | |
| public static readonly GUIContent filters = new GUIContent("Filters", ""); | |
| public static readonly GUIContent profileTable = new GUIContent("Marker Details for currently selected range", ""); | |
| public static readonly GUIContent comparisonTable = new GUIContent("Marker Comparison for currently selected range", ""); | |
| public static readonly GUIContent depthTitle = new GUIContent("Depth Slice : ", "Marker callstack depth to analyze"); | |
| public static readonly GUIContent leftDepthTitle = new GUIContent("Left : ", "Marker callstack depth to analyze"); | |
| public static readonly GUIContent rightDepthTitle = new GUIContent("Right : ", "Marker callstack depth to analyze"); | |
| public static readonly string autoDepthTitleText = "Auto Depth (Diff: {0:+##;-##;None})"; | |
| public static readonly GUIContent autoDepthTitle = new GUIContent("Auto Depth", "Match up the depth levels based on the most common difference between markers present in both data sets. If the selected depth is at a depth not present in the other data set, after applying this difference, it will use the deepest level."); | |
| public static readonly GUIContent parentMarker = new GUIContent("Parent Marker : ", "Marker to start analysis from.\nParent of the hierarchy to analyze."); | |
| public static readonly GUIContent selectParentMarker = new GUIContent("None", "Select using right click context menu on marker names in marker table"); | |
| public static readonly GUIContent topMarkerRatio = new GUIContent("Ratio : ", "Normalize\tNormalized to time of the individual set\nLongest\tRatio based on longest time of the two"); | |
| public static readonly GUIContent firstFrame = new GUIContent("First frame", ""); | |
| public static readonly GUIContent[] topTenDisplayOptions = | |
| { | |
| new GUIContent("Normalized", "Ratio normalized to time of the individual data set"), | |
| new GUIContent("Longest", "Ratio based on longest time of the two data sets") | |
| }; | |
| public static readonly GUIContent[] nameFilterOperation = | |
| { | |
| new GUIContent("All", "Marker name contains all strings"), | |
| new GUIContent("Any", "Marker name contains any of the strings") | |
| }; | |
| public static readonly GUIContent[] removeMarkerOperation = | |
| { | |
| new GUIContent("None", "All markers shown. None removed."), | |
| new GUIContent("FPS Wait", "Remove the WaitForTargetFPS marker which represents targeted FPS (*) time\n\n(*) Via Application.targetFrameRate API usage (common on Mobile)"), | |
| new GUIContent("Present Wait", "Remove the Gfx.WaitForPresentOnGfxThread marker which represents time waiting for GPU to complete (common on consoles)"), | |
| new GUIContent("Custom", "Right click on a marker in the table and select 'Remove Marker' to remove a specific marker (and all children too)") | |
| }; | |
| public static readonly GUIContent menuItemSelectFramesInAll = new GUIContent("Select Frames that contain this marker (within whole data set)", ""); | |
| public static readonly GUIContent menuItemSelectFramesInCurrent = new GUIContent("Select Frames that contain this marker (within current selection)", ""); | |
| public static readonly GUIContent menuItemSelectFramesAll = new GUIContent("Clear Selection", ""); | |
| public static readonly GUIContent frameCosts = new GUIContent(" by frame costs", "Contains accumulated marker cost within the frame"); | |
| public static readonly GUIContent dataMissing = new GUIContent("Pull or load a data set for analysis", "Pull data from Unity Profiler or load a previously saved analysis data set"); | |
| public static readonly GUIContent comparisonDataMissing = new GUIContent("Pull or load a data set for comparison", "Pull data from Unity Profiler or load previously saved analysis data sets"); | |
| public static readonly string topMarkersTooltip = "Top markers for the median frame.\nThe length of this frame is the median of those in the data set.\nIt is likely to be the most representative frame."; | |
| public static readonly string medianFrameTooltip = "The length of this frame is the median of those in the data set.\nIt is likely to be the most representative frame."; | |
| public static readonly string helpText = | |
| @"This tool can analyze Unity Profiler data, to find representative frames and perform comparisons of data sets. | |
| To gather data to analyze: | |
| * Open the Unity Profiler. Either via the Unity menu under 'Windows', 'Analysis' or via the 'Open Profile Window' in the tool bar. | |
| * Capture some profiling data in the Unity Profiler by selecting a target application and click the 'Record' button. | |
| * Stop the capture by clicking again on the 'Record' button. | |
| To analyze the data: | |
| * Pull the Unity Profiler data into this tool by clicking the 'Pull Data' button in the single or compare views. | |
| * The analysis will be automatically triggered (in the compare view two data sets are required before analysis is performed). | |
| * Select a marker to see more detailed information about its time utilization over the frame time range. | |
| * Save off a data file from here to keep for future use. (Recommend saving the profile .data file in the same folder). | |
| To compare two data sets: | |
| * Click the compare tab. The data in the single tab will be used by default. You can also load previously saved analysis data. | |
| * Drag select a region in the frame time graph (above) to choose 1 or more frames for each of the two data sets. | |
| * The comparison will be automatically triggered as the selection is made."; | |
| } | |
| const float k_ProgressBarHeight = 2f; | |
| ProgressBarDisplay m_ProgressBar; | |
| ProfileAnalyzer m_ProfileAnalyzer; | |
| ProfilerWindowInterface m_ProfilerWindowInterface; | |
| string m_LastProfilerSelectedMarker; | |
| string m_LastMarkerSuccesfullySyncedWithProfilerWindow = null; | |
| [] bool m_SelectionEventFromProfilerWindowInProgress = false; | |
| int m_TopNumber; | |
| string[] m_TopStrings; | |
| int[] m_TopValues; | |
| [] DepthSliceUI m_DepthSliceUI; | |
| [] | |
| TimingOptions.TimingOption m_TimingOption = TimingOptions.TimingOption.Time; | |
| [] | |
| string m_ParentMarker = null; | |
| List<string> m_ThreadUINames = new List<string>(); | |
| List<string> m_ThreadNames = new List<string>(); | |
| Dictionary<string, string> m_ThreadNameToUIName; | |
| GUIContent[] m_removeMarkerDisplay = null; | |
| int[] m_removeMarkerValues = null; | |
| bool m_removeMarkerSomeMissing = false; | |
| struct MarkerFilter | |
| { | |
| public Dictionary<string, bool> MarkerCache; | |
| public bool NeedsRebuild; | |
| public List<string> IncludeFilter; | |
| public List<string> ExcludeFilter; | |
| public NameFilterOperation IncludeOperation; | |
| public NameFilterOperation ExcludeOperation; | |
| public void Clear() | |
| { | |
| MarkerCache.Clear(); | |
| NeedsRebuild = true; | |
| } | |
| private void Rebuild(ProfileAnalyzerWindow window) | |
| { | |
| NeedsRebuild = false; | |
| IncludeFilter = window.GetNameFilters(); | |
| ExcludeFilter = window.GetNameExcludes(); | |
| IncludeOperation = window.m_NameFilterOperation; | |
| ExcludeOperation = window.m_NameExcludeOperation; | |
| } | |
| private bool ComputeFilter(string marker) | |
| { | |
| if (IncludeFilter.Count > 0) | |
| { | |
| if (!NameInFilterList(marker, IncludeFilter, IncludeOperation)) | |
| return false; | |
| } | |
| if (ExcludeFilter.Count > 0) | |
| { | |
| if (NameInFilterList(marker, ExcludeFilter, ExcludeOperation)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| public bool DoesMarkerPassFilter(ProfileAnalyzerWindow window, string marker) | |
| { | |
| if (NeedsRebuild) | |
| Rebuild(window); | |
| if (MarkerCache.TryGetValue(marker, out var value)) | |
| return value; | |
| bool passesFilter = ComputeFilter(marker); | |
| MarkerCache[marker] = passesFilter; | |
| return passesFilter; | |
| } | |
| } | |
| MarkerFilter m_MarkerFilter; | |
| [] | |
| ThreadSelection m_ThreadSelection = new ThreadSelection(); | |
| ThreadSelection m_ThreadSelectionNew; | |
| string m_ThreadSelectionSummary; | |
| [] | |
| DisplayUnits m_DisplayUnits = new DisplayUnits(Units.Milliseconds); | |
| string[] m_UnitNames; | |
| [] | |
| string m_NameFilter = ""; | |
| [] | |
| string m_NameExclude = ""; | |
| [] | |
| MarkerColumnFilter m_SingleModeFilter = new MarkerColumnFilter(MarkerColumnFilter.Mode.TimeAndCount); | |
| [] | |
| MarkerColumnFilter m_CompareModeFilter = new MarkerColumnFilter(MarkerColumnFilter.Mode.TimeAndCount); | |
| [] | |
| TopTenDisplay m_TopTenDisplay = TopTenDisplay.Normalized; | |
| [] | |
| NameFilterOperation m_NameFilterOperation = NameFilterOperation.All; | |
| [] | |
| NameFilterOperation m_NameExcludeOperation = NameFilterOperation.Any; | |
| [] | |
| RemoveMarkerOperation m_removeMarkerOperation = RemoveMarkerOperation.ShowAll; | |
| [] | |
| bool m_hideRemovedMarkers = true; | |
| int m_ProfilerFirstFrameIndex = 0; | |
| int m_ProfilerLastFrameIndex = 0; | |
| const int k_ProfileDataDefaultDisplayOffset = 1; | |
| ActiveTab m_NextActiveTab = ActiveTab.Summary; | |
| ActiveTab m_ActiveTab = ActiveTab.Summary; | |
| bool m_OtherTabDirty = false; | |
| bool m_OtherTableDirty = false; | |
| [] | |
| string m_removeMarkerCustomRemoveMarker = null; | |
| [] | |
| bool m_ShowFilters = true; | |
| [] | |
| bool m_ShowTopNMarkers = true; | |
| [] | |
| bool m_ShowFrameSummary = true; | |
| [] | |
| bool m_ShowThreadSummary = false; | |
| [] | |
| bool m_ShowMarkerSummary = true; | |
| [] | |
| bool m_ShowMarkerTable = true; | |
| internal static class UIColor | |
| { | |
| static internal Color Color256(int r, int g, int b, int a) | |
| { | |
| return new Color((float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)a / 255.0f); | |
| } | |
| public static readonly Color white = new UnityEngine.Color(1.0f, 1.0f, 1.0f); | |
| public static readonly Color barBackground = new Color(0.5f, 0.5f, 0.5f); | |
| public static readonly Color barBackgroundSelected = new Color(0.6f, 0.6f, 0.6f); | |
| public static readonly Color boxAndWhiskerBoxColor = Color256(112, 112, 112, 255); | |
| public static readonly Color boxAndWhiskerLineColorLeft = Color256(206, 219, 238, 255); | |
| public static readonly Color boxAndWhiskerBoxColorLeft = Color256(59, 104, 144, 255); | |
| public static readonly Color boxAndWhiskerLineColorRight = Color256(247, 212, 201, 255); | |
| public static readonly Color boxAndWhiskerBoxColorRight = Color256(161, 83, 30, 255); | |
| public static readonly Color bar = new Color(0.95f, 0.95f, 0.95f); | |
| public static readonly Color barSelected = new Color(0.5f, 1.0f, 0.5f); | |
| public static readonly Color standardLine = new Color(1.0f, 1.0f, 1.0f); | |
| public static readonly Color gridLines = new Color(0.4f, 0.4f, 0.4f); | |
| public static readonly Color left = Color256(111, 163, 216, 255); | |
| public static readonly Color leftSelected = Color256(06, 219, 238, 255); | |
| public static readonly Color right = Color256(238, 134, 84, 255); | |
| public static readonly Color rightSelected = Color256(247, 212, 201, 255); | |
| public static readonly Color both = Color256(175, 150, 150, 255); | |
| public static readonly Color textTopMarkers = Color256(0, 0, 0, 255); | |
| public static readonly Color marker = new Color(0.0f, 0.5f, 0.5f); | |
| public static readonly Color markerSelected = new Color(0.0f, 0.6f, 0.6f); | |
| public static readonly Color thread = new Color(0.5f, 0.0f, 0.5f); | |
| public static readonly Color threadSelected = new Color(0.6f, 0.0f, 0.6f); | |
| } | |
| [] | |
| ProfileDataView m_ProfileSingleView; | |
| [] | |
| ProfileDataView m_ProfileLeftView; | |
| [] | |
| ProfileDataView m_ProfileRightView; | |
| [] ThreadMarkerInfo m_SelectedMarker = new ThreadMarkerInfo(); | |
| [] | |
| struct ThreadMarkerInfo | |
| { | |
| [] | |
| public int id; | |
| [] | |
| public string threadName; | |
| [] | |
| public string threadGroupName; | |
| [] | |
| public string name; | |
| } | |
| FrameTimeGraphGlobalSettings m_FrameTimeGraphGlobalSettings; | |
| FrameTimeGraph m_FrameTimeGraph; | |
| FrameTimeGraph m_LeftFrameTimeGraph; | |
| FrameTimeGraph m_RightFrameTimeGraph; | |
| bool m_FrameTimeGraphsPaired = true; | |
| TopMarkers m_TopMarkers; | |
| TopMarkers m_TopMarkersLeft; | |
| TopMarkers m_TopMarkersRight; | |
| List<MarkerPairing> m_PairingsNew; | |
| int m_TotalCombinedMarkerCountNew; | |
| [] | |
| List<MarkerPairing> m_Pairings = new List<MarkerPairing>(); | |
| int m_TotalCombinedMarkerCount = 0; | |
| [] | |
| int m_SelectedPairing = 0; | |
| [] | |
| TreeViewState m_ProfileTreeViewState; | |
| [] | |
| MultiColumnHeaderState m_ProfileMulticolumnHeaderState; | |
| ProfileTable m_ProfileTable; | |
| [] | |
| TreeViewState m_ComparisonTreeViewState; | |
| [] | |
| MultiColumnHeaderState m_ComparisonMulticolumnHeaderState; | |
| ComparisonTable m_ComparisonTable; | |
| internal static class LayoutSize | |
| { | |
| public static readonly int WidthColumn0 = 100; | |
| public static readonly int WidthColumn1 = 52; // +2 to prevent some | |
| public static readonly int WidthColumn2 = 52; | |
| public static readonly int WidthColumn3 = 52; | |
| public static readonly int WidthRHS = 290; // Column widths + label padding between (276) + scrollbar width | |
| public static readonly int FilterOptionsLeftLabelWidth = 100; | |
| public static readonly int FilterOptionsEnumWidth = 50; | |
| public static readonly int RemoveMarkerOptionsEnumWidth = 100; | |
| public static readonly int RemoveMarkerMissingOptionsEnumWidth = 200; | |
| public static readonly int FilterOptionsLockedEnumWidth = 120; | |
| public static readonly int FilterOptionsRightLabelWidth = 110; | |
| public static readonly int FilterOptionsRightEnumWidth = 150; | |
| public static readonly int HistogramWidth = 153; | |
| public static readonly int MinWindowWidth = 800 + WidthRHS; | |
| public static readonly int MinWindowHeight = 480; | |
| public static readonly int WindowWidth = MinWindowWidth; | |
| public static readonly int WindowHeight = 840; | |
| public static readonly int ScrollBarPadding = 6; // this is legacy and we might be able to kill it but it will slightly change the layout of the window. | |
| } | |
| Columns m_Columns = new Columns(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| [] | |
| ThreadRange m_ThreadRange = ThreadRange.UpperQuartile; | |
| internal Draw2D m_2D; | |
| bool m_Async = true; | |
| Thread m_BackgroundThread; | |
| ThreadActivity m_ThreadActivity; | |
| ProfileData m_ProfilerData; | |
| string m_Path; | |
| int m_ThreadPhase; | |
| int m_ThreadPhases; | |
| int m_ThreadProgress; | |
| bool m_RequestRepaint; | |
| bool m_RequestAnalysis; | |
| bool m_RequestCompare; | |
| bool m_FullAnalysisRequired; | |
| bool m_FullCompareRequired; | |
| [] | |
| int m_TopNBars = 10; | |
| bool m_EnableAnalysisProfiling = false; | |
| int m_AnalyzeInUpdatePhase = 0; | |
| string m_LastAnalysisTime = ""; | |
| string m_LastCompareTime = ""; | |
| float m_LastAnalysisTimeMilliseconds; | |
| float m_LastCompareTimeMilliseconds; | |
| bool m_NewDataLoaded = false; | |
| bool m_NewComparisonDataLoaded = false; | |
| Vector2 m_HelpScroll = new Vector2(0, 0); | |
| Vector2 m_ThreadScroll = new Vector2(0, 0); | |
| Vector2 m_MarkerSummaryScroll = new Vector2(0, 0); | |
| Rect m_ThreadsAreaRect = new Rect(); | |
| Rect m_ComparisonThreadsAreaRect = new Rect(); | |
| Vector2 m_LastScreenSize = new Vector2(0, 0); | |
| bool m_ScreenSizeChanged; | |
| double m_ScreenSizeChangedTimeStarted; | |
| double m_ScreenSizeChangedTimeFinished; | |
| ActiveTab m_ScreenSizeChangedTab; | |
| GUIStyle m_StyleMiddleRight; | |
| GUIStyle m_StyleUpperLeft; | |
| bool m_StylesSetup = false; | |
| static Regex quotedStringWithoutQuotes = new Regex("\"([^\"]*)\""); | |
| static Regex quotedString = new Regex("(\"[^\"]*\")"); | |
| static Regex stringWithoutWhiteSpace = new Regex("([^ \t]+)"); | |
| /* | |
| static Regex lastSpace = new Regex("(.+)[ ]([^ ]*)"); | |
| */ | |
| [] | |
| static void Init() | |
| { | |
| var window = GetWindow<ProfileAnalyzerWindow>("Profile Analyzer"); | |
| window.minSize = new Vector2(LayoutSize.MinWindowWidth, LayoutSize.MinWindowHeight); | |
| window.position.size.Set(LayoutSize.WindowWidth, LayoutSize.WindowHeight); | |
| window.Show(); | |
| window.m_LastScreenSize = window.position.size; | |
| } | |
| /// <summary> | |
| /// Open profile analyzer window | |
| /// </summary> | |
| public static void OpenProfileAnalyzer() | |
| { | |
| Init(); | |
| } | |
| void Awake() | |
| { | |
| m_ScreenSizeChanged = false; | |
| m_ScreenSizeChangedTimeStarted = 0.0; | |
| m_ScreenSizeChangedTimeFinished = 0.0; | |
| m_ScreenSizeChangedTab = ActiveTab.Summary; | |
| m_ProfileSingleView = new ProfileDataView(); | |
| m_ProfileLeftView = new ProfileDataView(); | |
| m_ProfileRightView = new ProfileDataView(); | |
| m_RequestRepaint = false; | |
| m_RequestAnalysis = false; | |
| m_RequestCompare = false; | |
| m_FrameTimeGraphGlobalSettings = new FrameTimeGraphGlobalSettings(); | |
| } | |
| static int s_TmpCount = 0; | |
| static string s_TmpDir = ""; | |
| ActiveView m_ActiveLoadingView; | |
| static string s_ApplicationDataPath; | |
| internal static string TmpDir | |
| { | |
| get | |
| { | |
| if (string.IsNullOrEmpty(s_ApplicationDataPath)) | |
| s_ApplicationDataPath = Application.dataPath; | |
| if (string.IsNullOrEmpty(s_TmpDir)) | |
| s_TmpDir = string.Format("{0}{1}ProfileAnalyzer{1}", Directory.GetParent(s_ApplicationDataPath).FullName, Path.DirectorySeparatorChar); | |
| return s_TmpDir; | |
| } | |
| } | |
| internal static string TmpPath | |
| { | |
| get | |
| { | |
| if (!Directory.Exists(TmpDir)) | |
| Directory.CreateDirectory(TmpDir); | |
| while (File.Exists(string.Format("{0}tmp{1}.pdata", TmpDir, s_TmpCount))) | |
| { | |
| s_TmpCount++; | |
| } | |
| return string.Format("{0}tmp{1}.pdata", TmpDir, s_TmpCount); | |
| } | |
| } | |
| void OnEnable() | |
| { | |
| //do this here to safeguard against Application.dataPAth being accessed off the main thread | |
| s_ApplicationDataPath = Application.dataPath; | |
| // Update styles so we get the theme changes | |
| m_StylesSetup = false; | |
| ProfileAnalyzerAnalytics.EnableAnalytics(); | |
| m_ProgressBar = new ProgressBarDisplay(); | |
| if (m_DepthSliceUI == null) | |
| m_DepthSliceUI = new DepthSliceUI(b => UpdateActiveTab(b)); | |
| else | |
| m_DepthSliceUI.OnEnable(b => UpdateActiveTab(b)); | |
| m_ProfilerWindowInterface = new ProfilerWindowInterface(m_ProgressBar); | |
| if (!m_ProfilerWindowInterface.IsReady()) | |
| { | |
| m_ProfilerWindowInterface.GetProfilerWindowHandle(); | |
| } | |
| if (IsSelectedMarkerNameValid()) | |
| { | |
| var oldSelectedMarkerName = m_SelectedMarker; | |
| m_SelectedMarker.name = null; | |
| // wait a frame for the ProfilerWindow to get Enabled before re-setting the selection | |
| EditorApplication.delayCall += () => SelectMarkerByName(oldSelectedMarkerName.name, oldSelectedMarkerName.threadGroupName, oldSelectedMarkerName.threadName); | |
| } | |
| m_ProfilerWindowInterface.selectedMarkerChanged -= OnProfilerWindowCpuModuleSelectionChanged; | |
| m_ProfilerWindowInterface.selectedMarkerChanged += OnProfilerWindowCpuModuleSelectionChanged; | |
| m_ProfilerWindowInterface.selectedFrameChanged -= OnProfilerWindowSelectedFrameChanged; | |
| m_ProfilerWindowInterface.selectedFrameChanged += OnProfilerWindowSelectedFrameChanged; | |
| m_MarkerFilter = new MarkerFilter | |
| { | |
| MarkerCache = new Dictionary<string, bool>(), | |
| NeedsRebuild = true, | |
| }; | |
| m_ProfileAnalyzer = new ProfileAnalyzer(); | |
| if (m_ThreadSelection == null || m_ThreadSelection.empty) | |
| { | |
| ThreadIdentifier mainThreadSelection = new ThreadIdentifier("Main Thread", 1); | |
| m_ThreadSelection.Set(mainThreadSelection.threadNameWithIndex); | |
| } | |
| if (m_ThreadSelectionNew != null && m_ThreadSelectionNew.empty) | |
| m_ThreadSelectionNew = null; | |
| m_2D = new Draw2D("Unlit/ProfileAnalyzerShader"); | |
| FrameTimeGraph.SetGlobalSettings(m_FrameTimeGraphGlobalSettings); | |
| m_FrameTimeGraph = new FrameTimeGraph(0, m_2D, m_DisplayUnits.Units, UIColor.barBackground, UIColor.barBackgroundSelected, UIColor.bar, UIColor.barSelected, UIColor.marker, UIColor.markerSelected, UIColor.thread, UIColor.threadSelected, UIColor.gridLines); | |
| m_FrameTimeGraph.SetRangeCallback(SetRange); | |
| m_FrameTimeGraph.SetActiveCallback(GraphActive); | |
| m_LeftFrameTimeGraph = new FrameTimeGraph(1, m_2D, m_DisplayUnits.Units, UIColor.barBackground, UIColor.barBackgroundSelected, UIColor.left, UIColor.leftSelected, UIColor.marker, UIColor.markerSelected, UIColor.thread, UIColor.threadSelected, UIColor.gridLines); | |
| m_LeftFrameTimeGraph.SetRangeCallback(SetLeftRange); | |
| m_LeftFrameTimeGraph.SetActiveCallback(GraphActive); | |
| m_RightFrameTimeGraph = new FrameTimeGraph(2, m_2D, m_DisplayUnits.Units, UIColor.barBackground, UIColor.barBackgroundSelected, UIColor.right, UIColor.rightSelected, UIColor.marker, UIColor.markerSelected, UIColor.thread, UIColor.threadSelected, UIColor.gridLines); | |
| m_RightFrameTimeGraph.SetRangeCallback(SetRightRange); | |
| m_RightFrameTimeGraph.SetActiveCallback(GraphActive); | |
| m_LeftFrameTimeGraph.PairWith(m_FrameTimeGraphsPaired ? m_RightFrameTimeGraph : null); | |
| m_TopMarkers = new TopMarkers(this, m_2D, UIColor.barBackground, UIColor.textTopMarkers); | |
| m_TopMarkersLeft = new TopMarkers(this, m_2D, UIColor.barBackground, UIColor.textTopMarkers); | |
| m_TopMarkersRight = new TopMarkers(this, m_2D, UIColor.barBackground, UIColor.textTopMarkers); | |
| m_ThreadActivity = ThreadActivity.None; | |
| m_ThreadProgress = 0; | |
| m_ThreadPhase = 0; | |
| List<int> values = new List<int>(); | |
| List<String> strings = new List<string>(); | |
| for (int i = 1; i <= 10; i++) | |
| { | |
| values.Add(i); | |
| strings.Add(i.ToString()); | |
| } | |
| m_TopValues = values.ToArray(); | |
| m_TopStrings = strings.ToArray(); | |
| m_TopNumber = 3; | |
| List<string> unitNames = new List<string>(DisplayUnits.UnitNames); | |
| unitNames.RemoveAt(unitNames.Count - 1); | |
| m_UnitNames = unitNames.ToArray(); | |
| // Regrenerate analysis if just re initialised with the existing profile data reloaded from serialisation (e.g. on enter play mode) | |
| // As we don't serialise the analysis itself. | |
| // UpdateActiveTab(true); | |
| UpdateThreadNames(); | |
| if (m_ProfileSingleView.analysis != null) | |
| { | |
| CreateProfileTable(); | |
| m_RequestRepaint = true; | |
| } | |
| if (m_ProfileLeftView.analysis != null && m_ProfileRightView.analysis != null) | |
| { | |
| CreateComparisonTable(); | |
| m_RequestRepaint = true; | |
| } | |
| // Mouse movement calls OnGui | |
| wantsMouseMove = true; | |
| } | |
| void OnDisable() | |
| { | |
| if (ProfileAnalyzerExportWindow.IsOpen()) | |
| ProfileAnalyzerExportWindow.CloseAll(); | |
| m_ProfilerWindowInterface.selectedMarkerChanged -= OnProfilerWindowCpuModuleSelectionChanged; | |
| m_ProfilerWindowInterface.selectedFrameChanged -= OnProfilerWindowSelectedFrameChanged; | |
| m_ProfilerWindowInterface.OnDisable(); | |
| m_ProfilerWindowInterface = null; | |
| } | |
| void OnDestroy() | |
| { | |
| if (m_BackgroundThread != null) | |
| m_BackgroundThread.Abort(); | |
| if (m_ProfileSingleView != null && m_ProfileSingleView.data != null) | |
| m_ProfileSingleView.data.DeleteTmpFiles(); | |
| if (m_ProfileLeftView != null && m_ProfileLeftView.data != null) | |
| m_ProfileLeftView.data.DeleteTmpFiles(); | |
| if (m_ProfileRightView != null && m_ProfileRightView.data != null) | |
| m_ProfileRightView.data.DeleteTmpFiles(); | |
| if (Directory.Exists(TmpDir) && Directory.GetFiles(TmpDir).Length == 0) | |
| Directory.Delete(TmpDir, true); | |
| } | |
| bool DisplayCount() | |
| { | |
| switch (m_SingleModeFilter.mode) | |
| { | |
| case MarkerColumnFilter.Mode.CountTotals: | |
| case MarkerColumnFilter.Mode.CountPerFrame: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| void OnGUI() | |
| { | |
| if (Event.current.type != EventType.MouseMove) | |
| { | |
| m_2D.OnGUI(); | |
| Draw(); | |
| } | |
| ProcessInput(); | |
| } | |
| bool TmpInUse(ProfileDataView dv, string path) | |
| { | |
| if (dv != m_ProfileSingleView && m_ProfileSingleView.data != null && m_ProfileSingleView.data.FilePath == path) | |
| return true; | |
| if (dv != m_ProfileLeftView && m_ProfileLeftView.data != null && m_ProfileLeftView.data.FilePath == path) | |
| return true; | |
| if (dv != m_ProfileRightView && m_ProfileRightView.data != null && m_ProfileRightView.data.FilePath == path) | |
| return true; | |
| return false; | |
| } | |
| void SetView(ProfileDataView dst, ProfileData data, string path, FrameTimeGraph graph) | |
| { | |
| if (!data.IsSame(dst.data)) | |
| { | |
| if (dst == m_ProfileSingleView) | |
| m_NewDataLoaded = true; | |
| else | |
| m_NewComparisonDataLoaded = true; | |
| } | |
| if (dst.data != null && (m_NewDataLoaded || m_NewComparisonDataLoaded) && !TmpInUse(dst, dst.data.FilePath)) | |
| dst.data.DeleteTmpFiles(); | |
| dst.data = data; | |
| dst.path = path; | |
| dst.SelectFullRange(); | |
| graph.Reset(); | |
| graph.SetData(GetFrameTimeData(dst.data)); | |
| // One of the views changed so make sure the export window knows if its open | |
| ProfileAnalyzerExportWindow exportWindow = ProfileAnalyzerExportWindow.FindOpenWindow(); | |
| if (exportWindow != null) | |
| { | |
| exportWindow.SetData(m_ProfileSingleView, m_ProfileLeftView, m_ProfileRightView); | |
| } | |
| } | |
| void SetView(ProfileDataView dst, ProfileDataView src, FrameTimeGraph graph) | |
| { | |
| SetView(dst, src.data, src.path, graph); | |
| } | |
| void UpdateThreadNames() | |
| { | |
| // Update threads list | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| GetThreadNames(m_ProfileSingleView.data, out m_ThreadUINames, out m_ThreadNames, out m_ThreadNameToUIName); | |
| break; | |
| case ActiveTab.Compare: | |
| GetThreadNames(m_ProfileLeftView.data, m_ProfileRightView.data, out m_ThreadUINames, out m_ThreadNames, out m_ThreadNameToUIName); | |
| break; | |
| } | |
| UpdateThreadGroupSelection(m_ThreadNames, m_ThreadSelection); | |
| m_ThreadSelectionSummary = CalculateSelectedThreadsSummary(); | |
| } | |
| void ProcessTabSwitch() | |
| { | |
| if (m_NextActiveTab != m_ActiveTab) | |
| { | |
| m_ActiveTab = m_NextActiveTab; | |
| // Copy data if none present for this tab | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| if (!m_ProfileSingleView.IsDataValid()) | |
| { | |
| if (m_ProfileLeftView.IsDataValid()) | |
| { | |
| SetView(m_ProfileSingleView, m_ProfileLeftView, m_FrameTimeGraph); | |
| m_RequestAnalysis = true; | |
| m_FullAnalysisRequired = true; | |
| } | |
| else if (m_ProfileRightView.IsDataValid()) | |
| { | |
| SetView(m_ProfileSingleView, m_ProfileRightView, m_FrameTimeGraph); | |
| m_RequestAnalysis = true; | |
| m_FullAnalysisRequired = true; | |
| } | |
| } | |
| break; | |
| case ActiveTab.Compare: | |
| if ((!m_ProfileLeftView.IsDataValid() || !m_ProfileRightView.IsDataValid()) && m_ProfileSingleView.IsDataValid()) | |
| { | |
| if (!m_ProfileLeftView.IsDataValid()) | |
| { | |
| SetView(m_ProfileLeftView, m_ProfileSingleView, m_LeftFrameTimeGraph); | |
| } | |
| if (!m_ProfileRightView.IsDataValid()) | |
| { | |
| SetView(m_ProfileRightView, m_ProfileSingleView, m_RightFrameTimeGraph); | |
| } | |
| // Remove pairing of both left/right point at the same data | |
| if (m_ProfileLeftView.path == m_ProfileRightView.path) | |
| { | |
| SetFrameTimeGraphPairing(false); | |
| } | |
| m_RequestCompare = true; | |
| m_FullCompareRequired = true; | |
| } | |
| break; | |
| } | |
| UpdateThreadNames(); | |
| BuildRemoveMarkerList(); | |
| if (!m_OtherTableDirty) | |
| SelectMarker(m_SelectedMarker.name); | |
| if (m_OtherTabDirty) | |
| { | |
| UpdateActiveTab(true, false); // Make sure any depth/thread updates are applied when switching tabs, but don't dirty the other tab | |
| m_OtherTabDirty = false; | |
| } | |
| if (m_OtherTableDirty) | |
| { | |
| UpdateMarkerTable(false); // Make sure any marker selection updates are applied when switching tabs, but don't dirty the other tab | |
| m_OtherTableDirty = false; | |
| } | |
| if (!m_RequestAnalysis && !m_RequestCompare) | |
| m_DepthSliceUI.UpdateDepthFilters(m_ActiveTab == ActiveTab.Summary, m_ProfileSingleView, m_ProfileLeftView, m_ProfileRightView); | |
| } | |
| } | |
| bool IsDocked() | |
| { | |
| return docked; | |
| } | |
| void CheckScreenSizeChanges() | |
| { | |
| // We get a 5 pixel change in y height during initialization. | |
| // We could wait before considering size changes but using a delta is also useful | |
| float sizeDeltaForChange = 10; | |
| Vector2 sizeDiff = position.size - m_LastScreenSize; | |
| if (Math.Abs(sizeDiff.x) > sizeDeltaForChange || Math.Abs(sizeDiff.y) > sizeDeltaForChange) | |
| { | |
| if (m_LastScreenSize.x != 0) // At initialization time the screen size has not yet been recorded. Don't consider this a screen size change | |
| { | |
| m_LastScreenSize = position.size; | |
| if (!m_ScreenSizeChanged) | |
| { | |
| // Record when we started the change | |
| m_ScreenSizeChanged = true; | |
| m_ScreenSizeChangedTimeStarted = EditorApplication.timeSinceStartup; | |
| } | |
| // Record the last time of a change | |
| m_ScreenSizeChangedTimeFinished = EditorApplication.timeSinceStartup; | |
| // Record which tab we were on when it was changed | |
| m_ScreenSizeChangedTab = m_ActiveTab; | |
| } | |
| } | |
| if (m_ScreenSizeChanged) | |
| { | |
| double secondsSinceChanged = (EditorApplication.timeSinceStartup - m_ScreenSizeChangedTimeFinished); | |
| double secondsToDelay = 3f; | |
| if (secondsSinceChanged > secondsToDelay) | |
| { | |
| // Send analytic | |
| var uiResizeView = m_ScreenSizeChangedTab == ActiveTab.Summary ? ProfileAnalyzerAnalytics.UIResizeView.Single : ProfileAnalyzerAnalytics.UIResizeView.Comparison; | |
| float durationInSeconds = (float)(m_ScreenSizeChangedTimeFinished - m_ScreenSizeChangedTimeStarted); | |
| ProfileAnalyzerAnalytics.SendUIResizeEvent(uiResizeView, durationInSeconds, position.size.x, position.size.y, IsDocked()); | |
| m_ScreenSizeChanged = false; | |
| } | |
| } | |
| } | |
| internal void RequestRepaint() | |
| { | |
| m_RequestRepaint = true; | |
| } | |
| void ProcessInput() | |
| { | |
| FrameTimeGraph.State inputStatus = FrameTimeGraph.State.None; | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| inputStatus = m_FrameTimeGraph.ProcessInput(); | |
| } | |
| else if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| if (m_ProfileLeftView.IsDataValid() && inputStatus == FrameTimeGraph.State.None) | |
| inputStatus = m_LeftFrameTimeGraph.ProcessInput(); | |
| if (m_ProfileRightView.IsDataValid() && inputStatus == FrameTimeGraph.State.None) | |
| inputStatus = m_RightFrameTimeGraph.ProcessInput(); | |
| } | |
| switch (inputStatus) | |
| { | |
| case FrameTimeGraph.State.Dragging: | |
| m_RequestRepaint = true; | |
| break; | |
| case FrameTimeGraph.State.DragComplete: | |
| m_RequestCompare = true; | |
| break; | |
| } | |
| if (Event.current.isKey && Event.current.type == EventType.KeyDown) | |
| { | |
| switch (Event.current.keyCode) | |
| { | |
| case KeyCode.Alpha1: | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| m_FrameTimeGraph.MakeGraphActive(true); | |
| GUI.FocusControl("FrameTimeGraph"); | |
| } | |
| else if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| m_LeftFrameTimeGraph.MakeGraphActive(true); | |
| GUI.FocusControl("LeftFrameTimeGraph"); | |
| } | |
| m_RequestRepaint = true; | |
| break; | |
| case KeyCode.Alpha2: | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| m_RightFrameTimeGraph.MakeGraphActive(true); | |
| GUI.FocusControl("RightFrameTimeGraph"); | |
| } | |
| m_RequestRepaint = true; | |
| break; | |
| } | |
| } | |
| } | |
| //Check if the ProfileDataView is in sync with the loaded frame data inside the profiler window | |
| //We are required to do this check in order to either enable or disable the ability to | |
| //jump into the matching frame data(loaded in the profiler window) for a specific profile analyzer capture | |
| void VerifyFrameDataInSyncWithProfilerWindow(ProfileDataView dataView) | |
| { | |
| var firstFrameIdx = m_ProfilerFirstFrameIndex - 1; | |
| var incompleteFrameCount = 0; | |
| if (dataView != null && dataView.data != null) | |
| incompleteFrameCount = (dataView.data.FirstFrameIncomplete ? 1 : 0) + (dataView.data.LastFrameIncomplete ? 1 : 0); | |
| var loadedFrameCount = m_ProfilerLastFrameIndex - m_ProfilerFirstFrameIndex + (firstFrameIdx != -1 ? 1 : 0) | |
| - incompleteFrameCount; | |
| if (loadedFrameCount == 0 | |
| || !dataView.IsDataValid() //check if the data is valid and potentially reload the file, .data shouldn't be accessed before this point | |
| || dataView.data.GetFrameCount() != loadedFrameCount | |
| || m_ProfilerWindowInterface.GetThreadCountForFrame(firstFrameIdx) != dataView.data.GetFrame(0).threads.Count) | |
| { | |
| dataView.inSyncWithProfilerData = false; | |
| } | |
| else | |
| { | |
| var pDataFrame = dataView.data.GetFrame(0); //get the first frame we don't care about the offset as we only need to compare frames | |
| var loadedFrame = m_ProfilerWindowInterface.GetProfileFrameForThread(firstFrameIdx, 0); | |
| //compare frame start time and duration | |
| //todo improve this | |
| if (pDataFrame.msStartTime != loadedFrame.msStartTime | |
| || pDataFrame.msFrame != loadedFrame.msFrame) | |
| { | |
| dataView.inSyncWithProfilerData = false; | |
| } | |
| else | |
| { | |
| dataView.inSyncWithProfilerData = true; | |
| } | |
| } | |
| } | |
| //Returns true if we were able to sync with the window, but not necessarily if the data is in sync | |
| bool SyncWithProfilerWindow() | |
| { | |
| if (m_ProfilerWindowInterface.IsReady()) | |
| { | |
| // Check if a new profile has been recorded (or loaded) by checking the frame index range. | |
| int first; | |
| int last; | |
| m_ProfilerWindowInterface.GetFrameRangeFromProfiler(out first, out last); | |
| if (first != m_ProfilerFirstFrameIndex || last != m_ProfilerLastFrameIndex) | |
| { | |
| // Store the updated range and alter the pull range | |
| m_ProfilerFirstFrameIndex = first; | |
| m_ProfilerLastFrameIndex = last; | |
| } | |
| VerifyFrameDataInSyncWithProfilerWindow(m_ProfileSingleView); | |
| VerifyFrameDataInSyncWithProfilerWindow(m_ProfileLeftView); | |
| VerifyFrameDataInSyncWithProfilerWindow(m_ProfileRightView); | |
| return true; | |
| } | |
| m_ProfilerWindowInterface.GetProfilerWindowHandle(); | |
| return false; | |
| } | |
| void OnProfilerWindowCpuModuleSelectionChanged(string selectedMarker, string threadGroupName, string threadName) | |
| { | |
| // selectedMarker can be "" if in play mode and no active timeline shown (on versions pre 2021.1 | |
| if (!string.IsNullOrEmpty(selectedMarker) && selectedMarker != m_LastProfilerSelectedMarker) | |
| { | |
| m_LastProfilerSelectedMarker = selectedMarker; | |
| m_SelectionEventFromProfilerWindowInProgress = true; | |
| SelectMarker(selectedMarker, threadGroupName, threadName); | |
| m_SelectionEventFromProfilerWindowInProgress = false; | |
| Repaint(); | |
| } | |
| } | |
| void OnProfilerWindowSelectedFrameChanged(int newlySelectedFrame) | |
| { | |
| // selectedMarker can be "" if in play mode and no active timeline shown (on versions pre 2021.1 | |
| if (!string.IsNullOrEmpty(m_LastProfilerSelectedMarker)) | |
| { | |
| m_SelectionEventFromProfilerWindowInProgress = true; | |
| UpdateSelectedMarkerName(m_LastProfilerSelectedMarker); | |
| m_SelectionEventFromProfilerWindowInProgress = false; | |
| Repaint(); | |
| } | |
| } | |
| void Update() | |
| { | |
| CheckScreenSizeChanges(); | |
| // Check if profiler is open | |
| if (SyncWithProfilerWindow()) | |
| { | |
| // Check if the selected marker in the profiler has changed | |
| m_ProfilerWindowInterface.PollProfilerWindowMarkerName(); | |
| m_ProfilerWindowInterface.PollSelectedFrameChanges(); | |
| } | |
| // Deferred to here so drawing isn't messed up by changing tab half way through a function rendering the old tab | |
| ProcessTabSwitch(); | |
| // Force repaint for the progress bar | |
| if (IsAnalysisRunning()) | |
| { | |
| int loadingProgress; | |
| int analysisProgress; | |
| if (IsLoading()) | |
| { | |
| loadingProgress = (int)(ProfileData.GetLoadingProgress() * 100); | |
| analysisProgress = 0; | |
| } | |
| else | |
| { | |
| loadingProgress = 100; | |
| analysisProgress = m_ProfileAnalyzer.GetProgress(); | |
| if (m_ThreadPhases > 1) | |
| { | |
| // Use thread phases to evaluate the progress as analysis process might contain multiple ProfileAnalyzer passes. | |
| analysisProgress = (100 * m_ThreadPhase) / m_ThreadPhases; | |
| } | |
| } | |
| int progress = (loadingProgress + analysisProgress) / 2; | |
| if (m_ThreadProgress != progress) | |
| { | |
| m_ThreadProgress = progress; | |
| m_RequestRepaint = true; | |
| } | |
| } | |
| if (m_ThreadSelectionNew != null) | |
| { | |
| m_ThreadSelection = new ThreadSelection(m_ThreadSelectionNew); | |
| m_ThreadSelectionNew = null; | |
| m_ThreadSelectionSummary = CalculateSelectedThreadsSummary(); | |
| } | |
| switch (m_ThreadActivity) | |
| { | |
| case ThreadActivity.AnalyzeDone: | |
| // Create table when analysis complete | |
| UpdateAnalysisFromAsyncProcessing(m_ProfileSingleView, m_FullAnalysisRequired); | |
| m_FullAnalysisRequired = false; | |
| UpdateThreadNames(); | |
| BuildRemoveMarkerList(); | |
| if (m_ProfileSingleView.analysis != null) | |
| { | |
| CreateProfileTable(); | |
| m_RequestRepaint = true; | |
| } | |
| m_ThreadActivity = ThreadActivity.None; | |
| if (m_NewDataLoaded) | |
| { | |
| if (m_ProfileSingleView.IsDataValid()) | |
| { | |
| // Don't bother sending an analytic if the data set is empty (should never occur anyway but consistent with comparison flow) | |
| ProfileAnalyzerAnalytics.SendUIUsageModeEvent(ProfileAnalyzerAnalytics.UIUsageMode.Single, m_LastAnalysisTimeMilliseconds / 1000f); | |
| } | |
| m_NewDataLoaded = false; | |
| } | |
| SelectMarker(m_SelectedMarker.name); | |
| break; | |
| case ThreadActivity.CompareDone: | |
| UpdateAnalysisFromAsyncProcessing(m_ProfileLeftView, m_FullCompareRequired); | |
| UpdateAnalysisFromAsyncProcessing(m_ProfileRightView, m_FullCompareRequired); | |
| m_FullCompareRequired = false; | |
| m_Pairings = m_PairingsNew; | |
| m_TotalCombinedMarkerCount = m_TotalCombinedMarkerCountNew; | |
| UpdateThreadNames(); | |
| BuildRemoveMarkerList(); | |
| if (m_ProfileLeftView.analysis != null && m_ProfileRightView.analysis != null) | |
| { | |
| CreateComparisonTable(); | |
| m_RequestRepaint = true; | |
| } | |
| m_ThreadActivity = ThreadActivity.None; | |
| if (m_NewComparisonDataLoaded) | |
| { | |
| if (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid()) | |
| { | |
| // Don't bother sending an analytic when one (or more) of the data sets is blank (as no comparison is really made) | |
| ProfileAnalyzerAnalytics.SendUIUsageModeEvent(ProfileAnalyzerAnalytics.UIUsageMode.Comparison, m_LastCompareTimeMilliseconds / 1000f); | |
| } | |
| m_NewComparisonDataLoaded = false; | |
| } | |
| SelectMarker(m_SelectedMarker.name); | |
| break; | |
| case ThreadActivity.LoadDone: | |
| SetView(GetActiveView, m_ProfilerData, m_Path, GetActiveFrameTimeGraph); | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Compare: | |
| // Remove pairing if both left/right point at the same data | |
| if (m_ProfileLeftView.path == m_ProfileRightView.path) | |
| { | |
| SetFrameTimeGraphPairing(false); | |
| } | |
| m_FullCompareRequired = true; | |
| m_RequestCompare = true; | |
| break; | |
| case ActiveTab.Summary: | |
| m_RequestAnalysis = true; | |
| m_FullAnalysisRequired = true; | |
| break; | |
| default: | |
| throw new ArgumentOutOfRangeException(); | |
| } | |
| m_ThreadActivity = ThreadActivity.None; | |
| break; | |
| } | |
| if (m_RequestAnalysis) | |
| { | |
| if (!IsAnalysisRunning()) | |
| { | |
| Analyze(); | |
| m_RequestAnalysis = false; | |
| } | |
| } | |
| if (m_RequestCompare) | |
| { | |
| if (!IsAnalysisRunning()) | |
| { | |
| Compare(); | |
| m_RequestCompare = false; | |
| } | |
| } | |
| if (m_RequestRepaint) | |
| { | |
| Repaint(); | |
| m_RequestRepaint = false; | |
| } | |
| if (m_AnalyzeInUpdatePhase > 0) | |
| { | |
| switch (m_AnalyzeInUpdatePhase) | |
| { | |
| case 1: | |
| UnityEngine.Profiling.Profiler.enabled = true; | |
| m_AnalyzeInUpdatePhase++; | |
| return; | |
| case 2: | |
| AnalyzeSync(); | |
| UpdateAnalysisFromAsyncProcessing(m_ProfileSingleView, m_FullAnalysisRequired); | |
| m_FullAnalysisRequired = false; | |
| m_AnalyzeInUpdatePhase++; | |
| return; | |
| case 3: | |
| m_AnalyzeInUpdatePhase++; | |
| return; | |
| case 4: | |
| UnityEngine.Profiling.Profiler.enabled = false; | |
| m_AnalyzeInUpdatePhase++; | |
| return; | |
| default: | |
| m_AnalyzeInUpdatePhase = 0; | |
| break; | |
| } | |
| } | |
| } | |
| ProfileDataView GetActiveView | |
| { | |
| get | |
| { | |
| switch (m_ActiveLoadingView) | |
| { | |
| case ActiveView.Single: | |
| return m_ProfileSingleView; | |
| case ActiveView.Left: | |
| return m_ProfileLeftView; | |
| case ActiveView.Right: | |
| return m_ProfileRightView; | |
| default: | |
| throw new ArgumentOutOfRangeException(); | |
| } | |
| } | |
| } | |
| FrameTimeGraph GetActiveFrameTimeGraph | |
| { | |
| get | |
| { | |
| switch (m_ActiveLoadingView) | |
| { | |
| case ActiveView.Single: | |
| return m_FrameTimeGraph; | |
| case ActiveView.Left: | |
| return m_LeftFrameTimeGraph; | |
| case ActiveView.Right: | |
| return m_RightFrameTimeGraph; | |
| default: | |
| throw new ArgumentOutOfRangeException(); | |
| } | |
| } | |
| } | |
| void UpdateAnalysisFromAsyncProcessing(ProfileDataView view, bool full) | |
| { | |
| view.analysis = view.analysisNew; | |
| if (full) | |
| { | |
| if (view.selectedIndices != null && view.IsDataValid() && view.selectedIndices.Count == view.data.GetFrameCount()) | |
| view.analysisFull = view.analysis; | |
| else | |
| view.analysisFull = view.analysisFullNew; | |
| if (view.IsDataValid()) | |
| view.FindKeyMarkers(); | |
| } | |
| } | |
| List<FrameTimeGraph.Data> GetFrameTimeData(ProfileData profileData) | |
| { | |
| List<FrameTimeGraph.Data> data = new List<FrameTimeGraph.Data>(); | |
| int frames = profileData.GetFrameCount(); | |
| bool removeFrameSyncTime = false; | |
| int removeMarkerIndex = -1; | |
| ThreadIdentifier mainThreadSelection = new ThreadIdentifier("Main Thread", 1);; | |
| string removeMarker = GetRemoveMarker(); | |
| if (removeMarker != null) | |
| { | |
| // Find marker to remove | |
| removeMarkerIndex = profileData.GetMarkerIndex(removeMarker); | |
| removeFrameSyncTime = (removeMarkerIndex != -1); | |
| } | |
| for (int frameOffset = 0; frameOffset < frames; frameOffset++) | |
| { | |
| ProfileFrame frame = profileData.GetFrame(frameOffset); | |
| float ms = frame.msFrame; | |
| if (removeFrameSyncTime) | |
| { | |
| for (int threadIndex = 0; threadIndex < frame.threads.Count; threadIndex++) | |
| { | |
| ProfileThread thread = frame.threads[threadIndex]; | |
| // Marker only on main thread | |
| if (profileData.GetThreadName(thread) == mainThreadSelection.threadNameWithIndex) | |
| { | |
| foreach (var marker in thread.markers) | |
| { | |
| if (marker.nameIndex != removeMarkerIndex) | |
| continue; | |
| // May be multiple instances of this marker in the 'custom' case (so we can't just break out of marker loop here) | |
| ms-= marker.msMarkerTotal; | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| FrameTimeGraph.Data dataPoint = new FrameTimeGraph.Data(ms, frameOffset); | |
| data.Add(dataPoint); | |
| } | |
| return data; | |
| } | |
| void Load() | |
| { | |
| m_Path = EditorUtility.OpenFilePanel("Load profile analyzer data file", "", "pdata"); | |
| if (m_Path.Length != 0) | |
| { | |
| m_ActiveLoadingView = ActiveView.Single; | |
| BeginAsyncAction(ThreadActivity.Load); | |
| } | |
| GUIUtility.ExitGUI(); | |
| } | |
| void UpdateMatchingProfileData(ProfileData data, ref string path, ProfileAnalysis analysis, string newPath) | |
| { | |
| // Update left/right data if we are effectively overwriting it. | |
| if (m_ProfileLeftView.path == newPath) | |
| { | |
| SetView(m_ProfileLeftView, data, newPath, m_LeftFrameTimeGraph); | |
| m_RequestCompare = true; | |
| m_FullCompareRequired = true; | |
| } | |
| if (m_ProfileRightView.path == newPath) | |
| { | |
| SetView(m_ProfileRightView, data, newPath, m_RightFrameTimeGraph); | |
| m_RequestCompare = true; | |
| m_FullCompareRequired = true; | |
| } | |
| // Update single view if needed | |
| if (m_ProfileSingleView.path == newPath) | |
| { | |
| SetView(m_ProfileSingleView, data, newPath, m_FrameTimeGraph); | |
| m_ProfileSingleView.analysis = analysis; | |
| } | |
| path = newPath; | |
| } | |
| void Save(ProfileDataView dataView, bool updateDataViewWithSelectedPath = false) | |
| { | |
| string newPath = EditorUtility.SaveFilePanel("Save profile analyzer data file", "", "capture.pdata", "pdata"); | |
| if (newPath.Length != 0) | |
| { | |
| if (updateDataViewWithSelectedPath) | |
| { | |
| dataView.path = newPath; | |
| } | |
| if (ProfileData.Save(newPath, dataView.data)) | |
| { | |
| UpdateMatchingProfileData(dataView.data, ref dataView.path, dataView.analysis, newPath); | |
| } | |
| } | |
| GUIUtility.ExitGUI(); | |
| } | |
| int GetTotalCombinedMarkerCount(ProfileData left, ProfileData right) | |
| { | |
| if (left == null) | |
| return 0; | |
| if (right == null) | |
| return 0; | |
| List<string> leftMarkers = left.GetMarkerNames(); | |
| if (leftMarkers == null) | |
| return 0; | |
| List<string> rightMarkers = right.GetMarkerNames(); | |
| if (rightMarkers == null) | |
| return 0; | |
| HashSet<string> markerPairs = new HashSet<string>(); | |
| for (int index = 0; index < leftMarkers.Count; index++) | |
| { | |
| string markerName = leftMarkers[index]; | |
| markerPairs.Add(markerName); | |
| } | |
| for (int index = 0; index < rightMarkers.Count; index++) | |
| { | |
| string markerName = rightMarkers[index]; | |
| if (!markerPairs.Contains(markerName)) | |
| { | |
| markerPairs.Add(markerName); | |
| } | |
| } | |
| return markerPairs.Count; | |
| } | |
| List<MarkerPairing> GeneratePairings(ProfileAnalysis leftAnalysis, ProfileAnalysis rightAnalysis) | |
| { | |
| if (leftAnalysis == null) | |
| return null; | |
| if (rightAnalysis == null) | |
| return null; | |
| List<MarkerData> leftMarkers = leftAnalysis.GetMarkers(); | |
| if (leftMarkers == null) | |
| return null; | |
| List<MarkerData> rightMarkers = rightAnalysis.GetMarkers(); | |
| if (rightMarkers == null) | |
| return null; | |
| Dictionary<string, MarkerPairing> markerPairs = new Dictionary<string, MarkerPairing>(); | |
| for (int index = 0; index < leftMarkers.Count; index++) | |
| { | |
| MarkerData marker = leftMarkers[index]; | |
| MarkerPairing pair = new MarkerPairing | |
| { | |
| name = marker.name, | |
| leftIndex = index, | |
| rightIndex = -1 | |
| }; | |
| markerPairs[marker.name] = pair; | |
| } | |
| for (int index = 0; index < rightMarkers.Count; index++) | |
| { | |
| MarkerData marker = rightMarkers[index]; | |
| if (markerPairs.ContainsKey(marker.name)) | |
| { | |
| MarkerPairing pair = markerPairs[marker.name]; | |
| pair.rightIndex = index; | |
| markerPairs[marker.name] = pair; | |
| } | |
| else | |
| { | |
| MarkerPairing pair = new MarkerPairing | |
| { | |
| name = marker.name, | |
| leftIndex = -1, | |
| rightIndex = index | |
| }; | |
| markerPairs[marker.name] = pair; | |
| } | |
| } | |
| List<MarkerPairing> pairings = new List<MarkerPairing>(); | |
| foreach (MarkerPairing pair in markerPairs.Values) | |
| pairings.Add(pair); | |
| return pairings; | |
| } | |
| void SetThreadPhaseCount(ThreadActivity activity) | |
| { | |
| // Will be refined by the analysis functions | |
| if (activity == ThreadActivity.Compare) | |
| { | |
| m_ThreadPhases = 8; | |
| } | |
| else | |
| { | |
| m_ThreadPhases = 2; | |
| } | |
| } | |
| void BeginAsyncAction(ThreadActivity activity) | |
| { | |
| if (IsAnalysisRunning()) | |
| return; | |
| m_ThreadActivity = activity; | |
| m_ThreadProgress = 0; | |
| m_ThreadPhase = 0; | |
| SetThreadPhaseCount(activity); | |
| m_BackgroundThread = new Thread(BackgroundThread); | |
| m_BackgroundThread.Start(); | |
| } | |
| void CreateComparisonTable() | |
| { | |
| UpdateThreadNames(); | |
| // Set default sorting state | |
| int sortedColumn = (int)ComparisonTable.MyColumns.AbsDiff; | |
| bool sortAscending = false; | |
| // Query last sorting state | |
| if (m_ComparisonMulticolumnHeaderState != null) | |
| { | |
| if (m_ComparisonMulticolumnHeaderState.sortedColumnIndex >= 0) | |
| { | |
| sortedColumn = m_ComparisonMulticolumnHeaderState.sortedColumnIndex; | |
| if (sortedColumn >= 0 && sortedColumn < m_ComparisonMulticolumnHeaderState.columns.Length) | |
| sortAscending = m_ComparisonMulticolumnHeaderState.columns[sortedColumn].sortedAscending; | |
| } | |
| } | |
| if (m_ComparisonTreeViewState == null) | |
| m_ComparisonTreeViewState = new TreeViewState(); | |
| m_ComparisonMulticolumnHeaderState = ComparisonTable.CreateDefaultMultiColumnHeaderState(m_CompareModeFilter); | |
| var multiColumnHeader = new MultiColumnHeader(m_ComparisonMulticolumnHeaderState); | |
| multiColumnHeader.SetSorting(sortedColumn, sortAscending); | |
| multiColumnHeader.ResizeToFit(); | |
| m_ComparisonTable = new ComparisonTable(m_ComparisonTreeViewState, multiColumnHeader, m_ProfileLeftView, m_ProfileRightView, m_Pairings, m_hideRemovedMarkers, this, m_2D, UIColor.left, UIColor.right); | |
| if (!IsSelectedMarkerNameValid()) | |
| SelectPairing(0); | |
| else | |
| SelectPairingByName(m_SelectedMarker.name); | |
| } | |
| void CalculatePairingbuckets(ProfileAnalysis left, ProfileAnalysis right, List<MarkerPairing> pairings) | |
| { | |
| var leftMarkers = left.GetMarkers(); | |
| var rightMarkers = right.GetMarkers(); | |
| // using a for loop instead of foreach is surprisingly faster on Mono | |
| for (int i = 0, n = pairings.Count; i < n; i++) | |
| { | |
| var pairing = pairings[i]; | |
| float min = float.MaxValue; | |
| float max = 0.0f; | |
| MarkerData leftMarker = null; | |
| MarkerData rightMarker = null; | |
| if (pairing.leftIndex >= 0) | |
| { | |
| leftMarker = leftMarkers[pairing.leftIndex]; | |
| max = Math.Max(max, leftMarker.msMax); | |
| min = Math.Min(min, leftMarker.msMin); | |
| } | |
| if (pairing.rightIndex >= 0) | |
| { | |
| rightMarker = rightMarkers[pairing.rightIndex]; | |
| max = Math.Max(max, rightMarker.msMax); | |
| min = Math.Min(min, rightMarker.msMin); | |
| } | |
| int countMin = int.MaxValue; | |
| int countMax = 0; | |
| if (leftMarker != null) | |
| { | |
| countMax = Math.Max(countMax, leftMarker.countMax); | |
| countMin = Math.Min(countMin, leftMarker.countMin); | |
| } | |
| if (rightMarker != null) | |
| { | |
| countMax = Math.Max(countMax, rightMarker.countMax); | |
| countMin = Math.Min(countMin, rightMarker.countMin); | |
| } | |
| if (leftMarker != null) | |
| { | |
| leftMarker.ComputeBuckets(min, max); | |
| leftMarker.ComputeCountBuckets(countMin, countMax); | |
| } | |
| if (rightMarker != null) | |
| { | |
| rightMarker.ComputeBuckets(min, max); | |
| rightMarker.ComputeCountBuckets(countMin, countMax); | |
| } | |
| } | |
| } | |
| bool CompareSync() | |
| { | |
| if (!m_ProfileLeftView.IsDataValid()) | |
| return false; | |
| if (!m_ProfileRightView.IsDataValid()) | |
| return false; | |
| List<string> threadUINamesNew; | |
| List<string> threadNamesNew; | |
| Dictionary<string, string> threadNameToUINameNew; | |
| GetThreadNames(m_ProfileLeftView.data, m_ProfileRightView.data, out threadUINamesNew, out threadNamesNew, out threadNameToUINameNew); | |
| List<string> threadSelection = GetLimitedThreadSelection(threadNamesNew, m_ThreadSelection); | |
| System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); | |
| stopwatch.Start(); | |
| int updateDepthPhase = m_NewComparisonDataLoaded ? 2 : 0; | |
| int fullLeftPhase = (m_FullCompareRequired && m_ProfileLeftView.selectedIndices.Count != m_ProfileLeftView.data.GetFrameCount()) ? 1 : 0; | |
| int fullRightPhase = (m_FullCompareRequired && m_ProfileLeftView.selectedIndices.Count != m_ProfileLeftView.data.GetFrameCount()) ? 1 : 0; | |
| m_ThreadPhases = 2 /*scan left and right*/ + updateDepthPhase + 2 /*fullLeftPhase and fullRightPhase*/ + 2 /*analyze left and right*/; | |
| bool selfTimes = IsSelfTime(); | |
| // First scan just the frames | |
| m_ThreadPhase = 0; | |
| var leftAnalysisNew = m_ProfileAnalyzer.Analyze(m_ProfileLeftView.data, m_ProfileLeftView.selectedIndices, null, m_DepthSliceUI.depthFilter1, selfTimes, m_ParentMarker, 0, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| var rightAnalysisNew = m_ProfileAnalyzer.Analyze(m_ProfileRightView.data, m_ProfileRightView.selectedIndices, null, m_DepthSliceUI.depthFilter2, selfTimes, m_ParentMarker, 0, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| if (leftAnalysisNew == null || rightAnalysisNew == null) | |
| { | |
| stopwatch.Stop(); | |
| return false; | |
| } | |
| // Calculate the max frame time of the two scans | |
| float timeScaleMax = Math.Max(leftAnalysisNew.GetFrameSummary().msMax, rightAnalysisNew.GetFrameSummary().msMax); | |
| // Need to recalculate the depth difference when thread filters change | |
| // For now do it always if the depth is auto and not 'all' | |
| if (updateDepthPhase != 0) | |
| { | |
| var leftAnalysis = m_ProfileAnalyzer.Analyze(m_ProfileLeftView.data, m_ProfileLeftView.selectedIndices, threadSelection, ProfileAnalyzer.kDepthAll, selfTimes, m_ParentMarker, timeScaleMax, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| var rightAnalysis = m_ProfileAnalyzer.Analyze(m_ProfileRightView.data, m_ProfileRightView.selectedIndices, threadSelection, ProfileAnalyzer.kDepthAll, selfTimes, m_ParentMarker, timeScaleMax, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| var pairings = GeneratePairings(leftAnalysis, rightAnalysis); | |
| if (m_DepthSliceUI.UpdateDepthForCompareSync(leftAnalysis, rightAnalysis, pairings, m_ProfileLeftView, m_ProfileRightView)) | |
| { | |
| // New depth diff calculated to we need to do the full analysis | |
| if (fullLeftPhase == 0) | |
| fullLeftPhase = 1; | |
| if (fullRightPhase == 0) | |
| fullRightPhase = 1; | |
| } | |
| } | |
| // Now process the markers and setup buckets using the overall max frame time | |
| List<int> selection = new List<int>(); | |
| if (fullLeftPhase != 0) | |
| { | |
| selection.Clear(); | |
| for (int frameOffset = 0; frameOffset < m_ProfileLeftView.data.GetFrameCount(); frameOffset++) | |
| { | |
| selection.Add(m_ProfileLeftView.data.OffsetToDisplayFrame(frameOffset)); | |
| } | |
| // We don't pass timeScaleMax as that is only for the selected region. | |
| // Pass 0 to auto select full range | |
| m_ProfileLeftView.analysisFullNew = m_ProfileAnalyzer.Analyze(m_ProfileLeftView.data, selection, threadSelection, m_DepthSliceUI.depthFilter1, selfTimes, m_ParentMarker, 0, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| } | |
| m_ThreadPhase++; | |
| if (fullRightPhase != 0) | |
| { | |
| selection.Clear(); | |
| for (int frameOffset = 0; frameOffset < m_ProfileRightView.data.GetFrameCount(); frameOffset++) | |
| { | |
| selection.Add(m_ProfileRightView.data.OffsetToDisplayFrame(frameOffset)); | |
| } | |
| // We don't pass timeScaleMax as that is only for the selected region. | |
| // Pass 0 to auto select full range | |
| m_ProfileRightView.analysisFullNew = m_ProfileAnalyzer.Analyze(m_ProfileRightView.data, selection, threadSelection, m_DepthSliceUI.depthFilter2, selfTimes, m_ParentMarker, 0, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| } | |
| m_ThreadPhase++; | |
| m_ProfileLeftView.analysisNew = m_ProfileAnalyzer.Analyze(m_ProfileLeftView.data, m_ProfileLeftView.selectedIndices, threadSelection, m_DepthSliceUI.depthFilter1, selfTimes, m_ParentMarker, timeScaleMax, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| m_ProfileRightView.analysisNew = m_ProfileAnalyzer.Analyze(m_ProfileRightView.data, m_ProfileRightView.selectedIndices, threadSelection, m_DepthSliceUI.depthFilter2, selfTimes, m_ParentMarker, timeScaleMax, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| m_TotalCombinedMarkerCountNew = GetTotalCombinedMarkerCount(m_ProfileLeftView.data, m_ProfileRightView.data); | |
| m_PairingsNew = GeneratePairings(m_ProfileLeftView.analysisNew, m_ProfileRightView.analysisNew); | |
| CalculatePairingbuckets(m_ProfileLeftView.analysisNew, m_ProfileRightView.analysisNew, m_PairingsNew); | |
| stopwatch.Stop(); | |
| m_LastCompareTimeMilliseconds = stopwatch.ElapsedMilliseconds; | |
| TimeSpan ts = stopwatch.Elapsed; | |
| if (ts.Minutes > 0) | |
| m_LastCompareTime = string.Format("Last compare time {0} mins {1} secs {2} ms ", ts.Minutes, ts.Seconds, ts.Milliseconds); | |
| else if (ts.Seconds > 0) | |
| m_LastCompareTime = string.Format("Last compare time {0} secs {1} ms ", ts.Seconds, ts.Milliseconds); | |
| else | |
| m_LastCompareTime = string.Format("Last compare time {0} ms ", ts.Milliseconds); | |
| return true; | |
| } | |
| void Compare() | |
| { | |
| if (m_Async) | |
| { | |
| //m_comparisonTable = null; | |
| //m_ProfileLeftView.analysis = null; | |
| //m_ProfileRightView.analysis = null; | |
| BeginAsyncAction(ThreadActivity.Compare); | |
| } | |
| else | |
| { | |
| CompareSync(); | |
| UpdateAnalysisFromAsyncProcessing(m_ProfileLeftView, m_FullCompareRequired); | |
| UpdateAnalysisFromAsyncProcessing(m_ProfileRightView, m_FullCompareRequired); | |
| m_FullCompareRequired = false; | |
| } | |
| } | |
| List<MarkerPairing> GetPairings() | |
| { | |
| return m_Pairings; | |
| } | |
| int GetUnsavedIndex(string path) | |
| { | |
| if (path == null) | |
| return 0; | |
| Regex unsavedRegExp = new Regex(@"^Unsaved[\s*]([\d]*)", RegexOptions.IgnoreCase); | |
| Match match = unsavedRegExp.Match(path); | |
| if (match.Length <= 0) | |
| return 0; | |
| return Int32.Parse(match.Groups[1].Value); | |
| } | |
| void PullFromProfiler(int firstFrame, int lastFrame, ProfileDataView view, FrameTimeGraph frameTimeGraph) | |
| { | |
| m_ProgressBar.InitProgressBar("Pulling Frames from Profiler", "Please wait...", lastFrame - firstFrame); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| ProfileData newProfileData = m_ProfilerWindowInterface.PullFromProfiler(firstFrame, lastFrame); | |
| ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.Pull, analytic); | |
| frameTimeGraph.Reset(); | |
| frameTimeGraph.SetData(GetFrameTimeData(newProfileData)); | |
| // Check if this is new data (rather than repulling the same data) | |
| if (!newProfileData.IsSame(view.data)) | |
| { | |
| if (view == m_ProfileSingleView) | |
| m_NewDataLoaded = true; | |
| else | |
| m_NewComparisonDataLoaded = true; | |
| } | |
| // Update the path to use the same saved file name if this is the same data as another view | |
| if (newProfileData.IsSame(m_ProfileLeftView.data)) | |
| { | |
| view.path = m_ProfileLeftView.path; | |
| } | |
| else if (newProfileData.IsSame(m_ProfileRightView.data)) | |
| { | |
| view.path = m_ProfileRightView.path; | |
| } | |
| else if (newProfileData.IsSame(m_ProfileSingleView.data)) | |
| { | |
| view.path = m_ProfileSingleView.path; | |
| } | |
| else | |
| { | |
| int lastIndex = 0; | |
| lastIndex = Math.Max(lastIndex, GetUnsavedIndex(m_ProfileSingleView.path)); | |
| lastIndex = Math.Max(lastIndex, GetUnsavedIndex(m_ProfileLeftView.path)); | |
| lastIndex = Math.Max(lastIndex, GetUnsavedIndex(m_ProfileRightView.path)); | |
| view.path = string.Format("Unsaved {0}", lastIndex + 1); | |
| } | |
| if (view.data != null && !TmpInUse(view, view.data.FilePath)) | |
| view.data.DeleteTmpFiles(); | |
| view.data = newProfileData; | |
| view.SelectFullRange(); | |
| // Remove pairing if both left/right point at the same data | |
| if (m_ProfileLeftView.path == m_ProfileRightView.path) | |
| { | |
| SetFrameTimeGraphPairing(false); | |
| } | |
| m_ProgressBar.ClearProgressBar(); | |
| } | |
| void BackgroundThread() | |
| { | |
| try | |
| { | |
| switch (m_ThreadActivity) | |
| { | |
| case ThreadActivity.Analyze: | |
| AnalyzeSync(); | |
| m_ThreadActivity = ThreadActivity.AnalyzeDone; | |
| break; | |
| case ThreadActivity.Compare: | |
| CompareSync(); | |
| m_ThreadActivity = ThreadActivity.CompareDone; | |
| break; | |
| case ThreadActivity.AnalyzeDone: | |
| break; | |
| case ThreadActivity.CompareDone: | |
| break; | |
| case ThreadActivity.Load: | |
| m_ThreadActivity = ProfileData.Load(m_Path, out m_ProfilerData) ? ThreadActivity.LoadDone : ThreadActivity.None; | |
| break; | |
| case ThreadActivity.LoadDone: | |
| break; | |
| default: | |
| // m_threadActivity = ThreadActivity.None; | |
| break; | |
| } | |
| } | |
| catch (ThreadAbortException) | |
| { | |
| var activity = (m_ThreadActivity == ThreadActivity.Load) ? "Load" : "Analysis"; | |
| Debug.LogFormat("{0} failed due to a domain reload. Please try again.", activity); | |
| } | |
| } | |
| void SelectFirstMarkerInTable() | |
| { | |
| // SelectMarkerByIndex(0) would only select the first one found, not the first in the sorted list | |
| if (m_ProfileTable == null) | |
| return; | |
| var rows = m_ProfileTable.GetRows(); | |
| if (rows == null || rows.Count < 1) | |
| return; | |
| SelectMarkerByName(rows[0].displayName); | |
| } | |
| bool IsSelectedMarkerNameValid() | |
| { | |
| if (string.IsNullOrEmpty(m_SelectedMarker.name)) | |
| return false; | |
| if (m_hideRemovedMarkers && m_SelectedMarker.name == GetRemoveMarker()) | |
| return false; | |
| return true; | |
| } | |
| void CreateProfileTable() | |
| { | |
| if (m_ProfileTreeViewState == null) | |
| m_ProfileTreeViewState = new TreeViewState(); | |
| // Set default sorting state | |
| int sortedColumn = (int)ProfileTable.MyColumns.Median; | |
| bool sortAscending = false; | |
| // Query last sorting state | |
| if (m_ProfileMulticolumnHeaderState != null) | |
| { | |
| if (m_ProfileMulticolumnHeaderState.sortedColumnIndex >= 0) | |
| { | |
| sortedColumn = m_ProfileMulticolumnHeaderState.sortedColumnIndex; | |
| if (sortedColumn >= 0 && sortedColumn < m_ProfileMulticolumnHeaderState.columns.Length) | |
| sortAscending = m_ProfileMulticolumnHeaderState.columns[sortedColumn].sortedAscending; | |
| } | |
| } | |
| m_ProfileMulticolumnHeaderState = ProfileTable.CreateDefaultMultiColumnHeaderState(m_SingleModeFilter); | |
| var multiColumnHeader = new MultiColumnHeader(m_ProfileMulticolumnHeaderState); | |
| multiColumnHeader.SetSorting(sortedColumn, sortAscending); | |
| multiColumnHeader.ResizeToFit(); | |
| m_ProfileTable = new ProfileTable(m_ProfileTreeViewState, multiColumnHeader, m_ProfileSingleView, m_hideRemovedMarkers, this, m_2D, UIColor.bar); | |
| if (!IsSelectedMarkerNameValid()) | |
| SelectFirstMarkerInTable(); | |
| else | |
| SelectMarkerByName(m_SelectedMarker.name); | |
| UpdateThreadNames(); | |
| } | |
| void AnalyzeSync() | |
| { | |
| if (!m_ProfileSingleView.IsDataValid()) | |
| return; | |
| System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); | |
| stopwatch.Start(); | |
| List<string> threadUINamesNew; | |
| List<string> threadNamesNew; | |
| Dictionary<string, string> threadNameToUINameNew; | |
| GetThreadNames(m_ProfileSingleView.data, out threadUINamesNew, out threadNamesNew, out threadNameToUINameNew); | |
| List<string> threadSelection = GetLimitedThreadSelection(threadNamesNew, m_ThreadSelection); | |
| int fullPhase = (m_FullAnalysisRequired && (m_ProfileSingleView.selectedIndices.Count != m_ProfileSingleView.data.GetFrameCount())) ? 1 : 0; | |
| m_ThreadPhases = 1 + fullPhase; | |
| bool selfTimes = IsSelfTime(); | |
| m_ThreadPhase = 0; | |
| if (fullPhase == 1) | |
| { | |
| List<int> selection = new List<int>(); | |
| for (int frameOffset = 0; frameOffset < m_ProfileSingleView.data.GetFrameCount(); frameOffset++) | |
| { | |
| selection.Add(m_ProfileSingleView.data.OffsetToDisplayFrame(frameOffset)); | |
| } | |
| m_ProfileSingleView.analysisFullNew = m_ProfileAnalyzer.Analyze(m_ProfileSingleView.data, selection, threadSelection, m_DepthSliceUI.depthFilter, selfTimes, m_ParentMarker, 0, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| } | |
| m_ProfileSingleView.analysisNew = m_ProfileAnalyzer.Analyze(m_ProfileSingleView.data, m_ProfileSingleView.selectedIndices, threadSelection, m_DepthSliceUI.depthFilter, selfTimes, m_ParentMarker, 0, GetRemoveMarker()); | |
| m_ThreadPhase++; | |
| stopwatch.Stop(); | |
| m_LastAnalysisTimeMilliseconds = stopwatch.ElapsedMilliseconds; | |
| TimeSpan ts = stopwatch.Elapsed; | |
| if (ts.Minutes > 0) | |
| m_LastAnalysisTime = string.Format("Last analysis time {0} mins {1} secs {2} ms ", ts.Minutes, ts.Seconds, ts.Milliseconds); | |
| else if (ts.Seconds > 0) | |
| m_LastAnalysisTime = string.Format("Last analysis time {0} secs {1} ms ", ts.Seconds, ts.Milliseconds); | |
| else | |
| m_LastAnalysisTime = string.Format("Last analysis time {0} ms ", ts.Milliseconds); | |
| } | |
| void Analyze() | |
| { | |
| if (m_EnableAnalysisProfiling) | |
| { | |
| m_AnalyzeInUpdatePhase = 1; | |
| return; | |
| } | |
| if (m_Async) | |
| { | |
| //m_profileTable = null; | |
| //m_ProfileSingleView.analysis = null; | |
| BeginAsyncAction(ThreadActivity.Analyze); | |
| } | |
| else | |
| { | |
| AnalyzeSync(); | |
| UpdateAnalysisFromAsyncProcessing(m_ProfileSingleView, m_FullAnalysisRequired); | |
| m_FullAnalysisRequired = false; | |
| } | |
| } | |
| void GetThreadNames(ProfileData profleData, out List<string> threadUINames, out List<string> threadFilters, out Dictionary<string, string> threadNameToUIName) | |
| { | |
| GetThreadNames(profleData, null, out threadUINames, out threadFilters, out threadNameToUIName); | |
| } | |
| public string GetUIThreadName(string threadNameWithIndex) | |
| { | |
| string threadName = ""; | |
| m_ThreadNameToUIName.TryGetValue(threadNameWithIndex, out threadName); | |
| return threadName; | |
| } | |
| string GetFriendlyThreadName(string threadNameWithIndex, bool single) | |
| { | |
| if (string.IsNullOrEmpty(threadNameWithIndex)) | |
| return ""; | |
| var info = threadNameWithIndex.Split(':'); | |
| int threadGroupIndex = int.Parse(info[0]); | |
| var threadName = info[1].Trim(); | |
| if (single) // Single instance of this thread name | |
| { | |
| return threadName; | |
| } | |
| else | |
| { | |
| // The original format was "Worker 0" | |
| // The internal formatting is 1:Worker (1+original value). | |
| // Hence the -1 here | |
| return string.Format("{0} {1}", threadName, threadGroupIndex - 1); | |
| } | |
| } | |
| internal int CompareUINames(string a, string b) | |
| { | |
| var aSpaceIndex = a.LastIndexOf(' '); | |
| var bSpaceIndex = b.LastIndexOf(' '); | |
| if (aSpaceIndex >= 0 && bSpaceIndex >= 0) | |
| { | |
| var aThreadName = a.Substring(0, aSpaceIndex); | |
| var bThreadName = b.Substring(0, bSpaceIndex); | |
| if (aThreadName == bThreadName) | |
| { | |
| var aThreadIndex = a.Substring(aSpaceIndex + 1); | |
| var bThreadIndex = b.Substring(bSpaceIndex + 1); | |
| if (aThreadIndex == "All" && bThreadIndex != "All") | |
| return -1; | |
| if (aThreadIndex != "All" && bThreadIndex == "All") | |
| return 1; | |
| int aGroupIndex; | |
| if (int.TryParse(aThreadIndex, out aGroupIndex)) | |
| { | |
| int bGroupIndex; | |
| if (int.TryParse(bThreadIndex, out bGroupIndex)) | |
| { | |
| return aGroupIndex.CompareTo(bGroupIndex); | |
| } | |
| } | |
| } | |
| } | |
| return a.CompareTo(b); | |
| } | |
| void GetThreadNames(ProfileData leftData, ProfileData rightData, out List<string> threadUINames, out List<string> threadFilters, out Dictionary<string, string> threadNameToUIName) | |
| { | |
| List<string> threadNames = (leftData != null) ? new List<string>(leftData.GetThreadNames()) : new List<string>(); | |
| if (rightData != null) | |
| { | |
| foreach (var threadNameWithIndex in rightData.GetThreadNames()) | |
| { | |
| if (!threadNames.Contains(threadNameWithIndex)) | |
| { | |
| // TODO: Insert after last thread with same name (or at end) | |
| threadNames.Add(threadNameWithIndex); | |
| } | |
| } | |
| } | |
| Dictionary<string, string> threadNamesDict = new Dictionary<string, string>(); | |
| for (int index = 0; index < threadNames.Count; index++) | |
| { | |
| var threadNameWithIndex = threadNames[index]; | |
| var threadIdentifier = new ThreadIdentifier(threadNameWithIndex); | |
| if (threadIdentifier.index == 1) | |
| { | |
| if (threadNames.Contains(string.Format("2:{0}", threadIdentifier.name))) | |
| { | |
| var threadGroupIdentifier = new ThreadIdentifier(threadIdentifier); | |
| threadGroupIdentifier.SetAll(); | |
| // First thread name of a group with the same name | |
| // Add an 'all' selection | |
| threadNamesDict[string.Format("{0} : All", threadIdentifier.name)] = threadGroupIdentifier.threadNameWithIndex; | |
| // And add the first item too | |
| threadNamesDict[GetFriendlyThreadName(threadNameWithIndex, false)] = threadNameWithIndex; | |
| } | |
| else | |
| { | |
| // Single instance of this thread name | |
| threadNamesDict[GetFriendlyThreadName(threadNameWithIndex, true)] = threadNameWithIndex; | |
| } | |
| } | |
| else | |
| { | |
| threadNamesDict[GetFriendlyThreadName(threadNameWithIndex, false)] = threadNameWithIndex; | |
| } | |
| } | |
| List<string> uiNames = new List<string>(); | |
| foreach (string uiName in threadNamesDict.Keys) | |
| uiNames.Add(uiName); | |
| uiNames.Sort(CompareUINames); | |
| var allThreadIdentifier = new ThreadIdentifier(); | |
| allThreadIdentifier.SetName("All"); | |
| allThreadIdentifier.SetAll(); | |
| threadUINames = new List<string>(); | |
| threadFilters = new List<string>(); | |
| threadNameToUIName = new Dictionary<string, string>(); | |
| threadUINames.Add(allThreadIdentifier.name); | |
| threadFilters.Add(allThreadIdentifier.threadNameWithIndex); | |
| threadNameToUIName[allThreadIdentifier.name] = allThreadIdentifier.threadNameWithIndex; | |
| foreach (string uiName in uiNames) | |
| { | |
| // Strip off the group name | |
| // Note we don't do this in GetFriendlyThreadName else we would collapse the same named threads (in different groups) in the dict | |
| string groupName; | |
| string threadName = ProfileData.GetThreadNameWithoutGroup(uiName, out groupName); | |
| string threadFilter = threadNamesDict[uiName]; | |
| threadUINames.Add(threadName); | |
| threadFilters.Add(threadFilter); | |
| threadNameToUIName[threadFilter] = threadName; | |
| } | |
| } | |
| void UpdateThreadGroupSelection(List<string> threadNames, ThreadSelection threadSelection) | |
| { | |
| // Make sure all members of active groups are present | |
| foreach (string threadGroupNameWithIndex in threadSelection.groups) | |
| { | |
| var threadGroupIdentifier = new ThreadIdentifier(threadGroupNameWithIndex); | |
| if (threadGroupIdentifier.name == "All" && threadGroupIdentifier.index == ThreadIdentifier.kAll) | |
| { | |
| foreach (string threadNameWithIndex in threadNames) | |
| { | |
| var threadIdentifier = new ThreadIdentifier(threadNameWithIndex); | |
| if (threadIdentifier.index != ThreadIdentifier.kAll) | |
| { | |
| if (!threadSelection.selection.Contains(threadNameWithIndex)) | |
| threadSelection.selection.Add(threadNameWithIndex); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| foreach (string threadNameWithIndex in threadNames) | |
| { | |
| var threadIdentifier = new ThreadIdentifier(threadNameWithIndex); | |
| if (threadIdentifier.name == threadGroupIdentifier.name && | |
| threadIdentifier.index != ThreadIdentifier.kAll) | |
| { | |
| if (!threadSelection.selection.Contains(threadNameWithIndex)) | |
| threadSelection.selection.Add(threadNameWithIndex); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| List<string> GetLimitedThreadSelection(List<string> threadNames, ThreadSelection threadSelection) | |
| { | |
| List<string> limitedThreadSelection = new List<string>(); | |
| if (threadSelection.selection == null) | |
| return limitedThreadSelection; | |
| foreach (string threadNameWithIndex in threadSelection.selection) | |
| { | |
| if (threadNames.Contains(threadNameWithIndex)) | |
| limitedThreadSelection.Add(threadNameWithIndex); | |
| } | |
| // Make sure all members of active groups are present | |
| foreach (string threadGroupNameWithIndex in threadSelection.groups) | |
| { | |
| var threadGroupIdentifier = new ThreadIdentifier(threadGroupNameWithIndex); | |
| if (threadGroupIdentifier.name == "All" && threadGroupIdentifier.index == ThreadIdentifier.kAll) | |
| { | |
| foreach (string threadNameWithIndex in threadNames) | |
| { | |
| var threadIdentifier = new ThreadIdentifier(threadNameWithIndex); | |
| if (threadIdentifier.index != ThreadIdentifier.kAll) | |
| { | |
| if (!limitedThreadSelection.Contains(threadNameWithIndex)) | |
| limitedThreadSelection.Add(threadNameWithIndex); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| foreach (string threadNameWithIndex in threadNames) | |
| { | |
| var threadIdentifier = new ThreadIdentifier(threadNameWithIndex); | |
| if (threadIdentifier.name == threadGroupIdentifier.name && | |
| threadIdentifier.index != ThreadIdentifier.kAll) | |
| { | |
| if (!limitedThreadSelection.Contains(threadNameWithIndex)) | |
| limitedThreadSelection.Add(threadNameWithIndex); | |
| } | |
| } | |
| } | |
| } | |
| return limitedThreadSelection; | |
| } | |
| int ClampToRange(int value, int min, int max) | |
| { | |
| if (value < min) | |
| value = min; | |
| if (value > max) | |
| value = max; | |
| return value; | |
| } | |
| void GraphActive(bool active) | |
| { | |
| RequestRepaint(); | |
| } | |
| void SetRange(List<int> selectedOffsets, int clickCount, FrameTimeGraph.State inputStatus) | |
| { | |
| if (inputStatus == FrameTimeGraph.State.Dragging) | |
| return; | |
| if (clickCount == 2) | |
| { | |
| if (selectedOffsets.Count > 0 && m_ProfileSingleView.inSyncWithProfilerData) | |
| JumpToFrame(m_ProfileSingleView.data.OffsetToDisplayFrame(selectedOffsets[0]), m_ProfileSingleView.data, false); | |
| } | |
| else | |
| { | |
| m_ProfileSingleView.selectedIndices.Clear(); | |
| foreach (int offset in selectedOffsets) | |
| { | |
| m_ProfileSingleView.selectedIndices.Add(m_ProfileSingleView.data.OffsetToDisplayFrame(offset)); | |
| } | |
| // Keep indices sorted | |
| m_ProfileSingleView.selectedIndices.Sort(); | |
| m_RequestAnalysis = true; | |
| } | |
| } | |
| internal void ClearSelection() | |
| { | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| m_ProfileSingleView.ClearSelection(); | |
| m_RequestAnalysis = true; | |
| } | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| m_ProfileLeftView.ClearSelection(); | |
| m_ProfileRightView.ClearSelection(); | |
| m_RequestCompare = true; | |
| } | |
| } | |
| internal void SelectAllFrames() | |
| { | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| m_ProfileSingleView.SelectFullRange(); | |
| m_RequestAnalysis = true; | |
| } | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| m_ProfileLeftView.SelectFullRange(); | |
| m_ProfileRightView.SelectFullRange(); | |
| m_RequestCompare = true; | |
| } | |
| } | |
| internal void SelectFramesContainingMarker(string markerName, bool inSelection) | |
| { | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| if (m_ProfileSingleView.SelectAllFramesContainingMarker(markerName, inSelection)) | |
| { | |
| m_RequestAnalysis = true; | |
| } | |
| } | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| if (m_ProfileLeftView.SelectAllFramesContainingMarker(markerName, inSelection)) | |
| { | |
| m_RequestCompare = true; | |
| } | |
| if (m_ProfileRightView.SelectAllFramesContainingMarker(markerName, inSelection)) | |
| { | |
| m_RequestCompare = true; | |
| } | |
| } | |
| } | |
| static List<string> GetNameFilters(string nameFilter) | |
| { | |
| List<string> nameFilters = new List<string>(); | |
| if (string.IsNullOrEmpty(nameFilter)) | |
| return nameFilters; | |
| // Get all quoted strings, without the quotes | |
| MatchCollection matches = quotedStringWithoutQuotes.Matches(nameFilter); | |
| foreach (Match match in matches) | |
| { | |
| var theData = match.Groups[1].Value; | |
| nameFilters.Add(theData); | |
| } | |
| // Get a new string with the quoted strings removed | |
| string remaining = quotedString.Replace(nameFilter, ""); | |
| // Get all the remaining strings (that are space separated) | |
| matches = stringWithoutWhiteSpace.Matches(remaining); | |
| foreach (Match match in matches) | |
| { | |
| string theData = match.Groups[1].Value; | |
| nameFilters.Add(theData); | |
| } | |
| return nameFilters; | |
| } | |
| internal List<string> GetNameFilters() | |
| { | |
| return GetNameFilters(m_NameFilter); | |
| } | |
| internal List<string> GetNameExcludes() | |
| { | |
| return GetNameFilters(m_NameExclude); | |
| } | |
| internal bool DoesMarkerPassFilter(string name) | |
| { | |
| return m_MarkerFilter.DoesMarkerPassFilter(this, name); | |
| } | |
| internal bool NameInIncludeList(string name, List<string> nameFilters) | |
| { | |
| return NameInFilterList(name, nameFilters, m_NameFilterOperation); | |
| } | |
| internal bool NameInExcludeList(string name, List<string> nameExcludes) | |
| { | |
| return NameInFilterList(name, nameExcludes, m_NameExcludeOperation); | |
| } | |
| static bool NameInFilterList(string name, List<string> nameFilters, NameFilterOperation operation) | |
| { | |
| switch (operation) | |
| { | |
| default: | |
| //case NameFilterOperation.All: | |
| { | |
| foreach (string subString in nameFilters) | |
| { | |
| // As soon as name doesn't match one in the list then return false | |
| if (name.IndexOf(subString, StringComparison.OrdinalIgnoreCase) < 0) | |
| return false; | |
| } | |
| // Name is matching all the filters in the list | |
| return true; | |
| } | |
| case NameFilterOperation.Any: | |
| { | |
| foreach (string subString in nameFilters) | |
| { | |
| // As soon as names matches one in the list then return true | |
| if (name.IndexOf(subString, StringComparison.OrdinalIgnoreCase) >= 0) | |
| return true; | |
| } | |
| return false; | |
| } | |
| } | |
| } | |
| static string FilterWithQuotes(string markerName) | |
| { | |
| return markerName.Contains(" ") ? string.Format("\"{0}\"", markerName) : markerName; | |
| } | |
| static void AddFilter(ref string filter, string quotedMarkerName) | |
| { | |
| if (string.IsNullOrEmpty(filter)) | |
| filter = quotedMarkerName; | |
| else | |
| filter = string.Format("{0} {1}", filter, quotedMarkerName); | |
| } | |
| static bool AddFilter(List<string> nameFilters, ref string filter, string markerName) | |
| { | |
| bool justAdded = false; | |
| string quotedMarkerName = FilterWithQuotes(markerName); | |
| if (!nameFilters.Contains(quotedMarkerName)) | |
| { | |
| AddFilter(ref filter, quotedMarkerName); | |
| justAdded = true; | |
| } | |
| return justAdded; | |
| } | |
| internal void AddToIncludeFilter(string markerName) | |
| { | |
| if (AddFilter(GetNameFilters(), ref m_NameFilter, markerName)) | |
| { | |
| m_MarkerFilter.Clear(); | |
| UpdateMarkerTable(); | |
| } | |
| // Remove from exclude list if in the include list | |
| RemoveFromExcludeFilter(markerName); | |
| } | |
| internal void AddToExcludeFilter(string markerName) | |
| { | |
| if (AddFilter(GetNameExcludes(), ref m_NameExclude, markerName)) | |
| { | |
| m_MarkerFilter.Clear(); | |
| UpdateMarkerTable(); | |
| } | |
| // Remove from include list if in the include list | |
| RemoveFromIncludeFilter(markerName); | |
| } | |
| internal void RemoveFromFilter(string markerName, List<string> nameFilters, ref string newFilters) | |
| { | |
| if (nameFilters.Count == 0) | |
| return; | |
| string nameFilterString = ""; | |
| bool updated = false; | |
| foreach (string filter in nameFilters) | |
| { | |
| if (string.Compare(filter, markerName, StringComparison.CurrentCultureIgnoreCase) != 0) | |
| AddFilter(ref nameFilterString, FilterWithQuotes(filter)); | |
| else | |
| updated = true; | |
| } | |
| if (updated) | |
| { | |
| newFilters = nameFilterString; | |
| m_MarkerFilter.Clear(); | |
| UpdateMarkerTable(); | |
| } | |
| } | |
| internal void RemoveFromIncludeFilter(string markerName) | |
| { | |
| RemoveFromFilter(markerName, GetNameFilters(), ref m_NameFilter); | |
| } | |
| internal void RemoveFromExcludeFilter(string markerName) | |
| { | |
| RemoveFromFilter(markerName, GetNameExcludes(), ref m_NameExclude); | |
| } | |
| internal void SetAsParentMarkerFilter(string markerName) | |
| { | |
| if (markerName != m_ParentMarker) | |
| { | |
| m_ParentMarker = markerName; | |
| UpdateActiveTab(true); | |
| } | |
| } | |
| float GetFilenameWidth(string path) | |
| { | |
| if (path == null) | |
| return 0f; | |
| string filename = System.IO.Path.GetFileName(path); | |
| GUIContent content = new GUIContent(filename, path); | |
| Vector2 size = GUI.skin.label.CalcSize(content); | |
| return size.x; | |
| } | |
| void ShowFilename(string path) | |
| { | |
| if (path != null) | |
| { | |
| string filename = System.IO.Path.GetFileNameWithoutExtension(path); | |
| GUIContent content = new GUIContent(filename, path); | |
| Vector2 size = GUI.skin.label.CalcSize(content); | |
| float width = Math.Min(size.x, 200f); | |
| EditorGUILayout.LabelField(content, GUILayout.MaxWidth(width)); | |
| } | |
| } | |
| void DrawLoadSave() | |
| { | |
| EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(300), GUILayout.ExpandWidth(false)); | |
| GUIStyle buttonStyle = GUI.skin.button; | |
| bool lastEnabled = GUI.enabled; | |
| bool isAnalysisRunning = IsAnalysisRunning(); | |
| GUI.enabled = !isAnalysisRunning; | |
| if (GUILayout.Button("Load", buttonStyle, GUILayout.ExpandWidth(false), GUILayout.Width(50))) | |
| Load(); | |
| GUI.enabled = !isAnalysisRunning && (m_ProfileSingleView.IsDataValid()); | |
| if (GUILayout.Button("Save", buttonStyle, GUILayout.ExpandWidth(false), GUILayout.Width(50))) | |
| Save(m_ProfileSingleView); | |
| GUI.enabled = lastEnabled; | |
| ShowFilename(m_ProfileSingleView.path); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| bool IsSelectedMarkerValid() | |
| { | |
| bool valid = true; | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| if (IsAnalysisValid()) | |
| { | |
| valid = false; | |
| List<MarkerData> markers = m_ProfileSingleView.analysis.GetMarkers(); | |
| if (markers != null) | |
| { | |
| int markerAt = m_SelectedMarker.id; | |
| if (markerAt >= 0 && markerAt < markers.Count) | |
| { | |
| valid = true; | |
| } | |
| } | |
| } | |
| } | |
| else if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| if (IsAnalysisValid()) | |
| { | |
| valid = false; | |
| List<MarkerData> leftMarkers = m_ProfileLeftView.analysis.GetMarkers(); | |
| List<MarkerData> rightMarkers = m_ProfileRightView.analysis.GetMarkers(); | |
| int pairingAt = m_SelectedPairing; | |
| if (leftMarkers != null && rightMarkers != null && m_Pairings != null) | |
| { | |
| if (pairingAt >= 0 && pairingAt < m_Pairings.Count) | |
| { | |
| valid = true; | |
| } | |
| } | |
| } | |
| } | |
| return valid; | |
| } | |
| void ShowSelectedMarker() | |
| { | |
| bool valid = IsSelectedMarkerValid(); | |
| if (valid) | |
| { | |
| DrawSelectedText(m_SelectedMarker.name); | |
| } | |
| else | |
| { | |
| var markerInThread = m_SelectedMarker.id == -1 && !string.IsNullOrEmpty(m_SelectedMarker.threadName); | |
| var threadText = markerInThread ? | |
| string.Format(" (Selected in: {0}{1}{2})", | |
| m_SelectedMarker.threadGroupName, | |
| string.IsNullOrEmpty(m_SelectedMarker.threadGroupName) ? "" : ".", | |
| m_SelectedMarker.threadName) : | |
| null; | |
| string text = string.Format("{0}{1} not in selection", m_SelectedMarker.name, threadText); | |
| GUIContent content = new GUIContent(text, text); | |
| Vector2 size = GUI.skin.label.CalcSize(content); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.MaxWidth(size.x), GUILayout.Height(size.y)); | |
| if (Event.current.type == EventType.Repaint) | |
| { | |
| GUI.Label(rect, content); | |
| } | |
| } | |
| } | |
| internal bool AllSelected() | |
| { | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| if (m_ProfileSingleView.AllSelected()) | |
| return true; | |
| } | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| if (m_ProfileLeftView.AllSelected() && m_ProfileRightView.AllSelected()) | |
| return true; | |
| } | |
| return false; | |
| } | |
| internal bool HasSelection() | |
| { | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| if (m_ProfileSingleView.HasSelection()) | |
| return true; | |
| } | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| if (m_ProfileLeftView.HasSelection()) | |
| return true; | |
| if (m_ProfileRightView.HasSelection()) | |
| return true; | |
| } | |
| return false; | |
| } | |
| internal int GetRemappedUIFrameIndex(int frameIndex, ProfileDataView context) | |
| { | |
| if (context.inSyncWithProfilerData) | |
| return RemapFrameIndex(frameIndex, context.data.FrameIndexOffset); | |
| else | |
| return k_ProfileDataDefaultDisplayOffset + context.data.DisplayFrameToOffset(frameIndex); | |
| } | |
| internal bool CanExportComparisonTable() | |
| { | |
| return m_ProfileLeftView != null && m_ProfileLeftView.IsDataValid() && m_ProfileRightView != null && m_ProfileRightView.IsDataValid() && | |
| m_ComparisonTable != null && m_ActiveTab == ActiveTab.Compare; | |
| } | |
| internal bool TryExportComparisonTable(StreamWriter writer) | |
| { | |
| if (m_ComparisonTable == null || m_ActiveTab != ActiveTab.Compare) | |
| return false; | |
| m_ComparisonTable.WriteTableContentsCSV(writer); | |
| return true; | |
| } | |
| int GetRemappedUIFirstFrameOffset(ProfileDataView context) | |
| { | |
| if (context.inSyncWithProfilerData) | |
| return RemapFrameIndex(context.data.OffsetToDisplayFrame(0), context.data.FrameIndexOffset); | |
| else | |
| return context.data.OffsetToDisplayFrame(0); | |
| } | |
| int GetRemappedUIFirstFrameDisplayOffset(ProfileDataView context) | |
| { | |
| if (context.inSyncWithProfilerData) | |
| return RemapFrameIndex(context.data.OffsetToDisplayFrame(0), context.data.FrameIndexOffset); | |
| else | |
| return k_ProfileDataDefaultDisplayOffset; | |
| } | |
| static readonly ProfilerMarkerAbstracted m_DrawFrameTimeGraphProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawFrameTimeGraph"); | |
| void DrawFrameTimeGraph(float height) | |
| { | |
| using (m_DrawFrameTimeGraphProfilerMarker.Auto()) | |
| { | |
| GUI.SetNextControlName("FrameTimeGraph"); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(height)); | |
| if (m_ProfileSingleView.IsDataValid()) | |
| { | |
| if (!m_FrameTimeGraph.HasData()) | |
| m_FrameTimeGraph.SetData(GetFrameTimeData(m_ProfileSingleView.data)); | |
| if (!m_ProfileSingleView.HasValidSelection()) | |
| m_ProfileSingleView.SelectFullRange(); | |
| List<int> selectedOffsets = new List<int>(); | |
| foreach (int index in m_ProfileSingleView.selectedIndices) | |
| { | |
| selectedOffsets.Add(m_ProfileSingleView.data.DisplayFrameToOffset(index)); | |
| } | |
| float yRange = m_FrameTimeGraph.GetDataRange(); | |
| int offsetToDisplayMapping = GetRemappedUIFirstFrameDisplayOffset(m_ProfileSingleView); | |
| int offsetToIndexMapping = GetRemappedUIFirstFrameOffset(m_ProfileSingleView); | |
| bool enabled = !IsAnalysisRunning(); | |
| m_FrameTimeGraph.SetEnabled(enabled); | |
| bool valid = IsSelectedMarkerValid(); | |
| string validMarkerName = valid ? m_SelectedMarker.name : ""; | |
| m_FrameTimeGraph.Draw(rect, m_ProfileSingleView.analysis, selectedOffsets, yRange, offsetToDisplayMapping, offsetToIndexMapping, | |
| validMarkerName, 0, m_ProfileSingleView.analysisFull); | |
| EditorGUILayout.BeginHorizontal(); | |
| GUILayout.FlexibleSpace(); | |
| ShowSelectedMarker(); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| else | |
| { | |
| GUI.Label(rect, Styles.dataMissing, m_StyleUpperLeft); | |
| } | |
| } | |
| } | |
| void DrawParentFilter() | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| EditorGUILayout.LabelField(Styles.parentMarker, GUILayout.Width(100)); | |
| if (!string.IsNullOrEmpty(m_ParentMarker)) | |
| { | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = !IsAnalysisRunning(); | |
| GUI.enabled = enabled; | |
| if (GUILayout.Button("Clear", EditorStyles.miniButton, GUILayout.MaxWidth(LayoutSize.FilterOptionsEnumWidth))) | |
| { | |
| SetAsParentMarkerFilter(""); | |
| } | |
| GUI.enabled = lastEnabled; | |
| DrawSelectedText(m_ParentMarker); | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField(Styles.selectParentMarker); | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| internal void SetThreadSelection(ThreadSelection threadSelection) | |
| { | |
| m_ThreadSelectionNew = new ThreadSelection(threadSelection); | |
| UpdateActiveTab(true); | |
| } | |
| string CalculateSelectedThreadsSummary() | |
| { | |
| if (m_ThreadSelection.selection == null || m_ThreadSelection.selection.Count == 0) | |
| return "None"; | |
| // Count all threads in a group | |
| var threadDict = new Dictionary<string, int>(); | |
| var threadSelectionDict = new Dictionary<string, int>(); | |
| foreach (var threadNameWithIndex in m_ThreadNames) | |
| { | |
| var threadIdentifier = new ThreadIdentifier(threadNameWithIndex); | |
| if (threadIdentifier.index == ThreadIdentifier.kAll) | |
| continue; | |
| int count; | |
| if (threadDict.TryGetValue(threadIdentifier.name, out count)) | |
| threadDict[threadIdentifier.name] = count + 1; | |
| else | |
| threadDict[threadIdentifier.name] = 1; | |
| threadSelectionDict[threadIdentifier.name] = 0; | |
| } | |
| // Count all the threads we have 'selected' in a group | |
| foreach (var threadNameWithIndex in m_ThreadSelection.selection) | |
| { | |
| var threadIdentifier = new ThreadIdentifier(threadNameWithIndex); | |
| if (threadDict.ContainsKey(threadIdentifier.name) && | |
| threadSelectionDict.ContainsKey(threadIdentifier.name) && | |
| threadIdentifier.index <= threadDict[threadIdentifier.name]) | |
| { | |
| // Selected thread valid and in the thread list | |
| // and also within the range of valid threads for this data set | |
| threadSelectionDict[threadIdentifier.name]++; | |
| } | |
| } | |
| // Count all thread groups where we have 'selected all the threads' | |
| int threadsSelected = 0; | |
| foreach (var threadName in threadDict.Keys) | |
| { | |
| if (threadSelectionDict[threadName] != threadDict[threadName]) | |
| continue; | |
| threadsSelected++; | |
| } | |
| // If we've just added all the thread names we have everything selected | |
| // Note we don't compare against the m_ThreadNames directly as this contains the 'all' versions | |
| if (threadsSelected == threadDict.Keys.Count) | |
| return "All"; | |
| // Add all the individual threads were we haven't already added the group | |
| List<string> threads = new List<string>(); | |
| foreach (var threadName in threadSelectionDict.Keys) | |
| { | |
| int selectionCount = threadSelectionDict[threadName]; | |
| if (selectionCount <= 0) | |
| continue; | |
| int threadCount = threadDict[threadName]; | |
| if (threadCount == 1) | |
| threads.Add(threadName); | |
| else if (selectionCount != threadCount) | |
| threads.Add(string.Format("{0} ({1} of {2})", threadName, selectionCount, threadCount)); | |
| else | |
| threads.Add(string.Format("{0} (All)", threadName)); | |
| } | |
| // Maintain alphabetical order | |
| threads.Sort(CompareUINames); | |
| if (threads.Count == 0) | |
| return "None"; | |
| string threadsSelectedText = string.Join(", ", threads.ToArray()); | |
| return threadsSelectedText; | |
| } | |
| string GetSelectedThreadsSummary() | |
| { | |
| return m_ThreadSelectionSummary; | |
| } | |
| void DrawThreadFilter(ProfileData profileData) | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| EditorGUILayout.LabelField(Styles.threadFilter, GUILayout.Width(LayoutSize.FilterOptionsLeftLabelWidth)); | |
| if (profileData != null) | |
| { | |
| if (m_ThreadNames.Count > 0) | |
| { | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = !IsAnalysisRunning() && !ThreadSelectionWindow.IsOpen(); | |
| GUI.enabled = enabled; | |
| if (GUILayout.Button(Styles.threadFilterSelect, GUILayout.Width(LayoutSize.FilterOptionsEnumWidth))) | |
| { | |
| Vector2 windowPosition = new Vector2(Event.current.mousePosition.x + LayoutSize.FilterOptionsEnumWidth, Event.current.mousePosition.y + GUI.skin.label.lineHeight); | |
| Vector2 screenPosition = GUIUtility.GUIToScreenPoint(windowPosition); | |
| ThreadSelectionWindow.Open(screenPosition.x, screenPosition.y, this, m_ThreadSelection, m_ThreadNames, m_ThreadUINames); | |
| EditorGUIUtility.ExitGUI(); | |
| } | |
| GUI.enabled = lastEnabled; | |
| ShowSelectedThreads(); | |
| GUILayout.FlexibleSpace(); | |
| } | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| void DrawSelectedText(string text) | |
| { | |
| if (text == null) | |
| return; | |
| GUIStyle treeViewSelectionStyle = "TV Selection"; | |
| GUIStyle backgroundStyle = new GUIStyle(treeViewSelectionStyle); | |
| GUIStyle treeViewLineStyle = "TV Line"; | |
| GUIStyle textStyle = new GUIStyle(treeViewLineStyle); | |
| GUIContent content = new GUIContent(text, text); | |
| Vector2 size = textStyle.CalcSize(content); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.MaxWidth(size.x), GUILayout.Height(size.y)); | |
| if (Event.current.type == EventType.Repaint) | |
| { | |
| backgroundStyle.Draw(rect, false, false, true, true); | |
| GUI.Label(rect, content, textStyle); | |
| } | |
| } | |
| void ShowSelectedThreads() | |
| { | |
| string threadsSelected = GetSelectedThreadsSummary(); | |
| DrawSelectedText(threadsSelected); | |
| } | |
| void DrawUnitFilter() | |
| { | |
| EditorGUILayout.BeginHorizontal(GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth + LayoutSize.FilterOptionsRightEnumWidth)); | |
| EditorGUILayout.LabelField(Styles.unitFilter, m_StyleMiddleRight, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth)); | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = !IsAnalysisRunning(); | |
| GUI.enabled = enabled; | |
| //Units units = (Units)EditorGUILayout.EnumPopup(m_DisplayUnits.Units, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth)); | |
| Units units = (Units)EditorGUILayout.Popup((int)m_DisplayUnits.Units, m_UnitNames, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth)); | |
| GUI.enabled = lastEnabled; | |
| if (units != m_DisplayUnits.Units) | |
| { | |
| SetUnits(units); | |
| m_FrameTimeGraph.SetUnits(m_DisplayUnits.Units); | |
| m_LeftFrameTimeGraph.SetUnits(m_DisplayUnits.Units); | |
| m_RightFrameTimeGraph.SetUnits(m_DisplayUnits.Units); | |
| UpdateMarkerTable(); | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| bool IsSelfTime() | |
| { | |
| return (m_TimingOption == TimingOptions.TimingOption.Self) ? true : false; | |
| } | |
| void DrawTimingFilter() | |
| { | |
| EditorGUILayout.BeginHorizontal(GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth + LayoutSize.FilterOptionsRightEnumWidth)); | |
| EditorGUILayout.LabelField(Styles.timingFilter, m_StyleMiddleRight, GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth)); | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = !IsAnalysisRunning(); | |
| GUI.enabled = enabled; | |
| var timingOption = (TimingOptions.TimingOption)EditorGUILayout.Popup((int)m_TimingOption, TimingOptions.TimingOptionNames, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth)); | |
| GUI.enabled = lastEnabled; | |
| if (timingOption != m_TimingOption) | |
| { | |
| m_TimingOption = timingOption; | |
| UpdateActiveTab(true, true); | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| internal string GetDisplayUnits() | |
| { | |
| return m_DisplayUnits.Postfix(); | |
| } | |
| internal string ToDisplayUnits(float ms, bool showUnits = false, int limitToDigits = 5, bool showFullValueWhenBelowZero = false) | |
| { | |
| return m_DisplayUnits.ToString(ms, showUnits, limitToDigits, showFullValueWhenBelowZero); | |
| } | |
| internal string ToDisplayUnits(double ms, bool showUnits = false, int limitToDigits = 5, bool showFullValueWhenBelowZero = false) | |
| { | |
| return m_DisplayUnits.ToString((float)ms, showUnits, limitToDigits, showFullValueWhenBelowZero); | |
| } | |
| internal string ToTooltipDisplayUnits(float ms, bool showUnits = false, int frameIndex = -1) | |
| { | |
| return m_DisplayUnits.ToTooltipString(ms, showUnits, frameIndex); | |
| } | |
| internal GUIContent ToDisplayUnitsWithTooltips(float ms, bool showUnits = false, int frameIndex = -1) | |
| { | |
| return m_DisplayUnits.ToGUIContentWithTooltips(ms, showUnits, 5, frameIndex); | |
| } | |
| void SetUnits(Units units) | |
| { | |
| m_DisplayUnits = new DisplayUnits(units); | |
| } | |
| void UpdateActiveTab(bool fullAnalysisRequired = false, bool markOtherDirty = true) | |
| { | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| m_RequestAnalysis = true; | |
| m_FullAnalysisRequired = fullAnalysisRequired; | |
| break; | |
| case ActiveTab.Compare: | |
| m_RequestCompare = true; | |
| m_FullCompareRequired = fullAnalysisRequired; | |
| break; | |
| } | |
| if (markOtherDirty) | |
| m_OtherTabDirty = true; | |
| } | |
| void UpdateMarkerTable(bool markOtherDirty = true) | |
| { | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| if (m_ProfileTable != null) | |
| m_ProfileTable.Reload(); | |
| break; | |
| case ActiveTab.Compare: | |
| if (m_ComparisonTable != null) | |
| m_ComparisonTable.Reload(); | |
| break; | |
| } | |
| if (markOtherDirty) | |
| m_OtherTableDirty = true; | |
| } | |
| void DrawNameFilter() | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| EditorGUILayout.LabelField(Styles.nameFilter, GUILayout.Width(LayoutSize.FilterOptionsLeftLabelWidth)); | |
| NameFilterOperation lastNameFilterOperation = m_NameFilterOperation; | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = !IsAnalysisRunning(); | |
| GUI.enabled = enabled; | |
| m_NameFilterOperation = (NameFilterOperation)EditorGUILayout.Popup((int)m_NameFilterOperation, Styles.nameFilterOperation, GUILayout.MaxWidth(LayoutSize.FilterOptionsEnumWidth)); | |
| GUI.enabled = lastEnabled; | |
| if (m_NameFilterOperation != lastNameFilterOperation) | |
| { | |
| m_MarkerFilter.Clear(); | |
| UpdateMarkerTable(); | |
| } | |
| string lastFilter = m_NameFilter; | |
| GUI.enabled = enabled; | |
| GUI.SetNextControlName("NameFilter"); | |
| m_NameFilter = EditorGUILayout.TextField(m_NameFilter, GUILayout.MinWidth(200 - LayoutSize.FilterOptionsEnumWidth)); | |
| GUI.enabled = lastEnabled; | |
| if (m_NameFilter != lastFilter) | |
| { | |
| m_MarkerFilter.Clear(); | |
| UpdateMarkerTable(); | |
| } | |
| EditorGUILayout.LabelField(Styles.nameExclude, GUILayout.Width(LayoutSize.FilterOptionsLeftLabelWidth)); | |
| NameFilterOperation lastNameExcludeOperation = m_NameExcludeOperation; | |
| GUI.enabled = enabled; | |
| m_NameExcludeOperation = (NameFilterOperation)EditorGUILayout.Popup((int)m_NameExcludeOperation, Styles.nameFilterOperation, GUILayout.MaxWidth(LayoutSize.FilterOptionsEnumWidth)); | |
| GUI.enabled = lastEnabled; | |
| if (m_NameExcludeOperation != lastNameExcludeOperation) | |
| { | |
| m_MarkerFilter.Clear(); | |
| UpdateMarkerTable(); | |
| } | |
| string lastExclude = m_NameExclude; | |
| GUI.enabled = enabled; | |
| GUI.SetNextControlName("ExcludeFilter"); | |
| m_NameExclude = EditorGUILayout.TextField(m_NameExclude, GUILayout.MinWidth(200 - LayoutSize.FilterOptionsEnumWidth)); | |
| GUI.enabled = lastEnabled; | |
| if (m_NameExclude != lastExclude) | |
| { | |
| m_MarkerFilter.Clear(); | |
| UpdateMarkerTable(); | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| internal void SetMode(MarkerColumnFilter.Mode newMode) | |
| { | |
| m_SingleModeFilter.mode = newMode; | |
| m_CompareModeFilter.mode = newMode; | |
| if (m_ProfileTable != null) | |
| m_ProfileTable.SetMode(m_SingleModeFilter); | |
| if (m_ComparisonTable != null) | |
| m_ComparisonTable.SetMode(m_CompareModeFilter); | |
| } | |
| internal void SetSingleModeColumns(int[] visibleColumns) | |
| { | |
| // If selecting the columns manually then override the currently stored selection with the current | |
| m_ProfileMulticolumnHeaderState.visibleColumns = visibleColumns; | |
| m_SingleModeFilter.mode = MarkerColumnFilter.Mode.Custom; | |
| m_SingleModeFilter.visibleColumns = visibleColumns; | |
| } | |
| internal void SetComparisonModeColumns(int[] visibleColumns) | |
| { | |
| // If selecting the columns manually then override the currently stored selection with the current | |
| m_ComparisonMulticolumnHeaderState.visibleColumns = visibleColumns; | |
| m_CompareModeFilter.mode = MarkerColumnFilter.Mode.Custom; | |
| m_CompareModeFilter.visibleColumns = visibleColumns; | |
| } | |
| void DrawMarkerColumnFilter() | |
| { | |
| EditorGUILayout.BeginHorizontal(GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth + LayoutSize.FilterOptionsRightEnumWidth)); | |
| EditorGUILayout.LabelField(Styles.markerColumns, m_StyleMiddleRight, GUILayout.Width(LayoutSize.FilterOptionsRightLabelWidth)); | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = !IsAnalysisRunning(); | |
| GUI.enabled = enabled; | |
| var filterMode = m_ActiveTab == ActiveTab.Summary ? m_SingleModeFilter : m_CompareModeFilter; | |
| var oldMode = filterMode.mode; | |
| filterMode.mode = (MarkerColumnFilter.Mode)EditorGUILayout.IntPopup((int)filterMode.mode, MarkerColumnFilter.ModeNames, MarkerColumnFilter.ModeValues, GUILayout.Width(LayoutSize.FilterOptionsRightEnumWidth)); | |
| if (filterMode.mode != oldMode) | |
| { | |
| if (m_ActiveTab == ActiveTab.Summary && m_ProfileTable != null) | |
| m_ProfileTable.SetMode(filterMode); | |
| else if (m_ActiveTab == ActiveTab.Compare && m_ComparisonTable != null) | |
| m_ComparisonTable.SetMode(filterMode); | |
| } | |
| GUI.enabled = lastEnabled; | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| enum InDataSet | |
| { | |
| Left, | |
| Both, | |
| Right | |
| }; | |
| int GetCombinedThreadCount(out int matchingCount, out int uniqueLeft, out int uniqueRight) | |
| { | |
| var threads = new Dictionary<string, InDataSet>(); | |
| foreach (var threadName in m_ProfileLeftView.data.GetThreadNames()) | |
| { | |
| threads[threadName] = InDataSet.Left; | |
| } | |
| foreach (var threadName in m_ProfileRightView.data.GetThreadNames()) | |
| { | |
| if (threads.ContainsKey(threadName)) | |
| threads[threadName] = InDataSet.Both; | |
| else | |
| threads[threadName] = InDataSet.Right; | |
| } | |
| matchingCount = 0; | |
| uniqueLeft = 0; | |
| uniqueRight = 0; | |
| int total = 0; | |
| foreach (var thread in threads) | |
| { | |
| switch (thread.Value) | |
| { | |
| case InDataSet.Left: | |
| uniqueLeft++; | |
| break; | |
| case InDataSet.Both: | |
| matchingCount++; | |
| break; | |
| case InDataSet.Right: | |
| uniqueRight++; | |
| break; | |
| } | |
| total++; | |
| } | |
| return total; | |
| } | |
| void DrawMarkerCount() | |
| { | |
| if (!IsAnalysisValid()) | |
| return; | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| int markersCount = m_ProfileSingleView.analysis.GetFrameSummary().totalMarkers; | |
| int filteredMarkersCount = (m_ProfileTable != null) ? m_ProfileTable.GetRows().Count : 0; | |
| var content = new GUIContent( | |
| String.Format("{0} of {1} markers", filteredMarkersCount, markersCount), | |
| "Number of markers in the filtered set, compared to the total in the data set"); | |
| Vector2 size = GUI.skin.label.CalcSize(content); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(size.x), GUILayout.Height(size.y)); | |
| EditorGUI.LabelField(rect, content); | |
| } | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| int markersCount = m_TotalCombinedMarkerCount; | |
| int filteredMarkersCount = (m_ComparisonTable != null) ? m_ComparisonTable.GetRows().Count : 0; | |
| var content = new GUIContent( | |
| String.Format("{0} of {1} markers", filteredMarkersCount, markersCount), | |
| "Number of markers in the filtered set, compared to total unique markers in the combined data sets"); | |
| Vector2 size = GUI.skin.label.CalcSize(content); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(size.x), GUILayout.Height(size.y)); | |
| EditorGUI.LabelField(rect, content); | |
| } | |
| } | |
| string GetThreadCountToolTipUnion(int allThreadsCount, int matchingCount) | |
| { | |
| return String.Format( | |
| "Total\n{0} Union : Combined over both data sets\n{1} Intersection : Matching in both data sets", | |
| allThreadsCount, | |
| matchingCount | |
| ); | |
| } | |
| string GetThreadCountToolTipDifference(int allThreadsCount, int matchingCount, int uniqueLeft, int uniqueRight) | |
| { | |
| return String.Format( | |
| "Difference\n{0}\n{1} Unique to left\n{2} Unique to right", | |
| allThreadsCount - matchingCount, | |
| uniqueLeft, | |
| uniqueRight); | |
| } | |
| string GetThreadCountToolTip(int allThreadsCount, int matchingCount, int uniqueLeft, int uniqueRight) | |
| { | |
| return String.Format( | |
| "{0}\n\n{1}", | |
| GetThreadCountToolTipUnion(allThreadsCount, matchingCount), | |
| GetThreadCountToolTipDifference(allThreadsCount, matchingCount, uniqueLeft, uniqueRight) | |
| ); | |
| } | |
| void DrawThreadCount() | |
| { | |
| if (!IsAnalysisValid()) | |
| return; | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| int allThreadsCount = m_ProfileSingleView.data.GetThreadNames().Count; | |
| List<string> threadSelection = GetLimitedThreadSelection(m_ThreadNames, m_ThreadSelection); | |
| int selectedThreads = threadSelection.Count; | |
| var content = new GUIContent( | |
| String.Format("{0} of {1} threads", selectedThreads, allThreadsCount), | |
| "Number of threads in the filtered set, compared to the total in the data set"); | |
| Vector2 size = GUI.skin.label.CalcSize(content); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(size.x), GUILayout.Height(size.y)); | |
| EditorGUI.LabelField(rect, content); | |
| } | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| int matchingCount, uniqueLeft, uniqueRight; | |
| int allThreadsCount = GetCombinedThreadCount(out matchingCount, out uniqueLeft, out uniqueRight); | |
| List<string> threadSelection = GetLimitedThreadSelection(m_ThreadNames, m_ThreadSelection); | |
| int selectedThreads = threadSelection.Count; | |
| string partialTooltip = GetThreadCountToolTip(allThreadsCount, matchingCount, uniqueLeft, uniqueRight); | |
| var content = new GUIContent( | |
| String.Format("{0} of {1} threads", selectedThreads, allThreadsCount), | |
| String.Format("Number of threads in the filtered set, compared to total unique threads in the combined data sets\n\n{0}", partialTooltip) | |
| ); | |
| Vector2 size = GUI.skin.label.CalcSize(content); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.Width(size.x), GUILayout.Height(size.y)); | |
| EditorGUI.LabelField(rect, content); | |
| } | |
| } | |
| static readonly ProfilerMarkerAbstracted m_DrawAnalysisOptionsProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawAnalysisOptions"); | |
| void UpdateRemoveMarkerDisplay() | |
| { | |
| UpdateActiveTab(true); | |
| // Force an update | |
| m_FrameTimeGraph.ClearData(); | |
| m_LeftFrameTimeGraph.ClearData(); | |
| m_RightFrameTimeGraph.ClearData(); | |
| } | |
| void BuildRemoveMarkerList() | |
| { | |
| var entries = new List<GUIContent>(); | |
| var values = new List<int>(); | |
| m_removeMarkerSomeMissing = false; | |
| foreach (RemoveMarkerOperation filterOperation in Enum.GetValues(typeof(RemoveMarkerOperation))) | |
| { | |
| int value = (int)filterOperation; | |
| GUIContent entry = new GUIContent(Styles.removeMarkerOperation[value]); | |
| bool found = true; | |
| switch(filterOperation) | |
| { | |
| case RemoveMarkerOperation.HideWaitForFPS: | |
| { | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| if (!m_ProfileSingleView.containsWaitForFPS) | |
| found = false; | |
| break; | |
| case ActiveTab.Compare: | |
| if (!m_ProfileLeftView.containsWaitForFPS && !m_ProfileRightView.containsWaitForFPS) | |
| found = false; | |
| break; | |
| } | |
| } | |
| break; | |
| case RemoveMarkerOperation.HideWaitForPresent: | |
| { | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| if (!m_ProfileSingleView.containsWaitForPresent) | |
| found = false; | |
| break; | |
| case ActiveTab.Compare: | |
| if (!m_ProfileLeftView.containsWaitForPresent && !m_ProfileRightView.containsWaitForPresent) | |
| found = false; | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| if (!found) | |
| { | |
| entry.text += " (not in capture)"; | |
| m_removeMarkerSomeMissing = true; | |
| } | |
| entries.Add(entry); | |
| values.Add(value); | |
| } | |
| m_removeMarkerDisplay = entries.ToArray(); | |
| m_removeMarkerValues = values.ToArray(); | |
| } | |
| void DrawRemoveMarker() | |
| { | |
| bool update = false; | |
| EditorGUILayout.BeginHorizontal(); | |
| EditorGUILayout.LabelField(Styles.removeMarker, GUILayout.Width(LayoutSize.FilterOptionsLeftLabelWidth)); | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = !IsAnalysisRunning(); | |
| GUI.enabled = enabled; | |
| if (m_removeMarkerDisplay == null || m_removeMarkerValues == null) | |
| BuildRemoveMarkerList(); | |
| // Hack to make popup wider if some markers missing, and have extra text to indicate that | |
| int width = (m_removeMarkerSomeMissing ? LayoutSize.RemoveMarkerMissingOptionsEnumWidth : LayoutSize.RemoveMarkerOptionsEnumWidth); | |
| RemoveMarkerOperation removeMarkerOperation = (RemoveMarkerOperation)EditorGUILayout.IntPopup((int)m_removeMarkerOperation, m_removeMarkerDisplay, m_removeMarkerValues, GUILayout.Width(width)); | |
| if (removeMarkerOperation != m_removeMarkerOperation) | |
| update = true; | |
| if (m_removeMarkerOperation != RemoveMarkerOperation.ShowAll) | |
| { | |
| bool hide = EditorGUILayout.Toggle(Styles.hideRemoveMarkers, m_hideRemovedMarkers, GUILayout.ExpandWidth(false)); | |
| if (hide != m_hideRemovedMarkers) | |
| { | |
| m_hideRemovedMarkers = hide; | |
| update = true; | |
| } | |
| } | |
| switch (m_removeMarkerOperation) | |
| { | |
| case RemoveMarkerOperation.ShowAll: | |
| EditorGUILayout.LabelField(""); | |
| break; | |
| /* | |
| // Could make custom marker editable, but we don't support multiple markers yet | |
| // So currently we limit this to being set by right click context menu | |
| case removeMarkerOperation.Custom: | |
| string removeMarkerCustomRemoveMarker = EditorGUILayout.DelayedTextField(m_removeMarkerCustomRemoveMarker, GUILayout.MinWidth(200 - LayoutSize.removeMarkerOptionsEnumWidth)); | |
| if (removeMarkerCustomRemoveMarker != m_removeMarkerCustomRemoveMarker) | |
| { | |
| m_removeMarkerCustomRemoveMarker = removeMarkerCustomRemoveMarker; | |
| update = true; | |
| } | |
| break; | |
| */ | |
| default: | |
| EditorGUILayout.LabelField(GetRemoveMarker()); | |
| break; | |
| } | |
| GUI.enabled = lastEnabled; | |
| EditorGUILayout.EndHorizontal(); | |
| if (update) | |
| { | |
| m_removeMarkerOperation = removeMarkerOperation; | |
| UpdateRemoveMarkerDisplay(); | |
| } | |
| } | |
| internal void SetAsRemoveMarker(string markerName) | |
| { | |
| if (markerName == "") | |
| { | |
| if (m_removeMarkerOperation != RemoveMarkerOperation.ShowAll) | |
| { | |
| m_removeMarkerOperation = RemoveMarkerOperation.ShowAll; | |
| UpdateRemoveMarkerDisplay(); | |
| } | |
| } | |
| else | |
| { | |
| m_removeMarkerCustomRemoveMarker = markerName; | |
| m_removeMarkerOperation = RemoveMarkerOperation.Custom; | |
| UpdateRemoveMarkerDisplay(); | |
| } | |
| } | |
| string GetRemoveMarker() | |
| { | |
| switch (m_removeMarkerOperation) | |
| { | |
| default: | |
| case RemoveMarkerOperation.ShowAll: | |
| return null; | |
| case RemoveMarkerOperation.HideWaitForFPS: | |
| // Gfx.WaitForPresentOnGfxThread is not always present on Android | |
| // E.g. when Application.targetFrameRate locks the frame rate | |
| // So we support removing WaitForTargetFPS | |
| // Often WaitForTargetFPS will be negligable on console and Gfx.WaitForPresentOnGfxThread is key | |
| return "WaitForTargetFPS"; | |
| case RemoveMarkerOperation.HideWaitForPresent: | |
| // Gfx.WaitForPresentOnGfxThread is seen on consoles | |
| return "Gfx.WaitForPresentOnGfxThread"; | |
| case RemoveMarkerOperation.Custom: | |
| if (m_removeMarkerCustomRemoveMarker == null || m_removeMarkerCustomRemoveMarker=="") | |
| m_removeMarkerCustomRemoveMarker = "WaitForTargetFPS"; | |
| return m_removeMarkerCustomRemoveMarker; | |
| } | |
| } | |
| void DrawAnalysisOptions() | |
| { | |
| using (m_DrawAnalysisOptionsProfilerMarker.Auto()) | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box); | |
| bool lastShowFilters = m_ShowFilters; | |
| m_ShowFilters = BoldFoldout(m_ShowFilters, Styles.filters); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowFilters) | |
| { | |
| DrawRemoveMarker(); | |
| DrawNameFilter(); | |
| EditorGUILayout.BeginHorizontal(); | |
| DrawThreadFilter(m_ProfileSingleView.data); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_DepthSliceUI.DrawDepthFilter(IsAnalysisRunning(), m_ActiveTab == ActiveTab.Summary, | |
| m_ProfileSingleView, m_ProfileLeftView, m_ProfileRightView); | |
| DrawTimingFilter(); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| DrawParentFilter(); | |
| DrawUnitFilter(); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| bool lastEnabled = GUI.enabled; | |
| GUI.enabled = !IsAnalysisRunning(); | |
| if (GUILayout.Button(new GUIContent("Analyze", m_LastAnalysisTime), GUILayout.Width(100))) | |
| m_RequestAnalysis = true; | |
| GUI.enabled = lastEnabled; | |
| DrawMarkerCount(); | |
| EditorGUILayout.LabelField(",", GUILayout.Width(10), GUILayout.ExpandWidth(false)); | |
| DrawThreadCount(); | |
| GUILayout.FlexibleSpace(); | |
| DrawMarkerColumnFilter(); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| if (m_ShowFilters != lastShowFilters) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Filters, | |
| analytic.GetDurationInSeconds(), m_ShowFilters); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| } | |
| internal bool IsAnalysisRunning() | |
| { | |
| return m_ThreadActivity != ThreadActivity.None; | |
| } | |
| internal bool IsLoading() | |
| { | |
| return m_ThreadActivity == ThreadActivity.Load; | |
| } | |
| float GetProgress() | |
| { | |
| // We return the value from the update loop so the data doesn't change over the time onGui is called for layout and repaint | |
| // m_ThreadProgress ranges from 0 to 100. | |
| return m_ThreadProgress * 0.01f; | |
| } | |
| bool IsAnalysisValid(ProfileDataView view, bool checkFrameCount = false) | |
| { | |
| if (!view.IsDataValid()) | |
| return false; | |
| if (view.analysis == null) | |
| return false; | |
| if (checkFrameCount) | |
| { | |
| if (view.analysis.GetFrameSummary().frames.Count <= 0) | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool IsAnalysisValid(bool checkFrameCount = false) | |
| { | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| if (!IsAnalysisValid(m_ProfileSingleView, checkFrameCount)) | |
| return false; | |
| break; | |
| case ActiveTab.Compare: | |
| if (!IsAnalysisValid(m_ProfileLeftView, checkFrameCount)) | |
| return false; | |
| if (!IsAnalysisValid(m_ProfileRightView, checkFrameCount)) | |
| return false; | |
| break; | |
| } | |
| //if (IsAnalysisRunning()) | |
| // return false; | |
| return true; | |
| } | |
| void DrawProgress(Rect rect) | |
| { | |
| if (IsAnalysisRunning()) | |
| { | |
| EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(140)); | |
| float x = 0; | |
| float y = 0; | |
| float width = rect.width; | |
| float height = k_ProgressBarHeight; | |
| if (m_2D.DrawStart(width, height, Draw2D.Origin.TopLeft)) | |
| { | |
| float barLength = width * GetProgress(); | |
| m_2D.DrawFilledBox(x, y, barLength, height, UIColor.white); | |
| m_2D.DrawEnd(); | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| else | |
| { | |
| EditorGUILayout.BeginVertical(); | |
| GUILayout.Space(k_ProgressBarHeight); | |
| EditorGUILayout.EndVertical(); | |
| } | |
| } | |
| void DrawPullButton(Color color, ProfileDataView view, FrameTimeGraph frameTimeGraph) | |
| { | |
| bool lastEnabled = GUI.enabled; | |
| GUI.enabled = !IsAnalysisRunning(); | |
| GUIContent content; | |
| if (!IsProfilerWindowOpen()) | |
| { | |
| content = Styles.pullOpen; | |
| GUI.enabled = false; | |
| } | |
| else if (m_ProfilerFirstFrameIndex == 0 && m_ProfilerLastFrameIndex == 0) | |
| { | |
| content = Styles.pullRange; | |
| GUI.enabled = false; | |
| } | |
| /* | |
| // Commented out so we can capture even if recording | |
| else if (m_ProfilerWindowInterface.IsRecording()) | |
| { | |
| content = Styles.pullRecording; | |
| GUI.enabled = false; | |
| } | |
| */ | |
| else | |
| { | |
| content = Styles.pull; | |
| } | |
| Color oldColor = GUI.backgroundColor; | |
| GUI.backgroundColor = color; | |
| bool pull = GUILayout.Button(content, GUILayout.Width(100)); | |
| GUI.backgroundColor = oldColor; | |
| if (pull) | |
| { | |
| PullFromProfiler(m_ProfilerFirstFrameIndex, m_ProfilerLastFrameIndex, view, frameTimeGraph); | |
| UpdateActiveTab(true, false); | |
| } | |
| GUI.enabled = lastEnabled; | |
| } | |
| static readonly ProfilerMarkerAbstracted m_DrawFilesLoadedProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawFilesLoaded"); | |
| void DrawFilesLoaded() | |
| { | |
| using (m_DrawFilesLoadedProfilerMarker.Auto()) | |
| { | |
| var boxStyle = GUI.skin.box; | |
| var rect = EditorGUILayout.BeginVertical(boxStyle); | |
| if (m_ActiveTab == ActiveTab.Summary) | |
| { | |
| EditorGUILayout.BeginHorizontal(GUILayout.Height(100 + GUI.skin.label.lineHeight + | |
| (2 * (GUI.skin.label.margin.vertical + | |
| GUI.skin.label.padding.vertical)))); | |
| float filenameWidth = GetFilenameWidth(m_ProfileSingleView.path); | |
| filenameWidth = Math.Min(filenameWidth, 200); | |
| EditorGUILayout.BeginVertical(GUILayout.MaxWidth(100 + filenameWidth), | |
| GUILayout.ExpandWidth(false)); | |
| DrawPullButton(GUI.backgroundColor, m_ProfileSingleView, m_FrameTimeGraph); | |
| DrawLoadSave(); | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true)); | |
| DrawFrameTimeGraph(100); | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| if (m_ActiveTab == ActiveTab.Compare) | |
| { | |
| DrawComparisonLoadSave(); | |
| } | |
| rect.width -= boxStyle.margin.right; | |
| DrawProgress(rect); | |
| EditorGUILayout.EndVertical(); | |
| } | |
| } | |
| void ShowHelp() | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box); | |
| m_HelpScroll = EditorGUILayout.BeginScrollView(m_HelpScroll, GUILayout.ExpandHeight(true)); | |
| GUILayout.TextArea(Styles.helpText); | |
| EditorGUILayout.EndScrollView(); | |
| EditorGUILayout.EndVertical(); | |
| } | |
| static readonly ProfilerMarkerAbstracted m_DrawAnalysisProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawAnalysis"); | |
| static readonly ProfilerMarkerAbstracted m_TopNMarkersProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.TopNMarkers"); | |
| static readonly ProfilerMarkerAbstracted m_DrawMarkerTableProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawMarkerTable"); | |
| void DrawAnalysis() | |
| { | |
| using (m_DrawAnalysisProfilerMarker.Auto()) | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| EditorGUILayout.BeginVertical(); | |
| DrawFilesLoaded(); | |
| if (m_ProfileSingleView.IsDataValid() && m_ProfileSingleView.data.GetFrameCount() > 0) | |
| { | |
| DrawAnalysisOptions(); | |
| if (IsAnalysisValid()) | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box); | |
| string title = string.Format("Top {0} markers on median frame", m_TopNBars); | |
| GUIContent markersTitle = new GUIContent(title, Styles.topMarkersTooltip); | |
| bool lastShowTopMarkers = m_ShowTopNMarkers; | |
| m_ShowTopNMarkers = BoldFoldout(m_ShowTopNMarkers, markersTitle); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowTopNMarkers) | |
| { | |
| using (m_TopNMarkersProfilerMarker.Auto()) | |
| { | |
| m_TopMarkers.SetData(m_ProfileSingleView, m_DepthSliceUI.depthFilter, GetNameFilters(), | |
| GetNameExcludes(), m_TimingOption, m_ThreadSelection.selection.Count, m_hideRemovedMarkers); | |
| EditorGUILayout.BeginVertical(GUILayout.Height(20)); | |
| EditorGUILayout.BeginHorizontal(); | |
| FrameSummary frameSummary = m_ProfileSingleView.analysis.GetFrameSummary(); | |
| if (frameSummary.count > 0) | |
| DrawFrameIndexButton(frameSummary.medianFrameIndex, m_ProfileSingleView); | |
| else | |
| GUILayout.Label("", GUILayout.MinWidth(50)); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.ExpandWidth(true), | |
| GUILayout.ExpandHeight(true)); | |
| float range = m_TopMarkers.GetTopMarkerTimeRange(); | |
| m_TopMarkers.Draw(rect, UIColor.bar, m_TopNBars, range, UIColor.barBackground, | |
| Color.black, Color.white, true, true); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.BeginVertical(GUILayout.Height(20)); | |
| GUILayout.Label(m_DepthSliceUI.GetUIInfo(false)); | |
| EditorGUILayout.EndVertical(); | |
| } | |
| } | |
| if (m_ShowTopNMarkers != lastShowTopMarkers) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.TopTen, | |
| analytic.GetDurationInSeconds(), m_ShowTopNMarkers); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| if (m_ProfileTable != null) | |
| { | |
| m_ShowMarkerTable = BoldFoldout(m_ShowMarkerTable, Styles.profileTable); | |
| if (m_ShowMarkerTable) | |
| { | |
| using (m_DrawMarkerTableProfilerMarker.Auto()) | |
| { | |
| Rect r = EditorGUILayout.GetControlRect(GUILayout.ExpandHeight(true)); | |
| float scrollBarWidth = GUI.skin.verticalScrollbar.fixedWidth + | |
| GUI.skin.verticalScrollbar.border.horizontal + | |
| GUI.skin.verticalScrollbar.margin.horizontal + | |
| GUI.skin.verticalScrollbar.padding.horizontal; | |
| scrollBarWidth += LayoutSize.ScrollBarPadding; | |
| //offset vertically to get correct clipping behaviour | |
| Rect clipRect = new Rect(r.x, m_ProfileTable.state.scrollPos.y, | |
| r.width - scrollBarWidth, | |
| r.height - | |
| (m_ProfileTable.multiColumnHeader.height + GUI.skin.box.padding.top) - | |
| (m_ProfileTable.ShowingHorizontalScroll | |
| ? (scrollBarWidth - LayoutSize.ScrollBarPadding) | |
| : 0)); | |
| m_2D.SetClipRect(clipRect); | |
| m_ProfileTable.OnGUI(r); | |
| m_2D.ClearClipRect(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| else | |
| { | |
| ShowHelp(); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.BeginVertical(GUILayout.Width(LayoutSize.WidthRHS)); | |
| GUILayout.Space(4); | |
| DrawFrameSummary(); | |
| DrawThreadSummary(); | |
| DrawSelected(); | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| } | |
| void SetRange(List<int> selectedOffsets, int clickCount, FrameTimeGraph.State inputStatus, ProfileDataView mainData, List<int> selectedIndices) | |
| { | |
| if (inputStatus == FrameTimeGraph.State.Dragging) | |
| return; | |
| var data = mainData.data; | |
| if (clickCount == 2) | |
| { | |
| if (mainData.inSyncWithProfilerData) | |
| { | |
| int index = data.OffsetToDisplayFrame(selectedOffsets[0]); | |
| JumpToFrame(index, mainData.data, false); | |
| } | |
| } | |
| else | |
| { | |
| selectedIndices.Clear(); | |
| foreach (int offset in selectedOffsets) | |
| { | |
| selectedIndices.Add(data.OffsetToDisplayFrame(offset)); | |
| } | |
| // Keep indices sorted | |
| selectedIndices.Sort(); | |
| m_RequestCompare = true; | |
| } | |
| } | |
| void SetLeftRange(List<int> selectedOffsets, int clickCount, FrameTimeGraph.State inputStatus) | |
| { | |
| SetRange(selectedOffsets, clickCount, inputStatus, m_ProfileLeftView, m_ProfileLeftView.selectedIndices); | |
| } | |
| void SetRightRange(List<int> selectedOffsets, int clickCount, FrameTimeGraph.State inputStatus) | |
| { | |
| SetRange(selectedOffsets, clickCount, inputStatus, m_ProfileRightView, m_ProfileRightView.selectedIndices); | |
| } | |
| void DrawComparisonLoadSaveButton(Color color, ProfileDataView view, FrameTimeGraph frameTimeGraph, ActiveView activeView) | |
| { | |
| bool lastEnabled = GUI.enabled; | |
| bool isAnalysisRunning = IsAnalysisRunning(); | |
| EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(300), GUILayout.ExpandWidth(false)); | |
| GUIStyle buttonStyle = GUI.skin.button; | |
| Color oldColor = GUI.backgroundColor; | |
| GUI.backgroundColor = color; | |
| GUI.enabled = !isAnalysisRunning; | |
| bool load = GUILayout.Button("Load", buttonStyle, GUILayout.ExpandWidth(false), GUILayout.Width(50)); | |
| GUI.enabled = lastEnabled; | |
| GUI.backgroundColor = oldColor; | |
| if (load) | |
| { | |
| m_Path = EditorUtility.OpenFilePanel("Load profile analyzer data file", "", "pdata"); | |
| if (m_Path.Length != 0) | |
| { | |
| m_ActiveLoadingView = activeView; | |
| BeginAsyncAction(ThreadActivity.Load); | |
| } | |
| GUIUtility.ExitGUI(); | |
| } | |
| GUI.backgroundColor = color; | |
| GUI.enabled = !isAnalysisRunning && view.IsDataValid(); | |
| bool save = GUILayout.Button("Save", buttonStyle, GUILayout.ExpandWidth(false), GUILayout.Width(50)); | |
| GUI.enabled = lastEnabled; | |
| GUI.backgroundColor = oldColor; | |
| if (save) | |
| { | |
| Save(view, true); | |
| } | |
| ShowFilename(view.path); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| float GetComparisonYRange() | |
| { | |
| float yRangeLeft = m_ProfileLeftView.IsDataValid() ? m_LeftFrameTimeGraph.GetDataRange() : 0f; | |
| float yRangeRight = m_ProfileRightView.IsDataValid() ? m_RightFrameTimeGraph.GetDataRange() : 0f; | |
| float yRange = Math.Max(yRangeLeft, yRangeRight); | |
| return yRange; | |
| } | |
| void SetFrameTimeGraphPairing(bool paired) | |
| { | |
| if (paired != m_FrameTimeGraphsPaired) | |
| { | |
| m_FrameTimeGraphsPaired = paired; | |
| m_LeftFrameTimeGraph.PairWith(m_FrameTimeGraphsPaired ? m_RightFrameTimeGraph : null); | |
| } | |
| } | |
| void DrawComparisonLoadSave() | |
| { | |
| int leftFrames = m_ProfileLeftView.IsDataValid() ? m_ProfileLeftView.data.GetFrameCount() : 0; | |
| int rightFrames = m_ProfileRightView.IsDataValid() ? m_ProfileRightView.data.GetFrameCount() : 0; | |
| int maxFrames = Math.Max(leftFrames, rightFrames); | |
| float yRange = GetComparisonYRange(); | |
| EditorGUILayout.BeginHorizontal(GUILayout.Height(100 + GUI.skin.label.lineHeight + (2 * (GUI.skin.label.margin.vertical + GUI.skin.label.padding.vertical)))); | |
| float leftFilenameWidth = GetFilenameWidth(m_ProfileLeftView.path); | |
| float rightFilenameWidth = GetFilenameWidth(m_ProfileRightView.path); | |
| float filenameWidth = Math.Max(leftFilenameWidth, rightFilenameWidth); | |
| filenameWidth = Math.Min(filenameWidth, 200); | |
| EditorGUILayout.BeginVertical(GUILayout.MaxWidth(100 + filenameWidth), GUILayout.ExpandWidth(false)); | |
| DrawPullButton(UIColor.left, m_ProfileLeftView, m_LeftFrameTimeGraph); | |
| DrawComparisonLoadSaveButton(UIColor.left, m_ProfileLeftView, m_LeftFrameTimeGraph, ActiveView.Left); | |
| DrawPullButton(UIColor.right, m_ProfileRightView, m_RightFrameTimeGraph); | |
| DrawComparisonLoadSaveButton(UIColor.right, m_ProfileRightView, m_RightFrameTimeGraph, ActiveView.Right); | |
| EditorGUILayout.EndVertical(); | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = !IsAnalysisRunning(); | |
| EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true)); | |
| bool valid = IsSelectedMarkerValid(); | |
| string validMarkerName = valid ? m_SelectedMarker.name : ""; | |
| GUI.SetNextControlName("LeftFrameTimeGraph"); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(50)); | |
| if (m_ProfileLeftView.IsDataValid()) | |
| { | |
| if (!m_LeftFrameTimeGraph.HasData()) | |
| m_LeftFrameTimeGraph.SetData(GetFrameTimeData(m_ProfileLeftView.data)); | |
| if (!m_ProfileLeftView.HasValidSelection()) | |
| m_ProfileLeftView.SelectFullRange(); | |
| List<int> selectedOffsets = new List<int>(); | |
| foreach (int index in m_ProfileLeftView.selectedIndices) | |
| { | |
| selectedOffsets.Add(m_ProfileLeftView.data.DisplayFrameToOffset(index)); | |
| } | |
| int offsetToDisplayMapping = GetRemappedUIFirstFrameDisplayOffset(m_ProfileLeftView); | |
| int offsetToIndexMapping = GetRemappedUIFirstFrameOffset(m_ProfileLeftView); | |
| m_LeftFrameTimeGraph.SetEnabled(enabled); | |
| m_LeftFrameTimeGraph.Draw(rect, m_ProfileLeftView.analysis, selectedOffsets, yRange, offsetToDisplayMapping, offsetToIndexMapping, validMarkerName, maxFrames, m_ProfileLeftView.analysisFull); | |
| } | |
| else | |
| { | |
| GUI.Label(rect, Styles.comparisonDataMissing, m_StyleUpperLeft); | |
| } | |
| GUI.SetNextControlName("RightFrameTimeGraph"); | |
| rect = EditorGUILayout.GetControlRect(GUILayout.Height(50)); | |
| if (m_ProfileRightView.IsDataValid()) | |
| { | |
| if (!m_RightFrameTimeGraph.HasData()) | |
| m_RightFrameTimeGraph.SetData(GetFrameTimeData(m_ProfileRightView.data)); | |
| if (!m_ProfileRightView.HasValidSelection()) | |
| m_ProfileRightView.SelectFullRange(); | |
| List<int> selectedOffsets = new List<int>(); | |
| foreach (int index in m_ProfileRightView.selectedIndices) | |
| { | |
| selectedOffsets.Add(m_ProfileRightView.data.DisplayFrameToOffset(index)); | |
| } | |
| int offsetToDisplayMapping = GetRemappedUIFirstFrameDisplayOffset(m_ProfileRightView); | |
| int offsetToIndexMapping = GetRemappedUIFirstFrameOffset(m_ProfileRightView); | |
| m_RightFrameTimeGraph.SetEnabled(enabled); | |
| m_RightFrameTimeGraph.Draw(rect, m_ProfileRightView.analysis, selectedOffsets, yRange, offsetToDisplayMapping, offsetToIndexMapping, validMarkerName, maxFrames, m_ProfileRightView.analysisFull); | |
| } | |
| else | |
| { | |
| GUI.Label(rect, Styles.comparisonDataMissing, m_StyleUpperLeft); | |
| } | |
| EditorGUILayout.BeginHorizontal(); | |
| if (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid() && m_ProfileLeftView.data.GetFrameCount() > 0 && m_ProfileRightView.data.GetFrameCount() > 0) | |
| { | |
| GUIStyle lockButtonStyle = "IN LockButton"; | |
| GUIStyle style = new GUIStyle(lockButtonStyle); | |
| style.padding.left = 20; | |
| //bool paired = GUILayout.Toggle(m_frameTimeGraphsPaired, Styles.graphPairing, style); | |
| GUI.enabled = enabled; | |
| bool paired = EditorGUILayout.ToggleLeft(Styles.graphPairing, m_FrameTimeGraphsPaired, style, GUILayout.MaxWidth(200)); | |
| GUI.enabled = lastEnabled; | |
| SetFrameTimeGraphPairing(paired); | |
| GUILayout.FlexibleSpace(); | |
| ShowSelectedMarker(); | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| void DrawComparisonHistogram(float height, float minValue, float maxValue, int bucketCount, int[] leftBuckets, int[] rightBuckets, int leftCount, int rightCount, bool leftValid, bool rightValid, DisplayUnits displayUnits) | |
| { | |
| Histogram histogram = new Histogram(m_2D, displayUnits.Units); | |
| float width = LayoutSize.HistogramWidth; | |
| float min = minValue; | |
| float max = maxValue; | |
| float spacing = 2; | |
| float range = max - min; | |
| // bucketCount = (range == 0f) ? 1 : bucketCount; | |
| float x = (spacing / 2); | |
| float y = 0; | |
| float w = ((width + spacing) / bucketCount) - spacing; | |
| float h = height; | |
| histogram.DrawStart(width); | |
| if (m_2D.DrawStart(width, height, Draw2D.Origin.BottomLeft)) | |
| { | |
| float bucketWidth = (range / bucketCount); | |
| Rect rect = GUILayoutUtility.GetLastRect(); | |
| histogram.DrawBackground(width, height, bucketCount, min, max, spacing); | |
| if (!IsAnalysisRunning()) | |
| { | |
| for (int bucketAt = 0; bucketAt < bucketCount; bucketAt++) | |
| { | |
| float leftBarCount = leftValid ? leftBuckets[bucketAt] : 0; | |
| float rightBarCount = rightValid ? rightBuckets[bucketAt] : 0; | |
| float leftBarHeight = leftValid ? ((h * leftBarCount) / leftCount) : 0; | |
| float rightBarHeight = rightValid ? ((h * rightBarCount) / rightCount) : 0; | |
| if (leftBarCount > 0) // Make sure we always slow a small bar if non zero | |
| leftBarHeight = Mathf.Max(1.0f, leftBarHeight); | |
| if (rightBarCount > 0) // Make sure we always slow a small bar if non zero | |
| rightBarHeight = Mathf.Max(1.0f, rightBarHeight); | |
| if ((int)rightBarHeight == (int)leftBarHeight) | |
| { | |
| m_2D.DrawFilledBox(x, y, w, leftBarHeight, UIColor.both); | |
| } | |
| else if (rightBarHeight > leftBarHeight) | |
| { | |
| m_2D.DrawFilledBox(x, y, w, rightBarHeight, UIColor.right); | |
| m_2D.DrawFilledBox(x, y, w, leftBarHeight, UIColor.both); | |
| } | |
| else | |
| { | |
| m_2D.DrawFilledBox(x, y, w, leftBarHeight, UIColor.left); | |
| m_2D.DrawFilledBox(x, y, w, rightBarHeight, UIColor.both); | |
| } | |
| float bucketStart = min + (bucketAt * bucketWidth); | |
| float bucketEnd = bucketStart + bucketWidth; | |
| string tooltip = string.Format( | |
| "{0}-{1}\nLeft: {2} {3}\nRight: {4} {5}\n\nBar width: {6}", | |
| displayUnits.ToTooltipString(bucketStart, false), | |
| displayUnits.ToTooltipString(bucketEnd, true), | |
| leftBarCount, leftBarCount == 1 ? "frame" : "frames", | |
| rightBarCount, rightBarCount == 1 ? "frame" : "frames", | |
| displayUnits.ToTooltipString(bucketWidth, true)); | |
| GUI.Label(new Rect(rect.x + x, rect.y + y, w, h), | |
| new GUIContent("", tooltip) | |
| ); | |
| x += w; | |
| x += spacing; | |
| } | |
| } | |
| m_2D.DrawEnd(); | |
| } | |
| histogram.DrawEnd(width, min, max, spacing); | |
| } | |
| void DrawComparisonFrameSummary() | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS)); | |
| bool lastShowFrameSummary = m_ShowFrameSummary; | |
| m_ShowFrameSummary = BoldFoldout(m_ShowFrameSummary, Styles.frameSummary); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowFrameSummary) | |
| { | |
| EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present | |
| if (IsAnalysisValid()) | |
| { | |
| var leftFrameSummary = m_ProfileLeftView.analysis.GetFrameSummary(); | |
| var rightFrameSummary = m_ProfileRightView.analysis.GetFrameSummary(); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| m_Columns.Draw4("", "Left", "Right", "Diff"); | |
| int diff = rightFrameSummary.count - leftFrameSummary.count; | |
| m_Columns.Draw4(Styles.frameCount, GetFrameCountText(m_ProfileLeftView), GetFrameCountText(m_ProfileRightView), new GUIContent(diff.ToString(), "")); | |
| m_Columns.Draw3(Styles.frameStart, GetFirstFrameText(m_ProfileLeftView), GetFirstFrameText(m_ProfileRightView)); | |
| m_Columns.Draw3(Styles.frameEnd, GetLastFrameText(m_ProfileLeftView), GetLastFrameText(m_ProfileRightView)); | |
| m_Columns.Draw(0, ""); | |
| string units = GetDisplayUnits(); | |
| m_Columns.Draw4("", units, units, units); | |
| Draw4DiffMs(Styles.max, leftFrameSummary.msMax, leftFrameSummary.maxFrameIndex, rightFrameSummary.msMax, rightFrameSummary.maxFrameIndex); | |
| Draw4DiffMs(Styles.upperQuartile, leftFrameSummary.msUpperQuartile, rightFrameSummary.msUpperQuartile); | |
| Draw4DiffMs(Styles.median, leftFrameSummary.msMedian, leftFrameSummary.medianFrameIndex, rightFrameSummary.msMedian, rightFrameSummary.medianFrameIndex); | |
| Draw4DiffMs(Styles.mean, leftFrameSummary.msMean, rightFrameSummary.msMean); | |
| Draw4DiffMs(Styles.lowerQuartile, leftFrameSummary.msLowerQuartile, rightFrameSummary.msLowerQuartile); | |
| Draw4DiffMs(Styles.min, leftFrameSummary.msMin, leftFrameSummary.minFrameIndex, rightFrameSummary.msMin, rightFrameSummary.minFrameIndex); | |
| GUIStyle style = GUI.skin.label; | |
| GUILayout.Space(style.lineHeight); | |
| EditorGUILayout.BeginHorizontal(); | |
| int leftBucketCount = leftFrameSummary.buckets.Length; | |
| int rightBucketCount = rightFrameSummary.buckets.Length; | |
| float msFrameMax = Math.Max(leftFrameSummary.msMax, rightFrameSummary.msMax); | |
| float yRange = msFrameMax; | |
| if (leftBucketCount != rightBucketCount) | |
| { | |
| Debug.Log("Error left frame summary bucket count doesn't equal right summary"); | |
| } | |
| else | |
| { | |
| DrawComparisonHistogram(40, 0, yRange, leftBucketCount, leftFrameSummary.buckets, rightFrameSummary.buckets, leftFrameSummary.count, rightFrameSummary.count, true, true, m_DisplayUnits); | |
| } | |
| BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, m_DisplayUnits.Units); | |
| float plotWidth = 40 + GUI.skin.box.padding.horizontal; | |
| float plotHeight = 40; | |
| plotWidth /= 2.0f; | |
| boxAndWhiskerPlot.Draw(plotWidth, plotHeight, leftFrameSummary.msMin, leftFrameSummary.msLowerQuartile, | |
| leftFrameSummary.msMedian, leftFrameSummary.msUpperQuartile, leftFrameSummary.msMax, 0, yRange, | |
| UIColor.boxAndWhiskerLineColorLeft, UIColor.boxAndWhiskerBoxColorLeft); | |
| boxAndWhiskerPlot.Draw(plotWidth, plotHeight, rightFrameSummary.msMin, rightFrameSummary.msLowerQuartile, | |
| rightFrameSummary.msMedian, rightFrameSummary.msUpperQuartile, rightFrameSummary.msMax, 0, yRange, | |
| UIColor.boxAndWhiskerLineColorRight, UIColor.boxAndWhiskerBoxColorRight); | |
| boxAndWhiskerPlot.DrawText(m_Columns.GetColumnWidth(3), plotHeight, 0, yRange, | |
| "Min frame time for selected frames in the 2 data sets", | |
| "Max frame time for selected frames in the 2 data sets"); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField("No analysis data selected"); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| if (m_ShowFrameSummary != lastShowFrameSummary) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Frames, analytic.GetDurationInSeconds(), m_ShowFrameSummary); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| void ShowThreadRange() | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, Styles.threadGraphScale); | |
| m_ThreadRange = (ThreadRange)EditorGUILayout.Popup((int)m_ThreadRange, Styles.threadRanges, GUILayout.Width(160)); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| float GetThreadTimeRange(ProfileAnalysis profileAnalysis) | |
| { | |
| if (profileAnalysis == null) | |
| return 0.0f; | |
| var frameSummary = profileAnalysis.GetFrameSummary(); | |
| float range = frameSummary.msMax; | |
| switch (m_ThreadRange) | |
| { | |
| case ThreadRange.Median: | |
| range = frameSummary.msMedian; | |
| break; | |
| case ThreadRange.UpperQuartile: | |
| range = frameSummary.msUpperQuartile; | |
| break; | |
| case ThreadRange.Max: | |
| range = frameSummary.msMax; | |
| break; | |
| } | |
| return range; | |
| } | |
| int GetThreadSelectionCount(out int leftSelectionCount, out int rightSelectionCount) | |
| { | |
| List<string> threadSelection = GetLimitedThreadSelection(m_ThreadNames, m_ThreadSelection); | |
| leftSelectionCount = 0; | |
| foreach (var threadName in m_ProfileLeftView.data.GetThreadNames()) | |
| { | |
| if (threadSelection.Contains(threadName)) | |
| { | |
| leftSelectionCount++; | |
| } | |
| } | |
| rightSelectionCount = 0; | |
| foreach (var threadName in m_ProfileRightView.data.GetThreadNames()) | |
| { | |
| if (threadSelection.Contains(threadName)) | |
| { | |
| rightSelectionCount++; | |
| } | |
| } | |
| return threadSelection.Count; | |
| } | |
| void DrawComparisonThreadSummary() | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS)); | |
| bool lastShowThreadSummary = m_ShowThreadSummary; | |
| m_ShowThreadSummary = BoldFoldout(m_ShowThreadSummary, Styles.threadSummary); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowThreadSummary) | |
| { | |
| EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present | |
| if (IsAnalysisValid()) | |
| { | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw4("", "Left", "Right", "Total"); | |
| EditorGUILayout.EndHorizontal(); | |
| int matchingCount, uniqueLeft, uniqueRight; | |
| int allThreadsCount = GetCombinedThreadCount(out matchingCount, out uniqueLeft, out uniqueRight); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, "Total Count : "); | |
| m_Columns.Draw(1, new GUIContent(m_ProfileLeftView.data.GetThreadCount().ToString(), "Total threads in left data set")); | |
| m_Columns.Draw(2, new GUIContent(m_ProfileRightView.data.GetThreadCount().ToString(), "Total threads in right data set")); | |
| string tooltip = GetThreadCountToolTipUnion(allThreadsCount, matchingCount); | |
| m_Columns.Draw(3, new GUIContent(allThreadsCount.ToString(), tooltip)); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, "Unique Count : "); | |
| m_Columns.Draw(1, new GUIContent(uniqueLeft.ToString(), "Unique to left data set")); | |
| m_Columns.Draw(2, new GUIContent(uniqueRight.ToString(), "Unique to right data set")); | |
| tooltip = GetThreadCountToolTipDifference(allThreadsCount, matchingCount, uniqueLeft, uniqueRight); | |
| m_Columns.Draw(3, new GUIContent((allThreadsCount - matchingCount).ToString(), tooltip)); | |
| EditorGUILayout.EndHorizontal(); | |
| int leftSelectionCount, rightSelectionCount; | |
| int selectedThreads = GetThreadSelectionCount(out leftSelectionCount, out rightSelectionCount); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, "Selected : "); | |
| m_Columns.Draw(1, new GUIContent(leftSelectionCount.ToString(), "Left selected")); | |
| m_Columns.Draw(2, new GUIContent(rightSelectionCount.ToString(), "Right selected")); | |
| m_Columns.Draw(3, new GUIContent(selectedThreads.ToString(), "Total selected")); | |
| EditorGUILayout.EndHorizontal(); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2 + LayoutSize.WidthColumn3, 0); | |
| ShowThreadRange(); | |
| float width = 100; | |
| float height = GUI.skin.label.lineHeight; | |
| float xAxisMin = 0.0f; | |
| float xAxisMax = GetThreadTimeRange(m_ProfileLeftView.analysis); | |
| m_Columns.Draw3(Styles.emptyString, Styles.median, Styles.thread); | |
| m_ThreadScroll = EditorGUILayout.BeginScrollView(m_ThreadScroll, GUIStyle.none, GUI.skin.verticalScrollbar); | |
| Rect clipRect = new Rect(m_ThreadScroll.x, m_ThreadScroll.y, m_ComparisonThreadsAreaRect.width, m_ComparisonThreadsAreaRect.height); | |
| m_2D.SetClipRect(clipRect); | |
| for (int i = 0; i < m_ThreadUINames.Count; i++) | |
| { | |
| string threadNameWithIndex = m_ThreadNames[i]; | |
| bool include = ProfileAnalyzer.MatchThreadFilter(threadNameWithIndex, m_ThreadSelection.selection); | |
| if (!include) | |
| continue; | |
| ThreadData threadLeft = m_ProfileLeftView.analysis.GetThreadByName(threadNameWithIndex); | |
| ThreadData threadRight = m_ProfileRightView.analysis.GetThreadByName(threadNameWithIndex); | |
| ThreadData thread = threadLeft != null ? threadLeft : threadRight; | |
| if (thread == null) | |
| continue; | |
| bool singleThread = thread.threadsInGroup > 1 ? false : true; | |
| BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, m_DisplayUnits.Units); | |
| EditorGUILayout.BeginHorizontal(); | |
| if (threadLeft != null) | |
| boxAndWhiskerPlot.DrawHorizontal(width, height, threadLeft.msMin, threadLeft.msLowerQuartile, threadLeft.msMedian, threadLeft.msUpperQuartile, threadLeft.msMax, xAxisMin, xAxisMax, UIColor.boxAndWhiskerLineColorLeft, UIColor.boxAndWhiskerBoxColorLeft, GUI.skin.label); | |
| else | |
| EditorGUILayout.LabelField(Styles.noThread, GUILayout.Width(width)); | |
| m_Columns.Draw(1, (threadLeft != null) ? ToDisplayUnitsWithTooltips(threadLeft.msMedian) : Styles.noThread); | |
| m_Columns.Draw(2, GetThreadNameWithGroupTooltip(thread.threadNameWithIndex, singleThread)); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| if (threadRight != null) | |
| boxAndWhiskerPlot.DrawHorizontal(width, height, threadRight.msMin, threadRight.msLowerQuartile, threadRight.msMedian, threadRight.msUpperQuartile, threadRight.msMax, xAxisMin, xAxisMax, UIColor.boxAndWhiskerLineColorRight, UIColor.boxAndWhiskerBoxColorRight, GUI.skin.label); | |
| else | |
| EditorGUILayout.LabelField(Styles.noThread, GUILayout.Width(width)); | |
| m_Columns.Draw(1, (threadRight != null) ? ToDisplayUnitsWithTooltips(threadRight.msMedian) : Styles.noThread); | |
| m_Columns.Draw(2, ""); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| m_2D.ClearClipRect(); | |
| EditorGUILayout.EndScrollView(); | |
| if (Event.current.type == EventType.Repaint) | |
| { | |
| // This value is not valid at layout phase | |
| m_ComparisonThreadsAreaRect = GUILayoutUtility.GetLastRect(); | |
| } | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField("No analysis data selected"); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| if (m_ShowThreadSummary != lastShowThreadSummary) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Threads, analytic.GetDurationInSeconds(), m_ShowThreadSummary); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| static readonly ProfilerMarkerAbstracted m_DrawCompareOptionsProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawCompareOptions"); | |
| void DrawCompareOptions() | |
| { | |
| using (m_DrawCompareOptionsProfilerMarker.Auto()) | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box); | |
| bool lastShowFilters = m_ShowFilters; | |
| m_ShowFilters = BoldFoldout(m_ShowFilters, Styles.filters); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowFilters) | |
| { | |
| DrawRemoveMarker(); | |
| DrawNameFilter(); | |
| EditorGUILayout.BeginHorizontal(); | |
| DrawThreadFilter(m_ProfileLeftView.data); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_DepthSliceUI.DrawDepthFilter(IsAnalysisRunning(), m_ActiveTab == ActiveTab.Summary, | |
| m_ProfileSingleView, m_ProfileLeftView, m_ProfileRightView); | |
| DrawTimingFilter(); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| DrawParentFilter(); | |
| DrawUnitFilter(); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| if (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid()) | |
| { | |
| bool lastEnabled = GUI.enabled; | |
| GUI.enabled = !IsAnalysisRunning(); | |
| if (GUILayout.Button(new GUIContent("Compare", m_LastCompareTime), GUILayout.Width(100))) | |
| m_RequestCompare = true; | |
| GUI.enabled = lastEnabled; | |
| } | |
| DrawMarkerCount(); | |
| EditorGUILayout.LabelField(",", GUILayout.Width(10), GUILayout.ExpandWidth(false)); | |
| DrawThreadCount(); | |
| GUILayout.FlexibleSpace(); | |
| DrawMarkerColumnFilter(); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| if (m_ShowFilters != lastShowFilters) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Filters, | |
| analytic.GetDurationInSeconds(), m_ShowFilters); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| } | |
| static readonly ProfilerMarkerAbstracted m_DrawComparisonProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawComparison"); | |
| static readonly ProfilerMarkerAbstracted m_DrawComparisonTableProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawComparisonTable"); | |
| void DrawComparison() | |
| { | |
| using (m_DrawComparisonProfilerMarker.Auto()) | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| EditorGUILayout.BeginVertical(); | |
| DrawFilesLoaded(); | |
| if (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid() && | |
| m_ProfileLeftView.data.GetFrameCount() > 0 && m_ProfileRightView.data.GetFrameCount() > 0) | |
| { | |
| DrawCompareOptions(); | |
| if (m_ComparisonTable != null) | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box); | |
| string title = string.Format("Top {0} markers on median frames", m_TopNBars); | |
| GUIContent markersTitle = new GUIContent(title, Styles.topMarkersTooltip); | |
| bool lastShowTopMarkers = m_ShowTopNMarkers; | |
| m_ShowTopNMarkers = BoldFoldout(m_ShowTopNMarkers, markersTitle); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowTopNMarkers) | |
| { | |
| using (m_TopNMarkersProfilerMarker.Auto()) | |
| { | |
| EditorGUILayout.BeginVertical(GUILayout.Height(40)); | |
| Rect rect = EditorGUILayout.GetControlRect(GUILayout.ExpandWidth(true), | |
| GUILayout.ExpandHeight(true)); | |
| rect.height = rect.height / 2; | |
| var nameFilters = GetNameFilters(); | |
| var nameExcludes = GetNameExcludes(); | |
| m_TopMarkersLeft.SetData(m_ProfileLeftView, m_DepthSliceUI.depthFilter1, nameFilters, | |
| nameExcludes, m_TimingOption, m_ThreadSelection.selection.Count, m_hideRemovedMarkers); | |
| m_TopMarkersRight.SetData(m_ProfileRightView, m_DepthSliceUI.depthFilter2, nameFilters, | |
| nameExcludes, m_TimingOption, m_ThreadSelection.selection.Count, m_hideRemovedMarkers); | |
| float leftRange = m_TopMarkersLeft.GetTopMarkerTimeRange(); | |
| float rightRange = m_TopMarkersRight.GetTopMarkerTimeRange(); | |
| if (m_TopTenDisplay == TopTenDisplay.LongestTime) | |
| { | |
| float max = Math.Max(leftRange, rightRange); | |
| leftRange = max; | |
| rightRange = max; | |
| } | |
| int leftMedian = 0; | |
| int rightMedian = 0; | |
| if (m_ProfileLeftView.analysis != null) | |
| { | |
| FrameSummary frameSummary = m_ProfileLeftView.analysis.GetFrameSummary(); | |
| if (frameSummary.count > 0) | |
| leftMedian = frameSummary.medianFrameIndex; | |
| } | |
| if (m_ProfileRightView.analysis != null) | |
| { | |
| FrameSummary frameSummary = m_ProfileRightView.analysis.GetFrameSummary(); | |
| if (frameSummary.count > 0) | |
| rightMedian = frameSummary.medianFrameIndex; | |
| } | |
| int maxMedian = Math.Max(leftMedian, rightMedian); | |
| Rect frameIndexRect = new Rect(rect); | |
| Vector2 size = | |
| GUI.skin.button.CalcSize(new GUIContent(string.Format("{0}", maxMedian))); | |
| frameIndexRect.width = | |
| Math.Max(size.x, 50); // DrawFrameIndexButton should always be at least 50 wide | |
| if (leftMedian != 0f) | |
| DrawFrameIndexButton(frameIndexRect, leftMedian, m_ProfileLeftView); | |
| else | |
| GUI.Label(frameIndexRect, ""); | |
| float padding = 2; | |
| rect.x += frameIndexRect.width + padding; | |
| rect.width -= frameIndexRect.width; | |
| m_TopMarkersLeft.Draw(rect, UIColor.left, m_TopNBars, leftRange, UIColor.barBackground, | |
| Color.black, Color.white, true, true); | |
| rect.y += rect.height; | |
| frameIndexRect.y += rect.height; | |
| if (rightMedian != 0f) | |
| DrawFrameIndexButton(frameIndexRect, rightMedian, m_ProfileRightView); | |
| else | |
| GUI.Label(frameIndexRect, ""); | |
| m_TopMarkersRight.Draw(rect, UIColor.right, m_TopNBars, rightRange, | |
| UIColor.barBackground, | |
| Color.black, Color.white, true, true); | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.BeginHorizontal(); | |
| GUILayout.Label(m_DepthSliceUI.GetUIInfo(true), GUILayout.ExpandWidth(true)); | |
| GUILayout.Label(Styles.topMarkerRatio, GUILayout.ExpandWidth(false)); | |
| m_TopTenDisplay = (TopTenDisplay)EditorGUILayout.Popup((int)m_TopTenDisplay, Styles.topTenDisplayOptions, GUILayout.MaxWidth(100)); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| } | |
| if (m_ShowTopNMarkers != lastShowTopMarkers) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent( | |
| ProfileAnalyzerAnalytics.UIVisibility.Markers, analytic.GetDurationInSeconds(), | |
| m_ShowTopNMarkers); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| if (m_ComparisonTable != null) | |
| { | |
| m_ShowMarkerTable = BoldFoldout(m_ShowMarkerTable, Styles.comparisonTable); | |
| if (m_ShowMarkerTable) | |
| { | |
| using (m_DrawComparisonTableProfilerMarker.Auto()) | |
| { | |
| Rect r = EditorGUILayout.GetControlRect(GUILayout.ExpandHeight(true)); | |
| float scrollBarWidth = GUI.skin.verticalScrollbar.fixedWidth + | |
| GUI.skin.verticalScrollbar.border.horizontal + | |
| GUI.skin.verticalScrollbar.margin.horizontal + | |
| GUI.skin.verticalScrollbar.padding.horizontal; | |
| scrollBarWidth += LayoutSize.ScrollBarPadding; | |
| //offset vertically to get correct clipping behaviour | |
| Rect clipRect = new Rect(r.x, m_ComparisonTable.state.scrollPos.y, | |
| r.width - scrollBarWidth, | |
| r.height - (m_ComparisonTable.multiColumnHeader.height + GUI.skin.box.padding.top) - | |
| (m_ComparisonTable.ShowingHorizontalScroll | |
| ? (scrollBarWidth - LayoutSize.ScrollBarPadding) | |
| : 0)); | |
| m_2D.SetClipRect(clipRect); | |
| m_ComparisonTable.OnGUI(r); | |
| m_2D.ClearClipRect(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| else | |
| { | |
| ShowHelp(); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.BeginVertical(GUILayout.Width(LayoutSize.WidthRHS)); | |
| GUILayout.Space(4); | |
| DrawComparisonFrameSummary(); | |
| DrawComparisonThreadSummary(); | |
| DrawComparisonSelected(); | |
| EditorGUILayout.EndVertical(); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| } | |
| bool BoldFoldout(bool toggle, GUIContent content) | |
| { | |
| GUIStyle foldoutStyle = new GUIStyle(EditorStyles.foldout); | |
| foldoutStyle.fontStyle = FontStyle.Bold; | |
| return EditorGUILayout.Foldout(toggle, content, true, foldoutStyle); | |
| } | |
| void DrawComparisonSelectedStats(MarkerData leftMarker, MarkerData rightMarker) | |
| { | |
| GUIStyle style = GUI.skin.label; | |
| string units = GetDisplayUnits(); | |
| m_Columns.Draw4("", units, units, units); | |
| Draw4DiffMs(Styles.max, MarkerData.GetMsMax(leftMarker), MarkerData.GetMaxFrameIndex(leftMarker), MarkerData.GetMsMax(rightMarker), MarkerData.GetMaxFrameIndex(rightMarker)); | |
| Draw4DiffMs(Styles.upperQuartile, MarkerData.GetMsUpperQuartile(leftMarker), MarkerData.GetMsUpperQuartile(rightMarker)); | |
| Draw4DiffMs(Styles.median, MarkerData.GetMsMedian(leftMarker), MarkerData.GetMedianFrameIndex(leftMarker), MarkerData.GetMsMedian(rightMarker), MarkerData.GetMedianFrameIndex(rightMarker)); | |
| Draw4DiffMs(Styles.mean, MarkerData.GetMsMean(leftMarker), MarkerData.GetMsMean(rightMarker)); | |
| Draw4DiffMs(Styles.lowerQuartile, MarkerData.GetMsLowerQuartile(leftMarker), MarkerData.GetMsLowerQuartile(rightMarker)); | |
| Draw4DiffMs(Styles.min, MarkerData.GetMsMin(leftMarker), MarkerData.GetMinFrameIndex(leftMarker), MarkerData.GetMsMin(rightMarker), MarkerData.GetMinFrameIndex(rightMarker)); | |
| GUILayout.Space(style.lineHeight); | |
| Draw4DiffMs(Styles.individualMax, MarkerData.GetMsMaxIndividual(leftMarker), MarkerData.GetMsMaxIndividual(rightMarker)); | |
| Draw4DiffMs(Styles.individualMin, MarkerData.GetMsMinIndividual(leftMarker), MarkerData.GetMsMinIndividual(rightMarker)); | |
| } | |
| void DrawComparisonSelected() | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS)); | |
| GUIStyle style = GUI.skin.label; | |
| bool lastMarkerSummary = m_ShowMarkerSummary; | |
| m_ShowMarkerSummary = BoldFoldout(m_ShowMarkerSummary, Styles.markerSummary); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowMarkerSummary) | |
| { | |
| EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present | |
| if (IsAnalysisValid()) | |
| { | |
| List<MarkerData> leftMarkers = m_ProfileLeftView.analysis.GetMarkers(); | |
| List<MarkerData> rightMarkers = m_ProfileRightView.analysis.GetMarkers(); | |
| int pairingAt = m_SelectedPairing; | |
| if (leftMarkers != null && rightMarkers != null && m_Pairings != null) | |
| { | |
| if (pairingAt >= 0 && pairingAt < m_Pairings.Count) | |
| { | |
| m_MarkerSummaryScroll = GUILayout.BeginScrollView(m_MarkerSummaryScroll, GUIStyle.none, GUI.skin.verticalScrollbar); | |
| Rect clipRect = new Rect(m_MarkerSummaryScroll.x, m_MarkerSummaryScroll.y, LayoutSize.WidthRHS, 500); | |
| m_2D.SetClipRect(clipRect); | |
| EditorGUILayout.BeginVertical(); | |
| var pairing = m_Pairings[pairingAt]; | |
| var leftMarker = (pairing.leftIndex >= 0 && pairing.leftIndex < leftMarkers.Count) ? leftMarkers[pairing.leftIndex] : null; | |
| var rightMarker = (pairing.rightIndex >= 0 && pairing.rightIndex < rightMarkers.Count) ? rightMarkers[pairing.rightIndex] : null; | |
| EditorGUILayout.LabelField(pairing.name, | |
| GUILayout.MaxWidth(LayoutSize.WidthRHS - | |
| (GUI.skin.box.padding.horizontal + GUI.skin.box.margin.horizontal))); | |
| DrawComparisonFrameRatio(leftMarker, rightMarker); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, Styles.firstFrame); | |
| if (leftMarker != null) | |
| DrawFrameIndexButton(leftMarker.firstFrameIndex, m_ProfileLeftView); | |
| else | |
| m_Columns.Draw(1, Styles.emptyString); | |
| if (rightMarker != null) | |
| DrawFrameIndexButton(rightMarker.firstFrameIndex, m_ProfileRightView); | |
| else | |
| m_Columns.Draw(2, Styles.emptyString); | |
| EditorGUILayout.EndHorizontal(); | |
| DrawTopComparison(leftMarker, rightMarker); | |
| GUILayout.Space(style.lineHeight); | |
| EditorGUILayout.BeginHorizontal(); | |
| int leftBucketCount = leftMarker != null ? leftMarker.buckets.Length : 0; | |
| int rightBucketCount = rightMarker != null ? rightMarker.buckets.Length : 0; | |
| float leftMin = MarkerData.GetMsMin(leftMarker); | |
| float rightMin = MarkerData.GetMsMin(rightMarker); | |
| float leftMax = MarkerData.GetMsMax(leftMarker); | |
| float rightMax = MarkerData.GetMsMax(rightMarker); | |
| int[] leftBuckets = leftMarker != null ? leftMarker.buckets : new int[0]; | |
| int[] rightBuckets = rightMarker != null ? rightMarker.buckets : new int[0]; | |
| Units units = m_DisplayUnits.Units; | |
| string unitName = "marker time"; | |
| if (DisplayCount()) | |
| { | |
| units = Units.Count; | |
| unitName = "count"; | |
| leftBucketCount = leftMarker != null ? leftMarker.countBuckets.Length : 0; | |
| rightBucketCount = rightMarker != null ? rightMarker.countBuckets.Length : 0; | |
| leftMin = MarkerData.GetCountMin(leftMarker); | |
| rightMin = MarkerData.GetCountMin(rightMarker); | |
| leftMax = MarkerData.GetCountMax(leftMarker); | |
| rightMax = MarkerData.GetCountMax(rightMarker); | |
| leftBuckets = leftMarker != null ? leftMarker.countBuckets : new int[0]; | |
| rightBuckets = rightMarker != null ? rightMarker.countBuckets : new int[0]; | |
| } | |
| DisplayUnits displayUnits = new DisplayUnits(units); | |
| float minValue; | |
| float maxValue; | |
| if (leftMarker != null && rightMarker != null) | |
| { | |
| minValue = Math.Min(leftMin, rightMin); | |
| maxValue = Math.Max(leftMax, rightMax); | |
| } | |
| else if (leftMarker != null) | |
| { | |
| minValue = leftMin; | |
| maxValue = leftMax; | |
| } | |
| else // Either valid or 0 | |
| { | |
| minValue = rightMin; | |
| maxValue = rightMax; | |
| } | |
| if (leftBucketCount > 0 && rightBucketCount > 0 && leftBucketCount != rightBucketCount) | |
| { | |
| Debug.Log("Error - number of buckets doesn't match in the left and right marker analysis"); | |
| } | |
| else | |
| { | |
| int bucketCount = Math.Max(leftBucketCount, rightBucketCount); | |
| int leftFrameCount = MarkerData.GetPresentOnFrameCount(leftMarker); | |
| int rightFrameCount = MarkerData.GetPresentOnFrameCount(rightMarker); | |
| DrawComparisonHistogram(100, minValue, maxValue, bucketCount, leftBuckets, rightBuckets, leftFrameCount, rightFrameCount, leftMarker != null, rightMarker != null, displayUnits); | |
| } | |
| float plotWidth = 40 + GUI.skin.box.padding.horizontal; | |
| float plotHeight = 100; | |
| plotWidth /= 2.0f; | |
| BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, units); | |
| DrawBoxAndWhiskerPlotForMarker(boxAndWhiskerPlot, plotWidth, plotHeight, m_ProfileLeftView.analysis, leftMarker, minValue, maxValue, | |
| UIColor.boxAndWhiskerLineColorLeft, UIColor.boxAndWhiskerBoxColorLeft); | |
| DrawBoxAndWhiskerPlotForMarker(boxAndWhiskerPlot, plotWidth, plotHeight, m_ProfileRightView.analysis, rightMarker, minValue, maxValue, | |
| UIColor.boxAndWhiskerLineColorRight, UIColor.boxAndWhiskerBoxColorRight); | |
| boxAndWhiskerPlot.DrawText(m_Columns.GetColumnWidth(3), plotHeight, minValue, maxValue, | |
| string.Format("Min {0} for selected frames in the 2 data sets", unitName), | |
| string.Format("Max {0} for selected frames in the 2 data sets", unitName)); | |
| EditorGUILayout.EndHorizontal(); | |
| GUILayout.Space(style.lineHeight); | |
| DrawComparisonSelectedStats(leftMarker, rightMarker); | |
| EditorGUILayout.EndVertical(); | |
| m_2D.ClearClipRect(); | |
| GUILayout.EndScrollView(); | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField("Marker not in selection"); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField("No marker data selected"); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| if (m_ShowMarkerSummary != lastMarkerSummary) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Markers, analytic.GetDurationInSeconds(), m_ShowMarkerSummary); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| void SelectTab(ActiveTab newTab) | |
| { | |
| m_NextActiveTab = newTab; | |
| } | |
| static readonly ProfilerMarkerAbstracted m_DrawToolbarProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.DrawToolbar"); | |
| void DrawToolbar() | |
| { | |
| using (m_DrawToolbarProfilerMarker.Auto()) | |
| { | |
| EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); | |
| EditorGUILayout.LabelField("Mode:", GUILayout.Width(40)); | |
| ActiveTab newTab = (ActiveTab)GUILayout.Toolbar((int)m_ActiveTab, new string[] {"Single", "Compare"}, | |
| EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)); | |
| if (newTab != m_ActiveTab) | |
| { | |
| SelectTab(newTab); | |
| } | |
| //GUILayout.FlexibleSpace(); | |
| EditorGUILayout.Separator(); | |
| bool lastEnabled = GUI.enabled; | |
| bool enabled = GUI.enabled; | |
| if (m_ProfileSingleView.IsDataValid() || | |
| (m_ProfileLeftView.IsDataValid() && m_ProfileRightView.IsDataValid())) | |
| GUI.enabled = true; | |
| else | |
| GUI.enabled = false; | |
| if (GUILayout.Button(Styles.export, EditorStyles.toolbarButton, GUILayout.Width(50))) | |
| { | |
| Vector2 windowPosition = new Vector2(Event.current.mousePosition.x, | |
| Event.current.mousePosition.y + GUI.skin.label.lineHeight); | |
| Vector2 screenPosition = GUIUtility.GUIToScreenPoint(windowPosition); | |
| ProfileAnalyzerExportWindow.Open(screenPosition.x, screenPosition.y, m_ProfileSingleView, | |
| m_ProfileLeftView, m_ProfileRightView, this); | |
| EditorGUIUtility.ExitGUI(); | |
| } | |
| GUI.enabled = lastEnabled; | |
| bool profilerOpen = IsProfilerWindowOpen(); | |
| if (!profilerOpen) | |
| { | |
| if (GUILayout.Toggle(profilerOpen, "Open Profiler Window", EditorStyles.toolbarButton, | |
| GUILayout.ExpandWidth(false)) == true) | |
| { | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| m_ProfilerWindowInterface.OpenProfilerOrUseExisting(); | |
| ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.OpenProfiler, | |
| analytic); | |
| EditorGUIUtility.ExitGUI(); | |
| } | |
| } | |
| else | |
| { | |
| if (GUILayout.Toggle(profilerOpen, "Close Profiler Window", EditorStyles.toolbarButton, | |
| GUILayout.ExpandWidth(false)) == false) | |
| { | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| m_ProfilerWindowInterface.CloseProfiler(); | |
| ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.CloseProfiler, | |
| analytic); | |
| EditorGUIUtility.ExitGUI(); | |
| } | |
| } | |
| EditorGUILayout.Separator(); | |
| GUILayout.FlexibleSpace(); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| } | |
| void SetupStyles() | |
| { | |
| if (!m_StylesSetup) | |
| { | |
| m_StyleMiddleRight = new GUIStyle(GUI.skin.label); | |
| m_StyleMiddleRight.alignment = TextAnchor.MiddleRight; | |
| m_StyleUpperLeft = new GUIStyle(GUI.skin.label); | |
| m_StyleUpperLeft.alignment = TextAnchor.UpperLeft; | |
| m_StylesSetup = true; | |
| } | |
| } | |
| static readonly ProfilerMarkerAbstracted m_DrawProfilerMarker = new ProfilerMarkerAbstracted("ProfileAnalyzer.Draw"); | |
| void Draw() | |
| { | |
| // Make sure we start enabled (in case something overrode it last frame) | |
| GUI.enabled = true; | |
| using (m_DrawProfilerMarker.Auto()) | |
| { | |
| SetupStyles(); | |
| EditorGUILayout.BeginVertical(); | |
| DrawToolbar(); | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| DrawAnalysis(); | |
| break; | |
| case ActiveTab.Compare: | |
| DrawComparison(); | |
| break; | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| } | |
| int FindSelectionByName(List<MarkerData> markers, string name) | |
| { | |
| int index = 0; | |
| foreach (var marker in markers) | |
| { | |
| if (marker.name == name) | |
| return index; | |
| index++; | |
| } | |
| return -1; // not found | |
| } | |
| /// <summary> | |
| /// Select marker to focus on | |
| /// </summary> | |
| /// <param name="name">Name of the marker</param> | |
| // Version 1.0 of the package exposed this API so we can't remove it until we increment the major package version. | |
| public void SelectMarker(string name) | |
| { | |
| SelectMarker(name, null, null); | |
| } | |
| void SelectMarker(string name, string threadGroupName = null, string threadName = null) | |
| { | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| SelectMarkerByName(name, threadGroupName, threadName); | |
| break; | |
| case ActiveTab.Compare: | |
| SelectPairingByName(name, threadGroupName, threadName); | |
| break; | |
| } | |
| } | |
| void UpdateSelectedMarkerName(string markerName) | |
| { | |
| m_SelectedMarker.name = markerName; | |
| // only update the Profiler Window if it wasn't updated successfully with this marker yet. | |
| if (m_LastMarkerSuccesfullySyncedWithProfilerWindow == markerName) | |
| return; | |
| var updatedSelectedSampleSuccesfully = false; | |
| if (m_ProfilerWindowInterface.IsReady() && !m_SelectionEventFromProfilerWindowInProgress && m_ThreadSelection.selection != null && m_ThreadSelection.selection.Count > 0) | |
| { | |
| updatedSelectedSampleSuccesfully = m_ProfilerWindowInterface.SetProfilerWindowMarkerName(markerName, m_ThreadSelection.selection); | |
| } | |
| if (updatedSelectedSampleSuccesfully) | |
| m_LastMarkerSuccesfullySyncedWithProfilerWindow = markerName; | |
| } | |
| internal void SelectMarkerByIndex(int index, string markerNameFallback = null, string threadGroupName = null, string threadName = null) | |
| { | |
| if (m_ProfileSingleView == null || m_ProfileSingleView.analysis == null) | |
| return; | |
| // Check if this marker is in the 'filtered' list | |
| var markers = m_ProfileSingleView.analysis.GetMarkers(); | |
| if (markers.Count <= 0) | |
| return; | |
| bool valid = true; | |
| if (index >= 0 && index < markers.Count) | |
| { | |
| var marker = markers[index]; | |
| valid = DoesMarkerPassFilter(marker.name); | |
| } | |
| m_SelectedMarker.id = index; | |
| if (m_ProfileTable != null) | |
| { | |
| List<int> selection = new List<int>(); | |
| if (index >= 0 && valid) | |
| selection.Add(index); | |
| m_ProfileTable.SetSelection(selection, TreeViewSelectionOptions.RevealAndFrame); | |
| } | |
| var markerName = GetMarkerName(index); | |
| if (index == -1 && !string.IsNullOrEmpty(markerNameFallback)) | |
| { | |
| markerName = markerNameFallback; | |
| if (!string.IsNullOrEmpty(threadName)) | |
| { | |
| m_SelectedMarker.threadGroupName = threadGroupName; | |
| m_SelectedMarker.threadName = threadName; | |
| } | |
| else | |
| { | |
| m_SelectedMarker.threadGroupName = null; | |
| m_SelectedMarker.threadName = null; | |
| } | |
| } | |
| else | |
| { | |
| m_SelectedMarker.threadGroupName = null; | |
| m_SelectedMarker.threadName = null; | |
| } | |
| if (markerName != null) | |
| UpdateSelectedMarkerName(markerName); | |
| } | |
| /// <summary> | |
| /// Get currently selected marker | |
| /// </summary> | |
| /// <returns>Name of currently selected marker, or null if none selected</returns> | |
| public string GetSelectedMarkerName() | |
| { | |
| switch (m_ActiveTab) | |
| { | |
| case ActiveTab.Summary: | |
| return GetMarkerName(m_SelectedMarker.id); | |
| case ActiveTab.Compare: | |
| return GetPairingName(m_SelectedPairing); | |
| } | |
| return null; | |
| } | |
| string GetMarkerName(int index) | |
| { | |
| if (m_ProfileSingleView.analysis == null) | |
| return null; | |
| var marker = m_ProfileSingleView.analysis.GetMarker(index); | |
| if (marker == null) | |
| return null; | |
| return marker.name; | |
| } | |
| void SelectMarkerByName(string markerName, string threadGroupName = null, string threadName = null) | |
| { | |
| int index = (m_ProfileSingleView.analysis != null) ? m_ProfileSingleView.analysis.GetMarkerIndexByName(markerName) : -1; | |
| SelectMarkerByIndex(index, markerName, threadGroupName, threadName); | |
| } | |
| internal void SelectPairing(int index, string threadGroupName = null, string threadName = null) | |
| { | |
| if (m_Pairings == null || m_Pairings.Count == 0) | |
| return; | |
| // Check if this marker is in the 'filtered' list | |
| bool valid = true; | |
| if (index >= 0 && index < m_Pairings.Count) | |
| { | |
| var pairing = m_Pairings[index]; | |
| valid = DoesMarkerPassFilter(pairing.name); | |
| } | |
| m_SelectedPairing = index; | |
| if (m_ComparisonTable != null) | |
| { | |
| List<int> selection = new List<int>(); | |
| if (index >= 0 && valid) | |
| selection.Add(index); | |
| m_ComparisonTable.SetSelection(selection, TreeViewSelectionOptions.RevealAndFrame); | |
| } | |
| var markerName = GetPairingName(index); | |
| if (markerName != null) | |
| UpdateSelectedMarkerName(markerName); | |
| } | |
| string GetPairingName(int index) | |
| { | |
| if (m_Pairings == null) | |
| return null; | |
| if (index < 0 || index >= m_Pairings.Count) | |
| return null; | |
| return m_Pairings[index].name; | |
| } | |
| void SelectPairingByName(string pairingName, string threadGroupName = null, string threadName = null) | |
| { | |
| if (m_Pairings != null && pairingName != null) | |
| { | |
| for (int index = 0; index < m_Pairings.Count; index++) | |
| { | |
| var pairing = m_Pairings[index]; | |
| if (pairing.name == pairingName) | |
| { | |
| SelectPairing(index, threadGroupName, threadName); | |
| return; | |
| } | |
| } | |
| } | |
| SelectPairing(-1, threadGroupName, threadName); | |
| } | |
| GUIContent GetFrameCountText(ProfileDataView context) | |
| { | |
| var frameSummary = context.analysis.GetFrameSummary(); | |
| string text; | |
| string tooltip; | |
| if (frameSummary.first == frameSummary.last) | |
| { | |
| text = string.Format("{0}", frameSummary.count); | |
| tooltip = ""; | |
| } | |
| else | |
| { | |
| int rangeSize = (1 + (frameSummary.last - frameSummary.first)); | |
| if (frameSummary.count == rangeSize) | |
| { | |
| text = string.Format("{0}", frameSummary.count); | |
| tooltip = string.Format("{0} frames selected\n\n{1} first selected frame\n{2} last selected frame\n\nConsecutive Sequence" | |
| , frameSummary.count | |
| , GetRemappedUIFrameIndex(frameSummary.first, context) | |
| , GetRemappedUIFrameIndex(frameSummary.last, context)); | |
| } | |
| else | |
| { | |
| text = string.Format("{0}*", frameSummary.count); | |
| var ranges = RangesText(context); | |
| tooltip = string.Format("{0} frames selected\n\nframe ranges: {1} \n\nNot a consecutive sequence", frameSummary.count, ranges); | |
| } | |
| } | |
| return new GUIContent(text, tooltip); | |
| } | |
| string RangesText(ProfileDataView context) | |
| { | |
| var sortedFrames = context.analysis.GetFrameSummary().frames.OrderBy(x => x.frameIndex).ToArray(); | |
| var ranges = ""; | |
| int lastAdded = GetRemappedUIFrameIndex(sortedFrames[0].frameIndex, context); | |
| ranges += lastAdded; | |
| for (int n = 1; n < sortedFrames.Length; ++n) | |
| { | |
| if (sortedFrames[n].frameIndex == (sortedFrames[n - 1].frameIndex + 1)) continue; | |
| int nIdx = GetRemappedUIFrameIndex(sortedFrames[n].frameIndex, context); | |
| int pNIdx = GetRemappedUIFrameIndex(sortedFrames[n - 1].frameIndex, context); | |
| if (lastAdded == pNIdx) | |
| { | |
| ranges += ", " + nIdx; | |
| } | |
| else | |
| { | |
| ranges += "-" + pNIdx + ", " + nIdx; | |
| } | |
| lastAdded = nIdx; | |
| } | |
| int remappedLastFrame = GetRemappedUIFrameIndex(sortedFrames.Last().frameIndex, context); | |
| if (lastAdded == remappedLastFrame) | |
| return ranges; | |
| ranges += "-" + remappedLastFrame; | |
| return ranges; | |
| } | |
| GUIContent GetFirstFrameText(ProfileDataView context) | |
| { | |
| var frameSummary = context.analysis.GetFrameSummary(); | |
| string text; | |
| string tooltip; | |
| if (frameSummary.count == 0) | |
| { | |
| text = ""; | |
| tooltip = ""; | |
| } | |
| else if (frameSummary.first == frameSummary.last) | |
| { | |
| int remappedFrame = GetRemappedUIFrameIndex(frameSummary.first, context); | |
| text = string.Format("{0}", remappedFrame); | |
| tooltip = string.Format("Frame {0} selected", remappedFrame); | |
| } | |
| else | |
| { | |
| int rangeSize = (1 + (frameSummary.last - frameSummary.first)); | |
| if (frameSummary.count == rangeSize) | |
| { | |
| int remappedFirstFrame = GetRemappedUIFrameIndex(frameSummary.first, context); | |
| text = string.Format("{0}", remappedFirstFrame); | |
| tooltip = string.Format("{0} frames selected\n\n{1} first selected frame\n{2} last selected frame\n\nConsecutive Sequence" | |
| , frameSummary.count | |
| , remappedFirstFrame | |
| , GetRemappedUIFrameIndex(frameSummary.last, context)); | |
| } | |
| else | |
| { | |
| text = string.Format("{0}*", GetRemappedUIFrameIndex(frameSummary.first, context)); | |
| var ranges = RangesText(context); | |
| tooltip = string.Format("{0} frames selected\n\nframe ranges: {1} \n\nNot a consecutive sequence", frameSummary.count, ranges); | |
| } | |
| } | |
| return new GUIContent(text, tooltip); | |
| } | |
| GUIContent GetLastFrameText(ProfileDataView context) | |
| { | |
| var frameSummary = context.analysis.GetFrameSummary(); | |
| string text; | |
| string tooltip; | |
| if (frameSummary.count == 0) | |
| { | |
| text = ""; | |
| tooltip = ""; | |
| } | |
| else if (frameSummary.first == frameSummary.last) | |
| { | |
| text = ""; | |
| tooltip = string.Format("Frame {0} selected", GetRemappedUIFrameIndex(frameSummary.first, context), context); | |
| } | |
| else | |
| { | |
| int rangeSize = (1 + (frameSummary.last - frameSummary.first)); | |
| if (frameSummary.count == rangeSize) | |
| { | |
| int remappedLastFrame = GetRemappedUIFrameIndex(frameSummary.last, context); | |
| text = string.Format("{0}", remappedLastFrame); | |
| tooltip = string.Format("{0} frames selected\n\n{1} first selected frame\n{2} last selected frame\n\nConsecutive Sequence", | |
| frameSummary.count, | |
| GetRemappedUIFrameIndex(frameSummary.first, context), | |
| remappedLastFrame); | |
| } | |
| else | |
| { | |
| text = string.Format("{0}*", GetRemappedUIFrameIndex(frameSummary.last, context)); | |
| var ranges = RangesText(context); | |
| tooltip = string.Format("{0} frames selected\n\nframe ranges: {1} \n\nNot a consecutive sequence", frameSummary.count, ranges);; | |
| } | |
| } | |
| return new GUIContent(text, tooltip); | |
| } | |
| void DrawFrameSummary() | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS)); | |
| bool lastShowFrameSummary = m_ShowFrameSummary; | |
| m_ShowFrameSummary = BoldFoldout(m_ShowFrameSummary, Styles.frameSummary); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowFrameSummary) | |
| { | |
| EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present | |
| if (IsAnalysisValid()) | |
| { | |
| var frameSummary = m_ProfileSingleView.analysis.GetFrameSummary(); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| m_Columns.Draw(0, ""); | |
| m_Columns.Draw2(Styles.frameCount, GetFrameCountText(m_ProfileSingleView)); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, Styles.frameStart); | |
| GUIContent firstFrameTextContent = GetFirstFrameText(m_ProfileSingleView); | |
| m_Columns.Draw(1, firstFrameTextContent); | |
| if (firstFrameTextContent.text != "") | |
| DrawFrameIndexButton(frameSummary.first, m_ProfileSingleView); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, Styles.frameEnd); | |
| GUIContent lastFrameTextContent = GetLastFrameText(m_ProfileSingleView); | |
| m_Columns.Draw(1, lastFrameTextContent); | |
| if (lastFrameTextContent.text != "") | |
| DrawFrameIndexButton(frameSummary.last, m_ProfileSingleView); | |
| EditorGUILayout.EndHorizontal(); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| m_Columns.Draw(0, ""); | |
| m_Columns.Draw3("", GetDisplayUnits(), "Frame"); | |
| Draw3LabelMsFrame(Styles.max, frameSummary.msMax, frameSummary.maxFrameIndex, m_ProfileSingleView); | |
| Draw2LabelMs(Styles.upperQuartile, frameSummary.msUpperQuartile); | |
| Draw3LabelMsFrame(Styles.median, frameSummary.msMedian, frameSummary.medianFrameIndex, m_ProfileSingleView); | |
| Draw2LabelMs(Styles.mean, frameSummary.msMean); | |
| Draw2LabelMs(Styles.lowerQuartile, frameSummary.msLowerQuartile); | |
| Draw3LabelMsFrame(Styles.min, frameSummary.msMin, frameSummary.minFrameIndex, m_ProfileSingleView); | |
| GUIStyle style = GUI.skin.label; | |
| GUILayout.Space(style.lineHeight); | |
| EditorGUILayout.BeginHorizontal(); | |
| Histogram histogram = new Histogram(m_2D, m_DisplayUnits.Units); | |
| histogram.Draw(LayoutSize.HistogramWidth, 40, frameSummary.buckets, frameSummary.count, 0, frameSummary.msMax, UIColor.bar); | |
| BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, m_DisplayUnits.Units); | |
| float plotWidth = 40 + GUI.skin.box.padding.horizontal; | |
| float plotHeight = 40; | |
| boxAndWhiskerPlot.Draw(plotWidth, plotHeight, frameSummary.msMin, frameSummary.msLowerQuartile, frameSummary.msMedian, frameSummary.msUpperQuartile, frameSummary.msMax, 0, frameSummary.msMax, UIColor.standardLine, UIColor.standardLine); | |
| boxAndWhiskerPlot.DrawText(m_Columns.GetColumnWidth(3), plotHeight, frameSummary.msMin, frameSummary.msMax, | |
| "Min frame time for selected frames", | |
| "Max frame time for selected frames"); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField("No analysis data selected"); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| if (m_ShowFrameSummary != lastShowFrameSummary) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Frames, analytic.GetDurationInSeconds(), m_ShowFrameSummary); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| GUIContent GetThreadNameWithGroupTooltip(string threadNameWithIndex, bool singleThread) | |
| { | |
| string friendlyThreadName = GetFriendlyThreadName(threadNameWithIndex, singleThread); | |
| string groupName; | |
| friendlyThreadName = ProfileData.GetThreadNameWithoutGroup(friendlyThreadName, out groupName); | |
| if (groupName == "") | |
| return new GUIContent(friendlyThreadName, string.Format("{0}", friendlyThreadName)); | |
| else | |
| return new GUIContent(friendlyThreadName, string.Format("{0}\n{1}", friendlyThreadName, groupName)); | |
| } | |
| void DrawThreadSummary() | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS)); | |
| bool lastShowThreadSummary = m_ShowThreadSummary; | |
| m_ShowThreadSummary = BoldFoldout(m_ShowThreadSummary, Styles.threadSummary); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowThreadSummary) | |
| { | |
| EditorGUILayout.BeginVertical(); // To match indenting on the marker summary where a scroll area is present | |
| if (IsAnalysisValid()) | |
| { | |
| float xAxisMin = 0.0f; | |
| float xAxisMax = GetThreadTimeRange(m_ProfileSingleView.analysis); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw4("", "", "", ""); | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, "Total Count : "); | |
| m_Columns.Draw(1, m_ProfileSingleView.data.GetThreadCount().ToString()); | |
| EditorGUILayout.EndHorizontal(); | |
| List<string> threadSelection = GetLimitedThreadSelection(m_ThreadNames, m_ThreadSelection); | |
| int selectedThreads = threadSelection.Count; | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, "Selected : "); | |
| m_Columns.Draw(1, selectedThreads.ToString()); | |
| EditorGUILayout.EndHorizontal(); | |
| ShowThreadRange(); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2 + LayoutSize.WidthColumn3, 0); | |
| m_Columns.Draw3("", "Median", "Thread"); | |
| m_ThreadScroll = EditorGUILayout.BeginScrollView(m_ThreadScroll, GUIStyle.none, GUI.skin.verticalScrollbar); | |
| Rect clipRect = new Rect(m_ThreadScroll.x, m_ThreadScroll.y, m_ThreadsAreaRect.width, m_ThreadsAreaRect.height); | |
| m_2D.SetClipRect(clipRect); | |
| for (int i = 0; i < m_ThreadUINames.Count; i++) | |
| { | |
| string threadNameWithIndex = m_ThreadNames[i]; | |
| if (!threadNameWithIndex.Contains(":")) | |
| continue; // Ignore 'All' | |
| bool include = ProfileAnalyzer.MatchThreadFilter(threadNameWithIndex, m_ThreadSelection.selection); | |
| if (!include) | |
| continue; | |
| ThreadData thread = m_ProfileSingleView.analysis.GetThreadByName(threadNameWithIndex); | |
| if (thread == null) // May be the 'all' field | |
| continue; | |
| bool singleThread = thread.threadsInGroup > 1 ? false : true; | |
| BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, m_DisplayUnits.Units); | |
| EditorGUILayout.BeginHorizontal(); | |
| boxAndWhiskerPlot.DrawHorizontal(100, GUI.skin.label.lineHeight, thread.msMin, thread.msLowerQuartile, thread.msMedian, thread.msUpperQuartile, thread.msMax, xAxisMin, xAxisMax, UIColor.bar, UIColor.barBackground, GUI.skin.label); | |
| m_Columns.Draw(1, ToDisplayUnitsWithTooltips(thread.msMedian)); | |
| m_Columns.Draw(2, GetThreadNameWithGroupTooltip(thread.threadNameWithIndex, singleThread)); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| m_2D.ClearClipRect(); | |
| EditorGUILayout.EndScrollView(); | |
| if (Event.current.type == EventType.Repaint) | |
| { | |
| // This value is not valid at layout phase | |
| m_ThreadsAreaRect = GUILayoutUtility.GetLastRect(); | |
| } | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField("No analysis data selected"); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| if (m_ShowThreadSummary != lastShowThreadSummary) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Threads, analytic.GetDurationInSeconds(), m_ShowThreadSummary); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| void DrawHistogramForMarker(Histogram histogram, MarkerData marker) | |
| { | |
| if (DisplayCount()) | |
| histogram.Draw(LayoutSize.HistogramWidth, 100, marker.countBuckets, marker.presentOnFrameCount, marker.countMin, marker.countMax, UIColor.bar); | |
| else | |
| histogram.Draw(LayoutSize.HistogramWidth, 100, marker.buckets, marker.presentOnFrameCount, marker.msMin, marker.msMax, UIColor.bar); | |
| } | |
| internal bool IsProfilerWindowOpen() | |
| { | |
| return m_ProfilerWindowInterface.IsReady(); | |
| } | |
| /// <summary> | |
| /// Used to remap frame indices when the loaded range in the profiler does not match the range present in the Profile Analyzer capture. | |
| /// This happens when we reload data into the Profiler Window as the index range becomes 1 -> n+1 | |
| /// </summary> | |
| /// <param name="frameIndex">target frame index</param> | |
| /// <param name="frameIndexOffset">capture frameIndex offset</param> | |
| /// <returns></returns> | |
| internal int RemapFrameIndex(int frameIndex, int frameIndexOffset) | |
| { | |
| if (m_ProfilerFirstFrameIndex == 1 && frameIndex > frameIndexOffset) | |
| return frameIndex - frameIndexOffset; | |
| else | |
| return frameIndex; | |
| } | |
| internal void JumpToFrame(int frameIndex, ProfileData frameContext, bool reportErrors = true) | |
| { | |
| if (!m_ProfilerWindowInterface.IsReady()) | |
| return; | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| m_ProfilerWindowInterface.JumpToFrame(RemapFrameIndex(frameIndex, frameContext.FrameIndexOffset)); | |
| if (IsSelectedMarkerNameValid()) | |
| m_ProfilerWindowInterface.SetProfilerWindowMarkerName(m_SelectedMarker.name, m_ThreadSelection.selection); | |
| ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.JumpToFrame, analytic); | |
| } | |
| internal float DrawFrameIndexButton(int frameIndex, ProfileDataView frameContext) | |
| { | |
| float defaultWidth = 50f; | |
| if (frameIndex < 0) | |
| return defaultWidth; | |
| bool enabled = GUI.enabled; | |
| if (!IsProfilerWindowOpen() || !frameContext.inSyncWithProfilerData) | |
| GUI.enabled = false; | |
| var remappedIndex = GetRemappedUIFrameIndex(frameIndex, frameContext); | |
| var content = new GUIContent(string.Format("{0}", remappedIndex), string.Format("Jump to frame {0} in the Unity Profiler", remappedIndex)); | |
| Vector2 size = GUI.skin.button.CalcSize(content); | |
| //float height = size.y; | |
| float maxWidth = Math.Max(defaultWidth, size.x); | |
| if (GUILayout.Button(content, GUILayout.MinWidth(defaultWidth), GUILayout.MaxWidth(maxWidth))) | |
| { | |
| JumpToFrame(frameIndex, frameContext.data); | |
| } | |
| GUI.enabled = enabled; | |
| return maxWidth; | |
| } | |
| internal void DrawFrameIndexButton(Rect rect, int frameIndex, ProfileDataView frameContext) | |
| { | |
| if (frameIndex < 0) | |
| return; | |
| bool enabled = GUI.enabled; | |
| if (!IsProfilerWindowOpen() || !frameContext.inSyncWithProfilerData) | |
| GUI.enabled = false; | |
| // Clamp to max height to match other buttons | |
| // And centre vertically if needed | |
| var remappedIndex = GetRemappedUIFrameIndex(frameIndex, frameContext); | |
| var content = new GUIContent(string.Format("{0}", remappedIndex), string.Format("Jump to frame {0} in the Unity Profiler", remappedIndex)); | |
| Vector2 size = GUI.skin.button.CalcSize(content); | |
| float height = size.y; // was 14 | |
| rect.y += (rect.height - height) / 2; | |
| rect.height = Math.Min(rect.height, height); | |
| if (GUI.Button(rect, content)) | |
| { | |
| JumpToFrame(frameIndex, frameContext.data); | |
| } | |
| GUI.enabled = enabled; | |
| } | |
| void Draw3LabelMsFrame(GUIContent col1, float ms, int frameIndex, ProfileDataView frameContext) | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, col1); | |
| m_Columns.Draw(1, ToDisplayUnitsWithTooltips(ms)); | |
| DrawFrameIndexButton(frameIndex, frameContext); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| void Draw2LabelMs(GUIContent col1, float ms) | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, col1); | |
| m_Columns.Draw(1, ToDisplayUnitsWithTooltips(ms)); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| void Draw4DiffMs(GUIContent col1, float msLeft, float msRight) | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, col1); | |
| m_Columns.Draw(1, ToDisplayUnitsWithTooltips(msLeft)); | |
| m_Columns.Draw(2, ToDisplayUnitsWithTooltips(msRight)); | |
| m_Columns.Draw(3, ToDisplayUnitsWithTooltips(msRight - msLeft)); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| void Draw4DiffMs(GUIContent col1, float msLeft, int frameIndexLeft, float msRight, int frameIndexRight) | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, col1); | |
| m_Columns.Draw(1, ToDisplayUnitsWithTooltips(msLeft, false, frameIndexLeft)); | |
| m_Columns.Draw(2, ToDisplayUnitsWithTooltips(msRight, false, frameIndexRight)); | |
| m_Columns.Draw(3, ToDisplayUnitsWithTooltips(msRight - msLeft)); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| void Draw4Ms(GUIContent col1, float value2, float value3, float value4) | |
| { | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, col1); | |
| m_Columns.Draw(1, ToDisplayUnitsWithTooltips(value2)); | |
| m_Columns.Draw(2, ToDisplayUnitsWithTooltips(value3)); | |
| m_Columns.Draw(3, ToDisplayUnitsWithTooltips(value4)); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| void DrawBoxAndWhiskerPlotForMarker(BoxAndWhiskerPlot boxAndWhiskerPlot, float width, float height, ProfileAnalysis analysis, MarkerData marker, float yAxisStart, float yAxisEnd, Color color, Color colorBackground) | |
| { | |
| if (marker == null) | |
| { | |
| boxAndWhiskerPlot.Draw(width, height, 0, 0, 0, 0, 0, yAxisStart, yAxisEnd, color, colorBackground); | |
| return; | |
| } | |
| if (DisplayCount()) | |
| boxAndWhiskerPlot.Draw(width, height, marker.countMin, marker.countLowerQuartile, marker.countMedian, marker.countUpperQuartile, marker.countMax, yAxisStart, yAxisEnd, color, colorBackground); | |
| else | |
| boxAndWhiskerPlot.Draw(width, height, marker.msMin, marker.msLowerQuartile, marker.msMedian, marker.msUpperQuartile, marker.msMax, yAxisStart, yAxisEnd, color, colorBackground); | |
| } | |
| void DrawBoxAndWhiskerPlotHorizontalForMarker(BoxAndWhiskerPlot boxAndWhiskerPlot, float width, float height, ProfileAnalysis analysis, MarkerData marker, float yAxisStart, float yAxisEnd, Color color, Color colorBackground) | |
| { | |
| boxAndWhiskerPlot.DrawHorizontal(width, height, marker.msMin, marker.msLowerQuartile, marker.msMedian, marker.msUpperQuartile, marker.msMax, yAxisStart, yAxisEnd, color, colorBackground); | |
| } | |
| void DrawFrameRatio(MarkerData marker) | |
| { | |
| var frameSummary = m_ProfileSingleView.analysis.GetFrameSummary(); | |
| GUIStyle style = GUI.skin.label; | |
| float w = LayoutSize.WidthColumn0; | |
| float h = style.lineHeight; | |
| float ySpacing = 2; | |
| float barHeight = h - ySpacing; | |
| EditorGUILayout.BeginVertical(GUILayout.Width(w + LayoutSize.WidthColumn1 + LayoutSize.WidthColumn2)); | |
| float barMax = frameSummary.msMean; | |
| float barValue = marker.msMean; | |
| string text = "Mean frame contribution"; | |
| Units units = m_DisplayUnits.Units; | |
| bool removed = marker.timeIgnored > 0; | |
| if (DisplayCount()) | |
| { | |
| units = Units.Count; | |
| barMax = frameSummary.markerCountMaxMean; | |
| barValue = marker.countMean; | |
| text = "Mean count"; | |
| } | |
| DisplayUnits displayUnits = new DisplayUnits(units); | |
| float barLength = Math.Min((w * barValue) / barMax, w); | |
| EditorGUILayout.LabelField(text); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| m_Columns.Draw2("", ""); | |
| EditorGUILayout.BeginHorizontal(); | |
| if (removed) | |
| { | |
| EditorGUILayout.LabelField("(Marker Removed from analysis)"); | |
| } | |
| else | |
| { | |
| // NOTE: This can effect the whole width of the region its inside | |
| // Not clear why | |
| if (m_2D.DrawStart(w, h, Draw2D.Origin.TopLeft, style)) | |
| { | |
| m_2D.DrawFilledBox(0, ySpacing, barLength, barHeight, UIColor.bar); | |
| m_2D.DrawFilledBox(barLength, ySpacing, w - barLength, barHeight, UIColor.barBackground); | |
| m_2D.DrawEnd(); | |
| Rect rect = GUILayoutUtility.GetLastRect(); | |
| string tooltip = string.Format("{0}", displayUnits.ToString(barValue, true, 5)); | |
| GUI.Label(rect, new GUIContent("", tooltip)); | |
| } | |
| EditorGUILayout.LabelField(ShowPercent((100 * barValue) / barMax), GUILayout.MaxWidth(50)); | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.EndVertical(); | |
| } | |
| GUIContent ShowPercent(float percent) | |
| { | |
| string text; | |
| string tooltip; | |
| if (percent >= 999.95f) | |
| { | |
| text = string.Format("{0:f0}%", percent); | |
| tooltip = string.Format("{0:f2}%", percent); | |
| } | |
| else if (percent >= 99.995f) | |
| { | |
| text = string.Format("{0:f1}%", percent); | |
| tooltip = string.Format("{0:f2}%", percent); | |
| } | |
| else | |
| { | |
| text = string.Format("{0:f2}%", percent); | |
| tooltip = text; | |
| } | |
| return new GUIContent(text, tooltip); | |
| } | |
| void DrawComparisonFrameRatio(MarkerData leftMarker, MarkerData rightMarker) | |
| { | |
| var leftFrameSummary = m_ProfileLeftView.analysis.GetFrameSummary(); | |
| var rightFrameSummary = m_ProfileRightView.analysis.GetFrameSummary(); | |
| GUIStyle style = GUI.skin.label; | |
| float w = LayoutSize.WidthColumn0; | |
| float h = style.lineHeight; | |
| float ySpacing = 2; | |
| float barHeight = (h - ySpacing) / 2; | |
| EditorGUILayout.BeginVertical(GUILayout.Width(w + LayoutSize.WidthColumn1 + LayoutSize.WidthColumn2)); | |
| float leftBarValue = MarkerData.GetMsMean(leftMarker); | |
| float rightBarValue = MarkerData.GetMsMean(rightMarker); | |
| float leftBarMax = leftFrameSummary.msMean; | |
| float rightBarMax = rightFrameSummary.msMean; | |
| string text = "Mean frame contribution"; | |
| Units units = m_DisplayUnits.Units; | |
| bool removed = MarkerData.GetTimeIgnored(leftMarker) > 0 || MarkerData.GetTimeIgnored(rightMarker) > 0; | |
| if (DisplayCount()) | |
| { | |
| units = Units.Count; | |
| leftBarValue = MarkerData.GetCountMean(leftMarker); | |
| rightBarValue = MarkerData.GetCountMean(rightMarker); | |
| leftBarMax = leftFrameSummary.markerCountMaxMean; | |
| rightBarMax = rightFrameSummary.markerCountMaxMean; | |
| text = "Mean count"; | |
| } | |
| DisplayUnits displayUnits = new DisplayUnits(units); | |
| float leftBarLength = (leftBarMax > 0) ? (w * leftBarValue) / leftBarMax : 0f; | |
| leftBarLength = Math.Min(leftBarLength, w); | |
| float rightBarLength = (rightBarMax > 0) ? (w * rightBarValue) / rightBarMax : 0f; | |
| rightBarLength = Math.Min(rightBarLength, w); | |
| EditorGUILayout.LabelField(text); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| if (removed) | |
| m_Columns.Draw4("", "", "", ""); | |
| else | |
| m_Columns.Draw4("", "Left", "Right", "Diff"); | |
| EditorGUILayout.BeginHorizontal(); | |
| if (removed) | |
| { | |
| EditorGUILayout.LabelField("(Marker Removed from analysis)"); | |
| } | |
| else | |
| { | |
| if (m_2D.DrawStart(w, h, Draw2D.Origin.TopLeft, style)) | |
| { | |
| m_2D.DrawFilledBox(0, ySpacing, w, h - ySpacing, UIColor.barBackground); | |
| m_2D.DrawFilledBox(0, ySpacing, leftBarLength, barHeight, UIColor.left); | |
| m_2D.DrawFilledBox(0, ySpacing + barHeight, rightBarLength, barHeight, UIColor.right); | |
| m_2D.DrawEnd(); | |
| Rect rect = GUILayoutUtility.GetLastRect(); | |
| string tooltip = string.Format("Left: {0}\nRight: {1}", displayUnits.ToTooltipString(leftBarValue, true), displayUnits.ToTooltipString(rightBarValue, true)); | |
| GUI.Label(rect, new GUIContent("", tooltip)); | |
| } | |
| float leftPercentage = leftBarMax > 0 ? (100 * leftBarValue) / leftBarMax : 0f; | |
| float rightPercentage = rightBarMax > 0 ? (100 * rightBarValue) / rightBarMax : 0f; | |
| EditorGUILayout.LabelField(ShowPercent(leftPercentage), GUILayout.Width(LayoutSize.WidthColumn1)); | |
| EditorGUILayout.LabelField(ShowPercent(rightPercentage), GUILayout.Width(LayoutSize.WidthColumn2)); | |
| if (leftMarker != null && rightMarker != null) | |
| EditorGUILayout.LabelField(ShowPercent(rightPercentage - leftPercentage), GUILayout.Width(LayoutSize.WidthColumn3)); | |
| } | |
| EditorGUILayout.EndHorizontal(); | |
| EditorGUILayout.EndVertical(); | |
| } | |
| void DrawTopComparison(MarkerData leftMarker, MarkerData rightMarker) | |
| { | |
| GUIStyle style = GUI.skin.label; | |
| float w = LayoutSize.WidthColumn0; | |
| float h = style.lineHeight; | |
| float ySpacing = 2; | |
| float barHeight = (h - ySpacing) / 2; | |
| EditorGUILayout.BeginVertical(GUILayout.Width(w + LayoutSize.WidthColumn1 + LayoutSize.WidthColumn2)); | |
| bool showCount = DisplayCount(); | |
| float leftMax = MarkerData.GetMsMax(leftMarker); | |
| float rightMax = MarkerData.GetMsMax(rightMarker); | |
| Units units = m_DisplayUnits.Units; | |
| if (showCount) | |
| { | |
| units = Units.Count; | |
| leftMax = MarkerData.GetCountMax(leftMarker); | |
| rightMax = MarkerData.GetCountMax(rightMarker); | |
| } | |
| DisplayUnits displayUnits = new DisplayUnits(units); | |
| TopMarkerList topMarkerList = new TopMarkerList(m_2D, units, | |
| LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3, | |
| UIColor.bar, UIColor.barBackground, DrawFrameIndexButton); | |
| m_TopNumber = topMarkerList.DrawTopNumber(m_TopNumber, m_TopStrings, m_TopValues); | |
| float barMax = Math.Max(leftMax, rightMax); | |
| List<FrameTime> leftFrames = leftMarker != null ? topMarkerList.GetTopN(leftMarker, m_TopNumber, showCount) : new List<FrameTime>(); | |
| List<FrameTime> rightFrames = rightMarker != null ? topMarkerList.GetTopN(rightMarker, m_TopNumber, showCount) : new List<FrameTime>(); | |
| FrameTime zeroFrameTime = new FrameTime(-1, 0.0f, 0); | |
| for (int i = 0; i < m_TopNumber; i++) | |
| { | |
| bool leftValid = i < leftFrames.Count; | |
| bool rightValid = i < rightFrames.Count; | |
| FrameTime leftFrameTime = leftValid ? leftFrames[i] : zeroFrameTime; | |
| FrameTime rightFrameTime = rightValid ? rightFrames[i] : zeroFrameTime; | |
| float leftBarValue = showCount ? leftFrameTime.count : leftFrameTime.ms; | |
| float rightBarValue = showCount ? rightFrameTime.count : rightFrameTime.ms; | |
| float leftBarLength = Math.Min((w * leftBarValue) / barMax, w); | |
| float rightBarLength = Math.Min((w * rightBarValue) / barMax, w); | |
| EditorGUILayout.BeginHorizontal(); | |
| if (m_2D.DrawStart(w, h, Draw2D.Origin.TopLeft, style)) | |
| { | |
| if (leftValid || rightValid) | |
| { | |
| m_2D.DrawFilledBox(0, ySpacing, w, h - ySpacing, UIColor.barBackground); | |
| m_2D.DrawFilledBox(0, ySpacing, leftBarLength, barHeight, UIColor.left); | |
| m_2D.DrawFilledBox(0, ySpacing + barHeight, rightBarLength, barHeight, UIColor.right); | |
| } | |
| m_2D.DrawEnd(); | |
| Rect rect = GUILayoutUtility.GetLastRect(); | |
| string leftContent = leftValid ? displayUnits.ToTooltipString(leftBarValue, true, leftFrameTime.frameIndex) : "None"; | |
| string rightContent = rightValid ? displayUnits.ToTooltipString(rightBarValue, true, rightFrameTime.frameIndex) : "None"; | |
| GUI.Label(rect, new GUIContent("", string.Format("Left:\t{0}\nRight:\t{1}", leftContent, rightContent))); | |
| } | |
| EditorGUILayout.LabelField(leftValid ? displayUnits.ToGUIContentWithTooltips(leftBarValue, frameIndex: leftFrameTime.frameIndex) : Styles.emptyString, GUILayout.Width(LayoutSize.WidthColumn1)); | |
| EditorGUILayout.LabelField(rightValid ? displayUnits.ToGUIContentWithTooltips(rightBarValue, frameIndex: rightFrameTime.frameIndex) : Styles.emptyString, GUILayout.Width(LayoutSize.WidthColumn2)); | |
| if (leftValid || rightValid) | |
| EditorGUILayout.LabelField(displayUnits.ToGUIContentWithTooltips(rightBarValue - leftBarValue), GUILayout.Width(LayoutSize.WidthColumn3)); | |
| EditorGUILayout.EndHorizontal(); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| void DrawSelectedStats(MarkerData marker, ProfileDataView markerContext) | |
| { | |
| GUIStyle style = GUI.skin.label; | |
| m_Columns.Draw3("", GetDisplayUnits(), "Frame"); | |
| Draw3LabelMsFrame(Styles.max, marker.msMax, marker.maxFrameIndex, markerContext); | |
| Draw2LabelMs(Styles.upperQuartile, marker.msUpperQuartile); | |
| Draw3LabelMsFrame(Styles.median, marker.msMedian, marker.medianFrameIndex, markerContext); | |
| Draw2LabelMs(Styles.mean, marker.msMean); | |
| Draw2LabelMs(Styles.lowerQuartile, marker.msLowerQuartile); | |
| Draw3LabelMsFrame(Styles.min, marker.msMin, marker.minFrameIndex, markerContext); | |
| GUILayout.Space(style.lineHeight); | |
| Draw3LabelMsFrame(Styles.individualMax, marker.msMaxIndividual, | |
| marker.maxIndividualFrameIndex, markerContext); | |
| Draw3LabelMsFrame(Styles.individualMin, marker.msMinIndividual, | |
| marker.minIndividualFrameIndex, markerContext); | |
| } | |
| void DrawSelected() | |
| { | |
| EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(LayoutSize.WidthRHS)); | |
| bool lastMarkerSummary = m_ShowMarkerSummary; | |
| m_ShowMarkerSummary = BoldFoldout(m_ShowMarkerSummary, Styles.markerSummary); | |
| var analytic = ProfileAnalyzerAnalytics.BeginAnalytic(); | |
| if (m_ShowMarkerSummary) | |
| { | |
| if (IsAnalysisValid()) | |
| { | |
| List<MarkerData> markers = m_ProfileSingleView.analysis.GetMarkers(); | |
| if (markers != null) | |
| { | |
| int markerAt = m_SelectedMarker.id; | |
| if (markerAt >= 0 && markerAt < markers.Count) | |
| { | |
| var marker = markers[markerAt]; | |
| m_MarkerSummaryScroll = GUILayout.BeginScrollView(m_MarkerSummaryScroll, GUIStyle.none, GUI.skin.verticalScrollbar); | |
| Rect clipRect = new Rect(m_MarkerSummaryScroll.x, m_MarkerSummaryScroll.y, LayoutSize.WidthRHS, 500); | |
| m_2D.SetClipRect(clipRect); | |
| EditorGUILayout.BeginVertical(); | |
| EditorGUILayout.LabelField(marker.name, | |
| GUILayout.MaxWidth(LayoutSize.WidthRHS - | |
| (GUI.skin.box.padding.horizontal + GUI.skin.box.margin.horizontal))); | |
| DrawFrameRatio(marker); | |
| m_Columns.SetColumnSizes(LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3); | |
| EditorGUILayout.BeginHorizontal(); | |
| m_Columns.Draw(0, Styles.firstFrame); | |
| m_Columns.Draw(1, Styles.emptyString); | |
| DrawFrameIndexButton(marker.firstFrameIndex, m_ProfileSingleView); | |
| EditorGUILayout.EndHorizontal(); | |
| GUIStyle style = GUI.skin.label; | |
| float min = marker.msMin; | |
| float max = marker.msMax; | |
| string fieldString = "marker time"; | |
| Units units = m_DisplayUnits.Units; | |
| if (DisplayCount()) | |
| { | |
| min = marker.countMin; | |
| max = marker.countMax; | |
| fieldString = "count"; | |
| units = Units.Count; | |
| } | |
| TopMarkerList topMarkerList = new TopMarkerList(m_2D, units, | |
| LayoutSize.WidthColumn0, LayoutSize.WidthColumn1, LayoutSize.WidthColumn2, LayoutSize.WidthColumn3, | |
| UIColor.bar, UIColor.barBackground, DrawFrameIndexButton); | |
| m_TopNumber = topMarkerList.Draw(marker, m_ProfileSingleView, m_TopNumber, m_TopStrings, m_TopValues); | |
| GUILayout.Space(style.lineHeight); | |
| float plotWidth = 40 + GUI.skin.box.padding.horizontal; | |
| float plotHeight = 100; | |
| EditorGUILayout.BeginHorizontal(); | |
| Histogram histogram = new Histogram(m_2D, units); | |
| DrawHistogramForMarker(histogram, marker); | |
| BoxAndWhiskerPlot boxAndWhiskerPlot = new BoxAndWhiskerPlot(m_2D, units); | |
| DrawBoxAndWhiskerPlotForMarker(boxAndWhiskerPlot, plotWidth, plotHeight, m_ProfileSingleView.analysis, marker, | |
| min, max, UIColor.standardLine, UIColor.boxAndWhiskerBoxColor); | |
| boxAndWhiskerPlot.DrawText(m_Columns.GetColumnWidth(3), plotHeight, min, max, | |
| string.Format("Min {0} for selected frames", fieldString), | |
| string.Format("Max {0} for selected frames", fieldString)); | |
| EditorGUILayout.EndHorizontal(); | |
| GUILayout.Space(style.lineHeight); | |
| DrawSelectedStats(marker, m_ProfileSingleView); | |
| EditorGUILayout.EndVertical(); | |
| m_2D.ClearClipRect(); | |
| GUILayout.EndScrollView(); | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField("Marker not in selection"); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| EditorGUILayout.LabelField("No marker data selected"); | |
| } | |
| } | |
| if (m_ShowMarkerSummary != lastMarkerSummary) | |
| { | |
| ProfileAnalyzerAnalytics.SendUIVisibilityEvent(ProfileAnalyzerAnalytics.UIVisibility.Markers, analytic.GetDurationInSeconds(), m_ShowMarkerSummary); | |
| } | |
| EditorGUILayout.EndVertical(); | |
| } | |
| internal static bool FileInTempDir(string filePath) | |
| { | |
| return Directory.Exists(TmpDir) && Directory.GetFiles(TmpDir).Contains(filePath); | |
| } | |
| } | |
| } | |