Files
XCEngine/docs/plan/Renderer_C++层第一阶段收口计划_2026-04-13_晚间.md

12 KiB
Raw Permalink Blame History

Renderer C++层第一阶段收口计划

日期: 2026-04-13

1. 文档定位

这份文档是 Render Graph 之前的最后一轮 native render kernel v1 收口计划。

目标不是继续往当前 Rendering 层里塞新功能,而是先把下面几件事彻底做清楚:

  • request -> frame plan -> execution 三层语义分开。
  • RenderSceneExtractor -> CullingResults -> RendererList -> DrawSettings 这条数据组织链正式化。
  • BuiltinForwardPipeline 从“大杂烩渲染器”收成“协调器 + 子能力”。
  • Editor 注入点、阴影、主场景、后处理之间的边界说清楚。
  • 给下一步 C++ Render Graph 留出稳定输入,而不是把当前混乱直接 graph 化。

结论先写在前面:

  • Render Graph 应该做在 C++ native 层。
  • 现在还不应该直接开做 Render GraphDeferred Renderer
  • 当前更优先的是把现有渲染层整理干净,再往 SRP/URP 方向演进。

2. 当前阶段已经完成的收口

截至 2026-04-13 晚间,已经落地的内容:

  • CameraRenderRequest -> CameraFramePlan 已经分层,CameraRenderer 开始正式消费 plan。
  • CullingResults / RendererList contract 已经进入 RenderSceneData
  • Forward / Depth / ObjectId 已经优先走 RendererList,旧 visibleItems 只保留过渡 fallback。
  • RendererListUtils 已经把可见项遍历规则收口成公共工具。
  • Gaussian / Volumetric 已经从 BuiltinForwardPipeline 中抽成统一 SceneRenderFeaturePass
  • FrameExecutionContext / ScenePhase / DrawSettings 已经引入。
  • RenderPipeline 已经支持新的 FrameExecutionContext 入口,旧三参数入口保留为兼容适配层。
  • BuiltinForwardPipeline 已经开始按 scene phase 组织主场景执行。
  • CameraRenderer 主场景阶段已经走新的 execution context而不是继续只传三参数。

本轮验证结果:

  • XCEditor 已重新编译通过。
  • rendering_unit_tests 已重新编译通过。
  • editor_tests 已重新编译通过。
  • editor_tests --gtest_filter=*ViewportRenderFlow*:*SceneViewportRenderPassBundle* --gtest_brief=1 通过 14/14
  • rendering_unit_tests --gtest_filter=*BuiltinForwardPipeline*:*RenderPass*:*CameraSceneRenderer*:*Shadow*:*ObjectId* --gtest_brief=1 通过 67/68
  • 唯一失败仍然是既有老问题: BuiltinForwardPipeline_Test.OpenGLRuntimeTranspilesForwardShadowVariantToLegacyClipConventions

3. 为什么现在还不能直接上 Render Graph

现在直接上 Render Graph,本质上是在 graph 里继续承接当前的隐式耦合,问题只会换个位置存在。

当前还没完全收口的核心问题不是“没有 graph”而是下面这些边界还不够正式

  • CameraRenderer 和各类 scene pass 之间,谁负责组织阶段,谁负责执行,还没有完全模块化。
  • Shadow 相关逻辑仍然分散在 planner、camera execution、pipeline、surface cache 几处。
  • BuiltinForwardPipeline 虽然已经开始瘦身,但还没有彻底收成一个明确的 coordinator。
  • Editor 的扩展点虽然已可用,但 contract 还偏“约定式”,还不够像以后给 C# SRP 暴露的正式接口。
  • 目录结构仍然在暴露旧历史:一些“子系统”目前还是文件堆,不是真正的模块。

如果这些问题不先解决,Render Graph 只会变成一个新的承压层,后面再拆更贵。

4. Render Graph 和未来 SRP/URP 的边界

未来的推荐结构仍然是这条线:

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

其中边界应当明确成这样:

  • C++ native 保留:
    • Render Graph
    • CullingResults / RendererList
    • draw / dispatch context
    • GPU resource cache
    • shadow map / atlas 执行能力
    • Gaussian 排序与 working set
    • Volume 资源上传与底层执行
  • URP-like 包层保留:
    • feature 编排
    • pass 注入顺序
    • renderer feature 配置
    • 用户级 pipeline 组合
    • 未来的 C# 自定义渲染管线 API

这意味着:

  • 阴影、Gaussian、Volumetric 不是整块搬去包层。
  • 包层更像 Unity 的 URP rendererrenderer feature
  • native 层更像 Unity 没公开给用户、但真正负责执行和图资源生命周期的内核。

5. 第一阶段还差哪些工作才能收口

5.1 Shadow 子系统正式化

这是当前最值得先做的一块。

现状问题:

  • 阴影规划在 Planning
  • 阴影 surface/cache 在 Execution/Caches
  • 阴影执行又是 standalone pass。
  • 主场景采样阴影的状态切换仍在 BuiltinForwardPipeline

这说明阴影现在“能跑”,但还不是一个正式子系统。

收口目标:

  • 明确 shadow planning / shadow resources / shadow execution / shadow sampling contract 四层边界。
  • 把方向光阴影相关共享数据收成一组正式类型,而不是散落在多个调用点上。
  • 为以后扩展 cascades / shadow atlas / additional light shadows / baked shadowmask 留出位置。

5.2 Editor 注入点正式化

现状问题:

  • 当前有 pre scene / post scene / overlay / object id / outline / grid 多种 editor 路径。
  • 这些能力已经能工作,但还没有统一成“正式 stage 注入 contract”。

收口目标:

  • 明确哪些注入点属于 runtime 正式阶段,哪些属于 editor-only extension。
  • CameraFramePlan 对这些阶段的描述更加稳定。
  • 给以后 C# renderer feature 的注入点设计提供 native 对照物。

5.3 BuiltinForwardPipeline 继续瘦身

本轮已经完成第一刀,但还没收完。

还需要继续做的事情:

  • 把 forward scene phase 执行和底层 draw/resource 逻辑彻底分开。
  • 把 forward renderer 自己的内部资源、skybox、feature 协调代码进一步收拢。
  • 保证 BuiltinForwardPipeline 自己只表达“阶段顺序和协调”,不再继续膨胀。

5.4 旧 fallback 的裁边

当前有一些过渡兼容层是故意保留的:

  • RenderPipeline::Render(context, surface, sceneData) 旧入口
  • visibleItems fallback
  • BuildRenderRequests 旧习惯入口

这些现在不该硬删,因为会影响现有 tests 和 editor 链路。

但第一阶段收口前,至少要做到:

  • 新主链默认只走正式 contract。
  • 旧入口只作为 adapter不再新增依赖。
  • 每个 fallback 都有明确退出条件。

5.5 测试和文档补齐

当前已有回归还不够系统。

还需要补的重点测试:

  • FrameExecutionContext 是否完整透传 source surface / source color view / state。
  • BuiltinForwardPipeline scene phase 顺序是否稳定。
  • feature pass 的 Prepare -> Execute 顺序是否稳定。
  • editor 注入点在有后处理和无后处理两种情况下是否仍能保持正确 source/destination surface。
  • shadow planning 和 main scene shadow sampling 之间的契约测试。

6. 下一步最优先该做什么

下一步最优先建议做:

Shadow 子系统收口

原因很直接:

  • 它横跨 Planning / Execution / Pipeline / Resources / Sampling
  • 它是以后 Deferred / Light Baking / ShadowMask / Additional Light Shadows 的共同基础。
  • 它也是未来 Render Graph 最敏感的一类资源依赖链。
  • 如果阴影先不收,后面 graph 化时 barrier、resource lifetime、pass dependency 都会更乱。

建议顺序:

  1. 先把方向光阴影的 plan/data/resource/execute contract 固化。
  2. 再把 editor 注入 contract 固化。
  3. 然后做第一轮目录收拢。
  4. 最后判断这一阶段是否可以正式收口,进入 Render Graph 设计。

7. 当前 Rendering 目录结构存在的问题

7.1 FrameData 目录太杂

现在 FrameData 里同时混着:

  • camera/environment 数据
  • scene snapshot
  • visible item
  • culling results
  • renderer list utils

这说明它不是一个明确模块,而是“跟帧有关的先放这”。

建议方向:

  • FrameData 保留纯只读帧快照类型。
  • CullingResults / RendererList / Filtering / Sorting / DrawSettings 逐步收进更明确的 ExecutionCulling 子域。
  • RendererListUtils 这种执行辅助逻辑,不适合长期留在 FrameData

7.2 Passes 目录混合了三类东西

现在 Passes 里同时有:

  • 主场景 pass
  • fullscreen/post-process pass
  • editor pass

这会让“runtime 正式渲染能力”和“editor 辅助渲染能力”继续混在一起。

建议方向:

  • Passes/Scene
  • Passes/Fullscreen
  • Passes/Editor
  • Passes/Shadow

不一定要一次性全搬,但后面新增文件不应继续平铺。

7.3 BuiltinForwardPipeline 还没形成真正子模块

当前 forward 相关代码已经分散在:

  • BuiltinForwardPipeline.h/.cpp
  • BuiltinForwardPipelineSkybox.cpp
  • Internal/BuiltinForwardPipelineResources.cpp

这已经具备“应该单独成目录”的条件。

建议方向:

  • Rendering/Pipelines/BuiltinForward/
    • BuiltinForwardPipeline.h
    • BuiltinForwardPipeline.cpp
    • BuiltinForwardPipelineScene.cpp
    • BuiltinForwardPipelineResources.cpp
    • BuiltinForwardPipelineSkybox.cpp

这样后面 forward / deferred / universal renderer 才能并列演进。

7.4 Shadow 目录还不算真正的 shadow 子系统

目前虽然已经有 Rendering/Shadow,但从全链路看,阴影的责任仍然散在其他目录。

建议方向:

  • Shadow 目录最终至少容纳:
    • planning types
    • shared frame data
    • runtime resources / caches
    • shadow executor or shadow renderer

7.5 Internal 边界不稳定

现在 src/Rendering/Internal 更像历史遗留缓冲区。

长期风险:

  • 真正属于 forward 的内部实现放在全局 Internal
  • 真正属于 volume / splat / shadow 的内部实现也可能继续堆进去

建议方向:

  • 全局 Internal 只保留跨模块公共 helper。
  • 各子系统自己的内部文件放回各自目录。

8. 建议的第一轮目录收拢动作

这一轮只做低风险收拢,不做大搬家。

建议动作:

  • BuiltinForwardPipeline* 相关实现收成 Pipelines/BuiltinForward/ 子目录。
  • Passes 至少先分出 EditorFullscreen 两个子目录。
  • Shadow 补一组正式 shared types再决定是否移动更多实现文件。
  • 暂时不大动 include 层 public 路径,先保证编译和引用稳定。

原则:

  • 先按职责拆,再按目录搬。
  • 不为了“好看”做纯目录手术。
  • 每次收拢都必须带编译和回归。

9. 第一阶段收口完成的判定标准

满足下面这些条件,就可以认为 Render Graph 之前这一阶段基本收口:

  • CameraFramePlan 成为主执行入口,旧 request 入口只保留兼容壳。
  • FrameExecutionContext / ScenePhase / DrawSettings 成为主场景执行正式 contract。
  • CullingResults / RendererList 成为主要 draw organization contract。
  • BuiltinForwardPipeline 被收成 coordinator而不是继续长成总垃圾堆。
  • Shadow 成为清晰子系统,而不是散点逻辑。
  • Editor 注入点形成稳定 contract。
  • 目录结构不再继续鼓励“新逻辑直接往老文件里塞”。
  • 相关 tests 和编译链稳定。

到这一步,再开始 C++ Render Graph,代价才是可控的。

10. 当前建议

当前最佳路线不变:

  • 短期:继续按 Unity 的 SRP + URP 方向做 native 基座。
  • 中期:先在 C++ 做出稳定的 Render Graph + RendererList + Feature Injection 内核。
  • 中长期:再暴露 C# 自定义渲染管线接口,做你自己的 URP-like 包层。

眼下最正确的动作不是“赶紧补 deferred”也不是“先把所有特效搬去包层”而是

先把当前 native rendering layer 整理成一个真正可扩展的内核。