5917 lines
255 KiB
C#
5917 lines
255 KiB
C#
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;
|
|
|
|
[NonSerialized] bool m_SelectionEventFromProfilerWindowInProgress = false;
|
|
|
|
int m_TopNumber;
|
|
string[] m_TopStrings;
|
|
int[] m_TopValues;
|
|
|
|
[SerializeField] DepthSliceUI m_DepthSliceUI;
|
|
|
|
[SerializeField]
|
|
TimingOptions.TimingOption m_TimingOption = TimingOptions.TimingOption.Time;
|
|
|
|
[SerializeField]
|
|
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;
|
|
|
|
[SerializeField]
|
|
ThreadSelection m_ThreadSelection = new ThreadSelection();
|
|
ThreadSelection m_ThreadSelectionNew;
|
|
string m_ThreadSelectionSummary;
|
|
|
|
[SerializeField]
|
|
DisplayUnits m_DisplayUnits = new DisplayUnits(Units.Milliseconds);
|
|
string[] m_UnitNames;
|
|
|
|
[SerializeField]
|
|
string m_NameFilter = "";
|
|
[SerializeField]
|
|
string m_NameExclude = "";
|
|
|
|
[SerializeField]
|
|
MarkerColumnFilter m_SingleModeFilter = new MarkerColumnFilter(MarkerColumnFilter.Mode.TimeAndCount);
|
|
[SerializeField]
|
|
MarkerColumnFilter m_CompareModeFilter = new MarkerColumnFilter(MarkerColumnFilter.Mode.TimeAndCount);
|
|
|
|
[SerializeField]
|
|
TopTenDisplay m_TopTenDisplay = TopTenDisplay.Normalized;
|
|
[SerializeField]
|
|
NameFilterOperation m_NameFilterOperation = NameFilterOperation.All;
|
|
[SerializeField]
|
|
NameFilterOperation m_NameExcludeOperation = NameFilterOperation.Any;
|
|
[SerializeField]
|
|
RemoveMarkerOperation m_removeMarkerOperation = RemoveMarkerOperation.ShowAll;
|
|
[SerializeField]
|
|
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;
|
|
|
|
[SerializeField]
|
|
string m_removeMarkerCustomRemoveMarker = null;
|
|
|
|
[SerializeField]
|
|
bool m_ShowFilters = true;
|
|
[SerializeField]
|
|
bool m_ShowTopNMarkers = true;
|
|
[SerializeField]
|
|
bool m_ShowFrameSummary = true;
|
|
[SerializeField]
|
|
bool m_ShowThreadSummary = false;
|
|
[SerializeField]
|
|
bool m_ShowMarkerSummary = true;
|
|
[SerializeField]
|
|
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);
|
|
}
|
|
|
|
[SerializeField]
|
|
ProfileDataView m_ProfileSingleView;
|
|
[SerializeField]
|
|
ProfileDataView m_ProfileLeftView;
|
|
[SerializeField]
|
|
ProfileDataView m_ProfileRightView;
|
|
|
|
[SerializeField] ThreadMarkerInfo m_SelectedMarker = new ThreadMarkerInfo();
|
|
|
|
[Serializable]
|
|
struct ThreadMarkerInfo
|
|
{
|
|
[SerializeField]
|
|
public int id;
|
|
|
|
[SerializeField]
|
|
public string threadName;
|
|
[SerializeField]
|
|
public string threadGroupName;
|
|
[SerializeField]
|
|
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;
|
|
|
|
[SerializeField]
|
|
List<MarkerPairing> m_Pairings = new List<MarkerPairing>();
|
|
int m_TotalCombinedMarkerCount = 0;
|
|
|
|
[SerializeField]
|
|
int m_SelectedPairing = 0;
|
|
|
|
[SerializeField]
|
|
TreeViewState m_ProfileTreeViewState;
|
|
[SerializeField]
|
|
MultiColumnHeaderState m_ProfileMulticolumnHeaderState;
|
|
ProfileTable m_ProfileTable;
|
|
|
|
[SerializeField]
|
|
TreeViewState m_ComparisonTreeViewState;
|
|
[SerializeField]
|
|
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);
|
|
|
|
[SerializeField]
|
|
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;
|
|
|
|
[SerializeField]
|
|
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("(.+)[ ]([^ ]*)");
|
|
*/
|
|
|
|
[MenuItem("Window/Analysis/Profile Analyzer")]
|
|
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);
|
|
}
|
|
}
|
|
}
|