123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- // TProfilingSampler<TEnum>.samples should just be an array. Unfortunately, Enum cannot be converted to int without generating garbage.
- // This could be worked around by using Unsafe but it's not available at the moment.
- // So in the meantime we use a Dictionary with a perf hit...
- //#define USE_UNSAFE
-
- #if UNITY_2020_1_OR_NEWER
- #define UNITY_USE_RECORDER
- #endif
-
- using System;
- using System.Linq;
- using System.Collections.Generic;
- using UnityEngine.Profiling;
- using Unity.Profiling;
-
-
- namespace UnityEngine.Rendering
- {
- class TProfilingSampler<TEnum> : ProfilingSampler where TEnum : Enum
- {
- #if USE_UNSAFE
- internal static TProfilingSampler<TEnum>[] samples;
- #else
- internal static Dictionary<TEnum, TProfilingSampler<TEnum>> samples = new Dictionary<TEnum, TProfilingSampler<TEnum>>();
- #endif
- static TProfilingSampler()
- {
- var names = Enum.GetNames(typeof(TEnum));
- #if USE_UNSAFE
- var values = Enum.GetValues(typeof(TEnum)).Cast<int>().ToArray();
- samples = new TProfilingSampler<TEnum>[values.Max() + 1];
- #else
- var values = Enum.GetValues(typeof(TEnum));
- #endif
-
- for (int i = 0; i < names.Length; i++)
- {
- var sample = new TProfilingSampler<TEnum>(names[i]);
- #if USE_UNSAFE
- samples[values[i]] = sample;
- #else
- samples.Add((TEnum)values.GetValue(i), sample);
- #endif
- }
- }
-
- public TProfilingSampler(string name)
- : base(name)
- {
- }
- }
-
- /// <summary>
- /// Wrapper around CPU and GPU profiling samplers.
- /// Use this along ProfilingScope to profile a piece of code.
- /// </summary>
- [IgnoredByDeepProfiler]
- public class ProfilingSampler
- {
- /// <summary>
- /// Get the sampler for the corresponding enumeration value.
- /// </summary>
- /// <typeparam name="TEnum">Type of the enumeration.</typeparam>
- /// <param name="marker">Enumeration value.</param>
- /// <returns>The profiling sampler for the given enumeration value.</returns>
- #if DEVELOPMENT_BUILD || UNITY_EDITOR
- public static ProfilingSampler Get<TEnum>(TEnum marker)
- where TEnum : Enum
- {
- #if USE_UNSAFE
- return TProfilingSampler<TEnum>.samples[Unsafe.As<TEnum, int>(ref marker)];
- #else
- TProfilingSampler<TEnum>.samples.TryGetValue(marker, out var sampler);
- return sampler;
- #endif
- }
- #else
- public static ProfilingSampler Get<TEnum>(TEnum marker)
- where TEnum : Enum
- {
- return null;
- }
- #endif
-
- /// <summary>
- /// Constructor.
- /// </summary>
- /// <param name="name">Name of the profiling sampler.</param>
- public ProfilingSampler(string name)
- {
- // Caution: Name of sampler MUST not match name provide to cmd.BeginSample(), otherwise
- // we get a mismatch of marker when enabling the profiler.
- #if UNITY_USE_RECORDER
- sampler = CustomSampler.Create(name, true); // Event markers, command buffer CPU profiling and GPU profiling
- #else
- // In this case, we need to use the BeginSample(string) API, since it creates a new sampler by that name under the hood,
- // we need rename this sampler to not clash with the implicit one (it won't be used in this case)
- sampler = CustomSampler.Create($"Dummy_{name}");
- #endif
- inlineSampler = CustomSampler.Create($"Inl_{name}"); // Profiles code "immediately"
- this.name = name;
-
- #if UNITY_USE_RECORDER
- m_Recorder = sampler.GetRecorder();
- m_Recorder.enabled = false;
- m_InlineRecorder = inlineSampler.GetRecorder();
- m_InlineRecorder.enabled = false;
- #endif
- }
-
- /// <summary>
- /// Begin the profiling block.
- /// </summary>
- /// <param name="cmd">Command buffer used by the profiling block.</param>
- public void Begin(CommandBuffer cmd)
- {
- if (cmd != null)
- #if UNITY_USE_RECORDER
- if (sampler != null && sampler.isValid)
- cmd.BeginSample(sampler);
- else
- cmd.BeginSample(name);
- #else
- cmd.BeginSample(name);
- #endif
- inlineSampler?.Begin();
- }
-
- /// <summary>
- /// End the profiling block.
- /// </summary>
- /// <param name="cmd">Command buffer used by the profiling block.</param>
- public void End(CommandBuffer cmd)
- {
- if (cmd != null)
- #if UNITY_USE_RECORDER
- if (sampler != null && sampler.isValid)
- cmd.EndSample(sampler);
- else
- cmd.EndSample(name);
- #else
- m_Cmd.EndSample(name);
- #endif
- inlineSampler?.End();
- }
-
- internal bool IsValid() { return (sampler != null && inlineSampler != null); }
-
- internal CustomSampler sampler { get; private set; }
- internal CustomSampler inlineSampler { get; private set; }
- /// <summary>
- /// Name of the Profiling Sampler
- /// </summary>
- public string name { get; private set; }
-
- #if UNITY_USE_RECORDER
- Recorder m_Recorder;
- Recorder m_InlineRecorder;
- #endif
-
- /// <summary>
- /// Set to true to enable recording of profiling sampler timings.
- /// </summary>
- public bool enableRecording
- {
- set
- {
- #if UNITY_USE_RECORDER
- m_Recorder.enabled = value;
- m_InlineRecorder.enabled = value;
- #endif
- }
- }
-
- #if UNITY_USE_RECORDER
- /// <summary>
- /// GPU Elapsed time in milliseconds.
- /// </summary>
- public float gpuElapsedTime => m_Recorder.enabled ? m_Recorder.gpuElapsedNanoseconds / 1000000.0f : 0.0f;
- /// <summary>
- /// Number of times the Profiling Sampler has hit on the GPU
- /// </summary>
- public int gpuSampleCount => m_Recorder.enabled ? m_Recorder.gpuSampleBlockCount : 0;
- /// <summary>
- /// CPU Elapsed time in milliseconds (Command Buffer execution).
- /// </summary>
- public float cpuElapsedTime => m_Recorder.enabled ? m_Recorder.elapsedNanoseconds / 1000000.0f : 0.0f;
- /// <summary>
- /// Number of times the Profiling Sampler has hit on the CPU in the command buffer.
- /// </summary>
- public int cpuSampleCount => m_Recorder.enabled ? m_Recorder.sampleBlockCount : 0;
- /// <summary>
- /// CPU Elapsed time in milliseconds (Direct execution).
- /// </summary>
- public float inlineCpuElapsedTime => m_InlineRecorder.enabled ? m_InlineRecorder.elapsedNanoseconds / 1000000.0f : 0.0f;
- /// <summary>
- /// Number of times the Profiling Sampler has hit on the CPU.
- /// </summary>
- public int inlineCpuSampleCount => m_InlineRecorder.enabled ? m_InlineRecorder.sampleBlockCount : 0;
- #else
- /// <summary>
- /// GPU Elapsed time in milliseconds.
- /// </summary>
- public float gpuElapsedTime => 0.0f;
- /// <summary>
- /// Number of times the Profiling Sampler has hit on the GPU
- /// </summary>
- public int gpuSampleCount => 0;
- /// <summary>
- /// CPU Elapsed time in milliseconds (Command Buffer execution).
- /// </summary>
- public float cpuElapsedTime => 0.0f;
- /// <summary>
- /// Number of times the Profiling Sampler has hit on the CPU in the command buffer.
- /// </summary>
- public int cpuSampleCount => 0;
- /// <summary>
- /// CPU Elapsed time in milliseconds (Direct execution).
- /// </summary>
- public float inlineCpuElapsedTime => 0.0f;
- /// <summary>
- /// Number of times the Profiling Sampler has hit on the CPU.
- /// </summary>
- public int inlineCpuSampleCount => 0;
- #endif
- // Keep the constructor private
- ProfilingSampler() { }
- }
-
- #if DEVELOPMENT_BUILD || UNITY_EDITOR
- /// <summary>
- /// Scoped Profiling markers
- /// </summary>
- [IgnoredByDeepProfiler]
- public struct ProfilingScope : IDisposable
- {
- CommandBuffer m_Cmd;
- bool m_Disposed;
- ProfilingSampler m_Sampler;
-
- /// <summary>
- /// Profiling Scope constructor
- /// </summary>
- /// <param name="sampler">Profiling Sampler to be used for this scope.</param>
- public ProfilingScope(ProfilingSampler sampler)
- {
- m_Cmd = null;
- m_Disposed = false;
- m_Sampler = sampler;
- m_Sampler?.Begin(m_Cmd);
- }
-
- /// <summary>
- /// Profiling Scope constructor
- /// </summary>
- /// <param name="cmd">Command buffer used to add markers and compute execution timings.</param>
- /// <param name="sampler">Profiling Sampler to be used for this scope.</param>
- public ProfilingScope(CommandBuffer cmd, ProfilingSampler sampler)
- {
- // NOTE: Do not mix with named CommandBuffers.
- // Currently there's an issue which results in mismatched markers.
- // The named CommandBuffer will close its "profiling scope" on execution.
- // That will orphan ProfilingScope markers as the named CommandBuffer marker
- // is their "parent".
- // Resulting in following pattern:
- // exec(cmd.start, scope.start, cmd.end) and exec(cmd.start, scope.end, cmd.end)
- m_Cmd = cmd;
- m_Disposed = false;
- m_Sampler = sampler;
- m_Sampler?.Begin(m_Cmd);
- }
-
- /// <summary>
- /// Profiling Scope constructor
- /// </summary>
- /// <param name="cmd">Command buffer used to add markers and compute execution timings.</param>
- /// <param name="sampler">Profiling Sampler to be used for this scope.</param>
- public ProfilingScope(BaseCommandBuffer cmd, ProfilingSampler sampler)
- {
- // NOTE: Do not mix with named CommandBuffers.
- // Currently there's an issue which results in mismatched markers.
- // The named CommandBuffer will close its "profiling scope" on execution.
- // That will orphan ProfilingScope markers as the named CommandBuffer marker
- // is their "parent".
- // Resulting in following pattern:
- // exec(cmd.start, scope.start, cmd.end) and exec(cmd.start, scope.end, cmd.end)
- m_Cmd = cmd.m_WrappedCommandBuffer;
- m_Disposed = false;
- m_Sampler = sampler;
- m_Sampler?.Begin(m_Cmd);
- }
-
- /// <summary>
- /// Dispose pattern implementation
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
-
- // Protected implementation of Dispose pattern.
- void Dispose(bool disposing)
- {
- if (m_Disposed)
- return;
-
- // As this is a struct, it could have been initialized using an empty constructor so we
- // need to make sure `cmd` isn't null to avoid a crash. Switching to a class would fix
- // this but will generate garbage on every frame (and this struct is used quite a lot).
- if (disposing)
- {
- m_Sampler?.End(m_Cmd);
- }
-
- m_Disposed = true;
- }
- }
- #else
- /// <summary>
- /// Scoped Profiling markers
- /// </summary>
- public struct ProfilingScope : IDisposable
- {
- /// <summary>
- /// Profiling Scope constructor
- /// </summary>
- /// <param name="sampler">Profiling Sampler to be used for this scope.</param>
- public ProfilingScope(ProfilingSampler sampler)
- {
- }
-
- /// <summary>
- /// Profiling Scope constructor
- /// </summary>
- /// <param name="cmd">Command buffer used to add markers and compute execution timings.</param>
- /// <param name="sampler">Profiling Sampler to be used for this scope.</param>
- public ProfilingScope(CommandBuffer cmd, ProfilingSampler sampler)
- {
- }
-
- /// <summary>
- /// Profiling Scope constructor
- /// </summary>
- /// <param name="cmd">Command buffer used to add markers and compute execution timings.</param>
- /// <param name="sampler">Profiling Sampler to be used for this scope.</param>
- public ProfilingScope(BaseCommandBuffer cmd, ProfilingSampler sampler)
- {
- }
-
- /// <summary>
- /// Dispose pattern implementation
- /// </summary>
- public void Dispose()
- {
- }
- }
- #endif
-
-
- /// <summary>
- /// Profiling Sampler class.
- /// </summary>
- [System.Obsolete("Please use ProfilingScope")]
- [IgnoredByDeepProfiler]
- public struct ProfilingSample : IDisposable
- {
- readonly CommandBuffer m_Cmd;
- readonly string m_Name;
-
- bool m_Disposed;
- CustomSampler m_Sampler;
-
- /// <summary>
- /// Constructor
- /// </summary>
- /// <param name="cmd">Command Buffer.</param>
- /// <param name="name">Name of the profiling sample.</param>
- /// <param name="sampler">Custom sampler for CPU profiling.</param>
- public ProfilingSample(CommandBuffer cmd, string name, CustomSampler sampler = null)
- {
- m_Cmd = cmd;
- m_Name = name;
- m_Disposed = false;
- if (cmd != null && name != "")
- cmd.BeginSample(name);
- m_Sampler = sampler;
- m_Sampler?.Begin();
- }
-
- // Shortcut to string.Format() using only one argument (reduces Gen0 GC pressure)
- /// <summary>
- /// Constructor
- /// </summary>
- /// <param name="cmd">Command Buffer.</param>
- /// <param name="format">Formating of the profiling sample.</param>
- /// <param name="arg">Parameters for formating the name.</param>
- public ProfilingSample(CommandBuffer cmd, string format, object arg) : this(cmd, string.Format(format, arg))
- {
- }
-
- // Shortcut to string.Format() with variable amount of arguments - for performance critical
- // code you should pre-build & cache the marker name instead of using this
- /// <summary>
- /// Constructor.
- /// </summary>
- /// <param name="cmd">Command Buffer.</param>
- /// <param name="format">Formating of the profiling sample.</param>
- /// <param name="args">Parameters for formating the name.</param>
- public ProfilingSample(CommandBuffer cmd, string format, params object[] args) : this(cmd, string.Format(format, args))
- {
- }
-
- /// <summary>
- /// Dispose pattern implementation
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
-
- // Protected implementation of Dispose pattern.
- void Dispose(bool disposing)
- {
- if (m_Disposed)
- return;
-
- // As this is a struct, it could have been initialized using an empty constructor so we
- // need to make sure `cmd` isn't null to avoid a crash. Switching to a class would fix
- // this but will generate garbage on every frame (and this struct is used quite a lot).
- if (disposing)
- {
- if (m_Cmd != null && m_Name != "")
- m_Cmd.EndSample(m_Name);
- m_Sampler?.End();
- }
-
- m_Disposed = true;
- }
- }
- }
|