From 33e9041d6020a6ba6ec38aa8fd59ec9cf13de125 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 22 Apr 2026 01:23:52 +0800 Subject: [PATCH] refactor(srp): require explicit managed renderer stage planning --- ...itStagePlanningPlan_2026-04-22_完成归档.md | 82 +++++++++++ managed/GameScripts/RenderPipelineApiProbe.cs | 78 +++++++++++ .../ColorScalePostProcessRendererFeature.cs | 43 ++++++ .../Rendering/Universal/ScriptableRenderer.cs | 132 ------------------ 4 files changed, 203 insertions(+), 132 deletions(-) create mode 100644 docs/used/SRP_URP_RendererExplicitStagePlanningPlan_2026-04-22_完成归档.md diff --git a/docs/used/SRP_URP_RendererExplicitStagePlanningPlan_2026-04-22_完成归档.md b/docs/used/SRP_URP_RendererExplicitStagePlanningPlan_2026-04-22_完成归档.md new file mode 100644 index 00000000..09dec203 --- /dev/null +++ b/docs/used/SRP_URP_RendererExplicitStagePlanningPlan_2026-04-22_完成归档.md @@ -0,0 +1,82 @@ +# SRP / URP Renderer Explicit Stage Planning Plan + +时间:2026-04-22 + +## 背景 + +当前 managed `ScriptableRenderer / ScriptableRendererFeature / ScriptableRenderPass` 骨架已经可用, +但 renderer 这一层还有一个不干净的旧接缝: + +1. `ScriptableRenderer.FinalizeCameraFramePlan(...)` 仍然会根据 active pass queue 自动推断 + `ShadowCaster / DepthOnly / PostProcess / FinalOutput` stage +2. 这个推断依赖一份“planning 阶段伪造的 `RenderingData(MainScene)`”,并不是真正的 renderer planning contract +3. 结果是 renderer / feature 看起来已经在控制管线,但 stage ownership 仍然有一部分藏在基类启发式里 + +这不符合要对齐 Unity SRP / URP 的目标。 + +Unity 风格应该是: + +- renderer / feature 显式决定本帧需要哪些阶段 +- native C++ 只负责 stage 执行、RenderGraph、scene draw substrate +- 不能再让基类偷偷扫描 pass queue 替上层“猜阶段” + +## 本阶段目标 + +把 managed renderer 的 stage planning 从“隐式推断”收成“显式声明”: + +1. 去掉 `ScriptableRenderer` 基类里的 stage 推断逻辑 +2. 让真正拥有 pass 的 renderer / feature 自己在 planning hook 中申请所需 stage +3. 保证现有 Universal 默认路径和 probe/sample 路径仍然能工作 +4. 完成后,renderer-driven SRP 的组织权更清晰,后续再继续往 renderer pass / renderer feature 主线推进 + +## 范围 + +本阶段只处理 managed renderer 的显式 stage planning,不做: + +- deferred pipeline +- shadow 资源系统迁移 +- RenderGraph 新功能扩展 +- editor 逻辑 + +## 实施步骤 + +### 1. 清理 `ScriptableRenderer` 基类隐式推断 + +删除以下旧逻辑: + +- `CreatePlanningRenderingData(...)` +- `ApplyInferredFullscreenStageRequests(...)` +- `ApplyInferredStandaloneStageRequests(...)` +- `HasQueuedPassForStage(...)` + +`FinalizeCameraFramePlan(...)` 保持为空默认实现,不再隐式改 plan。 + +### 2. 把 stage planning 放回真正拥有 pass 的类型 + +至少补齐: + +- `ColorScalePostProcessRendererFeature` +- GameScripts 里的 fullscreen probe feature / renderer + +原则: + +- post-process feature 显式请求 `PostProcess` +- 如需把结果交给 `FinalOutput`,则显式申请 graph-managed output color +- 如有 final-output pass,则显式请求 `FinalOutput` + +### 3. 验证当前主线不回退 + +要求: + +1. `XCEditor` Debug 构建通过 +2. old editor 冒烟 15 秒通过 +3. `editor.log` 出现 `SceneReady` + +## 完成标准 + +满足以下条件才算收口: + +1. managed renderer 的 stage planning 不再依赖基类扫描 pass queue 的启发式 +2. 现有 Universal 默认 renderer 仍可运行 +3. probe/sample fullscreen renderer 走显式 planning 后仍能编译通过 +4. `XCEditor` 编译和 old editor 冒烟通过 diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index 3404616e..f852c85f 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -419,6 +419,84 @@ namespace Gameplay renderer.EnqueuePass(renderPass); } } + + public override void ConfigureCameraFramePlan( + ScriptableRenderPipelinePlanningContext context) + { + if (context == null) + { + return; + } + + bool hasPostProcessPass = false; + bool hasFinalOutputPass = false; + for (int i = 0; i < m_passes.Length; ++i) + { + ScriptableRenderPass renderPass = m_passes[i]; + if (renderPass == null) + { + continue; + } + + if (renderPass.SupportsStage( + CameraFrameStage.PostProcess)) + { + hasPostProcessPass = true; + } + + if (renderPass.SupportsStage( + CameraFrameStage.FinalOutput)) + { + hasFinalOutputPass = true; + } + } + + bool needsGraphManagedPostProcessOutput = + context.HasFinalColorProcessing() || + context.IsStageRequested( + CameraFrameStage.FinalOutput) || + hasFinalOutputPass; + + if (hasPostProcessPass) + { + if (!context.IsStageRequested( + CameraFrameStage.PostProcess)) + { + context.RequestFullscreenStage( + CameraFrameStage.PostProcess, + CameraFrameColorSource.MainSceneColor, + needsGraphManagedPostProcessOutput); + } + else if (needsGraphManagedPostProcessOutput && + context.GetStageColorSource( + CameraFrameStage.PostProcess) != + CameraFrameColorSource.ExplicitSurface && + !context.UsesGraphManagedOutputColor( + CameraFrameStage.PostProcess)) + { + CameraFrameColorSource source = + context.GetStageColorSource( + CameraFrameStage.PostProcess); + context.ClearFullscreenStage( + CameraFrameStage.PostProcess); + context.RequestFullscreenStage( + CameraFrameStage.PostProcess, + source, + true); + } + } + + if (hasFinalOutputPass && + !context.IsStageRequested( + CameraFrameStage.FinalOutput)) + { + context.RequestFullscreenStage( + CameraFrameStage.FinalOutput, + hasPostProcessPass + ? CameraFrameColorSource.PostProcessColor + : CameraFrameColorSource.MainSceneColor); + } + } } internal class ProbeSceneRenderer : ScriptableRenderer diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs index 2c78212a..36342433 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs @@ -65,6 +65,49 @@ namespace XCEngine.Rendering.Universal new ColorScalePostProcessPass(this); } + public override void ConfigureCameraFramePlan( + ScriptableRenderPipelinePlanningContext context) + { + if (context == null) + { + return; + } + + bool needsGraphManagedOutputColor = + context.HasFinalColorProcessing() || + context.IsStageRequested( + CameraFrameStage.FinalOutput); + + if (!context.IsStageRequested( + CameraFrameStage.PostProcess)) + { + context.RequestFullscreenStage( + CameraFrameStage.PostProcess, + CameraFrameColorSource.MainSceneColor, + needsGraphManagedOutputColor); + return; + } + + CameraFrameColorSource source = + context.GetStageColorSource( + CameraFrameStage.PostProcess); + if (!needsGraphManagedOutputColor || + source == + CameraFrameColorSource.ExplicitSurface || + context.UsesGraphManagedOutputColor( + CameraFrameStage.PostProcess)) + { + return; + } + + context.ClearFullscreenStage( + CameraFrameStage.PostProcess); + context.RequestFullscreenStage( + CameraFrameStage.PostProcess, + source, + true); + } + public override void AddRenderPasses( ScriptableRenderer renderer, RenderingData renderingData) diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs index d4740b3e..34d44850 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs @@ -202,13 +202,6 @@ namespace XCEngine.Rendering.Universal protected virtual void FinalizeCameraFramePlan( ScriptableRenderPipelinePlanningContext context) { - if (context == null) - { - return; - } - - ApplyInferredFullscreenStageRequests( - context); } protected virtual void ConfigureRenderSceneSetup( @@ -245,131 +238,6 @@ namespace XCEngine.Rendering.Universal protected virtual void ReleaseRuntimeResources() { } - - protected virtual RenderingData CreatePlanningRenderingData( - ScriptableRenderPipelinePlanningContext context) - { - return context != null - ? new RenderingData( - CameraFrameStage.MainScene, - context.rendererIndex) - : null; - } - - private void ApplyInferredFullscreenStageRequests( - ScriptableRenderPipelinePlanningContext context) - { - RenderingData planningData = - CreatePlanningRenderingData(context); - if (planningData == null) - { - return; - } - - BuildPassQueue(planningData); - ApplyInferredStandaloneStageRequests( - context); - - bool hasPostProcessPass = - HasQueuedPassForStage( - CameraFrameStage.PostProcess); - bool hasFinalOutputPass = - HasQueuedPassForStage( - CameraFrameStage.FinalOutput); - bool needsFinalOutputDependency = - context.HasFinalColorProcessing() || - context.IsStageRequested( - CameraFrameStage.FinalOutput) || - hasFinalOutputPass; - - if (hasPostProcessPass) - { - EnsureInferredPostProcessStage( - context, - needsFinalOutputDependency); - } - - if (hasFinalOutputPass && - !context.IsStageRequested( - CameraFrameStage.FinalOutput)) - { - context.RequestFullscreenStage( - CameraFrameStage.FinalOutput, - context.IsStageRequested( - CameraFrameStage.PostProcess) - ? CameraFrameColorSource.PostProcessColor - : CameraFrameColorSource.MainSceneColor); - } - } - - private void ApplyInferredStandaloneStageRequests( - ScriptableRenderPipelinePlanningContext context) - { - if (!context.HasExplicitShadowCasterStageConfiguration() && - HasQueuedPassForStage( - CameraFrameStage.ShadowCaster)) - { - context.RequestShadowCasterStage(); - } - - if (!context.HasExplicitDepthOnlyStageConfiguration() && - HasQueuedPassForStage( - CameraFrameStage.DepthOnly)) - { - context.RequestDepthOnlyStage(); - } - } - - private bool HasQueuedPassForStage( - CameraFrameStage stage) - { - for (int i = 0; i < m_activePassQueue.Count; ++i) - { - ScriptableRenderPass renderPass = - m_activePassQueue[i]; - if (renderPass != null && - renderPass.SupportsStage(stage)) - { - return true; - } - } - - return false; - } - - private static void EnsureInferredPostProcessStage( - ScriptableRenderPipelinePlanningContext context, - bool needsGraphManagedOutputColor) - { - if (!context.IsStageRequested( - CameraFrameStage.PostProcess)) - { - context.RequestFullscreenStage( - CameraFrameStage.PostProcess, - CameraFrameColorSource.MainSceneColor, - needsGraphManagedOutputColor); - return; - } - - CameraFrameColorSource source = - context.GetStageColorSource( - CameraFrameStage.PostProcess); - if (!needsGraphManagedOutputColor || - source == - CameraFrameColorSource.ExplicitSurface || - context.UsesGraphManagedOutputColor( - CameraFrameStage.PostProcess)) - { - return; - } - - context.ClearFullscreenStage( - CameraFrameStage.PostProcess); - context.RequestFullscreenStage( - CameraFrameStage.PostProcess, - source, - true); - } } }