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