216 lines
10 KiB
C#
216 lines
10 KiB
C#
|
//#define RTPROFILER_DEBUG
|
||
|
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using UnityEngine;
|
||
|
|
||
|
namespace UnityEngine.Rendering
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Debug frame timings class
|
||
|
/// </summary>
|
||
|
public class DebugFrameTiming
|
||
|
{
|
||
|
const string k_FpsFormatString = "{0:F1}";
|
||
|
const string k_MsFormatString = "{0:F2}ms";
|
||
|
const float k_RefreshRate = 1f / 5f;
|
||
|
|
||
|
internal FrameTimeSampleHistory m_FrameHistory;
|
||
|
internal BottleneckHistory m_BottleneckHistory;
|
||
|
|
||
|
/// <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>
|
||
|
/// Constructs the debug frame timing
|
||
|
/// </summary>
|
||
|
public DebugFrameTiming()
|
||
|
{
|
||
|
m_FrameHistory = new FrameTimeSampleHistory(sampleHistorySize);
|
||
|
m_BottleneckHistory = new BottleneckHistory(bottleneckHistorySize);
|
||
|
}
|
||
|
|
||
|
/// <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",
|
||
|
isHeader = true,
|
||
|
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",
|
||
|
isHeader = true,
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
}
|