Files
XCEngine/docs/plan/Renderer_C++层第一阶段重构计划_RenderGraph前_2026-04-13.md

15 KiB
Raw Permalink Blame History

Renderer C++层第一阶段重构计划Render Graph 前)

日期:2026-04-13

1. 文档定位

这份计划只处理 Render Graph 之前的那一步,也就是把当前 Rendering 原生主链先整理成一个可长期演进的正式架构。

这一步的目标不是新增大功能,而是先把下面这些边界收口:

  1. SceneRenderer / CameraRenderer / SceneRenderRequestPlanner 的职责边界
  2. RenderSceneExtractor / RenderSceneData / VisibleRenderItem 的数据边界
  3. RenderPipeline / RenderPass / BuiltinForwardPipeline 的执行边界
  4. Runtime 主链与 Editor 注入点的边界

这一步做完之后,下一阶段才适合引入真正的 Render Graph


2. 关于 Render Graph 的结论

结论很明确:

  1. Render Graph 应该先做在 C++ native
  2. 它不应该直接作为第一阶段的一部分
  3. 第一阶段的任务,是先把当前 native renderer 收口到足够清晰,避免后面只是把现有混乱机械搬进 graph

后面的目标结构应该是:

RHI -> Native Render Kernel -> Render Graph -> SRP-like Contract -> Universal Renderer -> C# Custom Pipeline

其中:

  1. Render Graph 属于 Native Render Kernel
  2. C# 层以后可以编排 pass但图资源生命周期、资源状态、barrier、跨 pass 读写依赖仍应先由 native graph 掌控

3. 当前代码里的真实主链

当前项目并不是没有架构,而是已经形成了一条手工编排的 mini-SRP 主链:

  1. SceneRenderRequestPlanner
    • 收集 camera
    • 生成 CameraRenderRequest
    • 负责方向光阴影规划参数
  2. SceneRenderer
    • 调 planner
    • 解析 final color policy
    • 为 post-process / final-output 附加 fullscreen stage request
  3. CameraRenderer
    • 做 shadow request resolve
    • 提取 RenderSceneData
    • 按固定 stage 顺序执行
  4. BuiltinForwardPipeline
    • 负责主场景绘制
    • 同时背着 shader 绑定、PSO 缓存、材质资源解析、skybox、splat、volumetric 等大量职责

对应的当前入口主要是:

  1. engine/include/XCEngine/Rendering/Execution/SceneRenderer.h
  2. engine/include/XCEngine/Rendering/Execution/CameraRenderer.h
  3. engine/include/XCEngine/Rendering/Planning/SceneRenderRequestPlanner.h
  4. engine/include/XCEngine/Rendering/Planning/CameraRenderRequest.h
  5. engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h
  6. engine/include/XCEngine/Rendering/FrameData/VisibleRenderItem.h
  7. engine/include/XCEngine/Rendering/RenderPipeline.h
  8. engine/include/XCEngine/Rendering/RenderPipelineAsset.h
  9. engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h
  10. engine/src/Rendering/Execution/SceneRenderer.cpp
  11. engine/src/Rendering/Execution/CameraRenderer.cpp
  12. engine/src/Rendering/Extraction/RenderSceneExtractor.cpp
  13. engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp
  14. engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp

4. 当前真正的问题

当前主要不是“功能太少”,而是“职责层次还不够正式”。

4.1 request、plan、execution 混在一起

CameraRenderRequest 现在既像外部请求,又像内部执行计划,还夹带了一部分 runtime surface / fullscreen chain 组织信息。
SceneRenderer 也同时承担了 request build、final color resolve、fullscreen intermediate surface ownership 这几类事情。

这样的问题是:

  1. 后面很难区分“用户想渲染什么”和“引擎决定怎么执行”
  2. Editor 也会继续直接依赖底层 stage 细节
  3. Render Graph 以后很难接到一个稳定的 frame plan 输入

4.2 extraction 数据还偏早期

RenderSceneExtractor 现在能工作,但产物仍然偏“把可见对象收集成数组”。
它还没有正式长成后续 SRP / Render Graph 真正需要的那种中间层:

  1. CullingResults
  2. RendererList
  3. FilteringSettings
  4. SortingSettings
  5. DrawSettings

如果这层不先正式化,后面 forward、deferred、shadow、object-id、editor overlay 都会继续各自拿 visibleItems 做自己的隐式解释。

4.3 BuiltinForwardPipeline 过重

当前 BuiltinForwardPipeline 不只是一个 renderer它已经同时包含

  1. 主场景 pass 编排
  2. 材质资源布局解析
  3. descriptor set 组织
  4. pipeline state cache
  5. lighting / shadow 绑定
  6. skybox 逻辑
  7. gaussian splat 特殊路径
  8. volumetric 特殊路径

这会带来两个后果:

  1. 后面任何主场景新能力都会继续往这个类里堆
  2. 未来要抽 Universal Renderer 或切出 Deferred 时,拆分成本会很高

4.4 runtime 主线和 editor 扩展点还没正式隔离

现在 Editor 已经深度依赖 request stage 注入点,这个方向本身没错,但 contract 还不够正式。
如果不先收口,后面 graph 化时 editor 特殊路径会变成额外复杂度来源。

4.5 pipeline contract 还不够像未来的 SRP 底座

当前 RenderPipeline 还是:

Render(context, surface, sceneData)

这个接口对当前 builtin forward 足够,但对未来这些东西不够:

  1. renderer list
  2. graph resource declaration
  3. per-frame context
  4. pass feature 注入
  5. 多渲染路径共存

第一阶段不要求一步到位,但至少要把它朝这个方向整理。


5. 本阶段明确不做的事

为了保证收口不失控,这一阶段明确不做下面这些:

  1. 不引入真正运行时 Render Graph
  2. 不做完整 Deferred Renderer
  3. 不做 C# SRP 暴露
  4. 不重写 RHI
  5. 不新增大型渲染特性来打断架构收口

本阶段只做一件事:

把当前 native rendering layer 整理成 Render Graph 之前的正式形态


6. 第一阶段完成后应该达到什么状态

本阶段完成后native renderer 至少应满足下面这些条件:

  1. 外部 request、内部 frame plan、实际 execution 三层语义分开
  2. extraction / culling / draw organization 有正式中间层,不再只靠裸 visibleItems
  3. BuiltinForwardPipeline 明显瘦身,不再继续当总垃圾桶
  4. runtime pass 与 editor pass 有明确注入 contract
  5. 后续 Render Graph 能接一个稳定的 Frame Plan + Renderer Lists + Resource Needs 输入

7. 第一阶段的重构原则

7.1 先 formalize再 graphize

先把 contract 和边界做清楚,再做 graph。
不要反过来。

7.2 不破坏现有主链闭环

当前 Scene / Game / Editor Viewport 已经能跑通,第一阶段不能为了“好看”把现有可运行链条打散。

7.3 新中间层优先 native internal

这一阶段新补的 Frame PlanCullingResultsRendererListDrawSettings 都先是 C++ 内部契约,不急着暴露给 C#。

7.4 先解耦职责,再决定目录长相

不要先做大范围目录搬家。
先把职责拆开,目录只是最后的外显结果。

7.5 editor contract 正式化,但不去特殊化主链

Editor 的 object-id、outline、grid、overlay 依然要保留,但它们应该依附在正式 contract 上,而不是继续共享 runtime 的隐式细节。


8. 工作包拆分

工作包 Arequest / frame plan 分层

目标:

把“用户想渲染什么”和“引擎这帧怎么执行”拆开。

建议结果:

  1. 保留 CameraRenderRequest 作为外部请求描述
  2. 新增内部 CameraFramePlan 或等价类型,承载:
    • 实际启用的 stage
    • fullscreen chain
    • resolved target/surface
    • 阴影计划结果
    • editor 注入点
  3. SceneRenderer 只负责:
    • 请求收集
    • 请求排序
    • request -> frame plan 转换
  4. CameraRenderer 只负责:
    • frame plan 执行
    • 调 scene extraction
    • 调 pipeline / standalone pass

优先涉及文件:

  1. engine/include/XCEngine/Rendering/Planning/CameraRenderRequest.h
  2. engine/include/XCEngine/Rendering/Execution/SceneRenderer.h
  3. engine/src/Rendering/Execution/SceneRenderer.cpp
  4. engine/include/XCEngine/Rendering/Execution/CameraRenderer.h
  5. engine/src/Rendering/Execution/CameraRenderer.cpp

完成标准:

  1. request 不再混入过多 runtime-owned intermediate state
  2. frame plan 成为 camera 执行的正式输入
  3. SceneRenderer 和 CameraRenderer 的边界可以一句话说清

工作包 Bscene extraction -> culling results / renderer list

目标:

把当前 RenderSceneExtractor 的数组式输出,推进为后续 SRP/RenderGraph 可用的原生中间层。

建议结果:

  1. 在 native 层引入等价于以下概念的类型:
    • CullingResults
    • RendererListDesc
    • RendererList
    • FilteringSettings
    • SortingSettings
    • DrawSettings
  2. RenderSceneData 不再承担过多“什么都往里塞”的职责
  3. VisibleRenderItem 继续保留为底层记录,但不再直接成为所有 pass 的唯一公共输入
  4. shadow、main scene、object-id 等路径逐步改为消费 RendererList

优先涉及文件:

  1. engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h
  2. engine/include/XCEngine/Rendering/FrameData/VisibleRenderItem.h
  3. engine/src/Rendering/Extraction/RenderSceneExtractor.cpp
  4. engine/include/XCEngine/Rendering/Extraction/
  5. engine/include/XCEngine/Rendering/Builtin/

完成标准:

  1. opaque、transparent、shadow、object-id 至少能共用同一套 draw organization contract
  2. 后面新增 deferred 时,不需要再发明第二套“可见对象数组解释逻辑”

工作包 Cpipeline contract 收口

目标:

把当前 RenderPipeline / RenderPass / RenderPipelineAsset 收口到更像未来 SRP native kernel 的形态。

建议结果:

  1. 继续保留 RenderPipelineAsset -> RenderPipeline 这条总方向
  2. 明确区分:
    • pipeline asset配置与默认策略
    • pipeline runtime本帧执行
    • standalone pass独立功能 pass
  3. 给主场景 pipeline 引入更正式的执行输入,而不是继续只吃 surface + sceneData
  4. 为下一阶段 graph 化预留清晰输入面:
    • frame context
    • renderer lists
    • per-frame resources

优先涉及文件:

  1. engine/include/XCEngine/Rendering/RenderPipeline.h
  2. engine/include/XCEngine/Rendering/RenderPipelineAsset.h
  3. engine/include/XCEngine/Rendering/RenderPass.h
  4. engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h

完成标准:

  1. 主 pipeline 输入面能明显映射到未来 Universal Renderer
  2. standalone pass 与 main scene pipeline 的边界清楚

工作包 DBuiltinForwardPipeline 瘦身

目标:

BuiltinForwardPipeline 从“大一统实现类”拆成可长期维护的 renderer 内部模块。

建议结果:

  1. 按职责切成内部模块或 helper
    • main scene draw
    • skybox draw
    • lighting binding
    • shadow binding
    • material binding
    • pipeline state cache
    • per-frame resource cache
  2. GaussianSplatVolumetric 继续保留,但从“主 pipeline 杂糅逻辑”变成清晰的 feature-style 子模块
  3. BuiltinForwardPipelineResources.cpp 里和运行时编排无关的资源/绑定组织进一步下沉

优先涉及文件:

  1. engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h
  2. engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp
  3. engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp
  4. engine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h

完成标准:

  1. BuiltinForwardPipeline 主类显著变薄
  2. 新增一种主场景能力时,不需要继续把所有逻辑堆回同一个 cpp

工作包 Eeditor 注入点正式化

目标:

保留当前 editor 渲染能力,但把它从“依赖阶段细节”变成“依赖正式 extension contract”。

建议结果:

  1. 继续保留:
    • object-id
    • outline
    • grid
    • overlay
  2. 把这些能力明确分成:
    • runtime stage
    • editor-only extension stage
  3. 收口 RenderPassSequence 在 request 上的使用边界,避免后续 graph 化时出现“谁都能往 request 里塞东西”的状态

优先涉及文件:

  1. engine/include/XCEngine/Rendering/Planning/CameraRenderRequest.h
  2. editor/src/Viewport/SceneViewportRenderPlan.h
  3. editor/src/Viewport/
  4. engine/src/Rendering/Execution/CameraRenderer.cpp

完成标准:

  1. Editor 仍能完整接入当前主链
  2. 但 editor 不再直接绑死 runtime 内部实现细节

工作包 F测试与文档同步

目标:

把第一阶段重构做成可回归、可交接的正式收口,而不是一次只靠手工验证的大重排。

建议结果:

  1. tests/Rendering/unit
  2. 保底回归现有 tests/Rendering/integration
  3. 必要时补 editor viewport 级验证
  4. 更新 API 文档与后续计划入口

完成标准:

  1. request / extractor / pipeline 边界都有对应测试覆盖
  2. 当前 forward、shadow、object-id、editor scene view 不回退

9. 执行顺序

必须按下面的顺序推进:

  1. 先做工作包 A
  2. 再做工作包 B
  3. 再做工作包 C
  4. 然后做工作包 D
  5. 接着做工作包 E
  6. 最后统一做工作包 F

原因:

  1. 不先分清 request 和 frame plan后面所有 contract 都会悬空
  2. 不先把 extraction 推进到 renderer listpipeline 收口只是表面整理
  3. 不先把 pipeline contract 稳住,BuiltinForwardPipeline 的瘦身会缺少稳定目标
  4. 不先把 native 主线收紧editor contract 很容易继续沿着旧细节扩张

10. 建议新增或调整的 native 类型

第一阶段不要求名字完全按这个来,但建议至少出现这些等价层:

  1. CameraFramePlan
  2. FrameExecutionContext
  3. CullingResults
  4. RendererListDesc
  5. RendererList
  6. DrawSettings
  7. FilteringSettings
  8. SortingSettings
  9. EditorRenderExtensions 或等价 editor 注入描述

注意:

  1. 这些类型先是 C++ internal contract
  2. 这一阶段不急着桥接到 managed
  3. 下一阶段 Render Graph 会直接消费其中一部分

11. 这一阶段完成的验收标准

当以下条件同时成立时,这一阶段才算完成:

  1. SceneRenderer / CameraRenderer / Planner 三者职责边界稳定
  2. native 渲染主链内部已经形成 request、frame plan、execution 三层
  3. RenderSceneExtractor 不再只是输出裸数组,而是能支持正式的 draw organization
  4. BuiltinForwardPipeline 不再承担明显超载职责
  5. editor 的注入点已经正规化,且没有破坏当前 Scene View / Game View 能力
  6. 现有 forward、shadow、post-process、final-output、object-id、overlay 主路径回归通过

12. 下一阶段如何接 Render Graph

等本阶段完成后,下一阶段才进入真正的 Render Graph

那时的接法应该是:

  1. CameraFramePlan 提供本帧逻辑阶段与 feature 需求
  2. CullingResults / RendererList 提供 draw 输入
  3. FrameExecutionContext 提供本帧统一资源上下文
  4. Render Graph 负责:
    • pass declaration
    • resource creation/import
    • read/write dependency
    • barrier / lifetime
    • transient resource reuse

也就是说,Render Graph 不是来替代第一阶段,而是建立在第一阶段之上。


13. 一句话结论

当前最佳路线不是立刻补 Deferred,也不是立刻补 Render Graph,而是先把你现有这条 native rendering 主链整理成真正的 Render Kernel v1;这一步做实了,后面的 Render GraphUniversal RendererC# SRP 才会接得稳。