Align URP pass queue to single camera snapshot

This commit is contained in:
2026-04-27 22:38:56 +08:00
parent 520413fa0d
commit eaa90d835f
9 changed files with 104 additions and 255 deletions

View File

@@ -246,11 +246,14 @@ package。
仍是兼容和高级策略 hook但它不能单独声明 shadow、depth、post 或 final-output stage没有被 pass queue
覆盖到的 side/fullscreen stage 必须在最终 plan 中清掉。
- `ConfigurePassQueueCameraFramePlanInstance` 必须在 planning 阶段为当前 renderer 和 `framePlanId`
生成一次 per-stage pass queue 快照,并用该快照派生 stage manifest。`SupportsStageRenderGraph`
生成一次 per-camera active pass queue 快照,并用同一份 queue 派生 stage manifest。`SupportsStageRenderGraph`
`RecordStageRenderGraph``framePlanId != 0` 时必须消费这个快照,不得重新运行
feature `SetupRenderPasses`、feature `AddRenderPasses` 或 renderer-owned `AddRenderPasses` 来重建队列。
找不到匹配 `framePlanId` 和 renderer index 的快照时必须返回不支持或 record 失败,让上层暴露错误;
不要退回 legacy queue rebuild 或 built-in fallback。
- URP features 的 `AddRenderPasses` 是 per-camera 声明点,不是 per-stage 回调。不要通过
`RenderingData.stage` 分阶段重复 enqueuepass 所属 stage 应由 `RenderPassEvent -> RendererBlock`
映射决定renderer recording 再按 block range 消费同一份 queue。
- `RendererBlock` 将 pass events 映射到 camera stagesshadow caster、depth prepass、main opaque、main
skybox、main transparent、post process 和 final output。
- `UniversalRenderer` 拥有具体 blocks
@@ -382,9 +385,9 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
test views 必须提供 texture-backed view。
- URP 现在已有 renderer data、renderer features、renderer pass queueing、renderer blocks、renderer-index
resolution 和 per-stage recording。
- URP stage planning 已收口到 renderer active pass queue 派生的 per-`framePlanId` 快照和 stage manifest。
Stage support 和 stage recording 现在消费 planning 阶段保存的快照,关闭了 feature planning hook、
support proberecording 各自重建 pass queue 的重复事实源。
- URP stage planning 已收口到 renderer active pass queue 派生的 per-`framePlanId` 单 queue 快照和
stage manifest。Stage support 和 stage recording 现在消费 planning 阶段保存的同一份 queue 快照,
关闭了 feature planning hook、support proberecording 和各 stage 分别重建 pass queue 的重复事实源。
- URP runtime-state invalidation 已覆盖 asset-level shadow/final-color settings 和 renderer-data-level
main-scene/shadow-caster/depth-prepass block settings。配置变更会通过 runtime resource version 释放
renderer caches并让后续 planning 重新生成 pass queue 快照和 stage manifest。

View File

@@ -380,8 +380,7 @@ namespace Gameplay
RenderingData renderingData)
{
if (renderer == null ||
renderingData == null ||
!renderingData.isShadowCasterStage)
renderingData == null)
{
return;
}
@@ -413,8 +412,7 @@ namespace Gameplay
for (int i = 0; i < m_passes.Length; ++i)
{
ScriptableRenderPass renderPass = m_passes[i];
if (renderPass == null ||
!renderPass.SupportsStage(renderingData.stage))
if (renderPass == null)
{
continue;
}
@@ -1694,40 +1692,41 @@ namespace Gameplay
new FullscreenPass(
RenderPassEvent.BeforeRenderingPostProcessing,
"ManagedPassQueueSnapshot.PostProcess");
private ulong m_enqueuedFramePlanId;
private bool m_enqueuedForCurrentFramePlan;
private readonly FullscreenPass m_unexpectedDuplicatePass =
new FullscreenPass(
RenderPassEvent.BeforeRenderingPostProcessing,
"ManagedPassQueueSnapshot.UnexpectedDuplicate");
private ulong m_addRenderPassesFramePlanId;
private int m_addRenderPassesCallCount;
public override void AddRenderPasses(
ScriptableRenderer renderer,
RenderingData renderingData)
{
if (renderer == null ||
renderingData == null ||
!renderingData.isPostProcessStage)
renderingData == null)
{
return;
}
if (renderingData.framePlanId == 0UL)
if (renderingData.framePlanId != 0UL &&
renderingData.framePlanId != m_addRenderPassesFramePlanId)
{
renderer.EnqueuePass(m_pass);
return;
}
if (renderingData.framePlanId != m_enqueuedFramePlanId)
{
m_enqueuedFramePlanId =
m_addRenderPassesFramePlanId =
renderingData.framePlanId;
m_enqueuedForCurrentFramePlan = false;
m_addRenderPassesCallCount = 0;
}
if (m_enqueuedForCurrentFramePlan)
if (renderingData.framePlanId != 0UL)
{
return;
m_addRenderPassesCallCount++;
if (m_addRenderPassesCallCount > 1)
{
renderer.EnqueuePass(m_unexpectedDuplicatePass);
}
}
renderer.EnqueuePass(m_pass);
m_enqueuedForCurrentFramePlan = true;
}
}

View File

@@ -76,8 +76,7 @@ namespace XCEngine.Rendering.Universal
RenderingData renderingData)
{
if (renderer == null ||
renderingData == null ||
!renderingData.isPostProcessStage)
renderingData == null)
{
return;
}

View File

@@ -40,13 +40,12 @@ namespace XCEngine.Rendering.Universal
}
internal static RenderingData CreatePlanning(
CameraFrameStage stage,
int rendererIndex,
bool finalColorRequiresProcessing,
ulong framePlanId)
{
return new RenderingData(
stage,
CameraFrameStage.MainScene,
rendererIndex,
framePlanId,
CameraData.Default,

View File

@@ -17,66 +17,24 @@ 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)
int rendererIndex,
List<ScriptableRenderPass> activePassQueue)
{
this.framePlanId = framePlanId;
this.rendererIndex = rendererIndex;
this.activePassQueue =
activePassQueue ??
new List<ScriptableRenderPass>();
}
public readonly ulong framePlanId;
public readonly int rendererIndex;
public readonly List<ScriptableRenderPass> activePassQueue;
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 =
@@ -90,9 +48,6 @@ namespace XCEngine.Rendering.Universal
new Dictionary<ulong, PassQueueFrameSnapshot>();
private readonly CommandBuffer m_finishCameraStackCommandBuffer =
new CommandBuffer("ScriptableRenderer.FinishCameraStack");
private bool m_isBuildingPassQueue;
private CameraFrameStage m_passQueueStage =
CameraFrameStage.MainScene;
private bool m_disposed;
protected ScriptableRenderer()
@@ -132,12 +87,6 @@ namespace XCEngine.Rendering.Universal
return;
}
if (m_isBuildingPassQueue &&
!renderPass.SupportsStage(m_passQueueStage))
{
return;
}
InsertActivePass(
renderPass);
}
@@ -246,8 +195,8 @@ namespace XCEngine.Rendering.Universal
RenderingData renderingData =
context.renderingData;
PassQueueStageSnapshot snapshot;
if (TryGetPassQueueStageSnapshot(
PassQueueFrameSnapshot snapshot;
if (TryGetPassQueueFrameSnapshot(
context,
out snapshot))
{
@@ -283,8 +232,8 @@ namespace XCEngine.Rendering.Universal
RenderingData renderingData =
context.renderingData;
PassQueueStageSnapshot snapshot;
if (TryGetPassQueueStageSnapshot(
PassQueueFrameSnapshot snapshot;
if (TryGetPassQueueFrameSnapshot(
context,
out snapshot))
{
@@ -481,101 +430,40 @@ namespace XCEngine.Rendering.Universal
int rendererIndex = context.rendererIndex;
ulong framePlanId = context.framePlanId;
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 =
snapshot.TryGetStageSnapshot(
CameraFrameStage.ShadowCaster,
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.ShadowCaster,
stageSnapshot.rendererBlocks),
depthOnly =
snapshot.TryGetStageSnapshot(
CameraFrameStage.DepthOnly,
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.DepthOnly,
stageSnapshot.rendererBlocks),
postProcess =
snapshot.TryGetStageSnapshot(
CameraFrameStage.PostProcess,
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.PostProcess,
stageSnapshot.rendererBlocks),
finalOutput =
snapshot.TryGetStageSnapshot(
CameraFrameStage.FinalOutput,
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.FinalOutput,
stageSnapshot.rendererBlocks)
};
return snapshot;
}
private PassQueueStageSnapshot BuildPassQueueStageSnapshot(
CameraFrameStage stage,
int rendererIndex,
bool finalColorRequiresProcessing,
ulong framePlanId)
{
BuildPassQueue(
RenderingData.CreatePlanning(
stage,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
List<ScriptableRenderPass> activePassQueue =
new List<ScriptableRenderPass>(
m_activePassQueue);
RendererBlocks rendererBlocks =
new RendererBlocks();
rendererBlocks.Build(activePassQueue);
return new PassQueueStageSnapshot(
stage,
activePassQueue,
rendererBlocks);
PassQueueFrameSnapshot snapshot =
new PassQueueFrameSnapshot(
framePlanId,
rendererIndex,
activePassQueue);
snapshot.manifest = new PassQueueStageManifest
{
shadowCaster = HasRendererStageBlocks(
CameraFrameStage.ShadowCaster,
rendererBlocks),
depthOnly = HasRendererStageBlocks(
CameraFrameStage.DepthOnly,
rendererBlocks),
postProcess = HasRendererStageBlocks(
CameraFrameStage.PostProcess,
rendererBlocks),
finalOutput = HasRendererStageBlocks(
CameraFrameStage.FinalOutput,
rendererBlocks)
};
return snapshot;
}
private static void ApplyPassQueueStageManifest(
@@ -863,18 +751,17 @@ namespace XCEngine.Rendering.Universal
snapshot;
}
private bool TryGetPassQueueStageSnapshot(
private bool TryGetPassQueueFrameSnapshot(
RendererRecordingContext context,
out PassQueueStageSnapshot stageSnapshot)
out PassQueueFrameSnapshot frameSnapshot)
{
stageSnapshot = null;
frameSnapshot = null;
if (context == null ||
context.framePlanId == 0UL)
{
return false;
}
PassQueueFrameSnapshot frameSnapshot;
if (!m_framePlanSnapshots.TryGetValue(
context.framePlanId,
out frameSnapshot) ||
@@ -885,16 +772,14 @@ namespace XCEngine.Rendering.Universal
return false;
}
return frameSnapshot.TryGetStageSnapshot(
context.stage,
out stageSnapshot);
return true;
}
private bool SupportsRendererRecordingFromSnapshot(
RendererRecordingContext context,
PassQueueStageSnapshot snapshot)
PassQueueFrameSnapshot snapshot)
{
LoadPassQueueStageSnapshot(snapshot);
LoadPassQueueFrameSnapshot(snapshot);
try
{
return SupportsRendererStage(context);
@@ -907,9 +792,9 @@ namespace XCEngine.Rendering.Universal
private bool RecordRendererFromSnapshot(
RendererRecordingContext context,
PassQueueStageSnapshot snapshot)
PassQueueFrameSnapshot snapshot)
{
LoadPassQueueStageSnapshot(snapshot);
LoadPassQueueFrameSnapshot(snapshot);
try
{
bool recorded;
@@ -936,8 +821,8 @@ namespace XCEngine.Rendering.Universal
}
}
private void LoadPassQueueStageSnapshot(
PassQueueStageSnapshot snapshot)
private void LoadPassQueueFrameSnapshot(
PassQueueFrameSnapshot snapshot)
{
ClearPassQueue();
if (snapshot == null ||
@@ -955,39 +840,25 @@ namespace XCEngine.Rendering.Universal
RenderingData renderingData)
{
ClearPassQueue();
m_isBuildingPassQueue = true;
m_passQueueStage =
renderingData != null
? renderingData.stage
: CameraFrameStage.MainScene;
try
for (int i = 0; i < m_features.Count; ++i)
{
for (int i = 0; i < m_features.Count; ++i)
ScriptableRendererFeature feature = m_features[i];
if (feature == null || !feature.isActive)
{
ScriptableRendererFeature feature = m_features[i];
if (feature == null || !feature.isActive)
{
continue;
}
feature.SetupRenderPasses(
this,
in renderingData);
feature.AddRenderPasses(
this,
ref renderingData);
continue;
}
AddRenderPasses(renderingData);
m_rendererBlocks.Build(m_activePassQueue);
}
finally
{
m_isBuildingPassQueue = false;
m_passQueueStage =
CameraFrameStage.MainScene;
feature.SetupRenderPasses(
this,
in renderingData);
feature.AddRenderPasses(
this,
ref renderingData);
}
AddRenderPasses(renderingData);
m_rendererBlocks.Build(m_activePassQueue);
}
private void ClearPassQueue()

View File

@@ -42,8 +42,7 @@ namespace XCEngine.Rendering.Universal
return;
}
if (!renderingData.isFinalOutputStage ||
!renderingData.finalColorData.requiresProcessing)
if (!renderingData.finalColorData.requiresProcessing)
{
return;
}

View File

@@ -8,8 +8,7 @@ namespace XCEngine.Rendering.Universal
public static bool IsActive(
RenderingData renderingData)
{
return renderingData != null &&
renderingData.isMainSceneStage;
return renderingData != null;
}
public static bool SupportsPass(
@@ -19,7 +18,7 @@ namespace XCEngine.Rendering.Universal
return IsActive(renderingData) &&
renderPass != null &&
renderPass.SupportsStage(
renderingData.stage);
CameraFrameStage.MainScene);
}
public static bool EnqueuePass(

View File

@@ -13,8 +13,7 @@ namespace XCEngine.Rendering.Universal
RenderingData renderingData)
{
if (renderer == null ||
renderingData == null ||
!renderingData.isPostProcessStage)
renderingData == null)
{
return;
}
@@ -70,8 +69,7 @@ namespace XCEngine.Rendering.Universal
{
if (renderer == null ||
renderingData == null ||
renderPass == null ||
!renderingData.isPostProcessStage)
renderPass == null)
{
return;
}

View File

@@ -86,39 +86,21 @@ namespace XCEngine.Rendering.Universal
protected override void AddRenderPasses(
RenderingData renderingData)
{
if (renderingData == null)
{
return;
}
switch (renderingData.stage)
{
case CameraFrameStage.ShadowCaster:
m_shadowCasterBlock.EnqueueRenderPasses(
this,
m_rendererData.GetShadowCasterBlockInstance());
break;
case CameraFrameStage.DepthOnly:
m_depthPrepassBlock.EnqueueRenderPasses(
this,
m_rendererData.GetDepthPrepassBlockInstance());
break;
case CameraFrameStage.MainScene:
m_mainSceneBlock.EnqueueRenderPasses(
this,
m_rendererData.GetMainSceneInstance());
break;
case CameraFrameStage.PostProcess:
m_postProcessBlock.EnqueueRenderPasses(
this,
renderingData);
break;
case CameraFrameStage.FinalOutput:
m_finalOutputBlock.EnqueueRenderPasses(
this,
renderingData);
break;
}
m_shadowCasterBlock.EnqueueRenderPasses(
this,
m_rendererData.GetShadowCasterBlockInstance());
m_depthPrepassBlock.EnqueueRenderPasses(
this,
m_rendererData.GetDepthPrepassBlockInstance());
m_mainSceneBlock.EnqueueRenderPasses(
this,
m_rendererData.GetMainSceneInstance());
m_postProcessBlock.EnqueueRenderPasses(
this,
renderingData);
m_finalOutputBlock.EnqueueRenderPasses(
this,
renderingData);
}
}
}