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

5.3 KiB
Raw Blame History

Shader 统一预编译缓存并入 Library 正式方案

文档日期2026-04-11

1. 目标

把 shader 的预编译结果正式并入现有 Library/Artifacts 体系,不再允许出现:

  1. project shader 走 Librarybuiltin 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 升级为 6ShaderVariantArtifactHeader 新增:

  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

都必须建立在本方案之上,不允许再引入平行缓存体系。