123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
-
- namespace UnityEngine.Rendering
- {
- /// <summary>
- /// Base class for Rendering Debugger Display Stats.
- /// </summary>
- /// <typeparam name="TProfileId">Type of ProfileId the pipeline uses</typeparam>
- public abstract class DebugDisplayStats<TProfileId> where TProfileId : Enum
- {
- // Accumulate values to avg over one second.
- private class AccumulatedTiming
- {
- public float accumulatedValue = 0;
- public float lastAverage = 0;
-
- internal void UpdateLastAverage(int frameCount)
- {
- lastAverage = accumulatedValue / frameCount;
- accumulatedValue = 0.0f;
- }
- }
-
- private enum DebugProfilingType
- {
- CPU,
- InlineCPU,
- GPU
- }
-
- /// <summary>
- /// Enable profiling recorders.
- /// </summary>
- public abstract void EnableProfilingRecorders();
-
- /// <summary>
- /// Disable all active profiling recorders.
- /// </summary>
- public abstract void DisableProfilingRecorders();
-
- /// <summary>
- /// Add display stats widgets to the list provided.
- /// </summary>
- /// <param name="list">List to add the widgets to.</param>
- public abstract void RegisterDebugUI(List<DebugUI.Widget> list);
-
- /// <summary>
- /// Update the timing data displayed in Display Stats panel.
- /// </summary>
- public abstract void Update();
-
- /// <summary>
- /// Helper function to get all TProfilerId values of a given type to show in Detailed Stats section.
- /// </summary>
- /// <returns>List of TProfileId values excluding ones marked with [HideInDebugUI]</returns>
- protected List<TProfileId> GetProfilerIdsToDisplay()
- {
- List<TProfileId> ids = new();
- var type = typeof(TProfileId);
-
- var enumValues = Enum.GetValues(type);
- foreach (var enumValue in enumValues)
- {
- var memberInfos = type.GetMember(enumValue.ToString());
- var enumValueMemberInfo = memberInfos.First(m => m.DeclaringType == type);
- var hiddenAttribute = Attribute.GetCustomAttribute(enumValueMemberInfo, typeof(HideInDebugUIAttribute));
- if (hiddenAttribute == null)
- ids.Add((TProfileId)enumValue);
- }
-
- return ids;
- }
-
- /// <summary>
- /// Update the detailed stats
- /// </summary>
- /// <param name="samplers">List of samplers to update</param>
- protected void UpdateDetailedStats(List<TProfileId> samplers)
- {
- m_HiddenProfileIds.Clear();
-
- m_TimeSinceLastAvgValue += Time.unscaledDeltaTime;
- m_AccumulatedFrames++;
- bool needUpdatingAverages = m_TimeSinceLastAvgValue >= k_AccumulationTimeInSeconds;
-
- UpdateListOfAveragedProfilerTimings(needUpdatingAverages, samplers);
-
- if (needUpdatingAverages)
- {
- m_TimeSinceLastAvgValue = 0.0f;
- m_AccumulatedFrames = 0;
- }
- }
-
- private static readonly string[] k_DetailedStatsColumnLabels = {"CPU", "CPUInline", "GPU"};
- private Dictionary<TProfileId, AccumulatedTiming>[] m_AccumulatedTiming = { new(), new(), new() };
- private float m_TimeSinceLastAvgValue = 0.0f;
- private int m_AccumulatedFrames = 0;
- private HashSet<TProfileId> m_HiddenProfileIds = new();
-
- private const float k_AccumulationTimeInSeconds = 1.0f;
-
- /// <summary> Whether to display timings averaged over a second instead of updating every frame. </summary>
- protected bool averageProfilerTimingsOverASecond = false;
-
- /// <summary> Whether to hide empty scopes from UI. </summary>
- protected bool hideEmptyScopes = true;
-
- /// <summary>
- /// Helper function to build a list of sampler widgets for display stats
- /// </summary>
- /// <param name="title">Title for the stats list foldout</param>
- /// <param name="samplers">List of samplers to display</param>
- /// <returns>Foldout containing the list of sampler widgets</returns>
- protected DebugUI.Widget BuildDetailedStatsList(string title, List<TProfileId> samplers)
- {
- var foldout = new DebugUI.Foldout(title, BuildProfilingSamplerWidgetList(samplers), k_DetailedStatsColumnLabels);
- foldout.opened = true;
- return foldout;
- }
-
- private void UpdateListOfAveragedProfilerTimings(bool needUpdatingAverages, List<TProfileId> samplers)
- {
- foreach (var samplerId in samplers)
- {
- var sampler = ProfilingSampler.Get(samplerId);
-
- // Accumulate.
- bool allValuesZero = true;
- if (m_AccumulatedTiming[(int) DebugProfilingType.CPU].TryGetValue(samplerId, out var accCPUTiming))
- {
- accCPUTiming.accumulatedValue += sampler.cpuElapsedTime;
- allValuesZero &= accCPUTiming.accumulatedValue == 0;
- }
-
- if (m_AccumulatedTiming[(int)DebugProfilingType.InlineCPU].TryGetValue(samplerId, out var accInlineCPUTiming))
- {
- accInlineCPUTiming.accumulatedValue += sampler.inlineCpuElapsedTime;
- allValuesZero &= accInlineCPUTiming.accumulatedValue == 0;
- }
-
- if (m_AccumulatedTiming[(int)DebugProfilingType.GPU].TryGetValue(samplerId, out var accGPUTiming))
- {
- accGPUTiming.accumulatedValue += sampler.gpuElapsedTime;
- allValuesZero &= accGPUTiming.accumulatedValue == 0;
- }
-
- if (needUpdatingAverages)
- {
- accCPUTiming?.UpdateLastAverage(m_AccumulatedFrames);
- accInlineCPUTiming?.UpdateLastAverage(m_AccumulatedFrames);
- accGPUTiming?.UpdateLastAverage(m_AccumulatedFrames);
- }
-
- // Update visibility status based on whether each accumulated value of this scope is zero
- if (allValuesZero)
- m_HiddenProfileIds.Add(samplerId);
- }
- }
-
- private float GetSamplerTiming(TProfileId samplerId, ProfilingSampler sampler, DebugProfilingType type)
- {
- if (averageProfilerTimingsOverASecond)
- {
- // Find the right accumulated dictionary
- if (m_AccumulatedTiming[(int)type].TryGetValue(samplerId, out AccumulatedTiming accTiming))
- return accTiming.lastAverage;
- }
-
- return (type == DebugProfilingType.CPU)
- ? sampler.cpuElapsedTime
- : ((type == DebugProfilingType.GPU) ? sampler.gpuElapsedTime : sampler.inlineCpuElapsedTime);
- }
-
- private ObservableList<DebugUI.Widget> BuildProfilingSamplerWidgetList(IEnumerable<TProfileId> samplers)
- {
- var result = new ObservableList<DebugUI.Widget>();
-
- DebugUI.Value CreateWidgetForSampler(TProfileId samplerId, ProfilingSampler sampler,
- DebugProfilingType type)
- {
- // Find the right accumulated dictionary and add it there if not existing yet.
- var accumulatedDictionary = m_AccumulatedTiming[(int)type];
- if (!accumulatedDictionary.ContainsKey(samplerId))
- {
- accumulatedDictionary.Add(samplerId, new AccumulatedTiming());
- }
-
- return new()
- {
- formatString = "{0:F2}ms",
- refreshRate = 1.0f / 5.0f,
- getter = () => GetSamplerTiming(samplerId, sampler, type)
- };
- }
-
- foreach (var samplerId in samplers)
- {
- var sampler = ProfilingSampler.Get(samplerId);
-
- // In non-dev build ProfilingSampler.Get always returns null.
- if (sampler == null)
- continue;
-
- sampler.enableRecording = true;
-
- result.Add(new DebugUI.ValueTuple
- {
- displayName = sampler.name,
- isHiddenCallback = () =>
- {
- if (hideEmptyScopes && m_HiddenProfileIds.Contains(samplerId))
- return true;
- return false;
- },
- values = Enum.GetValues(typeof(DebugProfilingType)).Cast<DebugProfilingType>()
- .Select(e => CreateWidgetForSampler(samplerId, sampler, e)).ToArray()
- });
- }
-
- return result;
- }
- }
- }
|