Tighten URP pass queue snapshot ownership

This commit is contained in:
2026-04-27 15:21:57 +08:00
parent c0b829cd6a
commit 66d968c3ce
22 changed files with 849 additions and 51 deletions

View File

@@ -7,6 +7,8 @@ namespace XCEngine.Rendering.Universal
{
public abstract class ScriptableRenderer
{
private const int kMaxFramePlanSnapshotCount = 64;
private struct PassQueueStageManifest
{
public bool shadowCaster;
@@ -15,12 +17,77 @@ namespace XCEngine.Rendering.Universal
public bool finalOutput;
}
private sealed class PassQueueStageSnapshot
{
public PassQueueStageSnapshot(
CameraFrameStage stage,
List<ScriptableRenderPass> activePassQueue,
RendererBlocks rendererBlocks)
{
this.stage = stage;
this.activePassQueue =
activePassQueue ??
new List<ScriptableRenderPass>();
this.rendererBlocks =
rendererBlocks ?? new RendererBlocks();
}
public readonly CameraFrameStage stage;
public readonly List<ScriptableRenderPass> activePassQueue;
public readonly RendererBlocks rendererBlocks;
}
private sealed class PassQueueFrameSnapshot
{
private readonly Dictionary<
CameraFrameStage,
PassQueueStageSnapshot> m_stageSnapshots =
new Dictionary<
CameraFrameStage,
PassQueueStageSnapshot>();
public PassQueueFrameSnapshot(
ulong framePlanId,
int rendererIndex)
{
this.framePlanId = framePlanId;
this.rendererIndex = rendererIndex;
}
public readonly ulong framePlanId;
public readonly int rendererIndex;
public PassQueueStageManifest manifest;
public void SetStageSnapshot(
PassQueueStageSnapshot snapshot)
{
if (snapshot == null)
{
return;
}
m_stageSnapshots[snapshot.stage] = snapshot;
}
public bool TryGetStageSnapshot(
CameraFrameStage stage,
out PassQueueStageSnapshot snapshot)
{
return m_stageSnapshots.TryGetValue(
stage,
out snapshot);
}
}
private readonly List<ScriptableRendererFeature> m_features =
new List<ScriptableRendererFeature>();
private readonly List<ScriptableRenderPass> m_activePassQueue =
new List<ScriptableRenderPass>();
private readonly RendererBlocks m_rendererBlocks =
new RendererBlocks();
private readonly Dictionary<ulong, PassQueueFrameSnapshot>
m_framePlanSnapshots =
new Dictionary<ulong, PassQueueFrameSnapshot>();
private readonly CommandBuffer m_finishCameraStackCommandBuffer =
new CommandBuffer("ScriptableRenderer.FinishCameraStack");
private bool m_isBuildingPassQueue;
@@ -53,6 +120,7 @@ namespace XCEngine.Rendering.Universal
m_features.Clear();
m_activePassQueue.Clear();
m_rendererBlocks.Clear();
m_framePlanSnapshots.Clear();
m_disposed = true;
}
@@ -134,9 +202,10 @@ namespace XCEngine.Rendering.Universal
return;
}
PassQueueStageManifest manifest =
BuildPassQueueStageManifest(context);
ApplyPassQueueStageManifest(context, manifest);
PassQueueFrameSnapshot snapshot =
BuildPassQueueFrameSnapshot(context);
ApplyPassQueueStageManifest(context, snapshot.manifest);
StorePassQueueFrameSnapshot(snapshot);
ClearPassQueue();
}
@@ -177,8 +246,30 @@ namespace XCEngine.Rendering.Universal
RenderingData renderingData =
context.renderingData;
PassQueueStageSnapshot snapshot;
if (TryGetPassQueueStageSnapshot(
context,
out snapshot))
{
return SupportsRendererRecordingFromSnapshot(
context,
snapshot);
}
if (context.framePlanId != 0UL)
{
return false;
}
BuildPassQueue(renderingData);
return SupportsRendererStage(context);
try
{
return SupportsRendererStage(context);
}
finally
{
ClearPassQueue();
}
}
protected virtual bool RecordRenderer(
@@ -192,24 +283,46 @@ namespace XCEngine.Rendering.Universal
RenderingData renderingData =
context.renderingData;
BuildPassQueue(renderingData);
bool recorded;
using (ContextContainer frameData =
ScriptableRenderPass.BuildRenderGraphFrameData(
renderingData))
PassQueueStageSnapshot snapshot;
if (TryGetPassQueueStageSnapshot(
context,
out snapshot))
{
recorded =
RecordRendererStage(
context,
new RenderGraph(context.renderContext),
frameData);
}
if (recorded)
{
FinishCameraStackRendering();
return RecordRendererFromSnapshot(
context,
snapshot);
}
return recorded;
if (context.framePlanId != 0UL)
{
return false;
}
BuildPassQueue(renderingData);
try
{
bool recorded;
using (ContextContainer frameData =
ScriptableRenderPass.BuildRenderGraphFrameData(
renderingData))
{
recorded =
RecordRendererStage(
context,
new RenderGraph(context.renderContext),
frameData);
}
if (recorded)
{
FinishCameraStackRendering();
}
return recorded;
}
finally
{
ClearPassQueue();
}
}
protected internal virtual bool SupportsStageRenderGraph(
@@ -296,6 +409,14 @@ namespace XCEngine.Rendering.Universal
return m_rendererBlocks.HasPasses(block);
}
private static bool HasRendererBlock(
RendererBlocks rendererBlocks,
RendererBlock block)
{
return rendererBlocks != null &&
rendererBlocks.HasPasses(block);
}
protected virtual bool SupportsRendererStage(
RendererRecordingContext context)
{
@@ -309,76 +430,152 @@ namespace XCEngine.Rendering.Universal
private bool HasRendererStageBlocks(
CameraFrameStage stage)
{
return HasRendererStageBlocks(
stage,
m_rendererBlocks);
}
private static bool HasRendererStageBlocks(
CameraFrameStage stage,
RendererBlocks rendererBlocks)
{
switch (stage)
{
case CameraFrameStage.ShadowCaster:
return HasRendererBlock(
rendererBlocks,
RendererBlock.ShadowCaster);
case CameraFrameStage.DepthOnly:
return HasRendererBlock(
rendererBlocks,
RendererBlock.DepthPrepass);
case CameraFrameStage.MainScene:
return HasRendererBlock(
rendererBlocks,
RendererBlock.MainOpaque) ||
HasRendererBlock(
rendererBlocks,
RendererBlock.MainSkybox) ||
HasRendererBlock(
rendererBlocks,
RendererBlock.MainTransparent);
case CameraFrameStage.PostProcess:
return HasRendererBlock(
rendererBlocks,
RendererBlock.PostProcess);
case CameraFrameStage.FinalOutput:
return HasRendererBlock(
rendererBlocks,
RendererBlock.FinalOutput);
default:
return false;
}
}
private PassQueueStageManifest BuildPassQueueStageManifest(
private PassQueueFrameSnapshot BuildPassQueueFrameSnapshot(
ScriptableRenderPipelinePlanningContext context)
{
bool finalColorRequiresProcessing =
context.HasFinalColorProcessing();
int rendererIndex = context.rendererIndex;
ulong framePlanId = context.framePlanId;
return new PassQueueStageManifest
PassQueueFrameSnapshot snapshot =
new PassQueueFrameSnapshot(
framePlanId,
rendererIndex);
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.ShadowCaster,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.DepthOnly,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.MainScene,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.PostProcess,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.FinalOutput,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
PassQueueStageSnapshot stageSnapshot;
snapshot.manifest = new PassQueueStageManifest
{
shadowCaster =
HasPassQueueStage(
snapshot.TryGetStageSnapshot(
CameraFrameStage.ShadowCaster,
rendererIndex,
finalColorRequiresProcessing),
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.ShadowCaster,
stageSnapshot.rendererBlocks),
depthOnly =
HasPassQueueStage(
snapshot.TryGetStageSnapshot(
CameraFrameStage.DepthOnly,
rendererIndex,
finalColorRequiresProcessing),
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.DepthOnly,
stageSnapshot.rendererBlocks),
postProcess =
HasPassQueueStage(
snapshot.TryGetStageSnapshot(
CameraFrameStage.PostProcess,
rendererIndex,
finalColorRequiresProcessing),
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.PostProcess,
stageSnapshot.rendererBlocks),
finalOutput =
HasPassQueueStage(
snapshot.TryGetStageSnapshot(
CameraFrameStage.FinalOutput,
rendererIndex,
finalColorRequiresProcessing)
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.FinalOutput,
stageSnapshot.rendererBlocks)
};
return snapshot;
}
private bool HasPassQueueStage(
private PassQueueStageSnapshot BuildPassQueueStageSnapshot(
CameraFrameStage stage,
int rendererIndex,
bool finalColorRequiresProcessing)
bool finalColorRequiresProcessing,
ulong framePlanId)
{
BuildPassQueue(
RenderingData.CreatePlanning(
stage,
rendererIndex,
finalColorRequiresProcessing));
return HasRendererStageBlocks(stage);
finalColorRequiresProcessing,
framePlanId));
List<ScriptableRenderPass> activePassQueue =
new List<ScriptableRenderPass>(
m_activePassQueue);
RendererBlocks rendererBlocks =
new RendererBlocks();
rendererBlocks.Build(activePassQueue);
return new PassQueueStageSnapshot(
stage,
activePassQueue,
rendererBlocks);
}
private static void ApplyPassQueueStageManifest(
@@ -631,6 +828,129 @@ namespace XCEngine.Rendering.Universal
return true;
}
private void StorePassQueueFrameSnapshot(
PassQueueFrameSnapshot snapshot)
{
if (snapshot == null ||
snapshot.framePlanId == 0UL)
{
return;
}
if (!m_framePlanSnapshots.ContainsKey(
snapshot.framePlanId) &&
m_framePlanSnapshots.Count >=
kMaxFramePlanSnapshotCount)
{
ulong oldestFramePlanId = 0UL;
bool hasOldestFramePlanId = false;
foreach (ulong framePlanId in
m_framePlanSnapshots.Keys)
{
oldestFramePlanId = framePlanId;
hasOldestFramePlanId = true;
break;
}
if (hasOldestFramePlanId)
{
m_framePlanSnapshots.Remove(
oldestFramePlanId);
}
}
m_framePlanSnapshots[snapshot.framePlanId] =
snapshot;
}
private bool TryGetPassQueueStageSnapshot(
RendererRecordingContext context,
out PassQueueStageSnapshot stageSnapshot)
{
stageSnapshot = null;
if (context == null ||
context.framePlanId == 0UL)
{
return false;
}
PassQueueFrameSnapshot frameSnapshot;
if (!m_framePlanSnapshots.TryGetValue(
context.framePlanId,
out frameSnapshot) ||
frameSnapshot == null ||
frameSnapshot.rendererIndex !=
context.rendererIndex)
{
return false;
}
return frameSnapshot.TryGetStageSnapshot(
context.stage,
out stageSnapshot);
}
private bool SupportsRendererRecordingFromSnapshot(
RendererRecordingContext context,
PassQueueStageSnapshot snapshot)
{
LoadPassQueueStageSnapshot(snapshot);
try
{
return SupportsRendererStage(context);
}
finally
{
ClearPassQueue();
}
}
private bool RecordRendererFromSnapshot(
RendererRecordingContext context,
PassQueueStageSnapshot snapshot)
{
LoadPassQueueStageSnapshot(snapshot);
try
{
bool recorded;
using (ContextContainer frameData =
ScriptableRenderPass.BuildRenderGraphFrameData(
context.renderingData))
{
recorded =
RecordRendererStage(
context,
new RenderGraph(context.renderContext),
frameData);
}
if (recorded)
{
FinishCameraStackRendering();
}
return recorded;
}
finally
{
ClearPassQueue();
}
}
private void LoadPassQueueStageSnapshot(
PassQueueStageSnapshot snapshot)
{
ClearPassQueue();
if (snapshot == null ||
snapshot.activePassQueue == null)
{
return;
}
m_activePassQueue.AddRange(
snapshot.activePassQueue);
m_rendererBlocks.Build(m_activePassQueue);
}
private void BuildPassQueue(
RenderingData renderingData)
{