Add renderer structure cleanup plan
This commit is contained in:
415
docs/plan/Renderer结构收口与代码正式化计划_2026-04-05.md
Normal file
415
docs/plan/Renderer结构收口与代码正式化计划_2026-04-05.md
Normal file
@@ -0,0 +1,415 @@
|
||||
# Renderer 结构收口与代码正式化计划
|
||||
日期:`2026-04-05`
|
||||
|
||||
## 1. 阶段定位
|
||||
|
||||
当前 Rendering 主线在功能上已经完成了相当多闭环:
|
||||
|
||||
- 三后端统一的 runtime renderer 主链已经建立
|
||||
- directional shadow、multi-light、object-id、editor overlay 等能力都已接入
|
||||
- SceneView / GameView 基本共用了同一条 runtime 渲染路径
|
||||
|
||||
但从代码结构和职责边界上看,这一阶段还没有真正收口。现在的问题已经不再是“某个功能没接上”,而是:
|
||||
|
||||
**renderer 的核心模块里仍然混有明显的阶段性写法、特殊分支、职责堆叠和 editor/runtime 边界不清的问题。**
|
||||
|
||||
如果此时直接继续往上叠 skybox、环境、后处理、更多 renderer feature,后面会越来越难拆,最终重新把已经相对稳定的 renderer 主链拖回到“能跑但很难维护”的状态。
|
||||
|
||||
因此,本阶段的唯一目标不是加新功能,而是:
|
||||
|
||||
**把当前 renderer 这一阶段真正做成可持续演进的正式结构,为后续 Skybox / Environment / PostProcess / 更正式的 SRP 承接清掉结构债。**
|
||||
|
||||
## 2. 为什么现在必须先做结构收口
|
||||
|
||||
这不是“目录看着乱一点”的表面问题,而是已经影响后续演进的实质性架构问题。
|
||||
|
||||
### 2.1 `CameraRenderer` 仍然存在特殊通道
|
||||
|
||||
当前 `CameraRenderer` 虽然已经具备请求规划与多阶段执行能力,但 `object-id` 仍然是单独的一套特殊路径,而不是正式 frame composition 里的统一 pass 节点。
|
||||
|
||||
这带来的问题:
|
||||
|
||||
- 相机级执行顺序不是单一模型,而是“主链 + 特判”
|
||||
- 后续 skybox / post-process / capture / debug target 更难正规接入
|
||||
- 单元测试里被迫维护特殊 mock pass 类型,而不是统一的 pass contract
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/include/XCEngine/Rendering/ObjectIdPass.h`
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderer.h`
|
||||
- `engine/include/XCEngine/Rendering/CameraRenderRequest.h`
|
||||
- `engine/src/Rendering/CameraRenderer.cpp`
|
||||
|
||||
### 2.2 `BuiltinForwardPipeline.cpp` 已经是典型 god file
|
||||
|
||||
这个文件里当前同时混着:
|
||||
|
||||
- pass wrapper
|
||||
- shader pass resolve
|
||||
- graphics pipeline 创建
|
||||
- descriptor set layout 规划
|
||||
- descriptor set 资源写入
|
||||
- lighting 常量打包
|
||||
- material fallback
|
||||
- draw submission
|
||||
|
||||
这已经不是“文件有点长”,而是职责拆分失败。后续任何修改都会把高层语义、资源绑定、RHI 细节、draw 级逻辑一起牵动,测试也只能做大颗粒回归,无法精准保护。
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp`
|
||||
|
||||
### 2.3 `RenderMaterialUtility.h` 混合了契约、兼容层和运行时解析
|
||||
|
||||
当前这个头里至少混了五类职责:
|
||||
|
||||
- builtin pass contract 定义
|
||||
- shader/property/binding 查询
|
||||
- descriptor layout 规划
|
||||
- legacy/material fallback 兼容
|
||||
- runtime material resolve 与绑定辅助
|
||||
|
||||
这会导致:
|
||||
|
||||
- “正式 contract” 与 “过渡兼容逻辑” 难以分开演进
|
||||
- 很多 renderer 代码只能依赖一个超大工具头
|
||||
- 头文件膨胀,职责不可读,接口边界不清
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/include/XCEngine/Rendering/RenderMaterialUtility.h`
|
||||
|
||||
### 2.4 editor / debug pass 仍然混在 runtime renderer 核心层里
|
||||
|
||||
grid、outline 这些能力现在已经走到了比较正式的 runtime host path,但它们在 engine 里的组织方式仍然更接近“把 editor 需求塞进 renderer 核心”。
|
||||
|
||||
风险在于:
|
||||
|
||||
- runtime 核心会继续被 editor 语义污染
|
||||
- 后续 scene/game/editor 三条宿主路径边界会再次变模糊
|
||||
- 玩家运行时和编辑器专用渲染能力的依赖关系难以长期维护
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h`
|
||||
- `engine/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h`
|
||||
|
||||
### 2.5 文件拆分层次仍然不干净
|
||||
|
||||
例如 `BuiltinDepthStylePassBase.cpp` 的底部还直接放着 `BuiltinDepthOnlyPass`、`BuiltinShadowCasterPass` 具体实现,这说明“抽象基类”和“具体 pass”仍然没有彻底分层。
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp`
|
||||
|
||||
### 2.6 少量稳定性问题仍然暴露出“临时写法”
|
||||
|
||||
比如 scene extractor 里 visible item 的稳定排序仍然用 raw pointer 作为 tie-breaker,而 additional light 已经升级成了稳定的 `GameObject::ID` 语义。
|
||||
|
||||
这类问题虽然不大,但非常能说明当前代码里仍混有阶段性临时写法,必须顺手清理干净。
|
||||
|
||||
关键文件:
|
||||
|
||||
- `engine/src/Rendering/RenderSceneExtractor.cpp`
|
||||
|
||||
## 3. 本阶段的核心设计原则
|
||||
|
||||
本阶段继续严格遵循当前工程既定设计理念,并与 `RHI模块总览` 中的核心原则保持一致:
|
||||
|
||||
1. `RHI` 只负责 GPU 抽象,不感知 object-id、grid、outline、skybox、post-process 等高层语义。
|
||||
2. `Renderer` 负责 scene extraction、frame composition、material/shader contract、runtime pass orchestration。
|
||||
3. `Editor` 只作为 renderer 的宿主和扩展使用方,不把 editor 语义反向污染 RHI。
|
||||
4. 兼容层和正式 contract 必须拆开,不能继续把“临时兜底”混在正式主链里。
|
||||
5. 不引入 `render graph`。本阶段先把现有 renderer 结构做正式化,不跳级优化。
|
||||
6. 不做“修修补补式”文件搬家,必须同时修职责边界、执行路径和测试结构。
|
||||
|
||||
## 4. 阶段总目标
|
||||
|
||||
本阶段收口完成后,应达到以下状态:
|
||||
|
||||
1. `CameraRenderer` 形成单一、明确、可测试的 frame composition 模型。
|
||||
2. runtime pass 与 object-id / editor-debug pass 的边界清晰,接入点正式化。
|
||||
3. `BuiltinForwardPipeline` 不再由一个 god file 承担所有责任。
|
||||
4. `RenderMaterialUtility` 被拆分为“正式 contract 层”和“兼容/运行时辅助层”。
|
||||
5. renderer 文件结构与代码结构一致,抽象基类、具体 pass、绑定辅助、compat helper 各归其位。
|
||||
6. 当前所有 rendering / editor 相关测试继续通过,不破坏已闭环功能。
|
||||
|
||||
## 5. 非目标
|
||||
|
||||
本阶段明确不做:
|
||||
|
||||
- `render graph`
|
||||
- deferred / clustered / tiled lighting
|
||||
- 新一轮 editor 视觉特效堆叠
|
||||
- 大规模 shader authoring 体系重写
|
||||
- point / spot shadow
|
||||
- 重新设计 RHI
|
||||
|
||||
## 6. 分阶段执行方案
|
||||
|
||||
## 6.1 Phase A:Camera Frame Composition 正式化
|
||||
|
||||
### 目标
|
||||
|
||||
消灭 `CameraRenderer` 里 object-id 的特殊执行通道,把相机级执行模型统一成正式 frame composition。
|
||||
|
||||
### 要解决的根因
|
||||
|
||||
- 现在相机渲染顺序不是单一 contract
|
||||
- `ObjectIdPass` 是旁路抽象,不利于后续继续扩展 composition
|
||||
- 测试里存在针对 object-id 的特殊 mock pass 体系
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 重新审视 `CameraRenderRequest` 的阶段描述,明确:
|
||||
- pre-scene
|
||||
- shadow/depth
|
||||
- scene pipeline
|
||||
- auxiliary offscreen passes
|
||||
- post-scene
|
||||
- overlay
|
||||
2. 去掉 `ObjectIdPass` 作为并行特例抽象的地位。
|
||||
3. 把 object-id 统一纳入正式 pass 执行序列,必要时通过 pass category / target intent 标识语义,而不是再保留独立虚函数族。
|
||||
4. 简化 `CameraRenderer` 执行逻辑,让失败传播、目标准备、pass 顺序只走一套主链。
|
||||
5. 同步收敛相关单元测试,让测试验证“阶段顺序”和“失败传播”,而不是验证某个特判分支。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `CameraRenderer` 不再对 object-id 走特判主逻辑
|
||||
- `test_camera_scene_renderer` 等单测仍覆盖 object-id 顺序与失败传播
|
||||
- editor viewport object-id picking 不回退
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 6.2 Phase B:BuiltinForwardPipeline 职责拆分
|
||||
|
||||
### 目标
|
||||
|
||||
把 `BuiltinForwardPipeline.cpp` 从 god file 拆成正式的职责层次,但不改变现有 forward runtime 的对外行为。
|
||||
|
||||
### 要解决的根因
|
||||
|
||||
- pipeline resolve、resource layout、descriptor write、material resolve、lighting packing、draw submission 全部耦合
|
||||
- 任何小修改都会波及整个文件
|
||||
- 难以为 skybox / post-process / future pipeline 承接建立稳定接口
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 先按职责切出独立模块,优先拆成以下几层:
|
||||
- shader/pass resolve
|
||||
- pipeline cache/build
|
||||
- resource binding layout / descriptor planning
|
||||
- frame-scoped lighting / pass constants upload
|
||||
- draw item submission
|
||||
2. 让 `BuiltinForwardPipeline` 保留 orchestration 职责,而不是继续承载全部细节。
|
||||
3. 清理与 `RenderMaterialUtility` 的交叉依赖,为下一阶段拆 contract 做准备。
|
||||
4. 保证 unlit / lit / object-id / depth-only / shadow-caster 的绑定逻辑不被混淆。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `BuiltinForwardPipeline.cpp` 明显缩小,核心职责清晰
|
||||
- 新拆出的模块命名与职责稳定,不是单纯“工具类化”
|
||||
- forward 相关单测、集成测试全部不回退
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 6.3 Phase C:RenderMaterialUtility 正式拆层
|
||||
|
||||
### 目标
|
||||
|
||||
把 shader/material/pass 的正式 contract 与 legacy/compat/runtime helper 拆开。
|
||||
|
||||
### 要解决的根因
|
||||
|
||||
- 正式接口和过渡逻辑混在一起
|
||||
- 任何依赖 `RenderMaterialUtility.h` 的代码都会被迫包含大量不相干能力
|
||||
- 后续 shader/material 演进会被兼容逻辑长期绑死
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 明确拆成三层语义:
|
||||
- `contract`:builtin pass 名称、标准 binding 名称、正式解析规则
|
||||
- `runtime resolve`:材质/着色器运行时查询、pass 选择、绑定规划
|
||||
- `compat`:legacy property 名称、历史 fallback、过渡适配
|
||||
2. 避免再把大段实现继续塞在头文件里,能下沉到 `.cpp` 的尽量下沉。
|
||||
3. 对外只暴露最小且稳定的正式接口。
|
||||
4. 给 compat 层加清晰边界,避免以后继续被当作默认主路径使用。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- `RenderMaterialUtility.h` 体量显著下降,职责单一
|
||||
- renderer 主链依赖的是正式 contract / runtime resolve,而不是 compat 大杂烩
|
||||
- 现有 shader/material 行为与测试结果保持一致
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 6.4 Phase D:Runtime Pass 与 Editor/Debug Pass 边界重整
|
||||
|
||||
### 目标
|
||||
|
||||
明确 engine runtime rendering core 与 editor/debug-oriented rendering extension 的边界。
|
||||
|
||||
### 要解决的根因
|
||||
|
||||
- grid、outline 等语义虽然已经可用,但组织上仍偏临时
|
||||
- engine 核心层里混有 editor 专用概念
|
||||
- 后续 camera frame composition 扩展容易再次被 editor 需求污染
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 明确哪些是 runtime 正式能力,哪些是 editor/debug extension。
|
||||
2. 把 editor/debug pass 的注册与宿主接入方式整理成正式 extension seam。
|
||||
3. 保持 SceneView / GameView 继续复用 runtime renderer 主链,但 editor overlay / outline / grid 不侵入 runtime scene composition。
|
||||
4. 补足必要文档,说明 engine、renderer、editor 三者的责任边界。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- editor/debug pass 不再作为 runtime renderer 核心概念扩散
|
||||
- SceneView / GameView 显示、grid、outline、gizmo 宿主路径不回退
|
||||
- 新增代码结构能自然承接后续 icon/light gizmo/camera gizmo 等扩展
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 6.5 Phase E:稳定性清扫、文件收口与文档归档
|
||||
|
||||
### 目标
|
||||
|
||||
清掉这一阶段剩余的临时写法,让实现、测试、文档口径再次一致。
|
||||
|
||||
### 具体工作
|
||||
|
||||
1. 修正 `RenderSceneExtractor` 里仍然使用 raw pointer 的稳定排序 tie-breaker。
|
||||
2. 把 `BuiltinDepthStylePassBase.cpp` 中具体 pass 实现拆出到独立文件。
|
||||
3. 全面复查 renderer 相关文件命名、目录结构、头源分布是否仍有明显反模式。
|
||||
4. 更新 `tests/TEST_SPEC.md` 与相关 renderer / editor guide。
|
||||
5. 阶段完成后,把已过期 plan 归档到 `docs/used`。
|
||||
|
||||
### 验收标准
|
||||
|
||||
- renderer 核心目录结构与职责边界基本一致
|
||||
- 没有明显残留的阶段性临时代码入口
|
||||
- 文档、测试矩阵、实现状态三者一致
|
||||
|
||||
### 计划提交点
|
||||
|
||||
这一阶段完成后立即提交推送一次。
|
||||
|
||||
## 7. 测试策略
|
||||
|
||||
本阶段的测试必须是“每阶段落地即验证”,不能到最后一次性回归。
|
||||
|
||||
### 7.1 Unit
|
||||
|
||||
重点保护:
|
||||
|
||||
- `CameraRenderer` 阶段顺序与失败传播
|
||||
- `RenderSceneExtractor` 的稳定输出
|
||||
- `BuiltinForwardPipeline` 的绑定与材质解析
|
||||
- material/shader contract 拆层后的接口行为
|
||||
|
||||
优先关注:
|
||||
|
||||
- `tests/Rendering/unit/test_camera_scene_renderer.cpp`
|
||||
- `tests/Rendering/unit/test_builtin_forward_pipeline.cpp`
|
||||
- 与 material utility / scene extractor 相关的 unit tests
|
||||
|
||||
### 7.2 Editor / Runtime Integration
|
||||
|
||||
重点回归:
|
||||
|
||||
- object-id picking
|
||||
- SceneView / GameView runtime 渲染链
|
||||
- overlay / outline / grid
|
||||
- backpack / shadow / multi-light / camera stack / offscreen 等现有场景
|
||||
|
||||
至少覆盖:
|
||||
|
||||
- `tests/editor/test_viewport_render_flow_utils.cpp`
|
||||
- `tests/editor/test_scene_viewport_overlay_renderer.cpp`
|
||||
- `tests/editor/test_viewport_object_id_picker.cpp`
|
||||
- 现有 rendering integration matrix 中与 lighting、object-id、camera flow 相关的场景
|
||||
|
||||
### 7.3 编译与宿主验证
|
||||
|
||||
每一阶段至少执行:
|
||||
|
||||
1. 相关 test target 编译
|
||||
2. 相关 unit / integration tests
|
||||
3. 必要时编译 `XCEditor`
|
||||
4. 对 editor 中 SceneView / GameView 做 smoke 验证
|
||||
|
||||
## 8. 风险与控制
|
||||
|
||||
### 风险 1:把“结构重构”做成单纯的文件搬家
|
||||
|
||||
后果:
|
||||
|
||||
- 文件名变了,职责没变
|
||||
- 代码仍然继续跨层互相依赖
|
||||
|
||||
控制策略:
|
||||
|
||||
- 每次拆分都要同时调整接口边界和测试保护点
|
||||
|
||||
### 风险 2:为了图省事继续保留 object-id 特判
|
||||
|
||||
后果:
|
||||
|
||||
- Camera frame composition 永远无法正式化
|
||||
- 后续 skybox / post-process 会继续引入更多特判
|
||||
|
||||
控制策略:
|
||||
|
||||
- 第一阶段必须先砍掉这类特殊旁路
|
||||
|
||||
### 风险 3:compat 逻辑继续侵入正式 contract
|
||||
|
||||
后果:
|
||||
|
||||
- shader/material 体系长期混乱
|
||||
- 之后 Unity 风格 shader authoring 很难落地
|
||||
|
||||
控制策略:
|
||||
|
||||
- compat 层必须显式命名、显式隔离、显式限定使用场景
|
||||
|
||||
### 风险 4:editor/debug pass 重整时破坏现有 editor 体验
|
||||
|
||||
后果:
|
||||
|
||||
- 影响当前 SceneView 主线
|
||||
- 把结构收口又变成功能回退
|
||||
|
||||
控制策略:
|
||||
|
||||
- 每一阶段都要做 editor smoke 和既有测试回归
|
||||
|
||||
## 9. 阶段完成判定
|
||||
|
||||
满足以下条件时,本阶段才算真正收口:
|
||||
|
||||
1. `CameraRenderer` 已统一成正式 frame composition 执行模型。
|
||||
2. `BuiltinForwardPipeline` 与 `RenderMaterialUtility` 已完成职责拆分,核心 god file 问题消除。
|
||||
3. runtime pass 与 editor/debug pass 边界清晰,不再混成一团。
|
||||
4. 现有 rendering / editor tests 继续稳定通过。
|
||||
5. `docs/plan` 与 `docs/used` 的 plan 入口重新清晰,不再保留已过期的执行入口。
|
||||
|
||||
## 10. 与当前主线的关系
|
||||
|
||||
这份计划不是替代“Skybox 环境与 Frame Composition 正式化”方向,而是它的前置收口。
|
||||
|
||||
顺序必须是:
|
||||
|
||||
1. 先做 renderer 结构收口与代码正式化
|
||||
2. 再做 skybox / environment / post-process 的正式接入
|
||||
3. 最后才考虑更高阶的 renderer feature 与未来 SRP 承接
|
||||
|
||||
否则就是在结构债未清的情况下继续加层,后面只会越收越难。
|
||||
Reference in New Issue
Block a user