着色器特性
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_storage
和 VK_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 中将变量指针
定义为
逻辑指针类型的指针,它是以下指令之一的结果: |
启用此扩展后,调用私有指针可以是动态的和非均匀的。如果没有此扩展,则必须从指向同一结构的指针中选择可变指针,或者必须是 OpConstantNull
。
此扩展有两个级别。第一个是 variablePointersStorageBuffer
特性位,它允许实现仅支持在 SSBO 中使用变量指针。variablePointers
特性位允许在 SSBO 之外使用可变指针。
VK_EXT_shader_viewport_index_layer
在 Vulkan 1.2 中提升为核心 |
此扩展为顶点或细分着色器添加了用于导出的 ViewportIndex
和 Layer
内置变量。
在 GLSL 中,它们由 gl_ViewportIndex
和 gl_Layer
内置变量表示。
当使用 Vulkan 1.0 或 1.1 时,使用 ShaderViewportIndexLayerEXT
SPIR-V 功能。从 Vulkan 1.2 开始,ShaderViewportIndexLayerEXT
功能被拆分为新的 ShaderViewportIndex
和 ShaderLayer
功能。
VK_KHR_shader_draw_parameters
在 Vulkan 1.1 中提升为核心 |
此扩展为顶点着色器添加了 BaseInstance
、BaseVertex
和 DrawIndex
内置变量。添加此扩展是因为在 VertexId
和 InstanceId
中包含和排除 BaseVertex
或 BaseInstance
参数都有合理的用例。
在 GLSL 中,它们由 gl_BaseInstanceARB
、gl_BaseVertexARB
和 gl_BaseInstanceARB
内置变量表示。
VK_EXT_shader_stencil_export
此扩展允许着色器为每次调用生成模板参考值。当启用模板测试时,允许根据着色器中生成的值执行测试。
在 GLSL 中,它由 out int gl_FragStencilRefARB
内置变量表示。
VK_EXT_shader_demote_to_helper_invocation
在 Vulkan 1.3 中升级为核心功能 |
此扩展的创建是为了通过添加 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
变量可以在块中声明,然后使用与其他存储类相同的显式布局修饰符(例如,Offset
,ArrayStride
)。
一个用例是将大型矢量复制(例如,一次 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 也需要显式布局支持和某种形式的别名。