# 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 Graph` 或 `Deferred 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 renderer` 和 `renderer 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` 逐步收进更明确的 `Execution` 或 `Culling` 子域。 - `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` 至少先分出 `Editor` 和 `Fullscreen` 两个子目录。 - 给 `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 整理成一个真正可扩展的内核。`