Skip to content

Instantly share code, notes, and snippets.

@kornman00
Created February 21, 2016 21:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kornman00/6ef834f9254d4426594f to your computer and use it in GitHub Desktop.
Save kornman00/6ef834f9254d4426594f to your computer and use it in GitHub Desktop.
Preferences editor for Unity3D's profiler window
// Use at your own risk.
// Should work with any Unity Editor since at least 4.6
// These macros make it so changes aren't actually committed to the EditorPrefs. Instead, changes are Debug.Log'd
//#define VIEW_PREFS_READ_ONLY
#define CHART_PREFS_READ_ONLY
// You'll need this defined, unless you have my BitVector32 from https://bitbucket.org/KornnerStudios/ksoft
#define USE_SYSTEM_BITVECTOR
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
#if USE_SYSTEM_BITVECTOR
using BitVector32 = System.Collections.Specialized.BitVector32;
#else
using BitVector32 = KSoft.Collections.BitVector32;
#endif
namespace UnityEditorInternal
{
static class SystemBitVectorUtils
{
public static int ToBitVectorIndexer(this int bit)
{
#if USE_SYSTEM_BITVECTOR
return 1 << bit;
#else
return bit;
#endif
}
};
public static class ProfilerHierarchyInterop
{
private sealed class ProfilerHierarchyColumns
{
public string ColumnSettings;
public ProfilerColumn[] Columns;
private string[] mColumnNames;
private int mColumnSettingsMask;
private BitVector32 mRemovedForMinimal;
public ProfilerHierarchyColumns Setup()
{
mColumnNames = new string[Columns.Length];
for (int x = 0; x < Columns.Length; x++)
mColumnNames[x] = Columns[x].ToString();
mColumnSettingsMask = (1 << Columns.Length) - 1;
return this;
}
public ProfilerHierarchyColumns RemoveForMinimalView(params ProfilerColumn[] remove)
{
#if !USE_SYSTEM_BITVECTOR
mRemovedForMinimal.Clear();
foreach (var col in remove)
{
int index = Array.IndexOf(Columns, col);
mRemovedForMinimal[index] = true;
}
#endif
return this;
}
private void DoRemoveForMinimalGUI()
{
#if !USE_SYSTEM_BITVECTOR
if (mRemovedForMinimal.IsAllClear)
return;
var oldSettings = GetProfilerColumnSettings(ColumnSettings, Columns);
var newSettings = oldSettings.AndNot(mRemovedForMinimal);
if (newSettings == oldSettings)
return;
if (GUILayout.Button("Use minimal view"))
{
if (mOriginalVisibleSettings == null)
{
mOriginalVisibleSettings = oldSettings.Data;
}
SetProfilerColumnSettings(ColumnSettings, Columns, newSettings);
}
#endif
}
private bool mExpanded = true;
private int? mOriginalVisibleSettings;
public void DoGUI(string label)
{
mExpanded = EditorGUILayout.Foldout(mExpanded, label);
if (!mExpanded)
return;
GUILayout.BeginVertical("box");
DoRemoveForMinimalGUI();
var oldSettings = GetProfilerColumnSettings(ColumnSettings, Columns);
int newSettings = EditorGUILayout.MaskField("Visible", oldSettings.Data, mColumnNames);
// Ugh, MaskField's "Everything" sets bits that are outside the option set!
// Mask out non-options to ignore false positive changes
newSettings &= mColumnSettingsMask;
if (newSettings != oldSettings.Data)
{
#if VIEW_PREFS_READ_ONLY
Debug.LogFormat("{0} changed: {1} -> {2}",
ColumnSettings, oldSettings.Data, newSettings);
#else
if (mOriginalVisibleSettings == null)
{
mOriginalVisibleSettings = oldSettings.Data;
}
SetProfilerColumnSettings(ColumnSettings, Columns, new BitVector32(newSettings));
#endif
}
#if !VIEW_PREFS_READ_ONLY
if (mOriginalVisibleSettings.HasValue && GUILayout.Button("Revert"))
{
SetProfilerColumnSettings(ColumnSettings, Columns, new BitVector32(mOriginalVisibleSettings.Value));
mOriginalVisibleSettings = null;
}
#endif
GUILayout.EndVertical();
}
};
// No ProfilerWindow code uses this setting
private const string kProfilerVisibleGraphsSettings = "VisibleProfilerGraphs";
private static ProfilerHierarchyColumns kProfilerCpuColumns;
private static ProfilerHierarchyColumns kProfilerCpuDetailColumns;
private static ProfilerHierarchyColumns kProfilerGpuColumns;
private static ProfilerHierarchyColumns kProfilerGpuDetailColumns;
static ProfilerHierarchyInterop()
{
#region kProfilerCpuColumns
kProfilerCpuColumns = new ProfilerHierarchyColumns
{
ColumnSettings = "VisibleProfilerColumnsV2",
Columns = new[] {
ProfilerColumn.FunctionName, ProfilerColumn.TotalPercent, ProfilerColumn.SelfPercent,
ProfilerColumn.Calls, ProfilerColumn.GCMemory, ProfilerColumn.TotalTime,
ProfilerColumn.SelfTime, ProfilerColumn.WarningCount
},
};
kProfilerCpuColumns.Setup()
.RemoveForMinimalView(ProfilerColumn.GCMemory, ProfilerColumn.TotalTime, ProfilerColumn.SelfTime, ProfilerColumn.WarningCount);
#endregion
#region kProfilerCpuDetailColumns
kProfilerCpuDetailColumns = new ProfilerHierarchyColumns
{
ColumnSettings = "VisibleProfilerDetailColumns",
Columns = new[] {
ProfilerColumn.ObjectName, ProfilerColumn.TotalPercent, ProfilerColumn.SelfPercent,
ProfilerColumn.Calls, ProfilerColumn.GCMemory, ProfilerColumn.TotalTime,
ProfilerColumn.SelfTime
},
};
kProfilerCpuDetailColumns.Setup()
.RemoveForMinimalView(ProfilerColumn.GCMemory, ProfilerColumn.TotalTime, ProfilerColumn.SelfTime);
#endregion
#region kProfilerGpuColumns
kProfilerGpuColumns = new ProfilerHierarchyColumns
{
ColumnSettings = "VisibleProfilerGPUColumns",
Columns = new[] {
ProfilerColumn.FunctionName, ProfilerColumn.TotalGPUPercent, ProfilerColumn.DrawCalls, ProfilerColumn.TotalGPUTime
},
};
kProfilerGpuColumns.Setup();
#endregion
#region kProfilerGpuDetailColumns
kProfilerGpuDetailColumns = new ProfilerHierarchyColumns
{
ColumnSettings = "VisibleProfilerGPUDetailColumns",
Columns = new[] {
ProfilerColumn.ObjectName, ProfilerColumn.TotalGPUPercent, ProfilerColumn.DrawCalls, ProfilerColumn.TotalGPUTime
},
};
kProfilerGpuDetailColumns.Setup();
#endregion
}
private static BitVector32 GetProfilerColumnSettings(string columnSettingsName, ProfilerColumn[] columns)
{
string str = EditorPrefs.GetString(columnSettingsName);
var settings = new BitVector32();
if (string.IsNullOrEmpty(str))
{
// internal GUI code actually uses the settings string for determining columns to hide.
// So if the editor prefs doesn't have the string, no columns will get hidden
// See end of ProfilerHierarchyGUI.SetupSplitter
for (int x = 0; x < columns.Length; x++)
settings[x.ToBitVectorIndexer()] = true;
return settings;
}
if (str.Length < columns.Length)
{
Debug.LogWarningFormat("Settings string {0} is shorter than expected columns: {1} < {2}. {3}",
columnSettingsName, str.Length, columns.Length, str);
}
else if (str.Length > columns.Length)
{
Debug.LogWarningFormat("Settings string {0} is longer than expected columns: {1} > {2}. {3}",
columnSettingsName, str.Length, columns.Length, str);
}
for (int x = 0; x < str.Length; x++)
{
var c = str[x];
if (c != '0')
settings[x.ToBitVectorIndexer()] = true;
}
return settings;
}
private static void SetProfilerColumnSettings(string columnSettingsName, ProfilerColumn[] columns, BitVector32 settings)
{
var str = new System.Text.StringBuilder();
str.Append('1', columns.Length);
for (int x = 0; x < columns.Length; x++)
{
var bit = settings[x.ToBitVectorIndexer()];
if (!bit)
str[x] = '0';
}
EditorPrefs.SetString(columnSettingsName, str.ToString());
}
private static Vector2 sScrollPosition;
[PreferenceItem("Profiler View")]
private static void OnPreferencesGUI()
{
sScrollPosition = EditorGUILayout.BeginScrollView(sScrollPosition);
kProfilerCpuColumns.DoGUI("CPU Columns");
kProfilerCpuDetailColumns.DoGUI("CPU Detail Columns");
kProfilerGpuColumns.DoGUI("GPU Columns");
kProfilerGpuDetailColumns.DoGUI("GPU Detail Columns");
EditorGUILayout.EndScrollView();
}
};
public static class ProfilerChartInterop
{
// #TODO eventually support this
private const string kPrefCharts = "ProfilerChart";
private sealed class ProfilerChartSettings
{
public ProfilerArea Area;
private string mLabel;
private string mSettingsName;
private List<string> mStats;
private string[] mStatsArray;
private string mActiveSettingName;
private string mStatsVisibleSettingName;
private int mStatsVisibleMask;
private string mOrderingSettingName;
private List<int> mOrdering;
public ProfilerChartSettings(ProfilerArea area)
{
Area = area;
mLabel = Area.ToString();
mSettingsName = kPrefCharts + Area;
mStats = new List<string>();
mActiveSettingName = mSettingsName;
mStatsVisibleSettingName = mSettingsName + "Visible";
mOrderingSettingName = mSettingsName + "Order";
}
public ProfilerChartSettings AddStat(string name)
{
mStats.Add(name);
return this;
}
public ProfilerChartSettings AddChart(string name)
{
AddStat(name);
AddStat("Selected" + name);
return this;
}
public ProfilerChartSettings Setup()
{
mStatsVisibleMask = (1 << mStats.Count) - 1;
mStatsArray = mStats.ToArray();
mOrdering = new List<int>(mStats.Count);
for (int i = 0; i < mStats.Count; i++)
mOrdering.Add(mStats.Count - 1 - i);
return this;
}
private bool Active
{
get { return EditorPrefs.GetBool(mActiveSettingName, true); }
set { EditorPrefs.SetBool(mActiveSettingName, value); }
}
private bool mExpanded;
private bool mOrderingExpanded;
private int? mOriginalVisibleSettings;
public void DoGUI()
{
mExpanded = EditorGUILayout.Foldout(mExpanded, mLabel);
if (!mExpanded)
return;
GUILayout.BeginVertical("box");
#region Active
bool oldActive = Active;
bool newActive = EditorGUILayout.Toggle("Active", oldActive);
if (newActive != oldActive)
{
Active = newActive;
#if CHART_PREFS_READ_ONLY
Debug.LogFormat("{0} changed: {1} -> {2}",
mActiveSettingName, oldActive, newActive);
#else
Active = newActive;
#endif
}
#endregion
#region Visible
var oldSettings = GetChartAreaVisibleSettings(mStatsVisibleSettingName, mStats);
int newSettings = EditorGUILayout.MaskField("Visible", oldSettings.Data, mStatsArray);
// Ugh, MaskField's "Everything" sets bits that are outside the option set!
// Mask out non-options to ignore false positive changes
newSettings &= mStatsVisibleMask;
if (newSettings != oldSettings.Data)
{
#if CHART_PREFS_READ_ONLY
Debug.LogFormat("{0} changed: {1} -> {2}",
mStatsVisibleSettingName, oldSettings.Data, newSettings);
#else
if (mOriginalVisibleSettings == null)
{
mOriginalVisibleSettings = oldSettings.Data;
}
SetChartAreaVisibleSettings(mStatsVisibleSettingName, mStats, new BitVector32(newSettings));
#endif
#if !CHART_PREFS_READ_ONLY
if (mOriginalVisibleSettings.HasValue && GUILayout.Button("Revert"))
{
SetChartAreaVisibleSettings(mStatsVisibleSettingName, mStats, new BitVector32(mOriginalVisibleSettings.Value));
mOriginalVisibleSettings = null;
}
#endif
}
#endregion
#region Ordering TODO
mOrderingExpanded = EditorGUILayout.Foldout(mOrderingExpanded, "Ordering");
if (mOrderingExpanded)
{
for (int x = 0; x < mStats.Count; x++)
{
#if CHART_PREFS_READ_ONLY
EditorGUILayout.LabelField(mStats[x], mOrdering[x].ToString());
#endif
}
}
#endregion
GUILayout.EndVertical();
}
};
private static BitVector32 GetChartAreaVisibleSettings(string settingsName, List<string> chartNames)
{
string str = EditorPrefs.GetString(settingsName);
var settings = new BitVector32();
if (string.IsNullOrEmpty(str))
{
// internal GUI code actually uses the settings string for determining columns to hide.
// So if the editor prefs doesn't have the string, no columns will get hidden
// See Chart.LoadChartsSettings
for (int x = 0; x < chartNames.Count; x++)
settings[x.ToBitVectorIndexer()] = true;
return settings;
}
if (str.Length < chartNames.Count)
{
Debug.LogWarningFormat("Settings string {0} is shorter than expected: {1} < {2}. {3}",
settingsName, str.Length, chartNames.Count, str);
}
else if (str.Length > chartNames.Count)
{
Debug.LogWarningFormat("Settings string {0} is longer than expected: {1} > {2}. {3}",
settingsName, str.Length, chartNames.Count, str);
}
for (int x = 0; x < str.Length; x++)
{
var c = str[x];
if (c != '0')
settings[x.ToBitVectorIndexer()] = true;
}
return settings;
}
private static void SetChartAreaVisibleSettings(string settingsName, List<string> chartNames, BitVector32 settings)
{
var str = new System.Text.StringBuilder();
str.Append('1', chartNames.Count);
for (int x = 0; x < chartNames.Count; x++)
{
var bit = settings[x.ToBitVectorIndexer()];
if (!bit)
str[x] = '0';
}
EditorPrefs.SetString(settingsName, str.ToString());
}
#region kProfilerAreaCpu
private static ProfilerChartSettings kProfilerAreaCpu = new ProfilerChartSettings(ProfilerArea.CPU)
.AddChart("Rendering")
.AddChart("Scripts")
.AddChart("Physics")
.AddChart("GarbageCollector")
.AddChart("VSync")
.AddChart("Others")
.Setup();
#endregion
#region kProfilerAreaGpu
private static ProfilerChartSettings kProfilerAreaGpu = new ProfilerChartSettings(ProfilerArea.GPU)
.AddChart("Opaque")
.AddChart("Transparent")
.AddChart("Shadows/Depth")
.AddChart("Deferred PrePass")
.AddChart("Deferred Lighting")
.AddChart("PostProcess")
.AddChart("Other")
.Setup();
#endregion
#region kProfilerAreaRendering
private static ProfilerChartSettings kProfilerAreaRendering = new ProfilerChartSettings(ProfilerArea.Rendering)
.AddStat("Draw Calls")
.AddStat("Triangles")
.AddStat("Vertices")
.Setup();
#endregion
#region kProfilerAreaMemory
private static ProfilerChartSettings kProfilerAreaMemory = new ProfilerChartSettings(ProfilerArea.Memory)
.AddStat("Total Allocated")
.AddStat("Texture Memory")
.AddStat("Texture Count")
.AddStat("Mesh Count")
.AddStat("Material Count")
.AddStat("Object Count")
.Setup();
#endregion
#region kProfilerAreaAudio
private static ProfilerChartSettings kProfilerAreaAudio = new ProfilerChartSettings(ProfilerArea.Audio)
.AddStat("Total Sources")
.AddStat("Playing Sources")
.AddStat("Paused Sources")
.AddStat("Audio Clip Count")
.AddStat("Playing Voices")
.AddStat("Total Audio CPU")
.AddStat("DSP CPU")
.AddStat("Streaming CPU")
.AddStat("Other CPU")
.AddStat("Total Audio Memory")
.AddStat("Streaming Sound Memory")
.AddStat("Sample Sound Memory")
.AddStat("Voice Memory")
.AddStat("DSP Memory")
.AddStat("Extra DSP Buffer Memory")
.AddStat("Codec Memory")
.AddStat("Recording Memory")
.AddStat("Reverb Memory")
.AddStat("Other Audio Buffers")
.AddStat("Other Memory")
.Setup();
#endregion
#region kProfilerAreaPhysics
private static ProfilerChartSettings kProfilerAreaPhysics = new ProfilerChartSettings(ProfilerArea.Physics)
.AddStat("Active Rigidbodies")
.AddStat("Sleeping Rigidbodies")
.AddStat("Number of Contacts")
.AddStat("Static Colliders")
.AddStat("Dynamic Colliders")
.Setup();
#endregion
#region kProfilerAreaPhysics2D
private static ProfilerChartSettings kProfilerAreaPhysics2D = new ProfilerChartSettings(ProfilerArea.Physics2D)
.AddStat("Total Bodies")
.AddStat("Active Bodies")
.AddStat("Sleeping Bodies")
.AddStat("Dynamic Bodies")
.AddStat("Kinematic Bodies")
.AddStat("Discrete Bodies")
.AddStat("Continuous Bodies")
.AddStat("Joints")
.AddStat("Contacts")
.AddStat("Static Collider Shapes")
.AddStat("Active Collider Shapes")
.AddStat("Sleeping Collider Shapes")
.AddStat("Step Time")
.AddStat("Contact Time")
.AddStat("Solve Time")
.AddStat("Solve Initialization Time")
.AddStat("Solve Velocity Time")
.AddStat("Solve Position Time")
.AddStat("Solve Broadphase Time")
.AddStat("Solve TOI Time")
.Setup();
#endregion
private static Vector2 sScrollPosition;
[PreferenceItem("Profiler Charts")]
private static void OnPreferencesGUI()
{
sScrollPosition = EditorGUILayout.BeginScrollView(sScrollPosition);
kProfilerAreaCpu.DoGUI();
kProfilerAreaGpu.DoGUI();
kProfilerAreaRendering.DoGUI();
kProfilerAreaMemory.DoGUI();
kProfilerAreaAudio.DoGUI();
kProfilerAreaPhysics.DoGUI();
kProfilerAreaPhysics2D.DoGUI();
EditorGUILayout.EndScrollView();
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment