Centralize render-graph recording context builders
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
# Renderer C++层第五阶段计划:SRP Host v1骨架与 Builtin Forward 复用
|
||||
|
||||
日期:`2026-04-15`
|
||||
|
||||
## 1. 阶段定位
|
||||
|
||||
第四阶段已经把 `native planning -> graph build` 这条主线基本收口:
|
||||
|
||||
- `CameraFramePlanBuilder` 的 fullscreen/color-chain 规划已拆出
|
||||
- `CameraFramePlan` 已从 public 头里下沉主要实现
|
||||
- `BuiltinForwardPipeline` 的 main-scene graph 录制已抽出 internal builder
|
||||
- `CameraFrame` / `SceneRenderFeatureHost` / `BuiltinForward` 共用的 graph recording context 已集中到统一 builder
|
||||
|
||||
现在可以正式进入下一阶段:
|
||||
|
||||
`SRP Host v1`
|
||||
|
||||
这一阶段不是直接做完整 URP,也不是直接开放最终用户级 API。
|
||||
这一阶段的目标是先把 native host、renderer builder、C# 管线入口三者接稳。
|
||||
|
||||
## 2. 这一阶段要解决的核心问题
|
||||
|
||||
### 2.1 目前还没有正式的 SRP host
|
||||
|
||||
当前引擎已经有:
|
||||
|
||||
- `SceneRenderer`
|
||||
- `CameraRenderer`
|
||||
- `RenderGraph`
|
||||
- `BuiltinForwardPipeline`
|
||||
|
||||
但还没有一个明确的“脚本侧组织 renderer / feature / pass,native 侧负责承载与执行”的正式宿主层。
|
||||
|
||||
### 2.2 当前 builtin forward 仍然是“内建管线实现”,不是“可被 SRP host 复用的 builtin renderer”
|
||||
|
||||
虽然第四阶段已经把 main-scene graph builder 抽出来了,但 `BuiltinForwardPipeline` 仍然更像一个完整管线对象。
|
||||
SRP host 要求它至少能被看成:
|
||||
|
||||
- 一个可复用的 native renderer
|
||||
- 一个可被 host 调度的 graph contributor
|
||||
- 一个默认 builtin implementation
|
||||
|
||||
而不是 host 本身。
|
||||
|
||||
### 2.3 C# 入口现在还缺少正式边界
|
||||
|
||||
后面如果要像 Unity 一样让用户用 C# 组织 renderer / feature / pass,最先需要的不是完整功能,而是稳定边界:
|
||||
|
||||
- C# 能创建/持有一条 render pipeline asset
|
||||
- native 能向脚本暴露受控的 renderer host 入口
|
||||
- host 能回落复用 builtin forward renderer
|
||||
|
||||
## 3. 本阶段总目标
|
||||
|
||||
这一阶段结束后,主线应接近:
|
||||
|
||||
`C# Pipeline Asset / Native Pipeline Asset -> SRP Host -> Builtin Renderer / Renderer Feature -> RenderGraph Host -> Execute`
|
||||
|
||||
也就是:
|
||||
|
||||
- `RenderGraph` 仍然留在 C++ 层
|
||||
- `SRP Host` 负责组织一帧 renderer graph contribution
|
||||
- `BuiltinForward` 首先作为默认 builtin renderer 被 host 复用
|
||||
- 后续 `URP-like package` 再放到更高层,不直接塞回 C++ host
|
||||
|
||||
## 4. 明确边界
|
||||
|
||||
### 4.1 这一阶段明确不做
|
||||
|
||||
- 不做完整 deferred renderer
|
||||
- 不做完整 URP 包
|
||||
- 不做完整 renderer feature 生态
|
||||
- 不做最终用户可自由扩展的全部 C# API
|
||||
|
||||
### 4.2 这一阶段必须做成
|
||||
|
||||
- 一个正式的 `SRP Host v1`
|
||||
- 一个可被 host 调度的 builtin forward renderer 入口
|
||||
- 一个最小可运行的 C# pipeline asset / host bridge
|
||||
- host 能把 builtin forward 作为默认 fallback 跑通
|
||||
|
||||
## 5. 具体执行顺序
|
||||
|
||||
### 5.1 先把 builtin forward 从“管线实现”整理成“host 可复用 renderer”
|
||||
|
||||
目标:
|
||||
|
||||
- 明确 builtin forward 的 renderer 身份
|
||||
- 减少 host 未来直接依赖 `BuiltinForwardPipeline` 大对象内部细节
|
||||
- 为后续 deferred / custom renderer 留统一接缝
|
||||
|
||||
建议收口方向:
|
||||
|
||||
- `BuiltinForwardRenderer` 或等价 internal builder host
|
||||
- `RenderPipeline` 与 `Renderer Builder` 边界再清一次
|
||||
|
||||
### 5.2 引入 `SRP Host v1` native 宿主
|
||||
|
||||
目标:
|
||||
|
||||
- host 持有一帧 renderer graph contribution 列表
|
||||
- host 负责组织 main scene / feature / fullscreen stages 的 graph 录制
|
||||
- 默认仍可回落到 builtin forward renderer
|
||||
|
||||
第一版不追求花哨能力,先把“host 存在且稳定”做出来。
|
||||
|
||||
### 5.3 打通最小 C# 管线入口
|
||||
|
||||
目标:
|
||||
|
||||
- C# 侧能描述“使用哪条 pipeline asset / renderer”
|
||||
- native 侧能从脚本入口落到 `SRP Host v1`
|
||||
- 至少存在一条 builtin forward 的脚本可达路径
|
||||
|
||||
这一层只做最小闭环,不追求完整编辑器 UX。
|
||||
|
||||
### 5.4 最后再决定是否开 `URP-like builtin package`
|
||||
|
||||
当下面三件事成立,再开下一阶段:
|
||||
|
||||
1. `SRP Host v1` 已稳定
|
||||
2. `BuiltinForward` 已经是可复用 builtin renderer,而不是一坨特殊 case
|
||||
3. C# 入口已经能稳定驱动默认 renderer
|
||||
|
||||
## 6. 完成标准
|
||||
|
||||
满足下面这些条件,就可以认定第五阶段收口:
|
||||
|
||||
- native 已存在正式的 `SRP Host v1`
|
||||
- builtin forward 已能作为 host 下的默认 builtin renderer 运行
|
||||
- 脚本侧至少能以最小路径创建并驱动默认 pipeline
|
||||
- `CameraRenderer` 不会重新长成新的总调度中心
|
||||
- `rendering_unit_tests / editor_tests / XCEditor smoke` 继续稳定
|
||||
|
||||
## 7. 阶段后下一步
|
||||
|
||||
第五阶段完成后,才进入:
|
||||
|
||||
`URP-like Builtin Package v1`
|
||||
|
||||
也就是:
|
||||
|
||||
- C# 侧 builtin package
|
||||
- renderer feature / pass 组合能力
|
||||
- forward first,deferred later
|
||||
|
||||
正确顺序仍然是:
|
||||
|
||||
1. `SRP Host v1`
|
||||
2. `URP-like builtin package`
|
||||
3. `Deferred renderer`
|
||||
4. 更复杂的 feature 生态
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Rendering/Execution/Internal/CameraFrameRenderGraphStageState.h"
|
||||
#include "Rendering/Execution/Internal/CameraFrameRenderGraphSurfaceUtils.h"
|
||||
#include "Rendering/Graph/RenderGraph.h"
|
||||
#include "Rendering/Internal/RenderGraphRecordingContextBuilders.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
@@ -46,7 +47,7 @@ bool TryRecordCameraFrameStageStandaloneRenderGraphPass(
|
||||
|
||||
return true;
|
||||
};
|
||||
const RenderPassRenderGraphContext standalonePassContext = {
|
||||
const Internal::RenderGraphRecordingContextCommon commonContext = {
|
||||
session.graphBuilder,
|
||||
stageState.stageName,
|
||||
context.plan.request.context,
|
||||
@@ -61,10 +62,12 @@ bool TryRecordCameraFrameStageStandaloneRenderGraphPass(
|
||||
std::vector<RenderGraphTextureHandle>{ stageState.outputColor },
|
||||
stageState.outputSurface.depthTexture,
|
||||
&session.stageExecutionSucceeded,
|
||||
beginStandalonePass,
|
||||
{},
|
||||
&session.blackboard
|
||||
};
|
||||
const RenderPassRenderGraphContext standalonePassContext =
|
||||
Internal::BuildRenderPassRenderGraphContext(
|
||||
commonContext,
|
||||
beginStandalonePass);
|
||||
if (!standaloneStagePass->RecordRenderGraph(standalonePassContext)) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
@@ -89,7 +92,7 @@ bool TryRecordCameraFrameMainSceneGraphPass(
|
||||
}
|
||||
|
||||
handled = true;
|
||||
const RenderPipelineMainSceneRenderGraphContext mainSceneContext = {
|
||||
const Internal::RenderGraphRecordingContextCommon commonContext = {
|
||||
session.graphBuilder,
|
||||
stageState.stageName,
|
||||
context.plan.request.context,
|
||||
@@ -100,11 +103,15 @@ bool TryRecordCameraFrameMainSceneGraphPass(
|
||||
: nullptr,
|
||||
stageState.sourceColorView,
|
||||
stageState.sourceColorState,
|
||||
GetPrimaryColorTexture(stageState.sourceSurface),
|
||||
std::vector<RenderGraphTextureHandle>{ stageState.outputColor },
|
||||
stageState.outputSurface.depthTexture,
|
||||
&session.stageExecutionSucceeded,
|
||||
&session.blackboard
|
||||
};
|
||||
const RenderPipelineMainSceneRenderGraphContext mainSceneContext =
|
||||
Internal::BuildRenderPipelineMainSceneRenderGraphContext(
|
||||
commonContext);
|
||||
if (!session.executionState.pipeline->RecordMainSceneRenderGraph(mainSceneContext)) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Rendering/Execution/Internal/CameraFrameRenderGraphStageState.h"
|
||||
#include "Rendering/Execution/Internal/CameraFrameRenderGraphSurfaceUtils.h"
|
||||
#include "Rendering/Graph/RenderGraph.h"
|
||||
#include "Rendering/Internal/RenderGraphRecordingContextBuilders.h"
|
||||
#include "Rendering/Internal/RenderPassGraphUtils.h"
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -103,7 +104,7 @@ bool RecordRegularPassSequenceStage(
|
||||
stageSequence->GetPassCount() == 1u
|
||||
? stageName
|
||||
: BuildRenderGraphSequencePassName(stageName, passIndex);
|
||||
const RenderPassRenderGraphContext passContext = {
|
||||
const Internal::RenderGraphRecordingContextCommon commonContext = {
|
||||
graphBuilder,
|
||||
passName,
|
||||
stagePassContext.renderContext,
|
||||
@@ -116,10 +117,12 @@ bool RecordRegularPassSequenceStage(
|
||||
outputSurface.colorTextures,
|
||||
outputSurface.depthTexture,
|
||||
&stageExecutionSucceeded,
|
||||
beginSequencePass,
|
||||
{},
|
||||
&blackboard
|
||||
};
|
||||
const RenderPassRenderGraphContext passContext =
|
||||
Internal::BuildRenderPassRenderGraphContext(
|
||||
commonContext,
|
||||
beginSequencePass);
|
||||
if (!RecordSequencePass(
|
||||
*pass,
|
||||
passContext,
|
||||
@@ -206,7 +209,7 @@ bool RecordFullscreenPassSequenceStage(
|
||||
passIndex == 0u
|
||||
? binding.sourceColorState
|
||||
: RHI::ResourceStates::PixelShaderResource;
|
||||
const RenderPassRenderGraphContext passContext = {
|
||||
const Internal::RenderGraphRecordingContextCommon commonContext = {
|
||||
graphBuilder,
|
||||
passName,
|
||||
stagePassContext.renderContext,
|
||||
@@ -219,10 +222,12 @@ bool RecordFullscreenPassSequenceStage(
|
||||
std::vector<RenderGraphTextureHandle>{ passOutputColor },
|
||||
{},
|
||||
&stageExecutionSucceeded,
|
||||
beginSequencePass,
|
||||
{},
|
||||
&blackboard
|
||||
};
|
||||
const RenderPassRenderGraphContext passContext =
|
||||
Internal::BuildRenderPassRenderGraphContext(
|
||||
commonContext,
|
||||
beginSequencePass);
|
||||
if (!RecordSequencePass(
|
||||
*pass,
|
||||
passContext,
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/RenderPipeline.h>
|
||||
#include <XCEngine/Rendering/SceneRenderFeaturePass.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
namespace Internal {
|
||||
|
||||
struct RenderGraphRecordingContextCommon {
|
||||
RenderGraphBuilder& graphBuilder;
|
||||
Containers::String passName = {};
|
||||
const RenderContext& renderContext;
|
||||
const RenderSceneData& sceneData;
|
||||
RenderSurface surface = {};
|
||||
const RenderSurface* sourceSurface = nullptr;
|
||||
RHI::RHIResourceView* sourceColorView = nullptr;
|
||||
RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common;
|
||||
RenderGraphTextureHandle sourceColorTexture = {};
|
||||
std::vector<RenderGraphTextureHandle> colorTargets = {};
|
||||
RenderGraphTextureHandle depthTarget = {};
|
||||
bool* executionSucceeded = nullptr;
|
||||
RenderGraphBlackboard* blackboard = nullptr;
|
||||
};
|
||||
|
||||
inline RenderPassRenderGraphContext BuildRenderPassRenderGraphContext(
|
||||
const RenderGraphRecordingContextCommon& common,
|
||||
RenderPassGraphBeginCallback beginPassCallback = {},
|
||||
RenderPassGraphEndCallback endPassCallback = {}) {
|
||||
return {
|
||||
common.graphBuilder,
|
||||
common.passName,
|
||||
common.renderContext,
|
||||
common.sceneData,
|
||||
common.surface,
|
||||
common.sourceSurface,
|
||||
common.sourceColorView,
|
||||
common.sourceColorState,
|
||||
common.sourceColorTexture,
|
||||
common.colorTargets,
|
||||
common.depthTarget,
|
||||
common.executionSucceeded,
|
||||
beginPassCallback,
|
||||
endPassCallback,
|
||||
common.blackboard
|
||||
};
|
||||
}
|
||||
|
||||
inline SceneRenderFeaturePassRenderGraphContext BuildSceneRenderFeaturePassRenderGraphContext(
|
||||
const RenderGraphRecordingContextCommon& common,
|
||||
bool clearAttachments = false,
|
||||
SceneRenderFeaturePassBeginCallback beginPassCallback = {},
|
||||
SceneRenderFeaturePassEndCallback endPassCallback = {}) {
|
||||
return {
|
||||
common.graphBuilder,
|
||||
common.passName,
|
||||
common.renderContext,
|
||||
common.sceneData,
|
||||
common.surface,
|
||||
common.sourceSurface,
|
||||
common.sourceColorView,
|
||||
common.sourceColorState,
|
||||
common.sourceColorTexture,
|
||||
common.colorTargets,
|
||||
common.depthTarget,
|
||||
clearAttachments,
|
||||
common.executionSucceeded,
|
||||
beginPassCallback,
|
||||
endPassCallback,
|
||||
common.blackboard
|
||||
};
|
||||
}
|
||||
|
||||
inline RenderPipelineMainSceneRenderGraphContext BuildRenderPipelineMainSceneRenderGraphContext(
|
||||
const RenderGraphRecordingContextCommon& common) {
|
||||
return {
|
||||
common.graphBuilder,
|
||||
common.passName,
|
||||
common.renderContext,
|
||||
common.sceneData,
|
||||
common.surface,
|
||||
common.sourceSurface,
|
||||
common.sourceColorView,
|
||||
common.sourceColorState,
|
||||
common.colorTargets,
|
||||
common.depthTarget,
|
||||
common.executionSucceeded,
|
||||
common.blackboard
|
||||
};
|
||||
}
|
||||
|
||||
inline SceneRenderFeaturePassRenderGraphContext CloneSceneRenderFeaturePassRenderGraphContext(
|
||||
const SceneRenderFeaturePassRenderGraphContext& context,
|
||||
const Containers::String& passName,
|
||||
bool clearAttachments) {
|
||||
return {
|
||||
context.graphBuilder,
|
||||
passName,
|
||||
context.renderContext,
|
||||
context.sceneData,
|
||||
context.surface,
|
||||
context.sourceSurface,
|
||||
context.sourceColorView,
|
||||
context.sourceColorState,
|
||||
context.sourceColorTexture,
|
||||
context.colorTargets,
|
||||
context.depthTarget,
|
||||
clearAttachments,
|
||||
context.executionSucceeded,
|
||||
context.beginPassCallback,
|
||||
context.endPassCallback,
|
||||
context.blackboard
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Debug/Logger.h"
|
||||
#include "Rendering/Graph/RenderGraph.h"
|
||||
#include "Rendering/Internal/RenderPassGraphUtils.h"
|
||||
#include "Rendering/Internal/RenderGraphRecordingContextBuilders.h"
|
||||
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
|
||||
#include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h"
|
||||
#include "Rendering/RenderSurface.h"
|
||||
@@ -131,28 +132,31 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
|
||||
[&pipeline](const RenderPassContext& passContext) {
|
||||
pipeline.EndForwardScenePass(passContext);
|
||||
};
|
||||
const ::XCEngine::Rendering::Internal::RenderGraphRecordingContextCommon commonContext = {
|
||||
context.graphBuilder,
|
||||
passName,
|
||||
renderContext,
|
||||
*sceneData,
|
||||
surfaceTemplate,
|
||||
hasSourceSurface ? &sourceSurface : nullptr,
|
||||
sourceColorView,
|
||||
sourceColorState,
|
||||
{},
|
||||
colorTargets,
|
||||
depthTarget,
|
||||
executionSucceeded,
|
||||
context.blackboard
|
||||
};
|
||||
|
||||
bool clearAttachments = true;
|
||||
for (const ForwardSceneStep& step : GetBuiltinForwardSceneSteps()) {
|
||||
if (step.type == ForwardSceneStepType::InjectionPoint) {
|
||||
const SceneRenderFeaturePassRenderGraphContext featureContext = {
|
||||
context.graphBuilder,
|
||||
passName,
|
||||
renderContext,
|
||||
*sceneData,
|
||||
surfaceTemplate,
|
||||
hasSourceSurface ? &sourceSurface : nullptr,
|
||||
sourceColorView,
|
||||
sourceColorState,
|
||||
{},
|
||||
colorTargets,
|
||||
depthTarget,
|
||||
clearAttachments,
|
||||
executionSucceeded,
|
||||
beginRecordedPass,
|
||||
endRecordedPass,
|
||||
context.blackboard
|
||||
};
|
||||
const SceneRenderFeaturePassRenderGraphContext featureContext =
|
||||
::XCEngine::Rendering::Internal::BuildSceneRenderFeaturePassRenderGraphContext(
|
||||
commonContext,
|
||||
clearAttachments,
|
||||
beginRecordedPass,
|
||||
endRecordedPass);
|
||||
bool recordedAnyPass = false;
|
||||
if (!pipeline.m_forwardSceneFeatureHost.Record(
|
||||
featureContext,
|
||||
@@ -178,23 +182,13 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
|
||||
mainDirectionalShadowTexture.IsValid()
|
||||
? std::vector<RenderGraphTextureHandle>{ mainDirectionalShadowTexture }
|
||||
: std::vector<RenderGraphTextureHandle>{};
|
||||
const RenderPassRenderGraphContext phaseContext = {
|
||||
context.graphBuilder,
|
||||
phasePassName,
|
||||
renderContext,
|
||||
*sceneData,
|
||||
surfaceTemplate,
|
||||
hasSourceSurface ? &sourceSurface : nullptr,
|
||||
sourceColorView,
|
||||
sourceColorState,
|
||||
{},
|
||||
colorTargets,
|
||||
depthTarget,
|
||||
executionSucceeded,
|
||||
beginPhasePass,
|
||||
endRecordedPass,
|
||||
context.blackboard
|
||||
};
|
||||
::XCEngine::Rendering::Internal::RenderGraphRecordingContextCommon phaseCommonContext = commonContext;
|
||||
phaseCommonContext.passName = phasePassName;
|
||||
const RenderPassRenderGraphContext phaseContext =
|
||||
::XCEngine::Rendering::Internal::BuildRenderPassRenderGraphContext(
|
||||
phaseCommonContext,
|
||||
beginPhasePass,
|
||||
endRecordedPass);
|
||||
if (!::XCEngine::Rendering::Internal::RecordCallbackRasterRenderPass(
|
||||
phaseContext,
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Rendering/SceneRenderFeatureHost.h"
|
||||
|
||||
#include "Debug/Logger.h"
|
||||
#include "Rendering/Internal/RenderGraphRecordingContextBuilders.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -118,28 +119,15 @@ bool SceneRenderFeatureHost::Record(
|
||||
continue;
|
||||
}
|
||||
|
||||
const SceneRenderFeaturePassRenderGraphContext featureContext = {
|
||||
context.graphBuilder,
|
||||
BuildFeatureGraphPassName(
|
||||
context.passName,
|
||||
injectionPoint,
|
||||
*featurePass,
|
||||
featureIndex),
|
||||
context.renderContext,
|
||||
context.sceneData,
|
||||
context.surface,
|
||||
context.sourceSurface,
|
||||
context.sourceColorView,
|
||||
context.sourceColorState,
|
||||
context.sourceColorTexture,
|
||||
context.colorTargets,
|
||||
context.depthTarget,
|
||||
clearAttachments,
|
||||
context.executionSucceeded,
|
||||
context.beginPassCallback,
|
||||
context.endPassCallback,
|
||||
context.blackboard
|
||||
};
|
||||
const SceneRenderFeaturePassRenderGraphContext featureContext =
|
||||
Internal::CloneSceneRenderFeaturePassRenderGraphContext(
|
||||
context,
|
||||
BuildFeatureGraphPassName(
|
||||
context.passName,
|
||||
injectionPoint,
|
||||
*featurePass,
|
||||
featureIndex),
|
||||
clearAttachments);
|
||||
if (!featurePass->RecordRenderGraph(featureContext)) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
|
||||
Reference in New Issue
Block a user