From eaa90d835f1fb05858dbb91af99014827e16fcb4 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Mon, 27 Apr 2026 22:38:56 +0800 Subject: [PATCH] Align URP pass queue to single camera snapshot --- engine/include/XCEngine/Rendering/AGENTS.md | 11 +- managed/GameScripts/RenderPipelineApiProbe.cs | 39 ++- .../ColorScalePostProcessRendererFeature.cs | 3 +- .../Rendering/Universal/RenderingData.cs | 3 +- .../Rendering/Universal/ScriptableRenderer.cs | 241 ++++-------------- .../Universal/UniversalFinalOutputBlock.cs | 3 +- .../UniversalMainSceneFeatureUtility.cs | 5 +- .../Universal/UniversalPostProcessBlock.cs | 6 +- .../Rendering/Universal/UniversalRenderer.cs | 48 ++-- 9 files changed, 104 insertions(+), 255 deletions(-) diff --git a/engine/include/XCEngine/Rendering/AGENTS.md b/engine/include/XCEngine/Rendering/AGENTS.md index 804b7130..0b1091ec 100644 --- a/engine/include/XCEngine/Rendering/AGENTS.md +++ b/engine/include/XCEngine/Rendering/AGENTS.md @@ -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` 分阶段重复 enqueue;pass 所属 stage 应由 `RenderPassEvent -> RendererBlock` + 映射决定,renderer recording 再按 block range 消费同一份 queue。 - `RendererBlock` 将 pass events 映射到 camera stages:shadow 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 probe 和 recording 各自重建 pass queue 的重复事实源。 +- URP stage planning 已收口到 renderer active pass queue 派生的 per-`framePlanId` 单 queue 快照和 + stage manifest。Stage support 和 stage recording 现在消费 planning 阶段保存的同一份 queue 快照, + 关闭了 feature planning hook、support probe、recording 和各 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。 diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index ac020a62..0097cbe4 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -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; } } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs index 5f5c3a0a..ca4923e3 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs @@ -76,8 +76,7 @@ namespace XCEngine.Rendering.Universal RenderingData renderingData) { if (renderer == null || - renderingData == null || - !renderingData.isPostProcessStage) + renderingData == null) { return; } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderingData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderingData.cs index af338989..5b8c441c 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderingData.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderingData.cs @@ -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, diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs index 82058b72..3ca072a6 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs @@ -17,66 +17,24 @@ namespace XCEngine.Rendering.Universal public bool finalOutput; } - private sealed class PassQueueStageSnapshot - { - public PassQueueStageSnapshot( - CameraFrameStage stage, - List activePassQueue, - RendererBlocks rendererBlocks) - { - this.stage = stage; - this.activePassQueue = - activePassQueue ?? - new List(); - this.rendererBlocks = - rendererBlocks ?? new RendererBlocks(); - } - - public readonly CameraFrameStage stage; - public readonly List 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 activePassQueue) { this.framePlanId = framePlanId; this.rendererIndex = rendererIndex; + this.activePassQueue = + activePassQueue ?? + new List(); } public readonly ulong framePlanId; public readonly int rendererIndex; + public readonly List 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 m_features = @@ -90,9 +48,6 @@ namespace XCEngine.Rendering.Universal new Dictionary(); 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 activePassQueue = new List( 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() diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs index 8add3d2d..c6aba029 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs @@ -42,8 +42,7 @@ namespace XCEngine.Rendering.Universal return; } - if (!renderingData.isFinalOutputStage || - !renderingData.finalColorData.requiresProcessing) + if (!renderingData.finalColorData.requiresProcessing) { return; } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs index 8bb629d9..9eba9894 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs @@ -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( diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalPostProcessBlock.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalPostProcessBlock.cs index 50cade2b..17633c37 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalPostProcessBlock.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalPostProcessBlock.cs @@ -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; } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs index 8a63445f..42fec790 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs @@ -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); } } }