From f064d6ed68fb94bcdd82bfbcb203c9e7d5a1a327 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 15 Apr 2026 00:53:51 +0800 Subject: [PATCH] Extract camera frame fullscreen stage planner --- ...enderGraph规划层收口与SRP启动前条件_2026-04-15.md | 180 ++++++++++++++++++ ...段计划_RenderGraph骨架与资源调度_2026-04-14.md | 0 engine/CMakeLists.txt | 2 + .../Planning/CameraFramePlanBuilder.cpp | 59 +----- .../CameraFrameFullscreenStagePlanner.cpp | 76 ++++++++ .../CameraFrameFullscreenStagePlanner.h | 21 ++ .../EditorHostCommandBridge.cpp | 0 .../EditorHostCommandBridge.h | 0 .../EditorShellAssetBuilder.cpp | 0 .../EditorShellAssetBuilder.h | 0 10 files changed, 284 insertions(+), 54 deletions(-) create mode 100644 docs/plan/Renderer_C++层第四阶段计划_RenderGraph规划层收口与SRP启动前条件_2026-04-15.md rename docs/plan/{ => used}/Renderer_C++层第三阶段计划_RenderGraph骨架与资源调度_2026-04-14.md (100%) create mode 100644 engine/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.cpp create mode 100644 engine/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.h rename new_editor/app/{Commands => Composition}/EditorHostCommandBridge.cpp (100%) rename new_editor/app/{Commands => Composition}/EditorHostCommandBridge.h (100%) rename new_editor/app/{Shell => Composition}/EditorShellAssetBuilder.cpp (100%) rename new_editor/app/{Shell => Composition}/EditorShellAssetBuilder.h (100%) diff --git a/docs/plan/Renderer_C++层第四阶段计划_RenderGraph规划层收口与SRP启动前条件_2026-04-15.md b/docs/plan/Renderer_C++层第四阶段计划_RenderGraph规划层收口与SRP启动前条件_2026-04-15.md new file mode 100644 index 00000000..f32baec8 --- /dev/null +++ b/docs/plan/Renderer_C++层第四阶段计划_RenderGraph规划层收口与SRP启动前条件_2026-04-15.md @@ -0,0 +1,180 @@ +# Renderer C++层第四阶段计划:RenderGraph规划层收口与SRP启动前条件 + +日期:`2026-04-15` + +## 1. 文档定位 + +第三阶段已经把 RenderGraph native host 的骨架、阶段上下文、运行时分发、stage surface/source 解析、fullscreen color chain 意图、internal policy 边界,连续收了一轮。 + +这一阶段不直接开做面向用户的 `C# SRP`。 + +这一阶段要做的是: + +- 把 `CameraFramePlanBuilder` 和 planning 层继续收干净。 +- 让 `CameraFramePlan` 更像高层 frame intent,而不是执行细节载体。 +- 给后面的 `native renderer builder / SRP host seam` 做稳定落点。 +- 明确“什么时候可以正式开始做 SRP”。 + +一句话: +先把 `native planning -> graph build` 这条线收口,再开 SRP。 + +## 2. 当前进展基线 + +截至当前主线,已经完成并推送: + +- `8232f59` `Extract camera frame render-graph recording session` +- `d92afa2` `Extract camera frame render-graph stage record context` +- `5edc4ed` `Extract camera frame render-graph stage pass runtime` +- `ac836ae` `Extract camera frame stage surface resolver` +- `00fa6ff` `Group camera frame fullscreen color chain intent` +- `0afcaa0` `Move render-graph stage policy into internal host` + +当前验证基线: + +- `rendering_unit_tests`: `209 passed / 1 failed` +- 唯一剩余失败:`BuiltinForwardPipeline_Test.OpenGLRuntimeTranspilesForwardShadowVariantToLegacyClipConventions` +- `editor_tests`: `191 passed` +- `XCEditor` 12s smoke:通过 + +## 3. 当前还没收口的核心问题 + +### 3.1 `CameraFramePlanBuilder` 仍然过重 + +现在 `AttachFullscreenStageRequests` 还在同时做: + +- camera 后处理/最终输出需求判断 +- graph-managed color chain 规划 +- sequence 绑定 +- destination/source 语义拼装 + +这说明 planning 还是有点像“半执行期拼装器”。 + +### 3.2 `CameraFramePlan` 还没有完全变成高层意图对象 + +虽然已经收掉了一批低层 getter,也把 fullscreen chain 收成了 `colorChain`,但 plan 里仍然保留着一些偏执行期的结构和默认推导痕迹。 + +后面如果直接在这上面做 SRP,暴露出去的 API 很容易绑定到当前 native 内部实现细节。 + +### 3.3 还缺一个稳定的 native renderer builder 接缝 + +现在的 RenderGraph host 已经能工作,但还没有一个非常明确的 “renderer 向 graph 注册本帧 pass” 的正式层。 + +这层不稳定,后面: + +- forward renderer +- deferred renderer +- feature injection +- C# SRP host + +都会继续直接碰当前 host 细节。 + +## 4. 本阶段总目标 + +这一阶段结束后,native 渲染主线要更接近: + +`Scene/Camera Planning -> CameraFramePlan -> Native Renderer Builder -> RenderGraph Host -> Execute` + +也就是: + +- `Planning` 只负责“这一帧需要什么”。 +- `Renderer Builder` 只负责“把这些意图翻译成 graph passes/resources”。 +- `RenderGraph Host` 只负责“记录、编译、执行图”。 + +## 5. 具体执行顺序 + +### 5.1 先拆 `CameraFramePlanBuilder` + +优先拆出与 fullscreen chain 相关的独立 planner / helper,目标是让 `SceneRenderer` 和 `CameraFramePlanBuilder` 不再直接持有一坨后处理/最终输出拼装逻辑。 + +建议收口方向: + +- `CameraFrameFullscreenStagePlanner` +- `CameraFrameColorChainPlanner` +- `CameraFramePlanValidation` 或等价 helper + +### 5.2 再把 plan 进一步高层化 + +继续减少 plan 里和具体 surface/source/sequence 拼装绑定过深的部分,让 plan 更像: + +- 有哪些 stage +- stage 之间是什么来源关系 +- 哪些输出是 graph-managed +- 哪些是 external/imported target + +而不是继续堆更多执行期细节。 + +### 5.3 引入 native renderer builder 接缝 + +在 native 侧明确一层“renderer 向 RenderGraph 贡献 pass”的正式接口。 + +这一层的目标不是立刻做成用户 API,而是先给: + +- `BuiltinForwardPipeline` +- 未来 `DeferredRenderer` +- 未来 `RendererFeature` + +提供统一宿主。 + +### 5.4 最后判断是否进入 SRP + +当且仅当下面三件事成立,才正式开始做 SRP: + +- `Planning` 和 `Execution` 边界稳定。 +- `Renderer Builder` 这一层稳定。 +- 新增渲染能力时,不需要再直接改 `CameraRenderer` 主线大函数。 + +## 6. 何时可以开始做 SRP + +当前结论仍然是: + +还不能正式开始做面向用户的 `C# SRP`。 + +可以开始 SRP 的条件不是“已经有 RenderGraph 了”,而是: + +1. `CameraFramePlanBuilder` 已收口。 +2. `CameraFramePlan` 已足够高层稳定。 +3. native `renderer builder` 接缝已存在。 +4. `BuiltinForwardPipeline` 已经可以被看成一个 builtin renderer,而不是一坨 host 逻辑。 + +满足这四条之后,就可以开始第一版 SRP。 + +我的判断是: + +- 如果第四阶段顺利,SRP 就已经进入可启动状态。 +- 也就是说,SRP 不是现在开。 +- SRP 是这一个 native 收口阶段之后开。 + +## 7. 本阶段完成标准 + +满足下面这些条件,就可以认定第四阶段收口: + +- `CameraFramePlanBuilder` 明显变薄,fullscreen/color-chain planning 被拆出。 +- `CameraFramePlan` 不再继续回流低层执行细节。 +- native 出现稳定的 renderer builder 接缝。 +- `CameraRenderer` 继续变薄,没有重新长回大调度函数。 +- `rendering_unit_tests / editor_tests / XCEditor smoke` 继续稳定通过。 + +## 8. 阶段后下一步 + +第四阶段完成后,下一步就是正式开: + +`SRP Host v1` + +但第一版 SRP 仍然只建议做到: + +- native host + C# 管线入口 +- C# 侧可组织 renderer / feature / pass +- 先复用 builtin forward renderer + +不建议一上来就同时做: + +- 完整 deferred +- 完整 URP 包 +- 全量 renderer feature 生态 + +正确顺序仍然是: + +1. 先 native 收口 +2. 再 SRP host +3. 再 URP-like builtin package +4. 最后再 deferred / 更复杂 feature diff --git a/docs/plan/Renderer_C++层第三阶段计划_RenderGraph骨架与资源调度_2026-04-14.md b/docs/plan/used/Renderer_C++层第三阶段计划_RenderGraph骨架与资源调度_2026-04-14.md similarity index 100% rename from docs/plan/Renderer_C++层第三阶段计划_RenderGraph骨架与资源调度_2026-04-14.md rename to docs/plan/used/Renderer_C++层第三阶段计划_RenderGraph骨架与资源调度_2026-04-14.md diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index fa179b52..dc57cc0f 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -583,6 +583,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/CameraFramePlanBuilder.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/CameraFramePlanBuilder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/SceneRenderRequestPlanner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Shadow/DirectionalShadowData.cpp diff --git a/engine/src/Rendering/Planning/CameraFramePlanBuilder.cpp b/engine/src/Rendering/Planning/CameraFramePlanBuilder.cpp index a649bfb8..cd037588 100644 --- a/engine/src/Rendering/Planning/CameraFramePlanBuilder.cpp +++ b/engine/src/Rendering/Planning/CameraFramePlanBuilder.cpp @@ -1,9 +1,7 @@ #include "Rendering/Planning/CameraFramePlanBuilder.h" #include "Components/CameraComponent.h" -#include "Debug/Logger.h" -#include "Rendering/Planning/CameraPostProcessPassFactory.h" -#include "Rendering/Planning/FinalColorPassFactory.h" +#include "Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.h" #include "Rendering/RenderPipelineAsset.h" namespace XCEngine { @@ -55,57 +53,10 @@ void CameraFramePlanBuilder::AttachFullscreenStageRequests( for (size_t index = 0; index < plans.size(); ++index) { CameraFramePlan& plan = plans[index]; - if (plan.request.camera == nullptr || - plan.request.context.device == nullptr || - !HasValidColorTarget(plan.request.surface)) { - continue; - } - - std::unique_ptr postProcessSequence = - BuildCameraPostProcessPassSequence(plan.request.camera->GetPostProcessPasses()); - std::unique_ptr finalOutputSequence = - BuildFinalColorPassSequence(plan.finalColorPolicy); - - const bool hasPostProcess = - postProcessSequence != nullptr && postProcessSequence->GetPassCount() > 0u; - const bool hasFinalOutput = - finalOutputSequence != nullptr && finalOutputSequence->GetPassCount() > 0u; - if (!hasPostProcess && !hasFinalOutput) { - continue; - } - - if (plan.request.surface.GetSampleCount() > 1u) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "SceneRenderer fullscreen post-process/final-output chain currently requires a single-sample main scene surface"); - continue; - } - - if (hasPostProcess) { - m_ownedPostProcessSequences[index] = std::move(postProcessSequence); - plan.postProcess.passes = m_ownedPostProcessSequences[index].get(); - plan.colorChain.usesGraphManagedMainSceneColor = true; - plan.colorChain.postProcess.source = CameraFrameColorSource::MainSceneColor; - plan.colorChain.postProcess.usesGraphManagedOutputColor = hasFinalOutput; - if (!hasFinalOutput) { - plan.postProcess.destinationSurface = plan.request.surface; - } - } - - if (hasFinalOutput) { - m_ownedFinalOutputSequences[index] = std::move(finalOutputSequence); - plan.finalOutput.passes = m_ownedFinalOutputSequences[index].get(); - plan.colorChain.usesGraphManagedMainSceneColor = true; - plan.colorChain.finalOutput.source = - hasPostProcess - ? CameraFrameColorSource::PostProcessColor - : CameraFrameColorSource::MainSceneColor; - plan.finalOutput.destinationSurface = plan.request.surface; - } - - if (plan.UsesGraphManagedMainSceneColor()) { - plan.ConfigureGraphManagedMainSceneSurface(); - } + Internal::PlanCameraFrameFullscreenStages( + plan, + m_ownedPostProcessSequences[index], + m_ownedFinalOutputSequences[index]); } } diff --git a/engine/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.cpp b/engine/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.cpp new file mode 100644 index 00000000..a67c865f --- /dev/null +++ b/engine/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.cpp @@ -0,0 +1,76 @@ +#include "Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.h" + +#include "Components/CameraComponent.h" +#include "Debug/Logger.h" + +#include +#include +#include + +namespace XCEngine { +namespace Rendering { +namespace Internal { + +void PlanCameraFrameFullscreenStages( + CameraFramePlan& plan, + std::unique_ptr& ownedPostProcessSequence, + std::unique_ptr& ownedFinalOutputSequence) { + ownedPostProcessSequence.reset(); + ownedFinalOutputSequence.reset(); + + if (plan.request.camera == nullptr || + plan.request.context.device == nullptr || + !HasValidColorTarget(plan.request.surface)) { + return; + } + + std::unique_ptr postProcessSequence = + BuildCameraPostProcessPassSequence(plan.request.camera->GetPostProcessPasses()); + std::unique_ptr finalOutputSequence = + BuildFinalColorPassSequence(plan.finalColorPolicy); + + const bool hasPostProcess = + postProcessSequence != nullptr && postProcessSequence->GetPassCount() > 0u; + const bool hasFinalOutput = + finalOutputSequence != nullptr && finalOutputSequence->GetPassCount() > 0u; + if (!hasPostProcess && !hasFinalOutput) { + return; + } + + if (plan.request.surface.GetSampleCount() > 1u) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "SceneRenderer fullscreen post-process/final-output chain currently requires a single-sample main scene surface"); + return; + } + + if (hasPostProcess) { + ownedPostProcessSequence = std::move(postProcessSequence); + plan.postProcess.passes = ownedPostProcessSequence.get(); + plan.colorChain.usesGraphManagedMainSceneColor = true; + plan.colorChain.postProcess.source = CameraFrameColorSource::MainSceneColor; + plan.colorChain.postProcess.usesGraphManagedOutputColor = hasFinalOutput; + if (!hasFinalOutput) { + plan.postProcess.destinationSurface = plan.request.surface; + } + } + + if (hasFinalOutput) { + ownedFinalOutputSequence = std::move(finalOutputSequence); + plan.finalOutput.passes = ownedFinalOutputSequence.get(); + plan.colorChain.usesGraphManagedMainSceneColor = true; + plan.colorChain.finalOutput.source = + hasPostProcess + ? CameraFrameColorSource::PostProcessColor + : CameraFrameColorSource::MainSceneColor; + plan.finalOutput.destinationSurface = plan.request.surface; + } + + if (plan.UsesGraphManagedMainSceneColor()) { + plan.ConfigureGraphManagedMainSceneSurface(); + } +} + +} // namespace Internal +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.h b/engine/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.h new file mode 100644 index 00000000..bd57946c --- /dev/null +++ b/engine/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +namespace XCEngine { +namespace Rendering { + +struct CameraFramePlan; + +namespace Internal { + +void PlanCameraFrameFullscreenStages( + CameraFramePlan& plan, + std::unique_ptr& ownedPostProcessSequence, + std::unique_ptr& ownedFinalOutputSequence); + +} // namespace Internal +} // namespace Rendering +} // namespace XCEngine diff --git a/new_editor/app/Commands/EditorHostCommandBridge.cpp b/new_editor/app/Composition/EditorHostCommandBridge.cpp similarity index 100% rename from new_editor/app/Commands/EditorHostCommandBridge.cpp rename to new_editor/app/Composition/EditorHostCommandBridge.cpp diff --git a/new_editor/app/Commands/EditorHostCommandBridge.h b/new_editor/app/Composition/EditorHostCommandBridge.h similarity index 100% rename from new_editor/app/Commands/EditorHostCommandBridge.h rename to new_editor/app/Composition/EditorHostCommandBridge.h diff --git a/new_editor/app/Shell/EditorShellAssetBuilder.cpp b/new_editor/app/Composition/EditorShellAssetBuilder.cpp similarity index 100% rename from new_editor/app/Shell/EditorShellAssetBuilder.cpp rename to new_editor/app/Composition/EditorShellAssetBuilder.cpp diff --git a/new_editor/app/Shell/EditorShellAssetBuilder.h b/new_editor/app/Composition/EditorShellAssetBuilder.h similarity index 100% rename from new_editor/app/Shell/EditorShellAssetBuilder.h rename to new_editor/app/Composition/EditorShellAssetBuilder.h