Files
XCEngine/docs/api/XCEngine/RHI/Vulkan/VulkanShader/VulkanShader.md
2026-03-29 01:36:53 +08:00

5.0 KiB
Raw Blame History

VulkanShader

命名空间: XCEngine::RHI

类型: class

头文件: XCEngine/RHI/Vulkan/VulkanShader.h

描述: Vulkan 后端里的 RHIShader 实现,负责把 SPIR-V 数据包装成 VkShaderModule,并解析 shader stage 与入口点。

概览

VulkanShader 的职责很聚焦:它不是完整的“着色器编译系统”,而是 Vulkan shader module 的持有者。

它主要处理三件事:

  • 接收 SPIR-V 字节流或 .spv 文件
  • 从 SPIR-V 元数据或 target hint 推断 ShaderType
  • 创建并持有 VkShaderModule

真正负责把 GLSL 文本编译成 SPIR-V 的,是 VulkanShaderCompiler。也就是说,这个类更接近“已编译 shader 对象”,而不是“编译器前端”。

生命周期

典型顺序是:

  1. VulkanDevice 创建 VulkanShader
  2. 设备先把输入编译或整理为 SPIR-V
  3. Compile()CompileFromFile() 创建 VkShaderModule
  4. pipeline state 在创建阶段引用该 shader
  5. 不再使用时 Shutdown()

析构函数会调用 Shutdown()。如果当前对象仍持有 VkShaderModuleShutdown() 会调用 vkDestroyShaderModule() 释放原生资源。

当前实现的真实行为

输入格式

  • Compile(const void* sourceData, size_t sourceSize, ...) 只接受 SPIR-V word 流
  • CompileFromFile(...) 也只接受二进制 .spv 文件
  • 如果输入不是 4 字节对齐的 SPIR-V payload会直接失败

这点很重要:VulkanShader 本身不编译 GLSL/HLSL 文本。文本输入必须先经过 VulkanShaderCompiler

stage 与入口点解析

InitializeFromSpirvWords() 的行为是:

  • 先验证 SPIR-V magic number 0x07230203
  • 优先从 SPIR-V OpEntryPoint 指令里解析执行模型和入口点
  • 如果解析不到 stage再退回 targetHint
  • 入口点优先级为:entryPointHint > SPIR-V 模块里解析出的入口点 > "main"

这意味着当前实现对标准 SPIR-V 模块相当友好,也允许调用方在必要时通过 hint 补足信息。

Uniform 反射

  • GetUniformInfos() 返回 m_uniformInfos
  • GetUniformInfo(name) 只是在这个数组里线性查找
  • 但当前实现并没有完整的 Vulkan/SPIR-V 反射流程去填充这些 uniform 信息

因此从真实行为看uniform 反射能力目前仍比较初级。不要把这个接口误解成已经等价于成熟商业引擎里的 shader reflection system。

线程语义

从源码看,VulkanShader 没有内部同步。更稳妥的约束是:

  • 创建、销毁和编译由调用方串行控制
  • 不要在多个线程上同时对同一个 VulkanShader 实例重复 Compile()

所有权与资源管理

  • VulkanShader 持有 VkShaderModule
  • SetDevice() 只是写入设备句柄,不转移设备所有权
  • GetNativeHandle() 返回 VkShaderModule

设计取向

把“文本编译”与“native module 持有”拆开,是比较合理的商业引擎设计:

  • 编译步骤通常依赖外部工具链、缓存或导入流程
  • runtime 持有对象则只需要稳定地包装 native handle

这样做的好处是职责清晰,后续更容易插入 shader cache、离线编译或 asset import 管线;代价是使用者必须理解 VulkanShaderVulkanShaderCompiler 不是同一个层次的东西。

如果对照 Unity可以把 VulkanShader 理解成更接近 backend 内部的编译后着色器句柄,而不是 Unity ShaderLab 那种同时承载 authoring、编译和运行时绑定的统一概念。

当前限制

  • 只直接接受 SPIR-V 输入
  • 当前 uniform 反射信息基本没有真正建起来
  • 不包含 GLSL/HLSL 文本编译逻辑
  • 不负责 shader cache、变体管理或 asset 导入

主要公开方法

  • bool CompileFromFile(const wchar_t* filePath, const char* entryPoint, const char* target)
  • bool Compile(const void* sourceData, size_t sourceSize, const char* entryPoint, const char* target)
  • void Shutdown()
  • ShaderType GetType() const
  • bool IsValid() const
  • void* GetNativeHandle()
  • const std::vector<UniformInfo>& GetUniformInfos() const
  • const UniformInfo* GetUniformInfo(const char* name) const
  • void SetDevice(VkDevice device)
  • VkShaderModule GetShaderModule() const
  • const char* GetEntryPoint() const

相关测试与使用线索

  • tests/RHI/unit/test_shader.cpp 覆盖了抽象 shader 创建、类型获取、native handle 与 shutdown 行为
  • tests/RHI/unit/test_vulkan_graphics.cpp 明确验证了从 SPIR-V、GLSL 源码和 GLSL 文件创建 Vulkan shader 的路径
  • tests/RHI/unit/test_compute.cpptests/RHI/unit/test_command_list.cpp 会把 compute shader 接入 pipeline 与 dispatch 流程

相关文档