12 KiB
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 / RendererListcontract 已经进入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 GraphCullingResults / 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)旧入口visibleItemsfallbackBuildRenderRequests旧习惯入口
这些现在不该硬删,因为会影响现有 tests 和 editor 链路。
但第一阶段收口前,至少要做到:
- 新主链默认只走正式 contract。
- 旧入口只作为 adapter,不再新增依赖。
- 每个 fallback 都有明确退出条件。
5.5 测试和文档补齐
当前已有回归还不够系统。
还需要补的重点测试:
FrameExecutionContext是否完整透传 source surface / source color view / state。BuiltinForwardPipelinescene 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 都会更乱。
建议顺序:
- 先把方向光阴影的 plan/data/resource/execute contract 固化。
- 再把 editor 注入 contract 固化。
- 然后做第一轮目录收拢。
- 最后判断这一阶段是否可以正式收口,进入
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/ScenePasses/FullscreenPasses/EditorPasses/Shadow
不一定要一次性全搬,但后面新增文件不应继续平铺。
7.3 BuiltinForwardPipeline 还没形成真正子模块
当前 forward 相关代码已经分散在:
BuiltinForwardPipeline.h/.cppBuiltinForwardPipelineSkybox.cppInternal/BuiltinForwardPipelineResources.cpp
这已经具备“应该单独成目录”的条件。
建议方向:
Rendering/Pipelines/BuiltinForward/BuiltinForwardPipeline.hBuiltinForwardPipeline.cppBuiltinForwardPipelineScene.cppBuiltinForwardPipelineResources.cppBuiltinForwardPipelineSkybox.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 整理成一个真正可扩展的内核。