5.3 KiB
5.3 KiB
Shader 统一预编译缓存并入 Library 正式方案
文档日期:2026-04-11
1. 目标
把 shader 的预编译结果正式并入现有 Library/Artifacts 体系,不再允许出现:
- project shader 走
Library,builtin shader 走裸加载。 - 运行时每次开编辑器都重新现场编译重 shader。
- 只修 D3D12,一到 Vulkan / OpenGL 又退回运行时临时编译。
本轮正式目标:
- builtin shader 与 project shader 统一走同一个 artifact 产物格式。
ShaderStageVariant保持现有 authoring / runtime 变体模型不变,不扩成“每后端一个 variant”。- 每个 variant 可以携带“按后端区分的已编译 payload”。
- D3D12 / Vulkan / OpenGL 运行时优先消费 artifact 中的 payload,只有 miss 时才 fallback 到现场编译。
2. 架构原则
2.1 唯一正式缓存位置
唯一正式 shader 预编译缓存位置是:
project/Library/Artifacts/.../main.xcshader
不新增独立 runtime cache 目录,不做旁路缓存。
2.2 builtin shader 也必须纳入 Library
外部标识仍然保持:
builtin://shaders/...
但在有 project root 时,真实加载路径应优先变成:
Library/Artifacts/.../main.xcshader
这样 builtin shader 与普通 Assets/*.shader 在缓存语义上完全一致。
2.3 不改变现有 variant 选择逻辑
现有很多代码和测试都依赖:
- authoring variant 可以是
Generic - 运行时按 backend 查找时先找精确 backend,再回退
Generic
因此不把一个 Generic variant 展开成三份后端 variant,而是让一个 variant 内部携带:
- 旧字段
compiledBinary - 新字段
backendCompiledBinaries
3. 数据模型
3.1 运行时 compile desc
RHI::ShaderCompileDesc 新增:
compiledBinaryBackendcompiledBinary
含义:
- source / fileName 仍然描述“可回退到现场编译的输入”
- compiledBinary 描述“当前后端可直接消费的已编译 payload”
3.2 shader variant
Resources::ShaderStageVariant 支持:
GetCompiledBinaryForBackend(...)SetCompiledBinaryForBackend(...)
语义:
- backend-specific variant 可以继续把本后端 payload 放在旧
compiledBinary Genericvariant 的 D3D12 / Vulkan / OpenGL payload 放在backendCompiledBinaries
3.3 artifact schema
shader artifact schema 升级为 6,ShaderVariantArtifactHeader 新增:
backendCompiledBinaryCount
并追加 ShaderBackendCompiledBinaryArtifactHeader + payload 序列。
4. 导入阶段
导入 .shader 时:
- 先用
ShaderLoader生成现有 authoring/runtime variant。 - 对每个 pass / variant 生成运行时 compile desc。
- 按目标后端尝试预编译:
- D3D12:生成 DXBC
- Vulkan:生成 Vulkan SPIR-V
- OpenGL:生成 OpenGL-target SPIR-V
- 把结果写回 variant。
- 最终统一写入
main.xcshaderartifact。
后端目标规则:
Genericvariant 预编译到 D3D12 / Vulkan / OpenGL- backend-specific variant 只预编译自己的 backend
失败策略:
- 预编译失败不阻断 import
- artifact 仍然照常生成
- 运行时保持 fallback 能力
- 日志明确记录 pass / stage / backend / reason
5. builtin shader 接入方式
5.1 ResourceManager
ResourceManager::LoadResource(...) 在 shader + builtin path 情况下:
- 解析到真实 builtin shader 资产路径
- 在当前 project 的
Library中确保对应 artifact - 真正加载 artifact
- 资源对外 path 仍然恢复成
builtin://...
5.2 AssetDatabase
由于 builtin shader 资产位于 project root 外部:
- 允许绝对路径解析成相对 project root 的
../engine/... - 这些外部 source record 不写
.meta - 使用 synthetic GUID / synthetic meta hash
Refresh()时显式扫描 builtin shader 资产,避免下次启动把 builtin artifact 当 orphan 清掉
6. 运行时消费
6.1 D3D12
D3D12Shader增加InitializeFromBytecode(...)CompileD3D12Shader(...)优先命中compiledBinaryBackend == D3D12- miss 时才走
D3DCompile(...) CreateShader(...)统一复用这条逻辑
6.2 Vulkan
CompileSpirvShader(...)支持直接消费compiledBinaryBackend == VulkanVulkanDevice / VulkanPipelineState通过既有CompileVulkanShader(...)自动命中缓存 payload
6.3 OpenGL
- 若命中
compiledBinaryBackend == OpenGL - 则把该 payload 作为 SPIR-V 输入
- 继续走现有
SPIR-V -> GLSL转译路径 - 原始 HLSL source 仍保留给 sampler/texture 绑定推导逻辑使用
7. 验收标准
7.1 工程标准
shader_tests全绿XCEditor能完整编译通过
7.2 行为标准
- shader artifact roundtrip 后不丢失 backend payload
- builtin shader 与 project shader 都能命中
Library/Artifacts - D3D12 / Vulkan / OpenGL 运行时在 payload 存在时不再重复现场编译
7.3 性能标准
- 影响 NanoVDB 首帧的 volumetric shader 不再在每次启动时重编
- editor 打开主场景时,shader stall 不再成为
SceneReady的主瓶颈
8. 后续扩展
后续若继续做:
- import-time completeness 标记
- 运行时回写 artifact
- PSO cache blob
- 更完整的 shader compile telemetry
都必须建立在本方案之上,不允许再引入平行缓存体系。