Extract camera frame fullscreen stage planner
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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<RenderPassSequence> postProcessSequence =
|
||||
BuildCameraPostProcessPassSequence(plan.request.camera->GetPostProcessPasses());
|
||||
std::unique_ptr<RenderPassSequence> 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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.h"
|
||||
|
||||
#include "Components/CameraComponent.h"
|
||||
#include "Debug/Logger.h"
|
||||
|
||||
#include <XCEngine/Rendering/Execution/CameraFramePlan.h>
|
||||
#include <XCEngine/Rendering/Planning/CameraPostProcessPassFactory.h>
|
||||
#include <XCEngine/Rendering/Planning/FinalColorPassFactory.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
namespace Internal {
|
||||
|
||||
void PlanCameraFrameFullscreenStages(
|
||||
CameraFramePlan& plan,
|
||||
std::unique_ptr<RenderPassSequence>& ownedPostProcessSequence,
|
||||
std::unique_ptr<RenderPassSequence>& ownedFinalOutputSequence) {
|
||||
ownedPostProcessSequence.reset();
|
||||
ownedFinalOutputSequence.reset();
|
||||
|
||||
if (plan.request.camera == nullptr ||
|
||||
plan.request.context.device == nullptr ||
|
||||
!HasValidColorTarget(plan.request.surface)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderPassSequence> postProcessSequence =
|
||||
BuildCameraPostProcessPassSequence(plan.request.camera->GetPostProcessPasses());
|
||||
std::unique_ptr<RenderPassSequence> 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
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/RenderPass.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
struct CameraFramePlan;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
void PlanCameraFrameFullscreenStages(
|
||||
CameraFramePlan& plan,
|
||||
std::unique_ptr<RenderPassSequence>& ownedPostProcessSequence,
|
||||
std::unique_ptr<RenderPassSequence>& ownedFinalOutputSequence);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user