Нема описа
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DebugDisplayStats.cs 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace UnityEngine.Rendering
  5. {
  6. /// <summary>
  7. /// Base class for Rendering Debugger Display Stats.
  8. /// </summary>
  9. /// <typeparam name="TProfileId">Type of ProfileId the pipeline uses</typeparam>
  10. public abstract class DebugDisplayStats<TProfileId> where TProfileId : Enum
  11. {
  12. // Accumulate values to avg over one second.
  13. private class AccumulatedTiming
  14. {
  15. public float accumulatedValue = 0;
  16. public float lastAverage = 0;
  17. internal void UpdateLastAverage(int frameCount)
  18. {
  19. lastAverage = accumulatedValue / frameCount;
  20. accumulatedValue = 0.0f;
  21. }
  22. }
  23. private enum DebugProfilingType
  24. {
  25. CPU,
  26. InlineCPU,
  27. GPU
  28. }
  29. /// <summary>
  30. /// Enable profiling recorders.
  31. /// </summary>
  32. public abstract void EnableProfilingRecorders();
  33. /// <summary>
  34. /// Disable all active profiling recorders.
  35. /// </summary>
  36. public abstract void DisableProfilingRecorders();
  37. /// <summary>
  38. /// Add display stats widgets to the list provided.
  39. /// </summary>
  40. /// <param name="list">List to add the widgets to.</param>
  41. public abstract void RegisterDebugUI(List<DebugUI.Widget> list);
  42. /// <summary>
  43. /// Update the timing data displayed in Display Stats panel.
  44. /// </summary>
  45. public abstract void Update();
  46. /// <summary>
  47. /// Helper function to get all TProfilerId values of a given type to show in Detailed Stats section.
  48. /// </summary>
  49. /// <returns>List of TProfileId values excluding ones marked with [HideInDebugUI]</returns>
  50. protected List<TProfileId> GetProfilerIdsToDisplay()
  51. {
  52. List<TProfileId> ids = new();
  53. var type = typeof(TProfileId);
  54. var enumValues = Enum.GetValues(type);
  55. foreach (var enumValue in enumValues)
  56. {
  57. var memberInfos = type.GetMember(enumValue.ToString());
  58. var enumValueMemberInfo = memberInfos.First(m => m.DeclaringType == type);
  59. var hiddenAttribute = Attribute.GetCustomAttribute(enumValueMemberInfo, typeof(HideInDebugUIAttribute));
  60. if (hiddenAttribute == null)
  61. ids.Add((TProfileId)enumValue);
  62. }
  63. return ids;
  64. }
  65. /// <summary>
  66. /// Update the detailed stats
  67. /// </summary>
  68. /// <param name="samplers">List of samplers to update</param>
  69. protected void UpdateDetailedStats(List<TProfileId> samplers)
  70. {
  71. m_HiddenProfileIds.Clear();
  72. m_TimeSinceLastAvgValue += Time.unscaledDeltaTime;
  73. m_AccumulatedFrames++;
  74. bool needUpdatingAverages = m_TimeSinceLastAvgValue >= k_AccumulationTimeInSeconds;
  75. UpdateListOfAveragedProfilerTimings(needUpdatingAverages, samplers);
  76. if (needUpdatingAverages)
  77. {
  78. m_TimeSinceLastAvgValue = 0.0f;
  79. m_AccumulatedFrames = 0;
  80. }
  81. }
  82. private static readonly string[] k_DetailedStatsColumnLabels = {"CPU", "CPUInline", "GPU"};
  83. private Dictionary<TProfileId, AccumulatedTiming>[] m_AccumulatedTiming = { new(), new(), new() };
  84. private float m_TimeSinceLastAvgValue = 0.0f;
  85. private int m_AccumulatedFrames = 0;
  86. private HashSet<TProfileId> m_HiddenProfileIds = new();
  87. private const float k_AccumulationTimeInSeconds = 1.0f;
  88. /// <summary> Whether to display timings averaged over a second instead of updating every frame. </summary>
  89. protected bool averageProfilerTimingsOverASecond = false;
  90. /// <summary> Whether to hide empty scopes from UI. </summary>
  91. protected bool hideEmptyScopes = true;
  92. /// <summary>
  93. /// Helper function to build a list of sampler widgets for display stats
  94. /// </summary>
  95. /// <param name="title">Title for the stats list foldout</param>
  96. /// <param name="samplers">List of samplers to display</param>
  97. /// <returns>Foldout containing the list of sampler widgets</returns>
  98. protected DebugUI.Widget BuildDetailedStatsList(string title, List<TProfileId> samplers)
  99. {
  100. var foldout = new DebugUI.Foldout(title, BuildProfilingSamplerWidgetList(samplers), k_DetailedStatsColumnLabels);
  101. foldout.opened = true;
  102. return foldout;
  103. }
  104. private void UpdateListOfAveragedProfilerTimings(bool needUpdatingAverages, List<TProfileId> samplers)
  105. {
  106. foreach (var samplerId in samplers)
  107. {
  108. var sampler = ProfilingSampler.Get(samplerId);
  109. // Accumulate.
  110. bool allValuesZero = true;
  111. if (m_AccumulatedTiming[(int) DebugProfilingType.CPU].TryGetValue(samplerId, out var accCPUTiming))
  112. {
  113. accCPUTiming.accumulatedValue += sampler.cpuElapsedTime;
  114. allValuesZero &= accCPUTiming.accumulatedValue == 0;
  115. }
  116. if (m_AccumulatedTiming[(int)DebugProfilingType.InlineCPU].TryGetValue(samplerId, out var accInlineCPUTiming))
  117. {
  118. accInlineCPUTiming.accumulatedValue += sampler.inlineCpuElapsedTime;
  119. allValuesZero &= accInlineCPUTiming.accumulatedValue == 0;
  120. }
  121. if (m_AccumulatedTiming[(int)DebugProfilingType.GPU].TryGetValue(samplerId, out var accGPUTiming))
  122. {
  123. accGPUTiming.accumulatedValue += sampler.gpuElapsedTime;
  124. allValuesZero &= accGPUTiming.accumulatedValue == 0;
  125. }
  126. if (needUpdatingAverages)
  127. {
  128. accCPUTiming?.UpdateLastAverage(m_AccumulatedFrames);
  129. accInlineCPUTiming?.UpdateLastAverage(m_AccumulatedFrames);
  130. accGPUTiming?.UpdateLastAverage(m_AccumulatedFrames);
  131. }
  132. // Update visibility status based on whether each accumulated value of this scope is zero
  133. if (allValuesZero)
  134. m_HiddenProfileIds.Add(samplerId);
  135. }
  136. }
  137. private float GetSamplerTiming(TProfileId samplerId, ProfilingSampler sampler, DebugProfilingType type)
  138. {
  139. if (averageProfilerTimingsOverASecond)
  140. {
  141. // Find the right accumulated dictionary
  142. if (m_AccumulatedTiming[(int)type].TryGetValue(samplerId, out AccumulatedTiming accTiming))
  143. return accTiming.lastAverage;
  144. }
  145. return (type == DebugProfilingType.CPU)
  146. ? sampler.cpuElapsedTime
  147. : ((type == DebugProfilingType.GPU) ? sampler.gpuElapsedTime : sampler.inlineCpuElapsedTime);
  148. }
  149. private ObservableList<DebugUI.Widget> BuildProfilingSamplerWidgetList(IEnumerable<TProfileId> samplers)
  150. {
  151. var result = new ObservableList<DebugUI.Widget>();
  152. DebugUI.Value CreateWidgetForSampler(TProfileId samplerId, ProfilingSampler sampler,
  153. DebugProfilingType type)
  154. {
  155. // Find the right accumulated dictionary and add it there if not existing yet.
  156. var accumulatedDictionary = m_AccumulatedTiming[(int)type];
  157. if (!accumulatedDictionary.ContainsKey(samplerId))
  158. {
  159. accumulatedDictionary.Add(samplerId, new AccumulatedTiming());
  160. }
  161. return new()
  162. {
  163. formatString = "{0:F2}ms",
  164. refreshRate = 1.0f / 5.0f,
  165. getter = () => GetSamplerTiming(samplerId, sampler, type)
  166. };
  167. }
  168. foreach (var samplerId in samplers)
  169. {
  170. var sampler = ProfilingSampler.Get(samplerId);
  171. // In non-dev build ProfilingSampler.Get always returns null.
  172. if (sampler == null)
  173. continue;
  174. sampler.enableRecording = true;
  175. result.Add(new DebugUI.ValueTuple
  176. {
  177. displayName = sampler.name,
  178. isHiddenCallback = () =>
  179. {
  180. if (hideEmptyScopes && m_HiddenProfileIds.Contains(samplerId))
  181. return true;
  182. return false;
  183. },
  184. values = Enum.GetValues(typeof(DebugProfilingType)).Cast<DebugProfilingType>()
  185. .Select(e => CreateWidgetForSampler(samplerId, sampler, e)).ToArray()
  186. });
  187. }
  188. return result;
  189. }
  190. }
  191. }