15 KiB
Renderer阶段收口:旧兼容路径清理与正式化计划
日期:2026-04-08
1. 背景
当前 Rendering 模块的主执行架构已经基本成型:
RenderSceneExtractorSceneRenderRequestPlannerSceneRenderer / CameraRenderer- built-in forward / shadow / object-id / outline / final-color / skybox
这些主链路已经能稳定支撑:
- runtime 场景渲染
- editor scene/game viewport
- 多光源、阴影、object-id、outline、skybox 等现有能力
因此,当前 Rendering 的主要问题已经不再是“能不能画出来”,而是:
- 还残留一些旧路线兼容代码
- 一些 built-in 运行契约仍然依赖隐式推断
- 少量路径仍然带有明显的过渡期实现痕迹
如果这些问题不在当前阶段彻底收口,后续继续推进:
- Renderer 模块扩展
- Material / Shader editor
- Unity 风格 SRP 底层承接
就会持续建立在一层“虽然能跑,但不是正式规则”的兼容逻辑之上。
这不符合当前阶段的目标。
当前阶段的正确方向不是新增更多渲染功能,而是:
- 清理旧兼容路径
- 去掉运行时语义猜测
- 把 built-in shader / material / pass contract 进一步正式化
2. 当前已确认的问题
基于本轮对 engine/include/XCEngine/Rendering、engine/src/Rendering、engine/src/Resources/Shader、engine/src/Resources/Mesh 的代码审查,当前确认存在以下问题。
2.1 Mesh 导入仍可生成“无 shader / 无 schema”的旧材质路线
当前 MeshLoader 导入子材质时,仍然直接写入:
baseColorbaseColorTextureopacitytwoSided
而不是直接落到正式 shader schema 对应的属性名与纹理槽位。
这导致 runtime 渲染阶段仍然需要兜底兼容这些旧名字。
典型位置:
engine/src/Resources/Mesh/MeshLoader.cppengine/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h
2.2 Rendering 仍通过属性别名表推断 built-in 材质语义
当前 RenderMaterialResolve.h 中,仍然保留了大量 builtin 属性/纹理别名表,例如:
baseColor_BaseColorcolor_ColorbaseColorTexture_BaseColorTexture_MainTextexture
这意味着 runtime 当前并不是“按 shader schema 正式解析”,而是:
- 优先找 semantic
- 找不到就继续按一批旧属性名字猜
这属于典型过渡兼容逻辑,不应成为正式长期实现。
2.3 BuiltinForward / Depth / Shadow 仍存在 per-material fallback constant 路线
当前如果材质没有正式 schema constant layout,管线仍会临时构造:
FallbackPerMaterialConstants
并继续提交 draw。
这说明 runtime 仍允许“非正式材质实例”继续进入正式绘制链路。
这条路径虽然提高了兼容性,但本质上绕开了已经建立的 shader/material 正式模型。
2.4 Built-in pass resource binding 仍依赖隐式硬编码
当前 builtin shader pass 如果未显式声明 resources,运行时仍会通过:
TryBuildImplicitBuiltinPassResourceBindings
自动补一套绑定布局。
这意味着资源绑定契约并不完全存在于 shader 资产中,而是仍有一部分硬编码在 C++ 中。
这会带来两个问题:
- shader 资产与 runtime 存在双份真相
- 后续继续演进 shader/material/editor 时,容易再次产生隐式规则
2.5 HLSL register 重写仍保留 legacy alias
当前 ShaderVariantUtils.h 仍保留:
ResolveLegacyHlslBindingDeclarationAlias
以及基于 gBaseColorTexture / gLinearSampler 一类旧命名的重写逻辑。
这说明 shader runtime 编译阶段仍在兼容旧命名风格。
这属于典型“过渡兼容层”,应在 built-in shader 显式资源契约完成后清掉。
2.6 Built-in pass 选择仍存在隐式默认规则
当前如果 shader 没有显式 builtin metadata,MatchesBuiltinPass(...) 仍会把它默认当成:
ForwardLit
这意味着 shader 即使没有明确声明自己属于哪个 built-in pass,也有可能继续进入主几何管线。
这不利于长期正式化。
2.7 Shader artifact 仍兼容多代旧 schema
当前 shader artifact loader 仍兼容:
XCSHD01XCSHD02XCSHD03XCSHD04- 当前 schema
但 shader artifact 本质上是 Library 中的可重建中间产物,不属于必须长期 runtime 兼容的用户资产格式。
如果继续保留多代 schema 分支,会让 shader 资源链路长期背着历史包袱。
3. 本阶段设计原则
本计划执行时,必须严格遵守以下原则。
3.1 正式路径只能有一条
对 built-in shader / material / pass 来说,正式路径必须是:
导入/authoring -> shader schema -> material instance -> explicit pass contract -> render pipeline
不能继续允许 runtime 依赖旧命名、旧别名、旧格式去自动猜测。
3.2 兼容应尽量前移到导入/重建阶段,而不是留在 runtime
如果确实存在历史资产问题,应优先采用:
- 重新导入
- 重新生成 artifact
- 一次性迁移
而不是继续在 runtime loader / renderer 中保留长期兼容分支。
3.3 Built-in shader 契约必须显式写进 shader 资产
以下内容必须属于 shader/pass 资产本身,而不是 runtime 猜出来:
- pass 类型
- pass metadata
- resource binding
- property semantic
3.4 Rendering 不再为“无正式 shader/schema 的材质”兜底渲染
当前阶段的目标是“收口”,不是“继续最大化兼容”。
因此:
- 非正式材质应尽快在导入层修正
- runtime 应逐步拒绝无 schema 的正式绘制路径
3.5 每一步都必须可验证
每个阶段完成后必须配套:
- unit test
- 必要的 integration test
- editor 编译/回归
不能只凭画面“看起来没问题”判断完成。
4. 本阶段目标
本阶段完成后,Rendering 模块应达到以下状态:
- Mesh 导入出来的材质直接走正式 shader/material 体系
- runtime 不再依赖
baseColor/_MainTex等别名表去维持 built-in 主链 - built-in pass resource binding 由 shader 资产显式声明,不再依赖隐式硬编码补全
- built-in pass 分类必须显式声明,不再存在“默认 ForwardLit”
- shader artifact runtime loader 不再长期兼容多代旧 schema
- 对应测试体系同步升级,保证收口后功能不回退
5. 明确不在本阶段处理的内容
以下内容不属于本阶段目标:
- render graph
- deferred renderer
- 新一轮后处理功能扩展
- C# SRP 脚本侧 API
- ShaderGraph
- 高级材质编辑器功能扩展
这些方向都依赖本阶段先把底层 contract 收紧。
6. 分阶段执行计划
Phase 1:建立基线与目标测试
目标
先把当前遗留兼容路径的行为边界用测试钉住,并同步写出“目标行为”的新测试。
任务
- 审查并整理当前覆盖以下行为的测试:
RenderMaterialResolve- builtin forward pipeline resource binding
- mesh material import
- shader artifact load
- 新增/调整测试,使其明确区分:
- 当前历史兼容行为
- 本阶段目标正式行为
- 对以下目标先写失败测试或待切换测试:
- imported mesh material 必须绑定正式 builtin shader
- imported material property 必须落到正式 schema 名称
- builtin pass 若无显式 metadata,不得进入主 pipeline
- builtin shader 若无显式 resources,不得依赖隐式 binding 补全
验收标准
- 能清楚列出哪些测试在保护旧行为,哪些测试在保护目标行为
- 后续每个阶段都能基于这些测试判断是否真正收口
Phase 2:收口 Mesh 导入材质到正式 shader/material 路径
目标
彻底去掉 imported mesh material 的“无 shader / 裸属性名”旧路线。
任务
- 调整
MeshLoader导入逻辑:- imported material 直接绑定正式 builtin shader
- 默认按现有主线落到 builtin lit/forward 合同
- 导入属性与纹理时,直接写正式 property name / texture slot:
- 例如
_BaseColor _MainTex_Cutoff- 其他已正式声明的 builtin 属性
- 例如
- 不再向 imported material 写入仅靠 runtime 别名识别的裸字段:
baseColorbaseColorTexturecolortexture
- 更新 mesh import 相关测试、render extractor 测试、相关 integration 资源测试
验收标准
- mesh import 结果中的材质都带有正式 shader 引用
- mesh import 结果中的属性/纹理绑定名称与 shader schema 对齐
- 不再需要 runtime 靠旧别名才能让导入材质正常渲染
Phase 3:移除 runtime builtin 材质语义别名与 fallback 常量路径
目标
让 built-in pipeline 只吃正式 schema 材质,不再继续兼容旧材质命名。
任务
- 清理
RenderMaterialResolve.h中的旧别名解析表:- base color property alias
- base texture alias
- skybox texture alias
- alpha cutoff alias
- 保留并强化基于
shader property semantic的正式解析路径 - 移除
FallbackPerMaterialConstants路线 - 当材质未携带正式 schema constant layout 时:
- 显式报错 / 记录诊断
- 拒绝进入需要正式材质常量的绘制路径
- 调整 forward / depth / shadow / skybox 相关单测
验收标准
- builtin pipeline 不再依赖属性别名表维持主链
- builtin pipeline 不再手工构造 per-material fallback constant 继续绘制
- runtime 只接受正式 shader/material 契约
Phase 4:显式化 builtin pass resource binding contract
目标
让 builtin shader pass 的资源绑定契约完全存在于 shader 资产中,而不是藏在 runtime 硬编码里。
任务
- 为所有 builtin shader pass 补齐显式
resources描述 - 覆盖至少以下 shader:
forward-lit.shaderdepth-only.shadershadow-caster.shaderobject-id.shaderskybox.shaderfinal-color.shader- 其他当前仍在主链中的 builtin shader
- 清理
TryBuildImplicitBuiltinPassResourceBindings - 清理
ShaderVariantUtils.h中围绕 implicit/legacy binding 的兼容逻辑:- legacy alias register rewrite
- 依赖
gXxx名称重写的分支
- 调整 shader loader / rendering pipeline / builtin pass 单测
验收标准
- builtin shader pass 缺少显式资源绑定时,构建或运行应明确失败
- runtime 不再替 shader 资产自动补 binding layout
- HLSL runtime 编译不再依赖 legacy alias register 重写
Phase 5:显式化 builtin pass metadata 与 pass 选择规则
目标
去掉“默认 ForwardLit”一类隐式 pass 归类规则。
任务
- 收紧
BuiltinPassMetadataUtils:- built-in pass 匹配必须依赖显式 pass name / tag
- 删除“无 metadata 默认归 ForwardLit”的逻辑
- 审查并统一 builtin shader 的 pass metadata:
NameLightMode- 其它当前正式要求的 tag
- 对进入 builtin 主线的 shader 建立硬约束:
- 没有显式 builtin metadata 的 shader,不得继续被当作主几何 shader 使用
- 更新 pass 匹配测试和 shader authoring 测试
验收标准
- builtin pass 选择全部基于显式 metadata
- 不存在 runtime 默认猜一个 pass 类型的行为
Phase 6:清理旧 shader artifact schema 兼容
目标
让 shader artifact runtime loader 与 material artifact 一样,收口到 current schema。
任务
- 清理
ShaderArtifactLoader.cpp中对旧 schema 的分支兼容:XCSHD01XCSHD02XCSHD03XCSHD04
- 将旧
Libraryartifact 的处理方式改为:- 识别为过期
- 触发重新导入 / 重新生成
- 或直接报错要求重建
Library
- 更新 asset database / shader load 相关测试
- 明确记录此阶段会带来的影响:
- 旧
Library无法直接沿用 - 需要一次性刷新或重建
- 旧
验收标准
- shader artifact loader 只接受 current schema
- 对旧 artifact 的处理边界清晰且可测试
Phase 7:全量验证与阶段收口
目标
确认 Rendering 在去掉旧兼容层之后没有破坏现有功能。
任务
- 编译并运行:
material_testsrendering_unit_testsasset_testseditor_tests- 受影响的 mesh/shader 资源测试
- 重新编译
XCEditor - 重点回归:
- scene viewport
- game viewport
- object-id picking
- selection outline
- skybox
- 阴影
- 多光源
- backpack / sphere / quad 等 integration scene
- 形成阶段收口报告
验收标准
- 所有直接相关测试通过
- editor 编译通过
- 关键 integration scene 渲染行为不回退
- 能明确宣告 runtime 旧兼容路径已移除
7. 风险与注意事项
7.1 这是一次“切正式路径”的收口,不是小修小补
本计划一旦执行,就会主动删除一部分兼容逻辑。
因此不能以“尽量少改代码”为目标,而应以:
- 正式路径唯一
- contract 清晰
- 后续 SRP 可承接
为目标。
7.2 Library 重建属于预期影响
一旦收掉旧 shader artifact schema 兼容,旧 Library 里的 shader artifact 失效是正常现象。
这不应被视为回归,而应被视为阶段性收口的合理代价。
7.3 必须避免引入新的“临时兼容层”
执行过程中需要特别警惕以下错误做法:
- 新加一层 alias 表,试图“先兼容一下”
- 把 runtime fallback 换个名字继续保留
- 在 editor 或 import 层再次引入一套过渡数据模型
如果遇到结构性问题,正确做法是:
- 直接改到正式模型
- 同步补测试
而不是再加一层短期兜底。
8. 建议执行顺序
建议严格按以下顺序推进:
Phase 1测试基线整理Phase 2mesh 导入材质正式化Phase 3runtime 材质别名与 fallback 常量清理Phase 4builtin pass 显式资源绑定Phase 5builtin pass metadata 显式化Phase 6shader artifact schema 收口Phase 7全量验证
原因是:
- 如果不先把 imported material 拉回正式路径
- 后面的 runtime alias / fallback 清理就一定会打断现有资源链路
9. 本阶段完成后的预期状态
本计划完成后,Rendering 模块应达到以下状态:
- built-in shader/material/pass contract 全部走正式显式路径
- runtime 不再依赖旧命名猜测材质语义
- runtime 不再替非正式材质拼接 fallback 常量布局
- builtin shader 资源绑定契约完全由 shader 资产声明
- builtin pass 类型选择完全依赖显式 metadata
- shader artifact runtime loader 不再背负旧 schema 包袱
- 整个 Rendering 模块更适合作为后续 Unity 风格 SRP 的底层承接
10. 一句话总结
当前 Rendering 真正需要的不是继续加功能,而是把残留的旧兼容路径彻底拔干净。
这一阶段的本质,是把:
- imported material
- built-in shader binding
- pass metadata
- shader artifact
全部拉回到同一套正式 contract 上,为后续 Renderer / Material / Shader / SRP 的继续推进打地基。