Skip to content

Commit ca2a322

Browse files
alexey-zakharovEvergreen
authored andcommitted
[COPT-3698] Add Object-context overload to ProfilingScope and CommandBuffer.BeginSample
1 parent 0eed3f6 commit ca2a322

File tree

5 files changed

+243
-35
lines changed

5 files changed

+243
-35
lines changed

Packages/com.unity.render-pipelines.core/Runtime/Debugging/ProfilingScope.cs

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ public void Begin(CommandBuffer cmd)
161161
#endif
162162
}
163163

164+
/// <summary>
165+
/// Begins the profiling block with a Unity Object context associated with the sample.
166+
/// Records both a command-buffer marker (if <paramref name="cmd"/> is non-null) and an
167+
/// inline CPU marker.
168+
/// </summary>
169+
/// <param name="cmd">Command buffer to receive the GPU-visible marker.
170+
/// Pass <c>null</c> for CPU-only inline profiling.</param>
171+
/// <param name="contextObject">Unity Object (e.g. Texture, Mesh, Material) to associate
172+
/// with this sample. The Profiler displays it in the sample hierarchy.</param>
173+
[Conditional("ENABLE_PROFILER")]
174+
[MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions)512)]
175+
public void Begin(CommandBuffer cmd, Object contextObject)
176+
{
177+
#if ENABLE_PROFILER
178+
cmd?.BeginSample(m_Marker, contextObject);
179+
m_InlineMarker.Begin(contextObject);
180+
#endif
181+
}
182+
164183
/// <summary>
165184
/// Ends the profiling block started by <see cref="Begin"/> call.
166185
/// </summary>
@@ -345,22 +364,32 @@ public struct ProfilingScope : IDisposable
345364
#if ENABLE_PROFILER
346365
ProfilingSampler m_Sampler;
347366
CommandBuffer m_Cmd;
348-
bool m_Disposed;
349367
#endif
350368

351369
/// <summary>
352370
/// Creates a profiling scope without a command buffer (inline CPU profiling only).
353371
/// </summary>
354372
/// <param name="sampler">The sampler that provides the underlying marker.
355373
/// May be <c>null</c>; the scope is a no-op in that case.</param>
374+
[MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions)512)]
356375
public ProfilingScope(ProfilingSampler sampler)
376+
: this((CommandBuffer)null, sampler)
377+
{
378+
}
379+
380+
/// <summary>
381+
/// Creates an inline CPU-only profiling scope with a Unity Object context associated
382+
/// with the sample. No command buffer is involved; the marker is emitted directly
383+
/// on the CPU timeline.
384+
/// </summary>
385+
/// <param name="sampler">The sampler that provides the underlying marker.
386+
/// May be <c>null</c>; the scope is a no-op in that case.</param>
387+
/// <param name="contextObject">Unity Object (e.g. Texture, Mesh, Material) to associate
388+
/// with this sample. The Profiler displays it in the sample hierarchy.</param>
389+
[MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions)512)]
390+
public ProfilingScope(ProfilingSampler sampler, Object contextObject)
391+
: this(null, sampler, contextObject)
357392
{
358-
#if ENABLE_PROFILER
359-
m_Sampler = sampler;
360-
m_Cmd = null;
361-
m_Disposed = false;
362-
m_Sampler?.Begin(m_Cmd);
363-
#endif
364393
}
365394

366395
/// <summary>
@@ -377,15 +406,29 @@ public ProfilingScope(ProfilingSampler sampler)
377406
public ProfilingScope(CommandBuffer cmd, ProfilingSampler sampler)
378407
{
379408
#if ENABLE_PROFILER
380-
if (sampler != null)
381-
{
382-
m_Disposed = false;
383-
sampler.Begin(cmd);
384-
}
385-
else
386-
{
387-
m_Disposed = true;
388-
}
409+
sampler?.Begin(cmd);
410+
m_Sampler = sampler;
411+
m_Cmd = cmd;
412+
#endif
413+
}
414+
415+
/// <summary>
416+
/// Creates a profiling scope that records markers into <paramref name="cmd"/> with a
417+
/// Unity Object context associated with the sample.
418+
/// </summary>
419+
/// <remarks>
420+
/// Do not use with a named <see cref="CommandBuffer"/>. A named command buffer inserts
421+
/// its own scope marker on execution, which orphans the markers added here.
422+
/// </remarks>
423+
/// <param name="cmd">Command buffer to receive the GPU-visible begin/end markers.</param>
424+
/// <param name="sampler">The sampler that provides the underlying marker.
425+
/// May be <c>null</c>; the scope is a no-op in that case.</param>
426+
/// <param name="contextObject">Unity Object (e.g. Texture, Mesh, Material) to associate
427+
/// with this sample. The Profiler displays it in the sample hierarchy.</param>
428+
public ProfilingScope(CommandBuffer cmd, ProfilingSampler sampler, Object contextObject)
429+
{
430+
#if ENABLE_PROFILER
431+
sampler?.Begin(cmd, contextObject);
389432
m_Sampler = sampler;
390433
m_Cmd = cmd;
391434
#endif
@@ -405,15 +448,7 @@ public ProfilingScope(CommandBuffer cmd, ProfilingSampler sampler)
405448
public ProfilingScope(BaseCommandBuffer cmd, ProfilingSampler sampler)
406449
{
407450
#if ENABLE_PROFILER
408-
if (sampler != null)
409-
{
410-
m_Disposed = false;
411-
sampler.Begin(cmd.m_WrappedCommandBuffer);
412-
}
413-
else
414-
{
415-
m_Disposed = true;
416-
}
451+
sampler?.Begin(cmd.m_WrappedCommandBuffer);
417452
m_Sampler = sampler;
418453
m_Cmd = cmd.m_WrappedCommandBuffer;
419454
#endif
@@ -425,11 +460,11 @@ public ProfilingScope(BaseCommandBuffer cmd, ProfilingSampler sampler)
425460
public void Dispose()
426461
{
427462
#if ENABLE_PROFILER
428-
if (m_Disposed)
463+
if (m_Sampler == null)
429464
return;
430465

431-
m_Sampler?.End(m_Cmd);
432-
m_Disposed = true;
466+
m_Sampler.End(m_Cmd);
467+
m_Sampler = null;
433468
#endif
434469
}
435470
}

Packages/com.unity.render-pipelines.core/Tests/Runtime/Debugging/ProfilingSamplerTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,34 @@ public IEnumerator ProfilingScope_InlineCpuElapsedTime_IsGreaterThanZero()
198198
Assert.Greater(m_Sampler.inlineCpuSampleCount, 0);
199199
}
200200

201+
[UnityTest]
202+
public IEnumerator ProfilingScopeWithObject_InlineCpu_IsCapturedByRecorder()
203+
{
204+
m_Sampler.enableRecording = true;
205+
206+
var texture = new Texture2D(1, 1) { name = "TestTexture" };
207+
for (int i = 0; i < 2; i++)
208+
{
209+
using (new ProfilingScope(m_Sampler, texture))
210+
{ }
211+
yield return null;
212+
}
213+
UnityEngine.Object.DestroyImmediate(texture);
214+
215+
Assert.Greater(m_Sampler.inlineCpuElapsedTime, 0.0f);
216+
Assert.Greater(m_Sampler.inlineCpuSampleCount, 0);
217+
}
218+
219+
[Test]
220+
public void ProfilingScopeWithNullObject_InlineCpu_DoesNotCrash()
221+
{
222+
Assert.DoesNotThrow(() =>
223+
{
224+
using (new ProfilingScope(m_Sampler, null))
225+
{ }
226+
});
227+
}
228+
201229
[Test]
202230
public void Dispose_DoesNotThrow()
203231
{
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
using System.Collections;
2+
using NUnit.Framework;
3+
using Unity.Profiling;
4+
using UnityEngine;
5+
using UnityEngine.Rendering;
6+
using UnityEngine.TestTools;
7+
8+
namespace UnityEngine.Rendering.Tests
9+
{
10+
class ProfilingSamplerWithCommandBufferTests
11+
{
12+
const int kWarmupFrames = 4 /*ProfilerRecorder.GPU_RESULTS_DELAY_FRAMES*/;
13+
const int kSampledFrames = 2;
14+
15+
Texture2D m_Texture;
16+
17+
[OneTimeSetUp]
18+
public void SetUp()
19+
{
20+
m_Texture = new Texture2D(1, 1) { name = "TestTexture" };
21+
}
22+
23+
[OneTimeTearDown]
24+
public void TearDown()
25+
{
26+
UnityEngine.Object.DestroyImmediate(m_Texture);
27+
}
28+
29+
[UnityTest]
30+
public IEnumerator CommandBufferBeginSample_IsCapturedByProfilerRecorder()
31+
{
32+
var sampler = new ProfilingSampler(nameof(CommandBufferBeginSample_IsCapturedByProfilerRecorder));
33+
using var recorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, sampler.name);
34+
35+
var commandBuffer = new CommandBuffer();
36+
using (new ProfilingScope(commandBuffer, sampler))
37+
{ }
38+
39+
yield return ExecuteCommandBufferForFrames(commandBuffer, kWarmupFrames, kSampledFrames);
40+
41+
commandBuffer.Dispose();
42+
Assert.AreEqual(1, recorder.Count);
43+
Assert.Greater(recorder.GetSample(0).Count, 0);
44+
}
45+
46+
[UnityTest]
47+
public IEnumerator CommandBufferBeginSampleWithObject_IsCapturedByProfilerRecorder()
48+
{
49+
var sampler = new ProfilingSampler(nameof(CommandBufferBeginSampleWithObject_IsCapturedByProfilerRecorder));
50+
using var recorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, sampler.name);
51+
52+
var commandBuffer = new CommandBuffer();
53+
using (new ProfilingScope(commandBuffer, sampler, m_Texture))
54+
{ }
55+
56+
yield return ExecuteCommandBufferForFrames(commandBuffer, kWarmupFrames, kSampledFrames);
57+
58+
commandBuffer.Dispose();
59+
Assert.AreEqual(1, recorder.Count);
60+
Assert.Greater(recorder.GetSample(0).Count, 0);
61+
}
62+
63+
[Test]
64+
public void CommandBufferBeginSampleWithNullObject_DoesNotCrash()
65+
{
66+
var sampler = new ProfilingSampler(nameof(CommandBufferBeginSampleWithNullObject_DoesNotCrash));
67+
var commandBuffer = new CommandBuffer();
68+
Assert.DoesNotThrow(() =>
69+
{
70+
using (new ProfilingScope(commandBuffer, sampler, null))
71+
{ }
72+
Graphics.ExecuteCommandBuffer(commandBuffer);
73+
});
74+
commandBuffer.Dispose();
75+
}
76+
77+
[UnityTest]
78+
[UnityPlatform(include = new[]
79+
{
80+
RuntimePlatform.WindowsPlayer,
81+
RuntimePlatform.WindowsEditor,
82+
RuntimePlatform.Android,
83+
RuntimePlatform.PS5,
84+
RuntimePlatform.Switch,
85+
RuntimePlatform.XboxOne
86+
})]
87+
public IEnumerator CommandBufferBeginSampleWithObject_GpuSamples_ReturnsNonZeroCount()
88+
{
89+
var sampler = new ProfilingSampler(nameof(CommandBufferBeginSampleWithObject_GpuSamples_ReturnsNonZeroCount));
90+
using var recorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, sampler.name, 1, ProfilerRecorderOptions.GpuRecorder | ProfilerRecorderOptions.Default);
91+
92+
var commandBuffer = new CommandBuffer();
93+
using (new ProfilingScope(commandBuffer, sampler, m_Texture))
94+
{ }
95+
96+
yield return ExecuteCommandBufferForFrames(commandBuffer, kWarmupFrames, kSampledFrames);
97+
98+
commandBuffer.Dispose();
99+
Assert.AreEqual(1, recorder.Count);
100+
Assert.Greater(recorder.GetSample(0).Count, 0);
101+
}
102+
103+
[UnityTest]
104+
[UnityPlatform(include = new[]
105+
{
106+
RuntimePlatform.WindowsPlayer,
107+
RuntimePlatform.WindowsEditor,
108+
RuntimePlatform.Android,
109+
RuntimePlatform.PS5,
110+
RuntimePlatform.Switch,
111+
RuntimePlatform.XboxOne
112+
})]
113+
public IEnumerator CommandBufferBeginSample_GpuSamples_ReturnsNonZeroCount()
114+
{
115+
var sampler = new ProfilingSampler(nameof(CommandBufferBeginSample_GpuSamples_ReturnsNonZeroCount));
116+
using var recorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, sampler.name, 1, ProfilerRecorderOptions.GpuRecorder | ProfilerRecorderOptions.Default);
117+
118+
var commandBuffer = new CommandBuffer();
119+
using (new ProfilingScope(commandBuffer, sampler))
120+
{ }
121+
122+
yield return ExecuteCommandBufferForFrames(commandBuffer, kWarmupFrames, kSampledFrames);
123+
124+
commandBuffer.Dispose();
125+
Assert.AreEqual(1, recorder.Count);
126+
Assert.Greater(recorder.GetSample(0).Count, 0);
127+
}
128+
129+
static IEnumerator ExecuteCommandBufferForFrames(CommandBuffer commandBuffer, int warmupFrames, int sampledFrames)
130+
{
131+
for (int i = 0; i < warmupFrames; i++)
132+
{
133+
Graphics.ExecuteCommandBuffer(commandBuffer);
134+
yield return null;
135+
}
136+
for (int i = 0; i < sampledFrames; i++)
137+
{
138+
Graphics.ExecuteCommandBuffer(commandBuffer);
139+
yield return null;
140+
}
141+
}
142+
}
143+
}

Packages/com.unity.render-pipelines.core/Tests/Runtime/Debugging/ProfilingSamplerWithCommandBufferTests.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipeline.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ private void DisposeAdditionalCameraData()
490490

491491
public CameraRenderingScope(ScriptableRenderContext context, Camera camera)
492492
{
493-
using (new ProfilingScope(beginCameraRenderingSampler))
493+
using (new ProfilingScope(beginCameraRenderingSampler, camera))
494494
{
495495
m_Context = context;
496496
m_Camera = camera;
@@ -501,7 +501,7 @@ public CameraRenderingScope(ScriptableRenderContext context, Camera camera)
501501

502502
public void Dispose()
503503
{
504-
using (new ProfilingScope(endCameraRenderingSampler))
504+
using (new ProfilingScope(endCameraRenderingSampler, m_Camera))
505505
{
506506
EndCameraRendering(m_Context, m_Camera);
507507
}
@@ -898,9 +898,9 @@ static void RenderSingleCamera(ScriptableRenderContext context, UniversalCameraD
898898
CommandBuffer cmdScope = cameraData.xr.enabled ? null : cmd;
899899

900900
var cameraMetadata = CameraMetadataCache.GetCached(camera);
901-
using (new ProfilingScope(cmdScope, cameraMetadata.sampler)) // Enqueues a "BeginSample" command into the CommandBuffer cmd
901+
using (new ProfilingScope(cmdScope, cameraMetadata.sampler, camera)) // Enqueues a "BeginSample" command into the CommandBuffer cmd
902902
{
903-
using (new ProfilingScope(Profiling.Pipeline.Renderer.setupCullingParameters))
903+
using (new ProfilingScope(Profiling.Pipeline.Renderer.setupCullingParameters, camera))
904904
{
905905
var legacyCameraData = new CameraData(frameData);
906906

@@ -978,7 +978,7 @@ static void RenderSingleCamera(ScriptableRenderContext context, UniversalCameraD
978978
UniversalShadowData shadowData;
979979
CullContextData cullData;
980980

981-
using (new ProfilingScope(Profiling.Pipeline.initializeRenderingData))
981+
using (new ProfilingScope(Profiling.Pipeline.initializeRenderingData, camera))
982982
{
983983
CreateUniversalResourceData(frameData);
984984
lightData = CreateLightData(frameData, asset, data.cullResults.visibleLights, renderingMode);
@@ -1006,7 +1006,7 @@ static void RenderSingleCamera(ScriptableRenderContext context, UniversalCameraD
10061006
context.ExecuteCommandBuffer(cmd); // Sends to ScriptableRenderContext all the commands enqueued since cmd.Clear, i.e the "EndSample" command
10071007
CommandBufferPool.Release(cmd);
10081008

1009-
using (new ProfilingScope(Profiling.Pipeline.Context.submit))
1009+
using (new ProfilingScope(Profiling.Pipeline.Context.submit, cameraData.camera))
10101010
{
10111011
context.Submit(); // Actually execute the commands that we previously sent to the ScriptableRenderContext context
10121012
}
@@ -1037,7 +1037,7 @@ private static void CreateShadowAtlasAndCullShadowCasters(UniversalLightData lig
10371037
/// <param name="isLastBaseCamera">True if this is the last base camera.</param>
10381038
static void RenderCameraStack(ScriptableRenderContext context, Camera baseCamera, bool isLastBaseCamera)
10391039
{
1040-
using var profScope = new ProfilingScope(ProfilingSampler.Get(URPProfileId.RenderCameraStack));
1040+
using var profScope = new ProfilingScope(ProfilingSampler.Get(URPProfileId.RenderCameraStack), baseCamera);
10411041

10421042
baseCamera.TryGetComponent<UniversalAdditionalCameraData>(out var baseCameraAdditionalData);
10431043

0 commit comments

Comments
 (0)