Files
XCEngine/docs/used/Shader统一预编译缓存并入Library正式方案_完成归档_2026-04-11.md

182 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
都必须建立在本方案之上,不允许再引入平行缓存体系。