From 76452a9c7302f0073ed05141f2342e3b9539cae7 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 21 Apr 2026 23:22:29 +0800 Subject: [PATCH] refactor(srp): move standalone stage fallback ownership into backend --- ...nershipBoundaryPlan_2026-04-21_完成归档.md | 48 +++++++++++++++++ .../Pipelines/ScriptableRenderPipelineHost.h | 4 +- .../Internal/RenderPipelineFactory.cpp | 15 ------ .../Internal/BuiltinForwardSceneSetup.cpp | 21 ++++++++ .../ScriptableRenderPipelineHost.cpp | 53 ++++++++++--------- 5 files changed, 99 insertions(+), 42 deletions(-) create mode 100644 docs/used/SRP_URP_StandaloneStageOwnershipBoundaryPlan_2026-04-21_完成归档.md diff --git a/docs/used/SRP_URP_StandaloneStageOwnershipBoundaryPlan_2026-04-21_完成归档.md b/docs/used/SRP_URP_StandaloneStageOwnershipBoundaryPlan_2026-04-21_完成归档.md new file mode 100644 index 00000000..7a9a9c84 --- /dev/null +++ b/docs/used/SRP_URP_StandaloneStageOwnershipBoundaryPlan_2026-04-21_完成归档.md @@ -0,0 +1,48 @@ +# SRP/URP Standalone Stage Ownership Boundary Plan + +日期:2026-04-21 + +## 背景 + +当前 SRP/URP 主线已经有了基本骨架,但 `ScriptableRenderPipelineHostAsset` 里仍然直接注册了三类 native standalone pass: + +1. `BuiltinShadowCasterPass` +2. `BuiltinDepthOnlyPass` +3. `BuiltinObjectIdPass` + +这会带来两个结构问题: + +1. 通用 SRP host 和 builtin forward fallback 耦合在一起。`ScriptableRenderPipelineHost` 本应只负责 managed/native 桥接、stage recorder 绑定和 fallback 分发,不应该自己偷偷决定 builtin standalone stage 的具体实现。 +2. ownership 不清晰。managed URP 已经在 C# 层负责 shadow/depth 的 stage 规划和 render-graph 录制,但 native fallback 仍然挂在 host asset 上,导致“谁拥有这些 fallback pass”这个问题没有收口。 + +这一步不是做新功能,而是把这条边界收干净,让后续继续按 Unity 风格往 SRP + URP 分层推进。 + +## 目标 + +1. 让 `ScriptableRenderPipelineHost` 退回到真正的 host 角色,不再在 asset 层硬编码 builtin standalone pass。 +2. 让 builtin forward backend 显式拥有自己的 native standalone fallback pass。 +3. 保持当前运行结果不变:managed renderer 录制 stage 时优先走 managed;需要 native fallback 时,由 backend 明确提供。 + +## 实施步骤 + +1. 在 `BuiltinForward...Setup` 路径中显式配置 builtin forward pipeline 的 standalone fallback pass,而不是在 host asset 中配置。 +2. 调整 `ScriptableRenderPipelineHost::GetCameraFrameStandalonePass(...)`,让 host 在自身未持有 pass 时,从 backend 查询 standalone pass。 +3. 清理 `ScriptableRenderPipelineHostAsset` 中已过期的 builtin pass 注入逻辑。 +4. 重新编译旧版 `XCEditor`,运行旧版 editor 冒烟至少 10 秒,确认没有把 fallback 链路改坏。 + +## 完成判定 + +1. `ScriptableRenderPipelineHostAsset` 不再直接注册 builtin shadow/depth/object-id standalone pass。 +2. builtin forward backend 仍可为 fallback stage 提供 native standalone pass。 +3. `cmake --build . --config Debug --target XCEditor` 成功。 +4. 旧版 `editor/bin/Debug/XCEngine.exe` 冒烟通过,并出现新的 `SceneReady` 记录。 + +## 结果 + +1. `ScriptableRenderPipelineHostAsset` 已移除 builtin standalone pass 注入逻辑。 +2. builtin forward backend 现在在自身 setup 中显式注册 `ObjectId`、`DepthOnly`、`ShadowCaster` fallback pass。 +3. `ScriptableRenderPipelineHost` 现在会在自身未持有 standalone pass 时,向 backend 查询 fallback pass。 +4. 验证结果: + `cmake --build . --config Debug --target XCEditor` 通过。 + 旧版 editor 冒烟 15 秒通过。 + 日志结果:`SceneReady elapsed_ms=5505 first_frame_ms=619`。 diff --git a/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h b/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h index dbc98f19..66480c48 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h @@ -69,6 +69,9 @@ public: private: bool EnsureInitialized(const RenderContext& context); + RenderPass* ResolvePipelineBackendStandalonePass( + CameraFrameStage stage, + int32_t rendererIndex) const; void BindStageRecorderPipelineBackend(); void ShutdownInitializedComponents(); void ResetInitializationState(); @@ -98,7 +101,6 @@ public: managedAssetRuntime); std::unique_ptr CreatePipeline() const override; - void ConfigurePipeline(RenderPipeline& pipeline) const override; FinalColorSettings GetDefaultFinalColorSettings() const override; private: diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp index 4d34959b..0111da97 100644 --- a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp +++ b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp @@ -8,9 +8,6 @@ #include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h" #include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" #include "Rendering/Pipelines/ScriptableRenderPipelineHost.h" -#include "Rendering/Passes/BuiltinDepthOnlyPass.h" -#include "Rendering/Passes/BuiltinObjectIdPass.h" -#include "Rendering/Passes/BuiltinShadowCasterPass.h" namespace XCEngine { namespace Rendering { @@ -26,18 +23,6 @@ CreateBuiltinForwardPipelineRendererAsset() { return s_builtinForwardPipelineAsset; } -std::unique_ptr CreateBuiltinDepthOnlyStandalonePass() { - return std::make_unique(); -} - -std::unique_ptr CreateBuiltinObjectIdStandalonePass() { - return std::make_unique(); -} - -std::unique_ptr CreateBuiltinShadowCasterStandalonePass() { - return std::make_unique(); -} - std::unique_ptr TryCreateSceneDrawBackendFromAsset( const std::shared_ptr& asset) { if (asset == nullptr) { diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.cpp index 78541641..c60e84ee 100644 --- a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.cpp +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.cpp @@ -2,6 +2,9 @@ #include "Rendering/Features/BuiltinGaussianSplatPass.h" #include "Rendering/Features/BuiltinVolumetricPass.h" +#include "Rendering/Passes/BuiltinDepthOnlyPass.h" +#include "Rendering/Passes/BuiltinObjectIdPass.h" +#include "Rendering/Passes/BuiltinShadowCasterPass.h" #include "Rendering/Pipelines/BuiltinForwardPipeline.h" #include @@ -11,8 +14,26 @@ namespace Rendering { namespace Pipelines { namespace Internal { +namespace { + +void ConfigureBuiltinForwardStandalonePasses( + BuiltinForwardPipeline& pipeline) { + pipeline.SetCameraFrameStandalonePass( + CameraFrameStage::ObjectId, + std::make_unique()); + pipeline.SetCameraFrameStandalonePass( + CameraFrameStage::DepthOnly, + std::make_unique()); + pipeline.SetCameraFrameStandalonePass( + CameraFrameStage::ShadowCaster, + std::make_unique()); +} + +} // namespace + void ConfigureBuiltinForwardPipeline( BuiltinForwardPipeline& pipeline) { + ConfigureBuiltinForwardStandalonePasses(pipeline); pipeline.AddForwardSceneFeaturePass( std::make_unique()); pipeline.AddForwardSceneFeaturePass( diff --git a/engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp b/engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp index 4f98da51..ddc5dcbe 100644 --- a/engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp +++ b/engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp @@ -5,9 +5,6 @@ #include "Rendering/Pipelines/BuiltinForwardPipeline.h" #include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h" #include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h" -#include "Rendering/Passes/BuiltinDepthOnlyPass.h" -#include "Rendering/Passes/BuiltinObjectIdPass.h" -#include "Rendering/Passes/BuiltinShadowCasterPass.h" namespace XCEngine { namespace Rendering { @@ -215,7 +212,15 @@ RenderPass* ScriptableRenderPipelineHost::GetCameraFrameStandalonePass( return nullptr; } - return RenderPipeline::GetCameraFrameStandalonePass(stage); + if (RenderPass* const hostPass = + RenderPipeline::GetCameraFrameStandalonePass(stage); + hostPass != nullptr) { + return hostPass; + } + + return ResolvePipelineBackendStandalonePass( + stage, + rendererIndex); } bool ScriptableRenderPipelineHost::EnsureInitialized(const RenderContext& context) { @@ -261,6 +266,21 @@ bool ScriptableRenderPipelineHost::EnsureInitialized(const RenderContext& contex return true; } +RenderPass* ScriptableRenderPipelineHost::ResolvePipelineBackendStandalonePass( + CameraFrameStage stage, + int32_t rendererIndex) const { + const auto* const backendPipeline = + dynamic_cast(m_pipelineBackend.get()); + if (backendPipeline == nullptr || + backendPipeline == this) { + return nullptr; + } + + return backendPipeline->GetCameraFrameStandalonePass( + stage, + rendererIndex); +} + void ScriptableRenderPipelineHost::BindStageRecorderPipelineBackend() { if (m_stageRecorder != nullptr) { m_stageRecorder->SetPipelineBackend(m_pipelineBackend.get()); @@ -353,28 +373,9 @@ ScriptableRenderPipelineHostAsset::ScriptableRenderPipelineHostAsset( } std::unique_ptr ScriptableRenderPipelineHostAsset::CreatePipeline() const { - std::unique_ptr pipeline = - std::make_unique( - m_pipelineBackendAsset, - m_managedAssetRuntime); - if (pipeline != nullptr) { - ConfigurePipeline(*pipeline); - } - - return pipeline; -} - -void ScriptableRenderPipelineHostAsset::ConfigurePipeline( - RenderPipeline& pipeline) const { - pipeline.SetCameraFrameStandalonePass( - CameraFrameStage::ObjectId, - std::make_unique()); - pipeline.SetCameraFrameStandalonePass( - CameraFrameStage::DepthOnly, - std::make_unique()); - pipeline.SetCameraFrameStandalonePass( - CameraFrameStage::ShadowCaster, - std::make_unique()); + return std::make_unique( + m_pipelineBackendAsset, + m_managedAssetRuntime); } FinalColorSettings ScriptableRenderPipelineHostAsset::GetDefaultFinalColorSettings() const {