Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
8989e11
Initial commit for first realtime profiler prototype.
arttu-peltonen Jan 26, 2021
5cd4cfa
Change project from URP to HDRP where DebugDisplay is available.
arttu-peltonen Jan 27, 2021
2223ee2
Rearrange document structure to be closer to current design.
arttu-peltonen Jan 27, 2021
57a2a3c
Switch test numbers to come from FrameTimingManager.
arttu-peltonen Jan 28, 2021
7d61278
Show new FrameTimings in UI (requires trunk/srp/graphics/realtimeprof…
arttu-peltonen Feb 3, 2021
0bcdd02
Move RealtimeProfiler proto to RP Core package
arttu-peltonen Feb 3, 2021
4b2f967
Fix runtime UI. Create RealtimeProfilerModel automatically.
arttu-peltonen Feb 4, 2021
d667a8b
Remove dependencies on scene GOs.
arttu-peltonen Feb 4, 2021
e15c2c8
Fix UI in Player builds.
arttu-peltonen Feb 5, 2021
246c150
For debugging purposes, plot frame times in Profiler with ProfilerCou…
arttu-peltonen Feb 8, 2021
9a4a208
Align UI with new native frametimings.
arttu-peltonen Feb 11, 2021
8df1dde
Add sample averaging & history size option to UI.
arttu-peltonen Feb 12, 2021
48e3fed
Draft bottleneck detection.
arttu-peltonen Feb 15, 2021
892b711
Merge branch 'master' into srp/realtimeprofiler-proto
arttu-peltonen Jun 8, 2021
5276e95
Add bottleneck progress bars.
arttu-peltonen Jun 8, 2021
851c3e2
Make proto compatible with engine branch profiler/feature/universal-f…
arttu-peltonen Jul 1, 2021
758d800
Add ProgressBarValue type to debug display.
arttu-peltonen Jul 2, 2021
b90d84f
Remove prototype UITK UI, move it under Rendering Debugger + implemen…
arttu-peltonen Jul 2, 2021
f165eae
Make test scene with bottleneck simulation for CPU & GPU.
arttu-peltonen Jul 9, 2021
0697812
Unit tests for RuntimeProfiler
arttu-peltonen Aug 3, 2021
03dc0e9
Show progressbar values as percentages in persistent runtime UI.
arttu-peltonen Aug 3, 2021
bf62e35
Use new MainThreadCPUPresentWaitTime to detect present limited bottle…
arttu-peltonen Aug 6, 2021
028c51a
Add numeric format support for DebugUI.Value, use 2 decimals for fram…
arttu-peltonen Aug 6, 2021
c0d93a3
Add DebugUI.ValueTuple that allows column-style layouts together with…
arttu-peltonen Aug 10, 2021
c916aae
Fix assertion when toggling Count Rays option at runtime.
arttu-peltonen Aug 10, 2021
f8f1f31
Rearrange ray tracing debug UI.
arttu-peltonen Aug 11, 2021
753f853
Optimize UpdateAveragedProfilerTimings().
arttu-peltonen Aug 11, 2021
5c1b346
Remove foreach usage because it allocates.
arttu-peltonen Aug 11, 2021
1933e07
Optimize frametime stats calculation & remove allocs.
arttu-peltonen Aug 11, 2021
74f2444
Move FrameTimingData update to render pipeline update.
arttu-peltonen Aug 12, 2021
6839ab6
Improved pinning for ValueTuples.
arttu-peltonen Aug 16, 2021
93b073f
Move DebugUI registering inside FrameTimingData class. Renaming & ref…
arttu-peltonen Aug 17, 2021
1ec6257
Rename, split & reorganize frame timing classes.
arttu-peltonen Aug 17, 2021
0e19d9d
Formatting & polish.
arttu-peltonen Aug 17, 2021
a054440
Add realtime profiler for URP.
arttu-peltonen Aug 18, 2021
28505ca
Merge commit 'a8abe4c5d857cbada0535ec3a5ca770265b639ed' into srp/real…
arttu-peltonen Aug 19, 2021
79599e6
Apply reformat changes
arttu-peltonen Aug 19, 2021
2515758
Merge commit '7d292932bec3b4257a4defaf698fc7d77e2027f5' into srp/real…
arttu-peltonen Aug 19, 2021
8fc32d8
Remove unused SRP_RealtimeProfiler test project.
arttu-peltonen Aug 19, 2021
ddc3d21
Fix test compilation + cleanup unnecessary changes.
arttu-peltonen Aug 24, 2021
3983c0e
Remove unstable bottleneck validation tests, leave sanity test for no…
arttu-peltonen Aug 24, 2021
3d0ef52
Enable frametiming data in HDRP_Tests instead of HDRP_RuntimeTests.
arttu-peltonen Aug 24, 2021
5e4f2dd
Use preprocessor defines instead of Debug.isDebugBuild, which can be …
arttu-peltonen Aug 25, 2021
9bea634
Fix mobile issues
arttu-peltonen Aug 25, 2021
a88be08
Fix bug where DebugManager.displayEditorUI sometimes had the wrong va…
arttu-peltonen Aug 26, 2021
f9e7daf
Merge branch 'master' into srp/realtimeprofiler-proto
arttu-peltonen Sep 8, 2021
ad106af
Refactor ComputeAggregateValues to ignore zero values.
arttu-peltonen Sep 9, 2021
1be2ac6
Code review feedback + better handling of invalid frametime values.
arttu-peltonen Sep 9, 2021
b9fe1c2
Review feedback
arttu-peltonen Sep 13, 2021
6b998bd
Fix unnecessary memory allocations.
arttu-peltonen Sep 14, 2021
dd1ce26
Merge branch 'master' into srp/realtimeprofiler-proto
arttu-peltonen Sep 28, 2021
83a4e46
Merge branch 'master' into srp/realtimeprofiler-proto
arttu-peltonen Sep 28, 2021
98aa73f
Adapt to new debugdisplay API changes, revert IDebugDisplaySettingsDa…
arttu-peltonen Sep 28, 2021
dbedc16
Merge branch 'master' into srp/realtimeprofiler-proto
arttu-peltonen Oct 21, 2021
84d4ca6
Changelogs
arttu-peltonen Oct 21, 2021
c29d651
Merge branch 'master' into srp/realtimeprofiler-proto
arttu-peltonen Oct 22, 2021
9afdf0f
Merge branch 'master' into srp/realtimeprofiler-proto
arttu-peltonen Oct 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ PlayerSettings:
enable360StereoCapture: 0
isWsaHolographicRemotingEnabled: 0
protectGraphicsMemory: 0
enableFrameTimingStats: 0
enableFrameTimingStats: 1
useHDRDisplay: 0
m_ColorGamuts: 00000000
targetPixelDensity: 30
Expand Down
2 changes: 2 additions & 0 deletions com.unity.render-pipelines.core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added
- Added support for high performant unsafe (uint only) Radix, Merge and Insertion sort algorithms on CoreUnsafeUtils.
- Added DebugFrameTiming class that can be used by render pipelines to display CPU/GPU frame timings and bottlenecks in Rendering Debugger.
- Added new DebugUI widget types: ProgressBarValue and ValueTuple

## [13.1.0] - 2021-09-24

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,70 @@ public override bool OnGUI(DebugUI.Widget widget, DebugState state)
{
var w = Cast<DebugUI.Value>(widget);
var rect = PrepareControlRect();
EditorGUI.LabelField(rect, EditorGUIUtility.TrTextContent(w.displayName), EditorGUIUtility.TrTextContent(w.GetValue().ToString()));
var value = w.GetValue();
EditorGUI.LabelField(rect, EditorGUIUtility.TrTextContent(w.displayName), EditorGUIUtility.TrTextContent(w.FormatString(value)));
return true;
}
}

/// <summary>
/// Builtin Drawer for ValueTuple Debug Items.
/// </summary>
[DebugUIDrawer(typeof(DebugUI.ValueTuple))]
public sealed class DebugUIDrawerValueTuple : DebugUIDrawer
{
/// <summary>
/// OnGUI implementation for ValueTuple DebugUIDrawer.
/// </summary>
/// <param name="widget">DebugUI Widget.</param>
/// <param name="state">Debug State associated with the Debug Item.</param>
/// <returns>The state of the widget.</returns>
public override bool OnGUI(DebugUI.Widget widget, DebugState state)
{
var w = Cast<DebugUI.ValueTuple>(widget);

var labelRect = PrepareControlRect();
EditorGUI.PrefixLabel(labelRect, EditorGUIUtility.TrTextContent(w.displayName));

// Following layout should match DebugUIDrawerFoldout to make column labels align
Rect drawRect = GUILayoutUtility.GetLastRect();
int indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0; //be at left of rects
for (int i = 0; i < w.numElements; i++)
{
var columnRect = drawRect;
columnRect.x += EditorGUIUtility.labelWidth + i * DebugWindow.Styles.foldoutColumnWidth;
columnRect.width = DebugWindow.Styles.foldoutColumnWidth;
var value = w.values[i].GetValue();
EditorGUI.LabelField(columnRect, w.values[i].FormatString(value));
}
EditorGUI.indentLevel = indent;

return true;
}
}

/// <summary>
/// Builtin Drawer for ProgressBarValue Debug Items.
/// </summary>
[DebugUIDrawer(typeof(DebugUI.ProgressBarValue))]
public sealed class DebugUIDrawerProgressBarValue : DebugUIDrawer
{
/// <summary>
/// OnGUI implementation for Value DebugUIDrawer.
/// </summary>
/// <param name="widget">DebugUI Widget.</param>
/// <param name="state">Debug State associated with the Debug Item.</param>
/// <returns>The state of the widget.</returns>
public override bool OnGUI(DebugUI.Widget widget, DebugState state)
{
var w = Cast<DebugUI.ProgressBarValue>(widget);

var labelRect = PrepareControlRect();
var progressBarRect = EditorGUI.PrefixLabel(labelRect, EditorGUIUtility.TrTextContent(w.displayName));
float value = (float)w.GetValue();
EditorGUI.ProgressBar(progressBarRect, value, w.FormatString(value));

return true;
}
}
Expand Down Expand Up @@ -435,14 +498,13 @@ public override void Begin(DebugUI.Widget widget, DebugState state)
Rect drawRect = GUILayoutUtility.GetLastRect();
if (w.columnLabels != null && value)
{
const int oneColumnWidth = 70;
int indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0; //be at left of rects
for (int i = 0; i < w.columnLabels.Length; i++)
{
var columnRect = drawRect;
columnRect.x += EditorGUIUtility.labelWidth + i * oneColumnWidth;
columnRect.width = oneColumnWidth;
columnRect.x += EditorGUIUtility.labelWidth + i * DebugWindow.Styles.foldoutColumnWidth;
columnRect.width = DebugWindow.Styles.foldoutColumnWidth;
string label = w.columnLabels[i] ?? "";
string tooltip = w.columnTooltips?.ElementAtOrDefault(i) ?? "";
EditorGUI.LabelField(columnRect, EditorGUIUtility.TrTextContent(label, tooltip), EditorStyles.miniBoldLabel);
Expand All @@ -461,8 +523,8 @@ public override void Begin(DebugUI.Widget widget, DebugState state)
/// <returns>The state of the widget.</returns>
public override bool OnGUI(DebugUI.Widget widget, DebugState state)
{
var s = Cast<DebugStateBool>(state);
return s.value;
var w = Cast<DebugUI.Foldout>(widget);
return w.opened;
}

/// <summary>
Expand Down
12 changes: 7 additions & 5 deletions com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,6 @@ static void Init()
{
var window = GetWindow<DebugWindow>();
window.titleContent = Styles.windowTitle;

if (OnDebugWindowToggled == null)
OnDebugWindowToggled += DebugManager.instance.ToggleEditorUI;

open = true;
}

[MenuItem("Window/Analysis/Rendering Debugger", validate = true)]
Expand All @@ -145,6 +140,11 @@ static bool ValidateMenuItem()

void OnEnable()
{
if (OnDebugWindowToggled == null)
OnDebugWindowToggled += DebugManager.instance.ToggleEditorUI;

open = true;

DebugManager.instance.refreshEditorRequested = false;

hideFlags = HideFlags.HideAndDontSave;
Expand Down Expand Up @@ -564,6 +564,8 @@ public class Styles
public readonly GUIStyle sectionHeader = new GUIStyle(EditorStyles.largeLabel);
public readonly Color skinBackgroundColor;

public static int foldoutColumnWidth = 70;

public Styles()
{
Color textColorDarkSkin = new Color32(210, 210, 210, 255);
Expand Down
1 change: 1 addition & 0 deletions com.unity.render-pipelines.core/Runtime/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
#endif

[assembly: InternalsVisibleTo("Unity.RenderPipelines.Core.Editor")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Core.Runtime.Tests")]
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public abstract class DebugDisplaySettingsPanel : IDebugDisplaySettingsPanelDisp

public abstract string PanelName { get; }
public DebugUI.Widget[] Widgets => m_Widgets.ToArray();
public virtual DebugUI.Flags Flags => DebugUI.Flags.None;

protected void AddWidget(DebugUI.Widget widget)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public void RegisterDebug(IDebugDisplaySettings settings)
DebugUI.Panel panel = debugManager.GetPanel(panelId, true);
ObservableList<DebugUI.Widget> panelChildren = panel.children;

panel.flags = disposableSettingsPanel.Flags;
panels.Add(disposableSettingsPanel);
panelChildren.Add(panelWidgets);
};
Expand Down
201 changes: 201 additions & 0 deletions com.unity.render-pipelines.core/Runtime/Debugging/DebugFrameTiming.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

//#define RTPROFILER_DEBUG

namespace UnityEngine.Rendering
{
public class DebugFrameTiming
{
const string k_FpsFormatString = "{0:F1}";
const string k_MsFormatString = "{0:F2}ms";
const float k_RefreshRate = 1f / 5f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this value for the refresh rate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same value that HDRP had previously, so I didn't change it. I guess the refresh rate is a compromise between performance (it costs something to update the value), readability (too quickly changing values can be hard to read) and correctness (how up to date the value is). I think this value is a reasonable compromise.


internal FrameTimeSampleHistory m_FrameHistory = new();
internal BottleneckHistory m_BottleneckHistory = new();

/// <summary>
/// Size of the Bottleneck History Window in number of samples.
/// </summary>
public int bottleneckHistorySize { get; set; } = 60;

/// <summary>
/// Size of the Sample History Window in number of samples.
/// </summary>
public int sampleHistorySize { get; set; } = 30;

FrameTiming[] m_Timing = new FrameTiming[1];
FrameTimeSample m_Sample = new FrameTimeSample();

/// <summary>
/// Update timing data from profiling counters.
/// </summary>
public void UpdateFrameTiming()
{
m_Timing[0] = default;
m_Sample = default;
FrameTimingManager.CaptureFrameTimings();
FrameTimingManager.GetLatestTimings(1, m_Timing);

if (m_Timing.Length > 0)
{
m_Sample.FullFrameTime = (float)m_Timing.First().cpuFrameTime;
m_Sample.FramesPerSecond = m_Sample.FullFrameTime > 0f ? 1000f / m_Sample.FullFrameTime : 0f;
m_Sample.MainThreadCPUFrameTime = (float)m_Timing.First().cpuMainThreadFrameTime;
m_Sample.MainThreadCPUPresentWaitTime = (float)m_Timing.First().cpuMainThreadPresentWaitTime;
m_Sample.RenderThreadCPUFrameTime = (float)m_Timing.First().cpuRenderThreadFrameTime;
m_Sample.GPUFrameTime = (float)m_Timing.First().gpuFrameTime;
}

m_FrameHistory.DiscardOldSamples(sampleHistorySize);
m_FrameHistory.Add(m_Sample);
m_FrameHistory.ComputeAggregateValues();

m_BottleneckHistory.DiscardOldSamples(bottleneckHistorySize);
m_BottleneckHistory.AddBottleneckFromAveragedSample(m_FrameHistory.SampleAverage);
m_BottleneckHistory.ComputeHistogram();
}

/// <summary>
/// Add frame timing data widgets to debug UI.
/// </summary>
/// <param name="list">List of widgets to add the stats.</param>
public void RegisterDebugUI(List<DebugUI.Widget> list)
{
list.Add(new DebugUI.Foldout()
{
displayName = "Frame Stats",
opened = true,
columnLabels = new string[] { "Avg", "Min", "Max" },
children =
{
new DebugUI.ValueTuple
{
displayName = "Frame Rate (FPS)",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FpsFormatString, getter = () => m_FrameHistory.SampleAverage.FramesPerSecond },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FpsFormatString, getter = () => m_FrameHistory.SampleMin.FramesPerSecond },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_FpsFormatString, getter = () => m_FrameHistory.SampleMax.FramesPerSecond },
}
},
new DebugUI.ValueTuple
{
displayName = "Frame Time",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.FullFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.FullFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.FullFrameTime },
}
},
new DebugUI.ValueTuple
{
displayName = "CPU Main Thread Frame",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.MainThreadCPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.MainThreadCPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.MainThreadCPUFrameTime },
}
},
new DebugUI.ValueTuple
{
displayName = "CPU Render Thread Frame",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.RenderThreadCPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.RenderThreadCPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.RenderThreadCPUFrameTime },
}
},
new DebugUI.ValueTuple
{
displayName = "CPU Present Wait",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.MainThreadCPUPresentWaitTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.MainThreadCPUPresentWaitTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.MainThreadCPUPresentWaitTime },
}
},
new DebugUI.ValueTuple
{
displayName = "GPU Frame",
values = new[]
{
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleAverage.GPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMin.GPUFrameTime },
new DebugUI.Value { refreshRate = k_RefreshRate, formatString = k_MsFormatString, getter = () => m_FrameHistory.SampleMax.GPUFrameTime },
}
}
}
});

list.Add(new DebugUI.Foldout
{
displayName = "Bottlenecks",
children =
{
#if UNITY_EDITOR
new DebugUI.Container { displayName = "Not supported in Editor" }
#else
new DebugUI.ProgressBarValue { displayName = "CPU", getter = () => m_BottleneckHistory.Histogram.CPU },
new DebugUI.ProgressBarValue { displayName = "GPU", getter = () => m_BottleneckHistory.Histogram.GPU },
new DebugUI.ProgressBarValue { displayName = "Present limited", getter = () => m_BottleneckHistory.Histogram.PresentLimited },
new DebugUI.ProgressBarValue { displayName = "Balanced", getter = () => m_BottleneckHistory.Histogram.Balanced },
#endif
}
});
#if RTPROFILER_DEBUG
list.Add(new DebugUI.Foldout
{
displayName = "Realtime Profiler Debug",
children =
{
new DebugUI.IntField
{
displayName = "Frame Time Sample History Size",
getter = () => SampleHistorySize,
setter = (value) => { SampleHistorySize = value; },
min = () => 1,
max = () => 100
},
new DebugUI.IntField
{
displayName = "Bottleneck History Size",
getter = () => BottleneckHistorySize,
setter = (value) => { BottleneckHistorySize = value; },
min = () => 1,
max = () => 100
},
new DebugUI.IntField
{
displayName = "Force VSyncCount",
min = () => - 1,
max = () => 4,
getter = () => QualitySettings.vSyncCount,
setter = (value) => { QualitySettings.vSyncCount = value; }
},
new DebugUI.IntField
{
displayName = "Force TargetFrameRate",
min = () => - 1,
max = () => 1000,
getter = () => Application.targetFrameRate,
setter = (value) => { Application.targetFrameRate = value; }
},
}
});
#endif
}

internal void Reset()
{
m_BottleneckHistory.Clear();
m_FrameHistory.Clear();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading