着色器特性

SPIR-V 的每个部分没有都暴露给 Vulkan 1.0 的原因有很多。随着时间的推移,Vulkan 工作组已经确定了一些用例,在这些用例中,公开新的 SPIR-V 特性是有意义的。

以下一些扩展是与 SPIR-V 扩展一起添加的。例如,VK_KHR_8bit_storage 扩展是与 SPV_KHR_8bit_storage 并行创建的。Vulkan 扩展的唯一目的是允许应用程序查询实现中的 SPIR-V 支持。SPIR-V 扩展是为了定义对 SPIR-V 中间表示所做的更改。

有关如何使用 SPIR-V 扩展的详细信息,请阅读 专门的 Vulkan 指南章节

VK_KHR_spirv_1_4

在 Vulkan 1.2 中提升为核心

此扩展旨在为 Vulkan 1.1 实现公开 SPIR-V 1.4 特性集。Vulkan 1.1 仅需要 SPIR-V 1.3,并且发现了一些用例,其中实现可能不会升级到 Vulkan 1.2,但仍希望提供 SPIR-V 1.4 特性。

VK_KHR_8bit_storage 和 VK_KHR_16bit_storage

添加 VK_KHR_8bit_storage(在 Vulkan 1.2 中提升)和 VK_KHR_16bit_storage(在 Vulkan 1.1 中提升)是为了允许使用小值作为 SPIR-V 存储对象的输入或输出。在这些扩展之前,所有 UBO、SSBO 和推送常量都需要至少消耗 4 个字节。有了这个,应用程序现在可以直接从缓冲区中使用 8 位或 16 位值。它通常也与使用 VK_KHR_shader_float16_int8 配对,因为此扩展仅处理存储接口。

以下是使用带有 GLSL 扩展的 SPV_KHR_8bit_storage 的示例

#version 450

// Without 8-bit storage each block variable has to be 32-bit wide
layout (set = 0, binding = 0) readonly buffer StorageBuffer {
    uint data; // 0x0000AABB
} ssbo;

void main() {
    uint a = ssbo.data & 0x0000FF00;
    uint b = ssbo.data & 0x000000FF;
}

使用扩展

#version 450
#extension GL_EXT_shader_8bit_storage : enable

layout (set = 0, binding = 0) readonly buffer StorageBuffer {
    uint8_t dataA; // 0xAA
    uint8_t dataB; // 0xBB
} ssbo;

void main() {
    uint a = uint(ssbo.dataA);
    uint b = uint(ssbo.dataB);
}

VK_KHR_shader_float16_int8

在 Vulkan 1.2 中提升为核心

此扩展允许对算术运算使用 8 位整数类型或 16 位浮点类型。这不允许在任何着色器输入和输出接口中使用 8 位整数类型或 16 位浮点类型,因此通常与使用 VK_KHR_8bit_storageVK_KHR_16bit_storage 配对。

VK_KHR_shader_float_controls

在 Vulkan 1.2 中提升为核心

此扩展允许设置如何处理浮点数的舍入。VkPhysicalDeviceFloatControlsProperties 显示了可以查询的完整特性列表。这在将 OpenCL 内核转换为 Vulkan 时非常有用。

VK_KHR_storage_buffer_storage_class

在 Vulkan 1.1 中提升为核心

最初,SPIR-V 将 UBO 和 SSBO 都组合到“Uniform”存储类中,并且仅通过额外的修饰来区分它们。由于某些硬件将 UBO 和 SSBO 视为两个不同的存储对象,因此 SPIR-V 希望反映这一点。此扩展的目的是扩展 SPIR-V 以具有新的 StorageBuffer 类。

如果您采用以下 GLSL 着色器代码段,则可以看到一个示例

layout(set = 0, binding = 0) buffer ssbo {
    int x;
};

如果您的目标是 Vulkan 1.0(需要 SPIR-V 1.0),则使用 glslang --target-env vulkan1.0,您将得到类似

    Decorate 7(ssbo) BufferBlock
8:  TypePointer Uniform 7(ssbo)
9:  8(ptr) Variable Uniform
12: TypePointer Uniform 6(int)

由于 SPV_KHR_storage_buffer_storage_class 已添加到 SPIR-V 1.3,如果您的目标是 Vulkan 1.1(需要 SPIR-V 1.3),则使用 glslang --target-env vulkan1.1,它将使用新的 StorageBuffer 类。

    Decorate 7(ssbo) Block
8:  TypePointer StorageBuffer 7(ssbo)
9:  8(ptr) Variable StorageBuffer
12: TypePointer StorageBuffer 6(int)

VK_KHR_variable_pointers

在 Vulkan 1.1 中提升为核心

SPIR-V 中将变量指针定义为

逻辑指针类型的指针,它是以下指令之一的结果:OpSelectOpPhiOpFunctionCallOpPtrAccessChainOpLoadOpConstantNull

启用此扩展后,调用私有指针可以是动态的和非均匀的。如果没有此扩展,则必须从指向同一结构的指针中选择可变指针,或者必须是 OpConstantNull

此扩展有两个级别。第一个是 variablePointersStorageBuffer 特性位,它允许实现仅支持在 SSBO 中使用变量指针。variablePointers 特性位允许在 SSBO 之外使用可变指针。

VK_KHR_vulkan_memory_model

在 Vulkan 1.2 中提升为核心

VK_EXT_shader_viewport_index_layer

此扩展为顶点或细分着色器添加了用于导出的 ViewportIndexLayer 内置变量。

在 GLSL 中,它们由 gl_ViewportIndexgl_Layer 内置变量表示。

当使用 Vulkan 1.0 或 1.1 时,使用 ShaderViewportIndexLayerEXT SPIR-V 功能。从 Vulkan 1.2 开始,ShaderViewportIndexLayerEXT 功能被拆分为新的 ShaderViewportIndexShaderLayer 功能。

VK_KHR_shader_draw_parameters

此扩展为顶点着色器添加了 BaseInstanceBaseVertexDrawIndex 内置变量。添加此扩展是因为在 VertexIdInstanceId 中包含和排除 BaseVertexBaseInstance 参数都有合理的用例。

在 GLSL 中,它们由 gl_BaseInstanceARBgl_BaseVertexARBgl_BaseInstanceARB 内置变量表示。

VK_EXT_shader_stencil_export

此扩展允许着色器为每次调用生成模板参考值。当启用模板测试时,允许根据着色器中生成的值执行测试。

在 GLSL 中,它由 out int gl_FragStencilRefARB 内置变量表示。

VK_EXT_shader_demote_to_helper_invocation

此扩展的创建是为了通过添加 demote 关键字来帮助匹配 SPIR-V 中的 HLSL discard 指令。当在片段着色器调用中使用 demote 时,它会变成辅助调用。此指令之后的任何内存存储都会被抑制,并且该片段不会将输出写入帧缓冲区。

VK_KHR_shader_clock

此扩展允许着色器读取由实现提供的单调递增计数器的值。这可以作为一种可能的调试方法,通过跟踪调用执行指令的顺序。值得注意的是,OpReadClockKHR 的添加会改变可能需要调试的着色器。这意味着,如果指令不存在,那么就存在一定程度的精度来表示顺序。

VK_KHR_shader_non_semantic_info

在 Vulkan 1.3 中升级为核心功能

此扩展公开了 SPV_KHR_non_semantic_info,它添加了声明对语义没有影响且可以安全地从模块中删除的扩展指令集的能力。

VK_KHR_shader_terminate_invocation

在 Vulkan 1.3 中升级为核心功能

此扩展添加了新的指令 OpTerminateInvocation,以提供与 OpKill 指令相比明确的功能。

VK_KHR_workgroup_memory_explicit_layout

此扩展提供了一种让着色器定义 Workgroup Storage Class 内存布局的方法。Workgroup 变量可以在块中声明,然后使用与其他存储类相同的显式布局修饰符(例如,OffsetArrayStride)。

一个用例是将大型矢量复制(例如,一次 uvec4)从缓冲区内存复制到共享内存,即使共享内存实际上是不同的类型(例如,标量 fp16)。

另一个用例是,开发人员可以潜在地使用它来重用共享内存,并通过使用以下方法来减少共享内存的总消耗:

pass1 - write shmem using type A
barrier()
pass2 - read shmem using type A
barrier()
pass3 - write shmem using type B
barrier()
pass4 - read shmem using type B

在 Vulkan 之上分层 OpenCL 也需要显式布局支持和某种形式的别名。

VK_KHR_zero_initialize_workgroup_memory

在 Vulkan 1.3 中升级为核心功能

此扩展允许带有 Workgroup Storage ClassOpVariable 使用 Initializer 操作数。

出于安全原因,运行不受信任内容(例如,Web 浏览器)的应用程序需要在工作组执行开始时能够将工作组内存归零。添加指令将所有工作组变量设置为零效率较低,不如某些硬件的能力,因为访问模式不佳。