This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,373 @@
using System;
using System.Collections.Generic;
using UnityEditor.Analytics;
using UnityEngine;
using UnityEngine.Analytics;
namespace UnityEditor.Performance.ProfileAnalyzer
{
class ProfileAnalyzerAnalytics
{
const int k_MaxEventsPerHour = 100;
const int k_MaxEventItems = 1000;
const string k_VendorKey = "unity.profileanalyzer";
const string k_EventTopicName = "usability";
static bool s_EnableAnalytics = false;
public static void EnableAnalytics()
{
AnalyticsResult result = EditorAnalytics.RegisterEventWithLimit(k_EventTopicName, k_MaxEventsPerHour, k_MaxEventItems, k_VendorKey);
if (result == AnalyticsResult.Ok)
s_EnableAnalytics = true;
}
public enum UIButton
{
Pull,
OpenProfiler,
CloseProfiler,
JumpToFrame,
ExportSingleFrames,
ExportComparisonFrames,
};
public enum UIUsageMode
{
Single,
Comparison,
};
public enum UIVisibility
{
FrameTimeContextMenu,
Filters,
TopTen,
Frames,
Threads,
Markers,
};
public enum UIResizeView
{
Single,
Comparison,
};
[Serializable]
struct ProfileAnalyzerUIButtonEventParameters
{
public string name;
public ProfileAnalyzerUIButtonEventParameters(string name)
{
this.name = name;
}
}
// camelCase since these events get serialized to Json and naming convention in analytics is camelCase
[Serializable]
struct ProfileAnalyzerUIButtonEvent
{
public ProfileAnalyzerUIButtonEvent(string name, float durationInTicks)
{
subtype = "profileAnalyzerUIButton";
// ts is auto added so no need to include it here
//ts = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
this.duration = durationInTicks;
parameters = new ProfileAnalyzerUIButtonEventParameters(name);
}
public string subtype;
//public int ts;
public float duration; // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
public ProfileAnalyzerUIButtonEventParameters parameters;
}
[Serializable]
struct ProfileAnalyzerUIUsageEventParameters
{
public string name;
public ProfileAnalyzerUIUsageEventParameters(string name)
{
this.name = name;
}
}
[Serializable]
struct ProfileAnalyzerUIUsageEvent
{
public ProfileAnalyzerUIUsageEvent(string name, float durationInTicks)
{
subtype = "profileAnalyzerModeUsage";
this.duration = durationInTicks;
parameters = new ProfileAnalyzerUIUsageEventParameters(name);
}
public string subtype;
public float duration; // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
public ProfileAnalyzerUIUsageEventParameters parameters;
}
[Serializable]
struct ProfileAnalyzerUIVisibilityEventParameters
{
public string name;
public bool show;
public ProfileAnalyzerUIVisibilityEventParameters(string name, bool show)
{
this.name = name;
this.show = show;
}
}
[Serializable]
struct ProfileAnalyzerUIVisibilityEvent
{
public ProfileAnalyzerUIVisibilityEvent(string name, float durationInTicks, bool show)
{
subtype = "profileAnalyzerUIVisibility";
this.duration = durationInTicks;
parameters = new ProfileAnalyzerUIVisibilityEventParameters(name, show);
}
public string subtype;
public float duration; // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
public ProfileAnalyzerUIVisibilityEventParameters parameters;
}
[Serializable]
struct ProfileAnalyzerUIResizeEventParameters
{
public string name;
public float width;
public float height;
public float screenWidth;
public float screenHeight;
public bool docked;
public ProfileAnalyzerUIResizeEventParameters(string name, float width, float height, float screenWidth, float screenHeight, bool isDocked)
{
this.name = name;
this.width = width;
this.height = height;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
docked = isDocked;
}
}
[Serializable]
struct ProfileAnalyzerUIResizeEvent
{
public ProfileAnalyzerUIResizeEvent(string name, float durationInTicks, float width, float height, float screenWidth, float screenHeight, bool isDocked)
{
subtype = "profileAnalyzerUIResize";
this.duration = durationInTicks;
parameters = new ProfileAnalyzerUIResizeEventParameters(name, width, height, screenWidth, screenHeight, isDocked);
}
public string subtype;
public float duration; // Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
public ProfileAnalyzerUIResizeEventParameters parameters;
}
static float SecondsToTicks(float durationInSeconds)
{
return durationInSeconds * 10000;
}
public static bool SendUIButtonEvent(UIButton uiButton, float durationInSeconds)
{
if (!s_EnableAnalytics)
return false;
// Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
float durationInTicks = SecondsToTicks(durationInSeconds);
ProfileAnalyzerUIButtonEvent uiButtonEvent;
switch (uiButton)
{
case UIButton.Pull:
uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerGrab", durationInTicks);
break;
case UIButton.OpenProfiler:
uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerOpenProfiler", durationInTicks);
break;
case UIButton.CloseProfiler:
uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerCloseProfiler", durationInTicks);
break;
case UIButton.JumpToFrame:
uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerJumpToFrame", durationInTicks);
break;
case UIButton.ExportSingleFrames:
uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerExportSingleFrames", durationInTicks);
break;
case UIButton.ExportComparisonFrames:
uiButtonEvent = new ProfileAnalyzerUIButtonEvent("profilerAnalyzerExportComparisonFrames", durationInTicks);
break;
default:
Debug.LogFormat("SendUIButtonEvent: Unsupported button type : {0}", uiButton);
return false;
}
AnalyticsResult result = EditorAnalytics.SendEventWithLimit(k_EventTopicName, uiButtonEvent);
if (result != AnalyticsResult.Ok)
return false;
return true;
}
public static bool SendUIUsageModeEvent(UIUsageMode uiUsageMode, float durationInSeconds)
{
if (!s_EnableAnalytics)
return false;
// Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
float durationInTicks = SecondsToTicks(durationInSeconds);
ProfileAnalyzerUIUsageEvent uiUsageEvent;
switch (uiUsageMode)
{
case UIUsageMode.Single:
uiUsageEvent = new ProfileAnalyzerUIUsageEvent("profileAnalyzerSingle", durationInTicks);
break;
case UIUsageMode.Comparison:
uiUsageEvent = new ProfileAnalyzerUIUsageEvent("profileAnalyzerCompare", durationInTicks);
break;
default:
Debug.LogFormat("SendUsageEvent: Unsupported usage mode : {0}", uiUsageMode);
return false;
}
AnalyticsResult result = EditorAnalytics.SendEventWithLimit(k_EventTopicName, uiUsageEvent);
if (result != AnalyticsResult.Ok)
return false;
return true;
}
public static bool SendUIVisibilityEvent(UIVisibility uiVisibility, float durationInSeconds, bool show)
{
if (!s_EnableAnalytics)
return false;
// Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
float durationInTicks = SecondsToTicks(durationInSeconds);
ProfileAnalyzerUIVisibilityEvent uiUsageEvent;
switch (uiVisibility)
{
case UIVisibility.FrameTimeContextMenu:
uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerFrameTimeContextMenu", durationInTicks, show);
break;
case UIVisibility.Filters:
uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerFilters", durationInTicks, show);
break;
case UIVisibility.TopTen:
uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerTopTen", durationInTicks, show);
break;
case UIVisibility.Frames:
uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerFrames", durationInTicks, show);
break;
case UIVisibility.Threads:
uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerThreads", durationInTicks, show);
break;
case UIVisibility.Markers:
uiUsageEvent = new ProfileAnalyzerUIVisibilityEvent("profilerAnalyzerMarkers", durationInTicks, show);
break;
default:
Debug.LogFormat("SendUIVisibilityEvent: Unsupported visibililty item : {0}", uiVisibility);
return false;
}
AnalyticsResult result = EditorAnalytics.SendEventWithLimit(k_EventTopicName, uiUsageEvent);
if (result != AnalyticsResult.Ok)
return false;
return true;
}
public static bool SendUIResizeEvent(UIResizeView uiResizeView, float durationInSeconds, float width, float height, bool isDocked)
{
if (!s_EnableAnalytics)
return false;
// Duration is in "ticks" 100 nanosecond intervals. I.e. 0.1 microseconds
float durationInTicks = SecondsToTicks(durationInSeconds);
ProfileAnalyzerUIResizeEvent uiResizeEvent;
switch (uiResizeView)
{
case UIResizeView.Single:
// Screen.width, Screen.height is game view size
uiResizeEvent = new ProfileAnalyzerUIResizeEvent("profileAnalyzerSingle", durationInTicks, width, height, Screen.currentResolution.width, Screen.currentResolution.height, isDocked);
break;
case UIResizeView.Comparison:
uiResizeEvent = new ProfileAnalyzerUIResizeEvent("profileAnalyzerCompare", durationInTicks, width, height, Screen.currentResolution.width, Screen.currentResolution.height, isDocked);
break;
default:
Debug.LogFormat("SendUIResizeEvent: Unsupported view : {0}", uiResizeView);
return false;
}
AnalyticsResult result = EditorAnalytics.SendEventWithLimit(k_EventTopicName, uiResizeEvent);
if (result != AnalyticsResult.Ok)
return false;
return true;
}
internal class Analytic
{
double m_StartTime;
float m_DurationInSeconds;
public Analytic()
{
m_StartTime = EditorApplication.timeSinceStartup;
m_DurationInSeconds = 0;
}
public void End()
{
m_DurationInSeconds = (float)(EditorApplication.timeSinceStartup - m_StartTime);
}
public float GetDurationInSeconds()
{
return m_DurationInSeconds;
}
}
static public Analytic BeginAnalytic()
{
return new Analytic();
}
static public void SendUIButtonEvent(UIButton uiButton, Analytic instance)
{
instance.End();
SendUIButtonEvent(uiButton, instance.GetDurationInSeconds());
}
static public void SendUIUsageModeEvent(UIUsageMode uiUsageMode, Analytic instance)
{
instance.End();
SendUIUsageModeEvent(uiUsageMode, instance.GetDurationInSeconds());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: adf9820979228054693ce2e8224012fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.Performance.Profile-Analyzer.EditorTests")]

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcb585fc3518ff949912f2d4fff2e26f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,240 @@
using UnityEngine;
using System;
namespace UnityEditor.Performance.ProfileAnalyzer
{
internal class BoxAndWhiskerPlot
{
Draw2D m_2D;
Color m_ColorBackground;
DisplayUnits m_Units;
string DisplayUnits()
{
return m_Units.Postfix();
}
string ToDisplayUnits(float ms, bool showUnits = false)
{
return m_Units.ToString(ms, showUnits, 5, true);
}
string ToTooltipDisplayUnits(float ms, bool showUnits = false)
{
return m_Units.ToTooltipString(ms, showUnits);
}
public void SetUnits(Units units)
{
m_Units = new DisplayUnits(units);
}
public BoxAndWhiskerPlot(Draw2D draw2D, Units units, Color colorBackground)
{
m_2D = draw2D;
SetUnits(units);
m_ColorBackground = colorBackground;
}
public BoxAndWhiskerPlot(Draw2D draw2D, Units units)
{
m_2D = draw2D;
SetUnits(units);
m_ColorBackground = new Color(0.4f, 0.4f, 0.4f);
}
float ClampToRange(float value, float min, float max)
{
return Math.Max(min, Math.Min(value, max));
}
public void Draw(float width, float height, float min, float lowerQuartile, float median, float upperQuartile, float max, float yAxisStart, float yAxisEnd, Color color, Color colorFilled)
{
if (m_2D.DrawStart(width, height, Draw2D.Origin.BottomLeft))
{
Rect rect = GUILayoutUtility.GetLastRect();
float x = 0;
float y = 0;
float w = width;
float h = height;
Draw(rect, x, y, w, h, min, lowerQuartile, median, upperQuartile, max, yAxisStart, yAxisEnd, color, colorFilled);
m_2D.DrawEnd();
}
}
string GetTooltip(float min, float lowerQuartile, float median, float upperQuartile, float max)
{
string tooltip = string.Format(
"Max :\t\t{0}\n\nUpper Quartile :\t{1}\nMedian :\t\t{2}\nLower Quartile :\t{3}\nInterquartile range : \t{4}\n\nMin :\t\t{5}\nUnits :\t\t{6}",
ToTooltipDisplayUnits(max),
ToTooltipDisplayUnits(upperQuartile),
ToTooltipDisplayUnits(median),
ToTooltipDisplayUnits(lowerQuartile),
ToTooltipDisplayUnits(upperQuartile - lowerQuartile),
ToTooltipDisplayUnits(min),
m_Units.Postfix()
);
return tooltip;
}
public void Draw(Rect rect, float x, float y, float w, float h, float min, float lowerQuartile, float median, float upperQuartile, float max, float yAxisStart, float yAxisEnd, Color color, Color colorFilled, bool clearFirst = true)
{
string tooltip = GetTooltip(min, lowerQuartile, median, upperQuartile, max);
GUI.Label(rect, new GUIContent("", tooltip));
if (clearFirst)
m_2D.DrawFilledBox(x, y, w, h, m_ColorBackground);
float first = yAxisStart;
float last = yAxisEnd;
float range = last - first;
bool startCap = (min >= first) ? true : false;
bool endCap = (max <= last) ? true : false;
// Range clamping
min = ClampToRange(min, first, last);
lowerQuartile = ClampToRange(lowerQuartile, first, last);
median = ClampToRange(median, first, last);
upperQuartile = ClampToRange(upperQuartile, first, last);
max = ClampToRange(max, first, last);
float hMax = h - 1;
float yMin = hMax * (min - first) / range;
float yLowerQuartile = hMax * (lowerQuartile - first) / range;
float yMedian = hMax * (median - first) / range;
float yUpperQuartile = hMax * (upperQuartile - first) / range;
float yMax = hMax * (max - first) / range;
// Min to max line
float xCentre = x + (w / 2);
m_2D.DrawLine(xCentre, y + yMin, xCentre, y + yLowerQuartile, color);
m_2D.DrawLine(xCentre, y + yUpperQuartile, xCentre, y + yMax, color);
// Quartile boxes
float xMargin = (2 * w / 8);
float x1 = x + xMargin;
float x2 = x + (w - xMargin);
float wBox = x2 - x1;
if (colorFilled != color)
m_2D.DrawFilledBox(x1, y + yLowerQuartile, wBox, (yMedian - yLowerQuartile), colorFilled);
m_2D.DrawBox(x1, y + yLowerQuartile, wBox, (yMedian - yLowerQuartile), color);
if (colorFilled != color)
m_2D.DrawFilledBox(x1, y + yMedian, wBox, (yUpperQuartile - yMedian), colorFilled);
m_2D.DrawBox(x1, y + yMedian, wBox, (yUpperQuartile - yMedian), color);
// Median line
//xMargin = (1 * w / 8);
//x1 = x + xMargin;
//x2 = x + (w - xMargin);
m_2D.DrawLine(x1, y + yMedian, x2, y + yMedian, color);
m_2D.DrawLine(x1, y + yMedian + 1, x2, y + yMedian + 1, color);
// Line caps
xMargin = (3 * w / 8);
x1 = x + xMargin;
x2 = x + (w - xMargin);
if (startCap)
m_2D.DrawLine(x1, y + yMin, x2, y + yMin, color);
if (endCap)
m_2D.DrawLine(x1, y + yMax, x2, y + yMax, color);
}
public void DrawHorizontal(float width, float height, float min, float lowerQuartile, float median, float upperQuartile, float max, float xAxisStart, float xAxisEnd, Color color, Color colorFilled, GUIStyle style = null)
{
if (m_2D.DrawStart(width, height, Draw2D.Origin.BottomLeft, style))
{
Rect rect = GUILayoutUtility.GetLastRect();
float x = 0;
float y = 0;
float w = width;
float h = height;
DrawHorizontal(rect, x, y, w, h, min, lowerQuartile, median, upperQuartile, max, xAxisStart, xAxisEnd, color, colorFilled);
m_2D.DrawEnd();
}
}
public void DrawHorizontal(Rect rect, float x, float y, float w, float h, float min, float lowerQuartile, float median, float upperQuartile, float max, float xAxisStart, float xAxisEnd, Color color, Color colorFilled, bool clearFirst = true)
{
string tooltip = GetTooltip(min, lowerQuartile, median, upperQuartile, max);
GUI.Label(rect, new GUIContent("", tooltip));
if (clearFirst)
m_2D.DrawFilledBox(x, y, w, h, m_ColorBackground);
float first = xAxisStart;
float last = xAxisEnd;
float range = last - first;
bool startCap = (min >= first) ? true : false;
bool endCap = (max <= last) ? true : false;
// Range clamping
min = ClampToRange(min, first, last);
lowerQuartile = ClampToRange(lowerQuartile, first, last);
median = ClampToRange(median, first, last);
upperQuartile = ClampToRange(upperQuartile, first, last);
max = ClampToRange(max, first, last);
float xMin = w * (min - first) / range;
float xLowerQuartile = w * (lowerQuartile - first) / range;
float xMedian = w * (median - first) / range;
float xUpperQuartile = w * (upperQuartile - first) / range;
float xMax = w * (max - first) / range;
// Min to max line
m_2D.DrawLine(x + xMin, y + (h / 2), x + xMax, y + (h / 2), color);
// Quartile boxes
float yMargin = (2 * h / 8);
float y1 = y + yMargin;
float y2 = y + (h - yMargin);
float hBox = y2 - y1;
if (colorFilled != color)
m_2D.DrawFilledBox(x + xLowerQuartile, y1, xMedian - xLowerQuartile, hBox, colorFilled);
m_2D.DrawBox(x + xLowerQuartile, y1, xMedian - xLowerQuartile, hBox, color);
if (colorFilled != color)
m_2D.DrawFilledBox(x + xMedian, y1, xUpperQuartile - xMedian, hBox, colorFilled);
m_2D.DrawBox(x + xMedian, y1, xUpperQuartile - xMedian, hBox, color);
// Median line
//yMargin = (1 * h / 8);
//y1 = y + yMargin;
//y2 = y + (h - yMargin);
m_2D.DrawLine(x + xMedian, y1, x + xMedian, y2, color);
m_2D.DrawLine(x + xMedian + 1, y1, x + xMedian + 1, y2, color);
// Line caps
yMargin = (3 * h / 8);
y1 = y + yMargin;
y2 = y + (h - yMargin);
if (startCap)
m_2D.DrawLine(x + xMin, y1, x + xMin, y2, color);
if (endCap)
m_2D.DrawLine(x + xMax, y1, x + xMax, y2, color);
}
public void DrawText(float width, float plotHeight, float min, float max, string minTooltip, string maxTooltip)
{
GUIStyle shiftUpStyle = new GUIStyle(GUI.skin.label);
shiftUpStyle.contentOffset = new Vector2(0, -5);
shiftUpStyle.alignment = TextAnchor.UpperLeft;
EditorGUILayout.BeginVertical(GUILayout.Height(plotHeight));
EditorGUILayout.LabelField(new GUIContent(ToDisplayUnits(max), maxTooltip), shiftUpStyle, GUILayout.Width(width));
GUILayout.FlexibleSpace();
GUIStyle shiftDownStyle = new GUIStyle(GUI.skin.label);
shiftDownStyle.contentOffset = new Vector2(0, 1);
shiftDownStyle.alignment = TextAnchor.LowerLeft;
EditorGUILayout.LabelField(new GUIContent(ToDisplayUnits(min), minTooltip), shiftDownStyle, GUILayout.Width(width));
EditorGUILayout.EndVertical();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae5c414d7d406467d8ce01807c211f90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,184 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
namespace UnityEditor.Performance.ProfileAnalyzer
{
internal class Columns
{
int[] m_ColumnWidth = new int[4];
public Columns(int a, int b, int c, int d)
{
SetColumnSizes(a, b, c, d);
}
public void SetColumnSizes(int a, int b, int c, int d)
{
m_ColumnWidth[0] = a;
m_ColumnWidth[1] = b;
m_ColumnWidth[2] = c;
m_ColumnWidth[3] = d;
}
public int GetColumnWidth(int n)
{
if (n < 0 || n >= m_ColumnWidth.Length)
return 0;
return m_ColumnWidth[n];
}
public void Draw(int n, string col)
{
if (n < 0 || n >= m_ColumnWidth.Length || m_ColumnWidth[n] <= 0)
EditorGUILayout.LabelField(col);
EditorGUILayout.LabelField(col, GUILayout.Width(m_ColumnWidth[n]));
}
public void Draw(int n, float value)
{
Draw(n, string.Format("{0:f2}", value));
}
public void Draw2(string col1, string col2)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, col2);
EditorGUILayout.EndHorizontal();
}
public void Draw2(string label, float value)
{
EditorGUILayout.BeginHorizontal();
Draw(0, label);
Draw(1, value);
EditorGUILayout.EndHorizontal();
}
public void Draw3(string col1, string col2, string col3)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, col2);
Draw(2, col3);
EditorGUILayout.EndHorizontal();
}
public void Draw3(string col1, float value2, float value3)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, value2);
Draw(2, value3);
EditorGUILayout.EndHorizontal();
}
public void Draw4(string col1, string col2, string col3, string col4)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, col2);
Draw(2, col3);
Draw(3, col4);
EditorGUILayout.EndHorizontal();
}
public void Draw4Diff(string col1, float left, float right)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, left);
Draw(2, right);
Draw(3, right - left);
EditorGUILayout.EndHorizontal();
}
public void Draw4(string col1, float value2, float value3, float value4)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, value2);
Draw(2, value3);
Draw(3, value4);
EditorGUILayout.EndHorizontal();
}
// GUIContent versions
public void Draw(int n, GUIContent col)
{
if (n < 0 || n >= m_ColumnWidth.Length || m_ColumnWidth[n] <= 0)
EditorGUILayout.LabelField(col);
EditorGUILayout.LabelField(col, GUILayout.Width(m_ColumnWidth[n]));
}
public void Draw2(GUIContent col1, GUIContent col2)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, col2);
EditorGUILayout.EndHorizontal();
}
public void Draw2(GUIContent label, float value)
{
EditorGUILayout.BeginHorizontal();
Draw(0, label);
Draw(1, value);
EditorGUILayout.EndHorizontal();
}
public void Draw3(GUIContent col1, GUIContent col2, GUIContent col3)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, col2);
Draw(2, col3);
EditorGUILayout.EndHorizontal();
}
public void Draw3(GUIContent col1, float value2, float value3)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, value2);
Draw(2, value3);
EditorGUILayout.EndHorizontal();
}
public void Draw4(GUIContent col1, GUIContent col2, GUIContent col3, GUIContent col4)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, col2);
Draw(2, col3);
Draw(3, col4);
EditorGUILayout.EndHorizontal();
}
public void Draw4Diff(GUIContent col1, float left, float right)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, left);
Draw(2, right);
Draw(3, right - left);
EditorGUILayout.EndHorizontal();
}
public void Draw4(GUIContent col1, float value2, float value3, float value4)
{
EditorGUILayout.BeginHorizontal();
Draw(0, col1);
Draw(1, value2);
Draw(2, value3);
Draw(3, value4);
EditorGUILayout.EndHorizontal();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a402cbe9b84984bd4a16cb20ca9c3bed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 281fa44557d2f417aafc937bd5d93ba5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
using System;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor.IMGUI.Controls;
namespace UnityEditor.Performance.ProfileAnalyzer
{
class DepthSliceDropdown : AdvancedDropdown
{
class DepthSliceDropdownItem : AdvancedDropdownItem
{
public int depthSlice;
public int depthSliceLeft;
public int depthSliceRight;
public DepthSliceDropdownItem(int depthSlice)
: base(DepthSliceUI.DepthFilterToString(depthSlice))
{
this.depthSlice = depthSlice;
depthSliceLeft = depthSlice;
depthSliceRight = depthSlice;
}
public DepthSliceDropdownItem(int depthSliceLeft, int depthSliceRight, bool leftIsMain)
: base(DepthSliceUI.DepthFilterToString(depthSliceLeft, depthSliceRight, leftIsMain))
{
depthSlice = Math.Max(depthSliceLeft, depthSliceRight);
this.depthSliceLeft = depthSliceLeft;
this.depthSliceRight = depthSliceRight;
}
}
Action<int, int, int> m_Callback = null;
int m_DepthSliceCount;
int m_DepthSliceCountRight;
int m_CurrentDepthSliceA;
int m_CurrentDepthSliceB;
int m_DepthDiff;
static FieldInfo m_DataSourceFieldInfo;
static Type m_DataSourceTypeInfo;
static PropertyInfo m_SelectedIdsFieldInfo;
public DepthSliceDropdown(int depthSliceCount, int currentDepthSliceA, int currentDepthSliceB, Action<int, int, int> callback, int depthDiff, int depthSliceCountRight = ProfileAnalyzer.kDepthAll) : base(new AdvancedDropdownState())
{
m_DepthSliceCount = depthSliceCount;
m_DepthSliceCountRight = depthSliceCountRight;
m_CurrentDepthSliceA = currentDepthSliceA;
m_CurrentDepthSliceB = currentDepthSliceB;
m_Callback = callback;
m_DepthDiff = depthDiff;
if (m_DataSourceFieldInfo == null || m_DataSourceFieldInfo == null || m_SelectedIdsFieldInfo == null)
{
Assembly assem = typeof(AdvancedDropdown).Assembly;
var advancedDropdownTypeInfo = typeof(AdvancedDropdown);
m_DataSourceTypeInfo = assem.GetType("UnityEditor.IMGUI.Controls.CallbackDataSource");
m_DataSourceFieldInfo = advancedDropdownTypeInfo.GetField("m_DataSource", BindingFlags.NonPublic | BindingFlags.Instance);
m_SelectedIdsFieldInfo = m_DataSourceTypeInfo.GetProperty("selectedIDs", BindingFlags.Public | BindingFlags.Instance);
}
}
protected override AdvancedDropdownItem BuildRoot()
{
var root = new AdvancedDropdownItem("Depth Slice");
var allItem = new DepthSliceDropdownItem(ProfileAnalyzer.kDepthAll);
root.AddChild(allItem);
if (m_CurrentDepthSliceA == ProfileAnalyzer.kDepthAll && m_CurrentDepthSliceB == ProfileAnalyzer.kDepthAll)
(m_SelectedIdsFieldInfo.GetValue(m_DataSourceFieldInfo.GetValue(this)) as List<int>).Add(allItem.id);
var count = m_DepthSliceCountRight == ProfileAnalyzer.kDepthAll ? m_DepthSliceCount :
Math.Max(m_DepthSliceCount + Math.Max(0, m_DepthDiff), m_DepthSliceCountRight - Math.Min(0, m_DepthDiff));
var leftIsMain = m_DepthDiff < 0;
var mainThreshold = leftIsMain ? m_DepthSliceCount : m_DepthSliceCountRight;
var secondaryMinThreshold = Math.Abs(m_DepthDiff);
var secondaryMaxThreshold = (leftIsMain ? m_DepthSliceCountRight : m_DepthSliceCount) + secondaryMinThreshold;
var startIndex = 1;
for (int i = startIndex; i <= count; i++)
{
var selected = false;
AdvancedDropdownItem child;
if (m_DepthSliceCountRight != ProfileAnalyzer.kDepthAll)
{
var left = Mathf.Clamp(i - Math.Max(0, m_DepthDiff), 1, m_DepthSliceCount);
var right = Mathf.Clamp(i - Math.Max(0, -m_DepthDiff), 1, m_DepthSliceCountRight);
if (m_DepthSliceCount <= 0)
left = -1;
else if (m_DepthSliceCountRight <= 0)
right = -1;
else
{
// Separators only make sense if there is data on both sides
// did we pass the threshold of the main's max depth and started clamping it down?
if (i == mainThreshold + 1
// ... or the threshold of the secondary's negative depth when adjusted for the depth diff, and stoped clamping it up?
|| (secondaryMinThreshold != 0 && i == secondaryMinThreshold + 1)
// ... or the threshold of the secondary's max depth when adjusted for the depth diff, and started clamping it down?
|| (i == secondaryMaxThreshold + 1))
root.AddSeparator();
}
child = new DepthSliceDropdownItem(left, right, leftIsMain);
selected = m_CurrentDepthSliceA == left && m_CurrentDepthSliceB == right;
}
else
{
child = new DepthSliceDropdownItem(i);
selected = m_CurrentDepthSliceA == i;
}
root.AddChild(child);
if (selected)
(m_SelectedIdsFieldInfo.GetValue(m_DataSourceFieldInfo.GetValue(this)) as List<int>).Add(child.id);
}
return root;
}
protected override void ItemSelected(AdvancedDropdownItem item)
{
base.ItemSelected(item);
if (m_Callback != null)
{
var sliceItem = (item as DepthSliceDropdownItem);
m_Callback(sliceItem.depthSlice, sliceItem.depthSliceLeft, sliceItem.depthSliceRight);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 71b9e5c19de243c386bd048443d1c5cc
timeCreated: 1608205585

View File

@@ -0,0 +1,460 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Video;
namespace UnityEditor.Performance.ProfileAnalyzer
{
[Serializable]
public class DepthSliceUI
{
[SerializeField] int m_DepthFilter = ProfileAnalyzer.kDepthAll;
public int depthFilter {get { return m_DepthFilter; }}
[SerializeField] int m_DepthFilter1 = ProfileAnalyzer.kDepthAll;
public int depthFilter1 {get { return m_DepthFilter1; }}
[SerializeField] int m_DepthFilter2 = ProfileAnalyzer.kDepthAll;
public int depthFilter2 {get { return m_DepthFilter2; }}
[SerializeField] bool m_DepthFilterAuto = true;
[SerializeField] int m_MostCommonDepthDiff = 0;
int mostCommonDepthDiff
{
set
{
if (m_MostCommonDepthDiff != value)
{
m_MostCommonDepthDiff = value;
UpdateAutoDepthTitleText();
}
}
get
{
return m_MostCommonDepthDiff;
}
}
void UpdateAutoDepthTitleText()
{
ProfileAnalyzerWindow.Styles.autoDepthTitle.text =
string.Format(ProfileAnalyzerWindow.Styles.autoDepthTitleText, mostCommonDepthDiff);
}
Action<bool> m_UpdateActiveTabCallback = null;
public DepthSliceUI(Action<bool> updateActiveTabCallback)
{
m_UpdateActiveTabCallback = updateActiveTabCallback;
UpdateAutoDepthTitleText();
}
public void OnEnable(Action<bool> updateActiveTabCallback)
{
m_UpdateActiveTabCallback = updateActiveTabCallback;
UpdateAutoDepthTitleText();
}
enum ViewType
{
Single,
Left,
Right,
Locked,
}
void DrawDepthFilterDropdown(GUIContent title, bool enabled, ProfileDataView view, Action<int, int, int> callback,
ViewType viewType, ProfileDataView profileSingleView, ProfileDataView profileLeftView, ProfileDataView profileRightView)
{
if(title !=null)
EditorGUILayout.LabelField(title, GUILayout.Width(ProfileAnalyzerWindow.LayoutSize.FilterOptionsEnumWidth));
int depthFilter = ProfileAnalyzer.kDepthAll;
int depthFilterOther = ProfileAnalyzer.kDepthAll;
var maxDepth = view.GetMaxDepth();
var maxDepthLeft = ProfileAnalyzer.kDepthAll;
var maxDepthRight = ProfileAnalyzer.kDepthAll;
var oldDepthFilter = ProfileAnalyzer.kDepthAll;
var oldDepthFilterOtherLocked = ProfileAnalyzer.kDepthAll;
var depthDiff = mostCommonDepthDiff;
GUIContent content;
switch (viewType)
{
case ViewType.Single:
oldDepthFilter = m_DepthFilter;
depthFilter = m_DepthFilter =
m_DepthFilter == ProfileAnalyzer.kDepthAll ?
ProfileAnalyzer.kDepthAll :
profileSingleView.ClampToValidDepthValue(m_DepthFilter);
content = new GUIContent(DepthFilterToString(depthFilter));
depthFilterOther = depthFilter;
depthDiff = 0;
break;
case ViewType.Left:
oldDepthFilter = m_DepthFilter1;
depthFilter = m_DepthFilter1 =
m_DepthFilter1 == ProfileAnalyzer.kDepthAll ?
ProfileAnalyzer.kDepthAll :
profileLeftView.ClampToValidDepthValue(m_DepthFilter1);
content = new GUIContent(DepthFilterToString(depthFilter));
depthFilterOther = depthFilter;
break;
case ViewType.Right:
oldDepthFilter = m_DepthFilter2;
depthFilter = m_DepthFilter2 = m_DepthFilter2 == ProfileAnalyzer.kDepthAll
? ProfileAnalyzer.kDepthAll
: profileRightView.ClampToValidDepthValue(m_DepthFilter2);
content = new GUIContent(DepthFilterToString(depthFilter));
depthFilterOther = depthFilter;
break;
case ViewType.Locked:
oldDepthFilter = m_DepthFilter1;
oldDepthFilterOtherLocked = m_DepthFilter2;
maxDepth = maxDepthLeft = profileLeftView.GetMaxDepth();
maxDepthRight = profileRightView.GetMaxDepth();
ClampDepthFilterForAutoRespectingDiff(ref m_DepthFilter1, ref m_DepthFilter2, profileLeftView, profileRightView);
depthFilter = m_DepthFilter1;
depthFilterOther = m_DepthFilter2;
content = new GUIContent(DepthFilterToString(m_DepthFilter1, m_DepthFilter2, mostCommonDepthDiff < 0));
break;
default:
throw new NotImplementedException();
}
var lastEnabled = GUI.enabled;
GUI.enabled = enabled;
var rect = GUILayoutUtility.GetRect(content, EditorStyles.popup, GUILayout.MinWidth(ProfileAnalyzerWindow.LayoutSize.FilterOptionsEnumWidth));
if (GUI.Button(rect, content, EditorStyles.popup))
{
var dropdown = new DepthSliceDropdown(maxDepth, depthFilter, depthFilterOther, (slice, left, right) =>
{
if (slice != depthFilter || (viewType == ViewType.Locked && (left != m_DepthFilter1 || right != m_DepthFilter2)))
{
callback(slice, left, right);
UpdateDepthFilters(viewType == ViewType.Single, profileSingleView, profileLeftView, profileRightView);
m_UpdateActiveTabCallback(true);
}
}, depthDiff, maxDepthRight);
dropdown.Show(rect);
EditorGUIUtility.ExitGUI();
}
else
{
// The depths can change because the data changed, not just because the user selected a different option in the dropdown
// in that case, the depth filters need to perform a refresh
if(oldDepthFilter != depthFilter || viewType == ViewType.Locked && oldDepthFilterOtherLocked != depthFilterOther)
{
UpdateDepthFilters(viewType == ViewType.Single, profileSingleView, profileLeftView, profileRightView);
m_UpdateActiveTabCallback(true);
}
}
GUI.enabled = lastEnabled;
}
int CalcSliceMenuEntryIndex(int filterDepthLeft, int filterDepthRight, int leftMax, int rightMax)
{
return mostCommonDepthDiff > 0 ?
filterDepthRight + Math.Max(0, filterDepthLeft - rightMax + (rightMax > 0 ? mostCommonDepthDiff : filterDepthLeft > 0 ? 1 : 0)) :
filterDepthLeft + Math.Max(0, filterDepthRight - leftMax - (leftMax > 0 ? mostCommonDepthDiff : filterDepthRight > 0 ? -1 :0));
}
void CalcAutoSlicesFromMenuEntryIndex(int depthSlcieMenuEntryIndex, ref int filterDepthLeft, ref int filterDepthRight, int leftMax, int rightMax)
{
if (mostCommonDepthDiff > 0)
{
filterDepthRight = Mathf.Clamp(depthSlcieMenuEntryIndex, 1, rightMax);
filterDepthLeft = Mathf.Clamp(depthSlcieMenuEntryIndex - (rightMax > 0 ? mostCommonDepthDiff : 0), 1, leftMax);
}
else
{
filterDepthLeft = Mathf.Clamp(depthSlcieMenuEntryIndex, 1, leftMax);
filterDepthRight = Mathf.Clamp(depthSlcieMenuEntryIndex + (leftMax > 0 ? mostCommonDepthDiff : 0), 1, rightMax);
}
// if a side has no depth, only allow All
if (leftMax <= 0)
filterDepthLeft = -1;
if (rightMax <= 0)
filterDepthRight = -1;
}
void ClampDepthFilterForAutoRespectingDiff(ref int filterDepthLeft, ref int filterDepthRight, ProfileDataView profileLeftView, ProfileDataView profileRightView)
{
if (filterDepthLeft == ProfileAnalyzer.kDepthAll && filterDepthRight == ProfileAnalyzer.kDepthAll)
{
// nothing to do here, keep showing all
return;
}
var leftMax = profileLeftView.GetMaxDepth();
var rightMax = profileRightView.GetMaxDepth();
var sliceMenuEntryIndex = CalcSliceMenuEntryIndex(filterDepthLeft, filterDepthRight, leftMax, rightMax);
CalcAutoSlicesFromMenuEntryIndex(sliceMenuEntryIndex, ref filterDepthLeft, ref filterDepthRight, leftMax, rightMax);
}
internal void DrawDepthFilter(bool isAnalysisRunning, bool singleView,
ProfileDataView profileSingleView, ProfileDataView profileLeftView, ProfileDataView profileRightView)
{
bool lastEnabled = GUI.enabled;
bool enabled = !isAnalysisRunning;
EditorGUILayout.BeginHorizontal();
if (singleView)
{
EditorGUILayout.LabelField(ProfileAnalyzerWindow.Styles.depthTitle, GUILayout.Width(ProfileAnalyzerWindow.LayoutSize.FilterOptionsLeftLabelWidth));
DrawDepthFilterDropdown(null, enabled,
profileSingleView, (primary, left, right) => m_DepthFilter = primary,
ViewType.Single, profileSingleView, profileLeftView, profileRightView);
}
else
{
EditorGUILayout.LabelField(ProfileAnalyzerWindow.Styles.depthTitle, GUILayout.Width(ProfileAnalyzerWindow.LayoutSize.FilterOptionsLeftLabelWidth));
if (m_DepthFilterAuto)
{
DrawDepthFilterDropdown(null, enabled, profileLeftView, (primary, left, right) =>
{
m_DepthFilter1 = left;
m_DepthFilter2 = right;
ClampDepthFilterForAutoRespectingDiff(ref m_DepthFilter1, ref m_DepthFilter2, profileLeftView, profileRightView);
},
ViewType.Locked, profileSingleView, profileLeftView, profileRightView);
}
else
{
DrawDepthFilterDropdown(ProfileAnalyzerWindow.Styles.leftDepthTitle, enabled, profileLeftView,
(primary, left, right) => m_DepthFilter1 = primary,
ViewType.Left, profileSingleView, profileLeftView, profileRightView);
DrawDepthFilterDropdown(ProfileAnalyzerWindow.Styles.rightDepthTitle, enabled && !m_DepthFilterAuto, profileRightView,
(primary, left, right) => m_DepthFilter2 = primary,
ViewType.Right, profileSingleView, profileLeftView, profileRightView);
}
bool lastDepthFilterLock = m_DepthFilterAuto;
GUI.enabled = enabled;
m_DepthFilterAuto = EditorGUILayout.ToggleLeft(ProfileAnalyzerWindow.Styles.autoDepthTitle, m_DepthFilterAuto);
GUI.enabled = lastEnabled;
if (m_DepthFilterAuto != lastDepthFilterLock)
{
if (UpdateDepthFilters(singleView, profileSingleView, profileLeftView, profileRightView))
m_UpdateActiveTabCallback(true);
}
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
internal bool UpdateDepthFilters(bool singleView, ProfileDataView profileSingleView, ProfileDataView profileLeftView, ProfileDataView profileRightView)
{
bool changed = false;
if (!singleView)
{
// First respect the auto flag
if (UpdateAutoDepthFilter(profileLeftView, profileRightView))
changed = true;
// Make sure Single matches the updated comparison view
if (profileLeftView.path == profileSingleView.path)
{
// Use same filter on single view if its the same file
if (m_DepthFilter != m_DepthFilter1)
{
m_DepthFilter = m_DepthFilter1;
changed = true;
}
}
if (profileRightView.path == profileSingleView.path)
{
// Use same filter on single view if its the same file
if (m_DepthFilter != m_DepthFilter2)
{
m_DepthFilter = m_DepthFilter2;
changed = true;
}
}
}
else
{
// Make sure comparisons match updated single view
if (profileLeftView.path == profileSingleView.path)
{
// Use same filter on comparison left view if its the same file
if (m_DepthFilter1 != m_DepthFilter)
{
m_DepthFilter1 = m_DepthFilter;
changed = true;
}
if (m_DepthFilterAuto)
{
var newDepthFilter2 = m_DepthFilter;
ClampDepthFilterForAutoRespectingDiff(ref m_DepthFilter1, ref newDepthFilter2, profileLeftView, profileRightView);
if (m_DepthFilter2 != newDepthFilter2)
{
m_DepthFilter2 = newDepthFilter2;
changed = true;
}
if (UpdateAutoDepthFilter(profileLeftView, profileRightView))
changed = true;
}
if (UpdateAutoDepthFilter(profileLeftView, profileRightView))
changed = true;
}
if (profileRightView.path == profileSingleView.path)
{
// Use same filter on comparison right view if its the same file
if (m_DepthFilter2 != m_DepthFilter)
{
m_DepthFilter2 = m_DepthFilter;
changed = true;
}
if (m_DepthFilterAuto)
{
var newDepthFilter1 = m_DepthFilter;
ClampDepthFilterForAutoRespectingDiff(ref newDepthFilter1, ref m_DepthFilter2, profileLeftView, profileRightView);
if (m_DepthFilter1 != newDepthFilter1)
{
m_DepthFilter1 = newDepthFilter1;
changed = true;
}
if (UpdateAutoDepthFilter(profileLeftView, profileRightView))
changed = true;
}
}
}
return changed;
}
int CalculateDepthDifference(ProfileAnalysis leftAnalysis, ProfileAnalysis rightAnalysis, List<MarkerPairing> pairings)
{
if (pairings.Count <= 0)
{
mostCommonDepthDiff = 0;
return 0;
}
var leftMarkers = leftAnalysis.GetMarkers();
var rightMarkers = rightAnalysis.GetMarkers();
int totalCount = 0;
Dictionary<int, int> depthDifferences = new Dictionary<int, int>();
foreach (var pairing in pairings)
{
if (pairing.leftIndex >= 0 && pairing.rightIndex >= 0)
{
MarkerData leftMarker = leftMarkers[pairing.leftIndex];
MarkerData rightMarker = rightMarkers[pairing.rightIndex];
int markerDepthDiff = rightMarker.minDepth - leftMarker.minDepth;
int value = 0;
depthDifferences.TryGetValue(markerDepthDiff, out value);
depthDifferences[markerDepthDiff] = value + 1;
totalCount += 1;
}
}
var newDepthDiff = 0;
// Find most common depth difference
int maxCount = 0;
foreach (var diff in depthDifferences.Keys)
{
if (depthDifferences[diff] > maxCount)
{
maxCount = depthDifferences[diff];
newDepthDiff = diff;
}
}
return mostCommonDepthDiff = newDepthDiff;
}
bool UpdateAutoDepthFilter(ProfileDataView profileLeftView, ProfileDataView profileRightView)
{
if (m_DepthFilterAuto)
{
var newDepthFilter1 = m_DepthFilter1;
var newDepthFilter2 = m_DepthFilter2;
ClampDepthFilterForAutoRespectingDiff(ref newDepthFilter1, ref newDepthFilter2, profileLeftView, profileRightView);
if (m_DepthFilter1 != newDepthFilter1)
{
m_DepthFilter1 = newDepthFilter1;
return true;
}
if (m_DepthFilter2 != newDepthFilter2)
{
m_DepthFilter2 = newDepthFilter2;
return true;
}
}
return false;
}
internal bool UpdateDepthForCompareSync(ProfileAnalysis leftAnalysis, ProfileAnalysis rightAnalysis, List<MarkerPairing> pairings, ProfileDataView profileLeftView, ProfileDataView profileRightView)
{
int originalDepthDiff = mostCommonDepthDiff;
int newDepthDiff = CalculateDepthDifference(leftAnalysis, rightAnalysis, pairings);
if (newDepthDiff != originalDepthDiff)
{
UpdateAutoDepthFilter(profileLeftView, profileRightView);
return true;
}
return false;
}
internal GUIContent GetUIInfo(bool compare)
{
GUIContent info;
if (compare && m_DepthFilter1 == ProfileAnalyzer.kDepthAll && m_DepthFilter2 == ProfileAnalyzer.kDepthAll ||
!compare && depthFilter == ProfileAnalyzer.kDepthAll)
{
info = new GUIContent("(All depths)", string.Format("{0}\n\nSet depth 1 to get an overview of the frame", ProfileAnalyzerWindow.Styles.medianFrameTooltip));
}
else
{
if (compare && depthFilter1 != depthFilter2)
{
if (m_DepthFilter1 == ProfileAnalyzer.kDepthAll)
info = new GUIContent(string.Format("(Filtered to 'all' depths in the first data set, and depth '{0}' in the second)", m_DepthFilter2), ProfileAnalyzerWindow.Styles.medianFrameTooltip);
else if (m_DepthFilter2 == ProfileAnalyzer.kDepthAll)
info = new GUIContent(string.Format("(Filtered to depth '{0}' in the first data set, and 'all' depths in the second)", m_DepthFilter1), ProfileAnalyzerWindow.Styles.medianFrameTooltip);
else
info = new GUIContent(string.Format("(Filtered to depth '{0}' in the first data set, and depth '{1}' in the second)", m_DepthFilter1, depthFilter2), ProfileAnalyzerWindow.Styles.medianFrameTooltip);
}
else
info = new GUIContent(string.Format("(Filtered to depth '{0}' only)", compare ? m_DepthFilter1 : depthFilter), ProfileAnalyzerWindow.Styles.medianFrameTooltip);
}
return info;
}
public static string DepthFilterToString(int depthFilter)
{
return depthFilter == ProfileAnalyzer.kDepthAll ? "All" : depthFilter.ToString();
}
public static string DepthFilterToString(int depthSliceLeft, int depthSliceRight, bool leftIsMain)
{
if(depthSliceLeft != depthSliceRight)
{
if (leftIsMain)
return string.Format("{0} ({1}{2})", DepthFilterToString(depthSliceLeft), ProfileAnalyzerWindow.Styles.rightDepthTitle.text, DepthFilterToString(depthSliceRight));
else
return string.Format("{0} ({1}{2})", DepthFilterToString(depthSliceRight), ProfileAnalyzerWindow.Styles.leftDepthTitle.text, DepthFilterToString(depthSliceLeft));
}
return DepthFilterToString(depthSliceLeft);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6d4bf3d974bf4f74a15e72b2e0a8ffa2
timeCreated: 1608212438

View File

@@ -0,0 +1,210 @@
using UnityEngine;
namespace UnityEditor.Performance.ProfileAnalyzer
{
internal class Draw2D
{
public enum Origin
{
TopLeft,
BottomLeft
};
Origin m_Origin = Origin.TopLeft;
GUIStyle m_GLStyle;
string m_ShaderName;
Material m_Material;
Rect m_Rect;
Vector4 m_ClipRect;
bool m_ClipRectEnabled = false;
public Draw2D(string shaderName)
{
m_ShaderName = shaderName;
CheckAndSetupMaterial();
}
public bool CheckAndSetupMaterial()
{
if (m_Material == null)
{
var shader = Shader.Find(m_ShaderName);
if (shader == null)
{
Debug.LogFormat("Unable to locate shader {0}", m_ShaderName);
return false;
}
m_Material = new Material(shader);
if (m_Material == null)
{
Debug.LogFormat("Unable to create material for {0}", m_ShaderName);
return false;
}
}
return true;
}
public bool IsMaterialValid()
{
if (m_Material == null)
return false;
return true;
}
public void OnGUI()
{
if (m_GLStyle == null)
{
m_GLStyle = new GUIStyle(GUI.skin.box);
m_GLStyle.padding = new RectOffset(0, 0, 0, 0);
m_GLStyle.margin = new RectOffset(0, 0, 0, 0);
}
}
public void SetClipRect(Rect clipRect)
{
m_ClipRect = new Vector4(clipRect.x, clipRect.y, clipRect.x + clipRect.width, clipRect.y + clipRect.height);
m_ClipRectEnabled = true;
if (CheckAndSetupMaterial())
{
m_Material.SetFloat("_UseClipRect", m_ClipRectEnabled ? 1f : 0f);
m_Material.SetVector("_ClipRect", m_ClipRect);
}
}
public void ClearClipRect()
{
m_ClipRectEnabled = false;
if (CheckAndSetupMaterial())
{
m_Material.SetFloat("_UseClipRect", m_ClipRectEnabled ? 1f : 0f);
m_Material.SetVector("_ClipRect", m_ClipRect);
}
}
public Rect GetClipRect()
{
return new Rect(m_ClipRect.x, m_ClipRect.y, m_ClipRect.z - m_ClipRect.x, m_ClipRect.w - m_ClipRect.y);
}
public bool DrawStart(Rect r, Origin origin = Origin.TopLeft)
{
if (Event.current.type != EventType.Repaint)
return false;
if (!CheckAndSetupMaterial())
return false;
m_Material.SetPass(0);
m_Rect = r;
m_Origin = origin;
return true;
}
public bool DrawStart(float w, float h, Origin origin = Origin.TopLeft, GUIStyle style = null)
{
Rect r = GUILayoutUtility.GetRect(w, h, style == null ? m_GLStyle : style, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(false));
return DrawStart(r, origin);
}
public void DrawEnd()
{
}
public void Translate(ref float x, ref float y)
{
// Translation done CPU side so we have world space coords in the shader for clipping.
if (m_Origin == Origin.BottomLeft)
{
x = m_Rect.xMin + x;
y = m_Rect.yMax - y;
}
else
{
x = m_Rect.xMin + x;
y = m_Rect.yMin + y;
}
}
public void DrawFilledBox(float x, float y, float w, float h, Color col)
{
float x2 = x + w;
float y2 = y + h;
Translate(ref x, ref y);
Translate(ref x2, ref y2);
if (m_Origin == Origin.BottomLeft)
{
GL.Begin(GL.TRIANGLE_STRIP);
GL.Color(col);
GL.Vertex3(x, y, 0);
GL.Vertex3(x, y2, 0);
GL.Vertex3(x2, y, 0);
GL.Vertex3(x2, y2, 0);
GL.End();
}
else
{
GL.Begin(GL.TRIANGLE_STRIP);
GL.Color(col);
GL.Vertex3(x, y, 0);
GL.Vertex3(x2, y, 0);
GL.Vertex3(x, y2, 0);
GL.Vertex3(x2, y2, 0);
GL.End();
}
}
public void DrawFilledBox(float x, float y, float w, float h, float r, float g, float b)
{
DrawFilledBox(x, y, w, h, new Color(r, g, b));
}
public void DrawLine(float x, float y, float x2, float y2, Color col)
{
Translate(ref x, ref y);
Translate(ref x2, ref y2);
GL.Begin(GL.LINES);
GL.Color(col);
GL.Vertex3(x, y, 0);
GL.Vertex3(x2, y2, 0);
GL.End();
}
public void DrawLine(float x, float y, float x2, float y2, float r, float g, float b)
{
DrawLine(x, y, x2, y2, new Color(r, g, b));
}
public void DrawBox(float x, float y, float w, float h, Color col)
{
float x2 = x + w;
float y2 = y + h;
Translate(ref x, ref y);
Translate(ref x2, ref y2);
GL.Begin(GL.LINE_STRIP);
GL.Color(col);
GL.Vertex3(x, y, 0);
GL.Vertex3(x2, y, 0);
GL.Vertex3(x2, y2, 0);
GL.Vertex3(x, y2, 0);
GL.Vertex3(x, y, 0);
GL.End();
}
public void DrawBox(float x, float y, float w, float h, float r, float g, float b)
{
DrawBox(x, y, w, h, new Color(r, g, b));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c27a543a692ef4bf19459f25898a0ba9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
namespace UnityEditor.Performance.ProfileAnalyzer
{
[Serializable]
internal class FrameSummary
{
public double msTotal;
public int first;
public int last;
public int count; // Valid frame count may not be last-first
public float msMean;
public float msMedian;
public float msLowerQuartile;
public float msUpperQuartile;
public float msMin;
public float msMax;
public int medianFrameIndex;
public int minFrameIndex;
public int maxFrameIndex;
public int maxMarkerDepth;
public int totalMarkers;
public int markerCountMax; // Largest marker count (over all frames)
public float markerCountMaxMean; // Largest marker count mean
public int[] buckets = new int[20]; // Each bucket contains 'number of frames' for frametime in that range
public List<FrameTime> frames = new List<FrameTime>();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af62f7ba5c15e47ceb426c7745e31835
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,112 @@
using System;
namespace UnityEditor.Performance.ProfileAnalyzer
{
[Serializable]
/// <summary>
/// Metrics related to an individual frame
/// </summary>
internal struct FrameTime : IComparable<FrameTime>
{
/// <summary>Duration in the frame in milliseconds</summary>
public float ms;
/// <summary>Index of which frame this time duration occured on. A zero based frame index</summary>
public int frameIndex;
/// <summary>Number of occurrences</summary>
public int count;
/// <summary>Initialise FrameTime</summary>
/// <param name="index"> The frame index</param>
/// <param name="msTime"> The duration of the frame in milliseconds</param>
/// <param name="_count"> The number of occurrences</param>
public FrameTime(int index, float msTime, int _count)
{
frameIndex = index;
ms = msTime;
count = _count;
}
/// <summary>Initialise from another FrameTime</summary>
/// <param name="t"> The FrameTime to assign</param>
public FrameTime(FrameTime t)
{
frameIndex = t.frameIndex;
ms = t.ms;
count = t.count;
}
/// <summary>Compare the time duration between the frames. Used for sorting in ascending order</summary>
/// <param name="other"> The other FrameTime to compare </param>
/// <returns>-1 if this is smaller, 0 if the same, 1 if this is larger</returns>
public int CompareTo(FrameTime other)
{
if (ms == other.ms)
{
// secondary sort by frame index order
return frameIndex.CompareTo(other.frameIndex);
}
return ms.CompareTo(other.ms);
}
/// <summary>Compare the time duration between two FrameTimes. Used for sorting in ascending order</summary>
/// <param name="a"> The first FrameTime to compare </param>
/// <param name="b"> The second FrameTime to compare </param>
/// <returns>-1 if a is smaller, 0 if the same, 1 if a is larger</returns>
public static int CompareMs(FrameTime a, FrameTime b)
{
if (a.ms == b.ms)
{
// secondary sort by frame index order
return a.frameIndex.CompareTo(b.frameIndex);
}
return a.ms.CompareTo(b.ms);
}
/// <summary>Compare the instance count between two FrameTimes. Used for sorting in ascending order</summary>
/// <param name="a"> The first FrameTime to compare </param>
/// <param name="b"> The second FrameTime to compare </param>
/// <returns>-1 if a is smaller, 0 if the same, 1 if a is larger</returns>
public static int CompareCount(FrameTime a, FrameTime b)
{
if (a.count == b.count)
{
// secondary sort by frame index order
return a.frameIndex.CompareTo(b.frameIndex);
}
return a.count.CompareTo(b.count);
}
/// <summary>Compare the time duration between two FrameTimes. Used for sorting in descending order</summary>
/// <param name="a"> The first FrameTime to compare </param>
/// <param name="b"> The second FrameTime to compare </param>
/// <returns>-1 if a is larger, 0 if the same, 1 if a is smaller</returns>
public static int CompareMsDescending(FrameTime a, FrameTime b)
{
if (a.ms == b.ms)
{
// secondary sort by frame index order
return a.frameIndex.CompareTo(b.frameIndex);
}
return -a.ms.CompareTo(b.ms);
}
/// <summary>Compare the instance count between two FrameTimes. Used for sorting in descending order</summary>
/// <param name="a"> The first FrameTime to compare </param>
/// <param name="b"> The second FrameTime to compare </param>
/// <returns>-1 if a is larger, 0 if the same, 1 if a is smaller</returns>
public static int CompareCountDescending(FrameTime a, FrameTime b)
{
if (a.count == b.count)
{
// secondary sort by frame index order
return a.frameIndex.CompareTo(b.frameIndex);
}
return -a.count.CompareTo(b.count);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11b7852f3babc4a938ba8e18940df7c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 54a4b8c3a2d924a4fb740b9044db9694
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,131 @@
using UnityEngine;
namespace UnityEditor.Performance.ProfileAnalyzer
{
internal class Histogram
{
Draw2D m_2D;
Color m_ColorBarBackground;
DisplayUnits m_Units;
public void SetUnits(Units units)
{
m_Units = new DisplayUnits(units);
}
public Histogram(Draw2D draw2D, Units units)
{
m_2D = draw2D;
SetUnits(units);
m_ColorBarBackground = new Color(0.5f, 0.5f, 0.5f);
}
public Histogram(Draw2D draw2D, Units units, Color barBackground)
{
m_2D = draw2D;
SetUnits(units);
m_ColorBarBackground = barBackground;
}
public void DrawStart(float width)
{
EditorGUILayout.BeginHorizontal(GUILayout.Width(width + 10));
EditorGUILayout.BeginVertical();
}
public void DrawEnd(float width, float min, float max, float spacing)
{
EditorGUILayout.BeginHorizontal();
float halfWidth = width / 2;
GUIStyle leftAlignStyle = new GUIStyle(GUI.skin.label);
leftAlignStyle.contentOffset = new Vector2(-5, 0);
leftAlignStyle.alignment = TextAnchor.MiddleLeft;
GUIStyle rightAlignStyle = new GUIStyle(GUI.skin.label);
rightAlignStyle.contentOffset = new Vector2(-5, 0);
rightAlignStyle.alignment = TextAnchor.MiddleRight;
EditorGUILayout.LabelField(m_Units.ToString(min, false, 5, true), leftAlignStyle, GUILayout.Width(halfWidth));
EditorGUILayout.LabelField(m_Units.ToString(max, false, 5, true), rightAlignStyle, GUILayout.Width(halfWidth));
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
public void DrawBackground(float width, float height, int bucketCount, float min, float max, float spacing)
{
//bucketCount = (range == 0f) ? 1 : bucketCount;
float x = (spacing / 2);
float y = 0;
float w = ((width + spacing) / bucketCount) - spacing;
float h = height;
for (int i = 0; i < bucketCount; i++)
{
m_2D.DrawFilledBox(x, y, w, h, m_ColorBarBackground);
x += w;
x += spacing;
}
}
public void DrawData(float width, float height, int[] buckets, int totalFrameCount, float min, float max, Color barColor, float spacing)
{
float range = max - min;
//int bucketCount = (range == 0f) ? 1 : buckets.Length;
int bucketCount = buckets.Length;
float x = (spacing / 2);
float y = 0;
float w = ((width + spacing) / bucketCount) - spacing;
float h = height;
float bucketWidth = (range / bucketCount);
Rect rect = GUILayoutUtility.GetLastRect();
for (int bucketAt = 0; bucketAt < bucketCount; bucketAt++)
{
var count = buckets[bucketAt];
float barHeight = (h * count) / totalFrameCount;
if (count > 0) // Make sure we always slow a small bar if non zero
barHeight = Mathf.Max(1.0f, barHeight);
m_2D.DrawFilledBox(x, y, w, barHeight, barColor);
float bucketStart = min + (bucketAt * bucketWidth);
float bucketEnd = bucketStart + bucketWidth;
var tooltip = string.Format("{0}-{1}\n{2} {3}\n\nBar width: {4}",
m_Units.ToTooltipString(bucketStart, false),
m_Units.ToTooltipString(bucketEnd, true),
count,
count == 1 ? "frame" : "frames",
m_Units.ToTooltipString(bucketWidth, true)
);
var content = new GUIContent("", tooltip);
GUI.Label(new Rect(rect.x + x, rect.y + y, w, h), content);
x += w;
x += spacing;
}
}
public void Draw(float width, float height, int[] buckets, int totalFrameCount, float min, float max, Color barColor)
{
DrawStart(width);
float spacing = 2;
if (m_2D.DrawStart(width, height, Draw2D.Origin.BottomLeft))
{
DrawBackground(width, height, buckets.Length, min, max, spacing);
DrawData(width, height, buckets, totalFrameCount, min, max, barColor, spacing);
m_2D.DrawEnd();
}
DrawEnd(width, min, max, spacing);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dbcb296fb16194f04af7827f37fb2187
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System;
using UnityEngine.Assertions;
namespace UnityEditor.Performance.ProfileAnalyzer
{
internal class MarkerColumnFilter
{
public enum Mode
{
TimeAndCount,
Time,
Totals,
TimeWithTotals,
CountTotals,
CountPerFrame,
Depth,
Threads,
Custom,
};
public static readonly string[] ModeNames =
{
"Time and Count",
"Time",
"Totals",
"Time With Totals",
"Count Totals",
"Count Per Frame",
"Depths",
"Threads",
"Custom",
};
public static readonly int[] ModeValues = (int[])Enum.GetValues(typeof(Mode));
public Mode mode;
public int[] visibleColumns;
public MarkerColumnFilter(Mode newMode)
{
Assert.AreEqual(ModeNames.Length, ModeValues.Length, "Number of ModeNames should match number of enum values ModeValues: You probably forgot to update one of them.");
mode = newMode;
if (mode == Mode.Custom)
mode = Mode.TimeAndCount;
visibleColumns = null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 814ccc2d0c490cf4b871d13ab2e4b7c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,343 @@
using System;
using System.Collections.Generic;
namespace UnityEditor.Performance.ProfileAnalyzer
{
[Serializable]
internal class MarkerData : IComparable<MarkerData>
{
public string name;
public int nameLowerCaseHash; // lower case name hash for faster comparisons
public double msTotal; // total time of this marker on a frame
public int count; // total number of marker calls in the timeline (multiple per frame)
public int countMin; // min count per frame
public int countMax; // max count per frame
public float countMean; // mean over all frames
public int countMedian; // median over all frames
public int countLowerQuartile; // over all frames
public int countUpperQuartile; // over all frames
public float countStandardDeviation;
public int lastFrame;
public int presentOnFrameCount; // number of frames containing this marker
public int firstFrameIndex;
public float msMean; // mean over all frames
public float msMedian; // median over all frames
public float msLowerQuartile; // over all frames
public float msUpperQuartile; // over all frames
public float msMin; // min total time per frame
public float msMax; // max total time per frame
public float msStandardDeviation;
public int minIndividualFrameIndex;
public int maxIndividualFrameIndex;
public float msMinIndividual; // min individual function call
public float msMaxIndividual; // max individual function call
public float msAtMedian; // time at median frame
public int medianFrameIndex; // frame this markers median value is found on
public int minFrameIndex;
public int maxFrameIndex;
public int minDepth;
public int maxDepth;
public List<string> threads;
const int bucketCount = 20;
public int[] buckets; // Each bucket contains 'number of frames' for 'sum of markers in the frame' in that range
public int[] countBuckets; // Each bucket contains 'number of frames' for 'count in the frame' in that range
public List<FrameTime> frames;
public double timeRemoved;
public double timeIgnored;
public MarkerData(string markerName)
{
buckets = new int[bucketCount];
countBuckets = new int[bucketCount];
frames = new List<FrameTime>();
threads = new List<string>();
name = markerName;
nameLowerCaseHash = markerName.ToLower().GetHashCode();
msTotal = 0.0;
count = 0;
countMin = 0;
countMax = 0;
countMean = 0f;
countMedian = 0;
countLowerQuartile = 0;
countUpperQuartile = 0;
countStandardDeviation = 0f;
lastFrame = -1;
presentOnFrameCount = 0;
firstFrameIndex = -1;
msMean = 0f;
msMedian = 0f;
msLowerQuartile = 0f;
msUpperQuartile = 0f;
msMin = float.MaxValue;
msMax = float.MinValue;
msStandardDeviation = 0f;
minIndividualFrameIndex = 0;
maxIndividualFrameIndex = 0;
msMinIndividual = float.MaxValue;
msMaxIndividual = float.MinValue;
msAtMedian = 0f;
medianFrameIndex = 0;
minFrameIndex = 0;
maxFrameIndex = 0;
minDepth = 0;
maxDepth = 0;
for (int b = 0; b < buckets.Length; b++)
{
buckets[b] = 0;
countBuckets[b] = 0;
}
timeRemoved = 0.0;
timeIgnored = 0.0;
}
/// <summary>Compare the time duration between the marker median times. Used for sorting in descending order</summary>
/// <param name="other"> The other MarkerData to compare </param>
/// <returns>-1 if this is larger, 0 if the same, 1 if this is smaller</returns>
public int CompareTo(MarkerData other)
{
if (msMedian == other.msMedian)
{
if (medianFrameIndex == other.medianFrameIndex)
{
// Tertiary sort by name order
return name.CompareTo(other.name);
}
// Secondary sort by frame index order
return medianFrameIndex.CompareTo(other.medianFrameIndex);
}
return -msMedian.CompareTo(other.msMedian);
}
public float GetFrameMs(int frameIndex)
{
foreach (var frameData in frames)
{
if (frameData.frameIndex == frameIndex)
return frameData.ms;
}
return 0f;
}
public void ComputeBuckets(float min, float max)
{
float first = min;
float last = max;
float range = last - first;
int maxBucketIndex = (buckets.Length - 1);
for (int bucketIndex = 0; bucketIndex < buckets.Length; bucketIndex++)
{
buckets[bucketIndex] = 0;
}
float scale = range > 0 ? buckets.Length / range : 0;
// using a for loop instead of foreach is surprisingly faster on Mono
for (int i = 0, n = frames.Count; i < n; i++)
{
var frameTime = frames[i];
var ms = frameTime.ms;
//int frameIndex = frameTime.frameIndex;
int bucketIndex = (int)((ms - first) * scale);
if (bucketIndex < 0 || bucketIndex > maxBucketIndex)
{
// This can happen if a single marker range is longer than the frame start end (which could occur if running on a separate thread)
// It can also occur for the highest entry in the range (max-min/range) = 1
// if (ms > max) // Check for the spilling case
// Debug.Log(string.Format("Marker {0} : {1}ms exceeds range {2}-{3} on frame {4}", marker.name, ms, first, last, 1+frameIndex));
if (bucketIndex > maxBucketIndex)
bucketIndex = maxBucketIndex;
else
bucketIndex = 0;
}
buckets[bucketIndex] += 1;
}
if (range == 0)
{
// All buckets will be the same
for (int bucketIndex = 1; bucketIndex < buckets.Length; bucketIndex++)
{
buckets[bucketIndex] = buckets[0];
}
}
}
public void ComputeCountBuckets(int min, int max)
{
float first = min;
float last = max;
float range = last - first;
int maxBucketIndex = (countBuckets.Length - 1);
for (int bucketIndex = 0; bucketIndex < countBuckets.Length; bucketIndex++)
{
countBuckets[bucketIndex] = 0;
}
float scale = range > 0 ? countBuckets.Length / range : 0;
// using a for loop instead of foreach is surprisingly faster on Mono
for (int i = 0, n = frames.Count; i < n; i++)
{
var frameTime = frames[i];
var count = frameTime.count;
int bucketIndex = (int)((count - first) * scale);
if (bucketIndex < 0 || bucketIndex > maxBucketIndex)
{
if (bucketIndex > maxBucketIndex)
bucketIndex = maxBucketIndex;
else
bucketIndex = 0;
}
countBuckets[bucketIndex] += 1;
}
if (range == 0)
{
// All buckets will be the same
for (int bucketIndex = 1; bucketIndex < countBuckets.Length; bucketIndex++)
{
countBuckets[bucketIndex] = countBuckets[0];
}
}
}
public static string GetFirstThread(MarkerData marker)
{
return marker != null ? marker.threads[0] : "";
}
public static float GetMsMax(MarkerData marker)
{
return marker != null ? marker.msMax : 0.0f;
}
public static int GetMaxFrameIndex(MarkerData marker)
{
return marker != null ? marker.maxFrameIndex : 0;
}
public static float GetMsMin(MarkerData marker)
{
return marker != null ? marker.msMin : 0.0f;
}
public static int GetMinFrameIndex(MarkerData marker)
{
return marker != null ? marker.minFrameIndex : 0;
}
public static float GetMsMedian(MarkerData marker)
{
return marker != null ? marker.msMedian : 0.0f;
}
public static int GetMedianFrameIndex(MarkerData marker)
{
return marker != null ? marker.medianFrameIndex : 0;
}
public static float GetMsUpperQuartile(MarkerData marker)
{
return marker != null ? marker.msUpperQuartile : 0.0f;
}
public static float GetMsLowerQuartile(MarkerData marker)
{
return marker != null ? marker.msLowerQuartile : 0.0f;
}
public static float GetMsMean(MarkerData marker)
{
return marker != null ? marker.msMean : 0.0f;
}
public static float GetMsMinIndividual(MarkerData marker)
{
return marker != null ? marker.msMinIndividual : 0.0f;
}
public static float GetMsMaxIndividual(MarkerData marker)
{
return marker != null ? marker.msMaxIndividual : 0.0f;
}
public static int GetPresentOnFrameCount(MarkerData marker)
{
return marker != null ? marker.presentOnFrameCount : 0;
}
public static float GetMsAtMedian(MarkerData marker)
{
return marker != null ? marker.msAtMedian : 0.0f;
}
public static int GetCountMin(MarkerData marker)
{
return marker != null ? marker.countMin : 0;
}
public static int GetCountMax(MarkerData marker)
{
return marker != null ? marker.countMax : 0;
}
public static int GetCount(MarkerData marker)
{
return marker != null ? marker.count : 0;
}
public static float GetCountMean(MarkerData marker)
{
return marker != null ? marker.countMean : 0.0f;
}
public static double GetMsTotal(MarkerData marker)
{
return marker != null ? marker.msTotal : 0.0;
}
public static int GetMinDepth(MarkerData marker)
{
return marker != null ? marker.minDepth : 0;
}
public static int GetMaxDepth(MarkerData marker)
{
return marker != null ? marker.maxDepth : 0;
}
public static double GetTimeRemoved(MarkerData marker)
{
return marker != null ? marker.timeRemoved : 0.0;
}
public static double GetTimeIgnored(MarkerData marker)
{
return marker != null ? marker.timeIgnored : 0.0;
}
public bool IsFullyIgnored()
{
if (timeIgnored > 0.0)
{
if (msTotal == 0.0)
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7636f77e28bd3480f9e22fecae16594a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
using System;
namespace UnityEditor.Performance.ProfileAnalyzer
{
[Serializable]
internal class MarkerPairing
{
public string name;
public int leftIndex;
public int rightIndex;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 60fdacb48a6fe46529c3c01c96f7d123
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,405 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Performance.ProfileAnalyzer
{
[Serializable]
internal class ProfileAnalysis
{
FrameSummary m_FrameSummary = new FrameSummary();
List<MarkerData> m_Markers = new List<MarkerData>();
List<ThreadData> m_Threads = new List<ThreadData>();
public ProfileAnalysis()
{
m_FrameSummary.first = 0;
m_FrameSummary.last = 0;
m_FrameSummary.count = 0;
m_FrameSummary.msTotal = 0.0;
m_FrameSummary.msMin = float.MaxValue;
m_FrameSummary.msMax = 0.0f;
m_FrameSummary.minFrameIndex = 0;
m_FrameSummary.maxFrameIndex = 0;
m_FrameSummary.maxMarkerDepth = 0;
m_FrameSummary.totalMarkers = 0;
m_FrameSummary.markerCountMax = 0;
m_FrameSummary.markerCountMaxMean = 0.0f;
for (int b = 0; b < m_FrameSummary.buckets.Length; b++)
m_FrameSummary.buckets[b] = 0;
m_Markers.Clear();
m_Threads.Clear();
}
public void SetRange(int firstFrameIndex, int lastFrameIndex)
{
m_FrameSummary.first = firstFrameIndex;
m_FrameSummary.last = lastFrameIndex;
// Ensure these are initialized to frame indices within the range
m_FrameSummary.minFrameIndex = firstFrameIndex;
// if this wasn't initialized, and all frames had 0 length, it wouldn't be set in the UpdateSummary step of the analysis and point out of range
m_FrameSummary.maxFrameIndex = firstFrameIndex;
}
public void AddMarker(MarkerData marker)
{
m_Markers.Add(marker);
}
public void AddThread(ThreadData thread)
{
m_Threads.Add(thread);
}
public void UpdateSummary(int frameIndex, float msFrame)
{
m_FrameSummary.msTotal += msFrame;
m_FrameSummary.count += 1;
if (msFrame < m_FrameSummary.msMin)
{
m_FrameSummary.msMin = msFrame;
m_FrameSummary.minFrameIndex = frameIndex;
}
if (msFrame > m_FrameSummary.msMax)
{
m_FrameSummary.msMax = msFrame;
m_FrameSummary.maxFrameIndex = frameIndex;
}
m_FrameSummary.frames.Add(new FrameTime(frameIndex, msFrame, 1));
}
FrameTime GetPercentageOffset(List<FrameTime> frames, float percent, out int outputFrameIndex)
{
int index = (int)((frames.Count - 1) * percent / 100);
outputFrameIndex = frames[index].frameIndex;
// True median is half of the sum of the middle 2 frames for an even count. However this would be a value never recorded so we avoid that.
return frames[index];
}
float GetThreadPercentageOffset(List<ThreadFrameTime> frames, float percent, out int outputFrameIndex)
{
int index = (int)((frames.Count - 1) * percent / 100);
outputFrameIndex = frames[index].frameIndex;
// True median is half of the sum of the middle 2 frames for an even count. However this would be a value never recorded so we avoid that.
return frames[index].ms;
}
void CalculateStandardDeviations(MarkerData marker)
{
if (marker.frames.Count <= 1)
{
marker.msStandardDeviation = 0;
marker.countStandardDeviation = 0;
return;
}
int frameCount = marker.frames.Count;
float msMean = marker.msMean;
float countMean = marker.countMean;
double msSum = 0.0;
double countSum = 0.0;
for (int i = 0; i < frameCount; ++i)
{
float delta = (marker.frames[i].ms - msMean);
msSum += (delta * delta);
delta = (marker.frames[i].count - countMean);
countSum += (delta * delta);
}
double variance = msSum / (frameCount - 1);
marker.msStandardDeviation = (float)Math.Sqrt(variance);
variance = countSum / (frameCount - 1);
marker.countStandardDeviation = (float)Math.Sqrt(variance);
}
public void SetupMarkers()
{
int countMax = 0;
float countMaxMean = 0.0f;
foreach (MarkerData marker in m_Markers)
{
marker.msAtMedian = 0.0f;
marker.msMin = float.MaxValue;
marker.msMax = float.MinValue;
marker.minFrameIndex = 0;
marker.maxFrameIndex = 0;
marker.countMin = int.MaxValue;
marker.countMax = int.MinValue;
foreach (FrameTime frameTime in marker.frames)
{
var ms = frameTime.ms;
int frameIndex = frameTime.frameIndex;
// Total time for marker over frame
if (ms < marker.msMin)
{
marker.msMin = ms;
marker.minFrameIndex = frameIndex;
}
if (ms > marker.msMax)
{
marker.msMax = ms;
marker.maxFrameIndex = frameIndex;
}
if (frameIndex == m_FrameSummary.medianFrameIndex)
marker.msAtMedian = ms;
var count = frameTime.count;
// count for marker over frame
if (count < marker.countMin)
{
marker.countMin = count;
}
if (count > marker.countMax)
{
marker.countMax = count;
}
}
int unusedIndex;
marker.msMean = marker.presentOnFrameCount > 0 ? (float)(marker.msTotal / marker.presentOnFrameCount) : 0f;
marker.frames.Sort(FrameTime.CompareCount);
marker.countMedian = GetPercentageOffset(marker.frames, 50, out marker.medianFrameIndex).count;
marker.countLowerQuartile = GetPercentageOffset(marker.frames, 25, out unusedIndex).count;
marker.countUpperQuartile = GetPercentageOffset(marker.frames, 75, out unusedIndex).count;
marker.countMean = marker.presentOnFrameCount > 0 ? (float)marker.count / marker.presentOnFrameCount : 0f;
marker.frames.Sort(FrameTime.CompareMs);
marker.msMedian = GetPercentageOffset(marker.frames, 50, out marker.medianFrameIndex).ms;
marker.msLowerQuartile = GetPercentageOffset(marker.frames, 25, out unusedIndex).ms;
marker.msUpperQuartile = GetPercentageOffset(marker.frames, 75, out unusedIndex).ms;
CalculateStandardDeviations(marker);
if (marker.countMax > countMax)
countMax = marker.countMax;
if (marker.countMean > countMaxMean)
countMaxMean = marker.countMean;
}
m_FrameSummary.markerCountMax = countMax;
m_FrameSummary.markerCountMaxMean = countMaxMean;
}
public void SetupMarkerBuckets()
{
// using a for loop instead of foreach is surprisingly faster on Mono
for (int i = 0, n = m_Markers.Count; i < n; i++)
{
var marker = m_Markers[i];
marker.ComputeBuckets(marker.msMin, marker.msMax);
marker.ComputeCountBuckets(marker.countMin, marker.countMax);
}
}
public void SetupFrameBuckets(float timeScaleMax)
{
float first = 0;
float last = timeScaleMax;
float range = last - first;
int maxBucketIndex = m_FrameSummary.buckets.Length - 1;
for (int bucketIndex = 0; bucketIndex < m_FrameSummary.buckets.Length; bucketIndex++)
{
m_FrameSummary.buckets[bucketIndex] = 0;
}
float scale = range > 0 ? m_FrameSummary.buckets.Length / range : 0;
// using a for loop instead of foreach is surprisingly faster on Mono
for (int i = 0, n = m_FrameSummary.frames.Count; i < n; i++)
{
var frameData = m_FrameSummary.frames[i];
var msFrame = frameData.ms;
//var frameIndex = frameData.frameIndex;
int bucketIndex = (int)((msFrame - first) * scale);
if (bucketIndex < 0 || bucketIndex > maxBucketIndex)
{
// It can occur for the highest entry in the range (max-min/range) = 1
// if (ms > max) // Check for the spilling case
// Debug.Log(string.Format("Frame {0}ms exceeds range {1}-{2} on frame {3}", msFrame, first, last, frameIndex));
if (bucketIndex > maxBucketIndex)
bucketIndex = maxBucketIndex;
else
bucketIndex = 0;
}
m_FrameSummary.buckets[bucketIndex] += 1;
}
if (range == 0)
{
// All buckets will be the same
for (int bucketIndex = 1; bucketIndex < m_FrameSummary.buckets.Length; bucketIndex++)
{
m_FrameSummary.buckets[bucketIndex] = m_FrameSummary.buckets[0];
}
}
}
void CalculateThreadMedians()
{
foreach (var thread in m_Threads)
{
if (thread.frames.Count > 0)
{
thread.frames.Sort();
int unusedIndex;
thread.msMin = GetThreadPercentageOffset(thread.frames, 0, out thread.minFrameIndex);
thread.msLowerQuartile = GetThreadPercentageOffset(thread.frames, 25, out unusedIndex);
thread.msMedian = GetThreadPercentageOffset(thread.frames, 50, out thread.medianFrameIndex);
thread.msUpperQuartile = GetThreadPercentageOffset(thread.frames, 75, out unusedIndex);
thread.msMax = GetThreadPercentageOffset(thread.frames, 100, out thread.maxFrameIndex);
// Put back in order of frames
thread.frames.Sort((a, b) => a.frameIndex.CompareTo(b.frameIndex));
}
else
{
thread.msMin = 0f;
thread.msLowerQuartile = 0f;
thread.msMedian = 0f;
thread.msUpperQuartile = 0f;
thread.msMax = 0f;
}
}
}
public void Finalise(float timeScaleMax, int maxMarkerDepth)
{
if (m_FrameSummary.frames.Count > 0)
{
m_FrameSummary.frames.Sort();
m_FrameSummary.msMean = (float)(m_FrameSummary.msTotal / m_FrameSummary.count);
m_FrameSummary.msMedian = GetPercentageOffset(m_FrameSummary.frames, 50, out m_FrameSummary.medianFrameIndex).ms;
int unusedIndex;
m_FrameSummary.msLowerQuartile = GetPercentageOffset(m_FrameSummary.frames, 25, out unusedIndex).ms;
m_FrameSummary.msUpperQuartile = GetPercentageOffset(m_FrameSummary.frames, 75, out unusedIndex).ms;
}
else
{
m_FrameSummary.msMean = 0f;
m_FrameSummary.msMedian = 0f;
m_FrameSummary.msLowerQuartile = 0f;
m_FrameSummary.msUpperQuartile = 0f;
// This started as float.MaxValue and won't have been updated
m_FrameSummary.msMin = 0f;
}
// No longer need the frame time list ?
//m_frameSummary.msFrame.Clear();
m_FrameSummary.maxMarkerDepth = maxMarkerDepth;
if (timeScaleMax <= 0.0f)
{
// If max frame time range not specified then use the max frame value found.
timeScaleMax = m_FrameSummary.msMax;
}
else if (timeScaleMax < m_FrameSummary.msMax)
{
Debug.Log(string.Format("Expanding timeScaleMax {0} to match max value found {1}", timeScaleMax, m_FrameSummary.msMax));
// If max frame time range too small we must expand it.
timeScaleMax = m_FrameSummary.msMax;
}
SetupMarkers();
SetupMarkerBuckets();
SetupFrameBuckets(timeScaleMax);
// Sort in median order (highest first)
m_Markers.Sort(SortByAtMedian);
CalculateThreadMedians();
}
int SortByAtMedian(MarkerData a, MarkerData b)
{
if (a.msAtMedian == b.msAtMedian)
return -a.medianFrameIndex.CompareTo(b.medianFrameIndex);
return -a.msAtMedian.CompareTo(b.msAtMedian);
}
public List<MarkerData> GetMarkers()
{
return m_Markers;
}
public List<ThreadData> GetThreads()
{
return m_Threads;
}
public ThreadData GetThreadByName(string threadNameWithIndex)
{
foreach (var thread in m_Threads)
{
if (thread.threadNameWithIndex == threadNameWithIndex)
return thread;
}
return null;
}
public FrameSummary GetFrameSummary()
{
return m_FrameSummary;
}
public MarkerData GetMarker(int index)
{
if (index < 0 || index >= m_Markers.Count)
return null;
return m_Markers[index];
}
public int GetMarkerIndexByName(string markerName)
{
if (markerName == null)
return -1;
for (int index = 0; index < m_Markers.Count; index++)
{
var marker = m_Markers[index];
if (marker.name == markerName)
{
return index;
}
}
return -1;
}
public MarkerData GetMarkerByName(string markerName)
{
if (markerName == null)
return null;
for (int index = 0; index < m_Markers.Count; index++)
{
var marker = m_Markers[index];
if (marker.name == markerName)
{
return marker;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7cee1025e74448acbbb8bf2b82d9bfc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 317d76fd107c444c7822b9c99d48bd30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,281 @@
using UnityEngine;
using System.IO;
using System;
using System.Collections.Generic;
using System.Globalization;
namespace UnityEditor.Performance.ProfileAnalyzer
{
internal class ProfileAnalyzerExportWindow : EditorWindow
{
internal static class Styles
{
public static readonly GUIContent markerTable = new GUIContent("Marker table", "Export data from the single view marker table");
public static readonly GUIContent singleFrameTimes = new GUIContent("Single Frame Times", "Export frame time data from the single view");
public static readonly GUIContent comparisonTables = new GUIContent("Comparison table", "Export data from the comparsion view marker table");
public static readonly GUIContent comparisonFrameTimes = new GUIContent("Comparison Frame Times", "Export frame time data from the comparison view");
}
ProfileAnalyzerWindow m_ProfileAnalyzerWindow;
ProfileDataView m_ProfileDataView;
ProfileDataView m_LeftDataView;
ProfileDataView m_RightDataView;
static public ProfileAnalyzerExportWindow FindOpenWindow()
{
UnityEngine.Object[] windows = Resources.FindObjectsOfTypeAll(typeof(ProfileAnalyzerExportWindow));
if (windows != null && windows.Length > 0)
return windows[0] as ProfileAnalyzerExportWindow;
return null;
}
static public bool IsOpen()
{
if (FindOpenWindow() != null)
return true;
return false;
}
static public ProfileAnalyzerExportWindow Open(float screenX, float screenY, ProfileDataView profileSingleView, ProfileDataView profileLeftView, ProfileDataView profileRightView, ProfileAnalyzerWindow profileAnalyzerWindow)
{
ProfileAnalyzerExportWindow window = GetWindow<ProfileAnalyzerExportWindow>("Export");
window.minSize = new Vector2(200, 180);
window.position = new Rect(screenX, screenY, 200, 180);
window.m_ProfileAnalyzerWindow = profileAnalyzerWindow;
window.SetData(profileSingleView, profileLeftView, profileRightView);
window.Show();
return window;
}
static public void CloseAll()
{
ProfileAnalyzerExportWindow window = GetWindow<ProfileAnalyzerExportWindow>("Export");
window.Close();
}
public void SetData(ProfileDataView profileDataView, ProfileDataView leftDataView, ProfileDataView rightDataView)
{
m_ProfileDataView = profileDataView;
m_LeftDataView = leftDataView;
m_RightDataView = rightDataView;
}
void OnGUI()
{
EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true));
GUILayout.Label("Export as CSV:");
GUILayout.Label("");
GUILayout.Label("Single View");
bool enabled = GUI.enabled;
if (m_ProfileDataView == null || !m_ProfileDataView.IsDataValid())
GUI.enabled = false;
if (GUILayout.Button(Styles.markerTable))
SaveMarkerTableCSV();
GUI.enabled = enabled;
if (m_ProfileDataView == null || m_ProfileDataView.analysis == null)
GUI.enabled = false;
if (GUILayout.Button(Styles.singleFrameTimes))
SaveFrameTimesCSV();
GUI.enabled = enabled;
GUILayout.Label("Comparison View");
if (!m_ProfileAnalyzerWindow.CanExportComparisonTable())
GUI.enabled = false;
if (GUILayout.Button(Styles.comparisonTables))
SaveComparisonTableCSV();
GUI.enabled = enabled;
if (m_LeftDataView == null || !m_LeftDataView.IsDataValid() || m_RightDataView == null || !m_RightDataView.IsDataValid())
GUI.enabled = false;
if (GUILayout.Button(Styles.comparisonFrameTimes))
SaveComparisonFrameTimesCSV();
GUI.enabled = enabled;
EditorGUILayout.EndVertical();
}
void SaveMarkerTableCSV()
{
if (m_ProfileDataView.analysis == null)
return;
string path = EditorUtility.SaveFilePanel("Save marker table CSV data", "", "markerTable.csv", "csv");
if (path.Length != 0)
{
var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
using (StreamWriter file = new StreamWriter(path))
{
file.Write("Name; ");
file.Write("Median Time; Min Time; Max Time; ");
file.Write("Median Frame Index; Min Frame Index; Max Frame Index; ");
file.Write("Min Depth; Max Depth; ");
file.Write("Total Time; ");
file.Write("Mean Time; Time Lower Quartile; Time Upper Quartile; ");
file.Write("Count Total; Count Median; Count Min; Count Max; ");
file.Write("Number of frames containing Marker; ");
file.Write("First Frame Index; ");
file.Write("Time Min Individual; Time Max Individual; ");
file.Write("Min Individual Frame; Max Individual Frame; ");
file.WriteLine("Time at Median Frame");
List<MarkerData> markerData = m_ProfileDataView.analysis.GetMarkers();
markerData.Sort();
foreach (MarkerData marker in markerData)
{
var markerName = marker.name;
if (markerName.IndexOf('\"') >= 0)
// replace all double quotation marks with single ones to avoid this breaking the escaping with double quotation marks
markerName = markerName.Replace('\"', '\'');
int medianFrameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(marker.medianFrameIndex, m_ProfileDataView);
int minFrameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(marker.minFrameIndex, m_ProfileDataView);
int maxFrameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(marker.maxFrameIndex, m_ProfileDataView);
int firstFrameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(marker.firstFrameIndex, m_ProfileDataView);
int minIndividualFrameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(marker.minIndividualFrameIndex, m_ProfileDataView);
int maxIndividualFrameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(marker.maxIndividualFrameIndex, m_ProfileDataView);
// "Escape" marker names in case it has commas in it.
file.Write("\"{0}\";", markerName);
file.Write(string.Format(CultureInfo.InvariantCulture,"{0};{1};{2};",
marker.msMedian, marker.msMin, marker.msMax));
file.Write("{0};{1};{2};",
medianFrameIndex, minFrameIndex, maxFrameIndex);
file.Write("{0};{1};",
marker.minDepth, marker.maxDepth);
file.Write(string.Format(CultureInfo.InvariantCulture, "{0};",
marker.msTotal));
file.Write(string.Format(CultureInfo.InvariantCulture, "{0};{1};{2};",
marker.msMean, marker.msLowerQuartile, marker.msUpperQuartile));
file.Write("{0};{1};{2};{3};",
marker.count, marker.countMedian, marker.countMin, marker.countMax);
file.Write("{0};", marker.presentOnFrameCount);
file.Write("{0};", firstFrameIndex);
file.Write(string.Format(CultureInfo.InvariantCulture, "{0};{1};",
marker.msMinIndividual, marker.msMaxIndividual));
file.Write("{0};{1};",
minIndividualFrameIndex, maxIndividualFrameIndex);
file.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", marker.msAtMedian));
}
}
ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.ExportSingleFrames, analytic);
}
}
void SaveFrameTimesCSV()
{
if (m_ProfileDataView == null)
return;
if (!m_ProfileDataView.IsDataValid())
return;
string path = EditorUtility.SaveFilePanel("Save frame time CSV data", "", "frameTime.csv", "csv");
if (path.Length != 0)
{
var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
using (StreamWriter file = new StreamWriter(path))
{
file.WriteLine("Frame Offset; Frame Index; Frame Time (ms); Time from first frame (ms)");
float maxFrames = m_ProfileDataView.data.GetFrameCount();
var frame = m_ProfileDataView.data.GetFrame(0);
// msStartTime isn't very accurate so we don't use it
double msTimePassed = 0.0;
for (int frameOffset = 0; frameOffset < maxFrames; frameOffset++)
{
frame = m_ProfileDataView.data.GetFrame(frameOffset);
int frameIndex = m_ProfileDataView.data.OffsetToDisplayFrame(frameOffset);
frameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(frameIndex, m_ProfileDataView);
float msFrame = frame.msFrame;
file.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0};{1};{2};{3}",
frameOffset, frameIndex, msFrame, msTimePassed));
msTimePassed += msFrame;
}
}
ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.ExportSingleFrames, analytic);
}
}
void SaveComparisonFrameTimesCSV()
{
if (m_LeftDataView == null || m_RightDataView == null)
return;
if (!m_LeftDataView.IsDataValid() || !m_RightDataView.IsDataValid())
return;
string path = EditorUtility.SaveFilePanel("Save comparison frame time CSV data", "", "frameTimeComparison.csv", "csv");
if (path.Length != 0)
{
var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
using (StreamWriter file = new StreamWriter(path))
{
file.Write("Frame Offset; ");
file.Write("Left Frame Index; Right Frame Index; ");
file.Write("Left Frame Time (ms); Left time from first frame (ms); ");
file.Write("Right Frame Time (ms); Right time from first frame (ms); ");
file.WriteLine("Frame Time Diff (ms)");
float maxFrames = Math.Max(m_LeftDataView.data.GetFrameCount(), m_RightDataView.data.GetFrameCount());
var leftFrame = m_LeftDataView.data.GetFrame(0);
var rightFrame = m_RightDataView.data.GetFrame(0);
// msStartTime isn't very accurate so we don't use it
double msTimePassedLeft = 0.0;
double msTimePassedRight = 0.0;
for (int frameOffset = 0; frameOffset < maxFrames; frameOffset++)
{
leftFrame = m_LeftDataView.data.GetFrame(frameOffset);
rightFrame = m_RightDataView.data.GetFrame(frameOffset);
int leftFrameIndex = m_LeftDataView.data.OffsetToDisplayFrame(frameOffset);
leftFrameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(leftFrameIndex, m_LeftDataView);
int rightFrameIndex = m_RightDataView.data.OffsetToDisplayFrame(frameOffset);
rightFrameIndex = m_ProfileAnalyzerWindow.GetRemappedUIFrameIndex(rightFrameIndex, m_RightDataView);
float msFrameLeft = leftFrame != null ? leftFrame.msFrame : 0;
float msFrameRight = rightFrame != null ? rightFrame.msFrame : 0;
float msFrameDiff = msFrameRight - msFrameLeft;
file.Write("{0};", frameOffset);
file.Write("{0};{1};", leftFrameIndex, rightFrameIndex);
file.Write(string.Format(CultureInfo.InvariantCulture, "{0};{1};", msFrameLeft, msTimePassedLeft));
file.Write(string.Format(CultureInfo.InvariantCulture, "{0};{1};", msFrameRight, msTimePassedRight));
file.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", msFrameDiff));
msTimePassedLeft += msFrameLeft;
msTimePassedRight += msFrameRight;
}
}
ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.ExportComparisonFrames, analytic);
}
}
void SaveComparisonTableCSV()
{
if (m_LeftDataView == null || m_RightDataView == null)
return;
if (!m_LeftDataView.IsDataValid() || !m_RightDataView.IsDataValid())
return;
string path = EditorUtility.SaveFilePanel("Save comparison table CSV data", "", "tableComparison.csv", "csv");
if (path.Length != 0)
{
var analytic = ProfileAnalyzerAnalytics.BeginAnalytic();
using (StreamWriter file = new StreamWriter(path))
{
m_ProfileAnalyzerWindow.TryExportComparisonTable(file);
}
ProfileAnalyzerAnalytics.SendUIButtonEvent(ProfileAnalyzerAnalytics.UIButton.ExportComparisonFrames, analytic);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ccd88eea7c8284ac18aff2e257aeb84a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,106 @@
Shader "Unlit/ProfileAnalyzerShader"
{
Properties
{
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
//Tags { "RenderType"="Transparent" }
LOD 100
//ZWrite Off
//Blend SrcAlpha OneMinusSrcAlpha
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile _ UNITY_UI_ALPHACLIP
struct appdata
{
float4 vertex : POSITION;
fixed4 color : COLOR;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float4 worldPosition : TEXCOORD1;
};
bool _UseClipRect;
float4 _ClipRect;
v2f vert (appdata v)
{
v2f o;
o.worldPosition = v.vertex;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color.rgba = v.color;
return o;
}
//fixed4 frag (v2f i) : SV_Target { return i.color; }
fixed4 frag (v2f i) : SV_Target
{
half4 color = i.color;
if (_UseClipRect)
color.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect);
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 493b79ea9c9be44ed8b702014d3ac49c
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0dc59854f72ef4a1fb6dbb8d0cd6aea3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5cea2082789f54c25b65fd1fef416863
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
namespace UnityEditor.Performance.ProfileAnalyzer
{
[Serializable]
internal class ProfileDataView
{
public string path;
public ProfileData data;
public ProfileAnalysis analysisFullNew;
public ProfileAnalysis analysisFull;
public ProfileAnalysis analysisNew;
public ProfileAnalysis analysis;
public List<int> selectedIndices = new List<int> { 0, 0 };
[NonSerialized]
public bool inSyncWithProfilerData;
public bool containsWaitForFPS { get; private set; }
public bool containsWaitForPresent { get; private set; }
public ProfileDataView()
{
}
public ProfileDataView(ProfileDataView dataView)
{
path = dataView.path;
data = dataView.data;
analysisFullNew = dataView.analysisFullNew;
analysisFull = dataView.analysisFull;
analysisNew = dataView.analysisNew;
analysis = dataView.analysis;
selectedIndices = new List<int>(dataView.selectedIndices);
inSyncWithProfilerData = dataView.inSyncWithProfilerData;
containsWaitForFPS = dataView.containsWaitForFPS;
containsWaitForPresent = dataView.containsWaitForPresent;
}
public void FindKeyMarkers()
{
containsWaitForFPS = data.GetMarkerIndex("WaitForTargetFPS") != -1;
containsWaitForPresent = data.GetMarkerIndex("Gfx.WaitForPresentOnGfxThread") != -1;
}
public bool IsDataValid()
{
if (data == null)
return false;
if (data.GetFrameCount() == 0)
return false;
if (data.NeedsMarkerRebuild)
{
if (!ProfileData.Load(data.FilePath, out data))
{
return false;
}
}
return true;
}
public bool HasValidSelection()
{
if (selectedIndices.Count == 2 && selectedIndices[0] == 0 && selectedIndices[1] == 0)
return false;
return true;
}
public bool HasSelection()
{
if (selectedIndices.Count == 0)
return false;
if (selectedIndices.Count == data.GetFrameCount())
return false;
return HasValidSelection();
}
public bool AllSelected()
{
if (selectedIndices.Count != data.GetFrameCount())
return false;
return true;
}
public int GetMaxDepth()
{
return (analysis == null) ? 1 : analysis.GetFrameSummary().maxMarkerDepth;
}
int Clamp(int value, int min, int max)
{
if (value < min)
value = min;
else if (value > max)
value = max;
return value;
}
public int ClampToValidDepthValue(int depthFilter)
{
// ProfileAnalyzer.kDepthAll is special case that we don't test for here
// If we have no depth values then return -1 for all (as clamp expects min<max)
int maxDepth = GetMaxDepth();
if (maxDepth < 1)
return ProfileAnalyzer.kDepthAll;
return Clamp(depthFilter, 1, maxDepth);
}
bool SelectAllFramesContainingMarker(string markerName, ProfileAnalysis inAnalysis)
{
if (inAnalysis == null)
return false;
selectedIndices.Clear();
MarkerData markerData = inAnalysis.GetMarkerByName(markerName);
if (markerData == null)
return true;
foreach (var frameTime in markerData.frames)
{
selectedIndices.Add(frameTime.frameIndex);
}
// Order from lowest to highest so the start/end frame display makes sense
selectedIndices.Sort();
return true;
}
public bool SelectAllFramesContainingMarker(string markerName, bool inSelection)
{
return SelectAllFramesContainingMarker(markerName, inSelection ? analysis : analysisFull);
}
int ClampToRange(int value, int min, int max)
{
if (value < min)
value = min;
if (value > max)
value = max;
return value;
}
public void ClearSelection()
{
selectedIndices.Clear();
}
public void SelectFullRange()
{
selectedIndices.Clear();
if (data == null)
return;
for (int offset = 0; offset < data.GetFrameCount(); offset++)
{
selectedIndices.Add(data.OffsetToDisplayFrame(offset));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 59e8a7346ec034a4387b6ca1ab20b83e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 981ca102f47f145c1977153854cce718
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d8e7ffaacab264ab6ba5aa70f432785f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
using UnityEngine;
namespace UnityEditor.Performance.ProfileAnalyzer
{
internal class ProgressBarDisplay
{
int m_TotalFrames;
int m_CurrentFrame;
string m_Title;
string m_Description;
public void InitProgressBar(string title, string description, int frames)
{
m_CurrentFrame = 0;
m_TotalFrames = frames;
m_Title = title;
m_Description = description;
EditorUtility.DisplayProgressBar(m_Title, m_Description, m_CurrentFrame);
}
public void AdvanceProgressBar()
{
m_CurrentFrame++;
int currentFrame = Mathf.Clamp(0, m_CurrentFrame, m_TotalFrames);
float progress = m_TotalFrames > 0 ? (float)currentFrame / m_TotalFrames : 0f;
EditorUtility.DisplayProgressBar(m_Title, m_Description, progress);
}
public void ClearProgressBar()
{
EditorUtility.ClearProgressBar();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c27dfe7bd1055403b9dac63caf69c135
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More