Shader Features

There are various reasons why every part of SPIR-V was not exposed to Vulkan 1.0. Over time the Vulkan Working Group has identified use cases where it makes sense to expose a new SPIR-V feature.

Some of the following extensions were added alongside a SPIR-V extension. For example, the VK_KHR_8bit_storage extension was created in parallel with SPV_KHR_8bit_storage. The Vulkan extension only purpose is to allow an application to query for SPIR-V support in the implementation. The SPIR-V extension is there to define the changes made to the SPIR-V intermediate representation.

For details how to use SPIR-V extension please read the dedicated Vulkan Guide chapter.

VK_KHR_spirv_1_4

Promoted to core in Vulkan 1.2

This extension is designed for a Vulkan 1.1 implementations to expose the SPIR-V 1.4 feature set. Vulkan 1.1 only requires SPIR-V 1.3 and some use cases were found where an implementation might not upgrade to Vulkan 1.2, but still want to offer SPIR-V 1.4 features.

VK_KHR_8bit_storage and VK_KHR_16bit_storage

Both VK_KHR_8bit_storage (promoted in Vulkan 1.2) and VK_KHR_16bit_storage (promoted in Vulkan 1.1) were added to allow the ability to use small values as input or output to a SPIR-V storage object. Prior to these extensions, all UBO, SSBO, and push constants needed to consume at least 4 bytes. With this, an application can now use 8-bit or 16-bit values directly from a buffer. It is also commonly paired with the use of VK_KHR_shader_float16_int8 as this extension only deals with the storage interfaces.

The following is an example of using SPV_KHR_8bit_storage with the GLSL extension:

#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;
}

With the extension

#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

Promoted to core in Vulkan 1.2

This extension allows the use of 8-bit integer types or 16-bit floating-point types for arithmetic operations. This does not allow for 8-bit integer types or 16-bit floating-point types in any shader input and output interfaces and therefore is commonly paired with the use of VK_KHR_8bit_storage and VK_KHR_16bit_storage.

VK_KHR_shader_float_controls

Promoted to core in Vulkan 1.2

This extension allows the ability to set how rounding of floats are handled. The VkPhysicalDeviceFloatControlsProperties shows the full list of features that can be queried. This is useful when converting OpenCL kernels to Vulkan.

VK_KHR_storage_buffer_storage_class

Promoted to core in Vulkan 1.1

Originally SPIR-V combined both UBO and SSBO into the 'Uniform' storage classes and differentiated them only through extra decorations. Because some hardware treats UBO and SSBO as two different storage objects, the SPIR-V wanted to reflect that. This extension serves the purpose of extending SPIR-V to have a new StorageBuffer class.

An example of this can be seen if you take the following GLSL shader snippet:

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

If you target Vulkan 1.0 (which requires SPIR-V 1.0), using glslang --target-env vulkan1.0, you will get something like:

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

Since SPV_KHR_storage_buffer_storage_class was added to SPIR-V 1.3, if you target Vulkan 1.1 (which requires SPIR-V 1.3) ,using glslang --target-env vulkan1.1, it will make use of the new StorageBuffer class.

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

VK_KHR_variable_pointers

Promoted to core in Vulkan 1.1

A Variable pointer is defined in SPIR-V as

A pointer of logical pointer type that results from one of the following instructions: OpSelect, OpPhi, OpFunctionCall, OpPtrAccessChain, OpLoad, or OpConstantNull

When this extension is enabled, invocation-private pointers can be dynamic and non-uniform. Without this extension a variable pointer must be selected from pointers pointing into the same structure or be OpConstantNull.

This extension has two levels to it. The first is the variablePointersStorageBuffer feature bit which allows implementations to support the use of variable pointers into a SSBO only. The variablePointers feature bit allows the use of variable pointers outside the SSBO as well.

VK_KHR_vulkan_memory_model

Promoted to core in Vulkan 1.2

The Vulkan Memory Model formally defines how to synchronize memory accesses to the same memory locations performed by multiple shader invocations and this extension exposes a boolean to let implementations to indicate support for it. This is important because with many things targeting Vulkan/SPIR-V it is important that any memory transfer operations an application might attempt to optimize doesn’t break across implementations.

VK_EXT_shader_viewport_index_layer

This extension adds the ViewportIndex, Layer built-in for exporting from vertex or tessellation shaders.

In GLSL these are represented by gl_ViewportIndex and gl_Layer built-ins.

When using Vulkan 1.0 or 1.1 the ShaderViewportIndexLayerEXT SPIR-V capability is used. Starting in Vulkan 1.2 the ShaderViewportIndexLayerEXT capability is split into the new ShaderViewportIndex and ShaderLayer capability.

VK_KHR_shader_draw_parameters

This extension adds the BaseInstance, BaseVertex, and DrawIndex built-in for vertex shaders. This was added as there are legitimate use cases for both inclusion and exclusion of the BaseVertex or BaseInstance parameters in VertexId and InstanceId, respectively.

In GLSL these are represented by gl_BaseInstanceARB, gl_BaseVertexARB and gl_BaseInstanceARB built-ins.

VK_EXT_shader_stencil_export

This extension allows a shader to generate the stencil reference value per invocation. When stencil testing is enabled, this allows the test to be performed against the value generated in the shader.

In GLSL this is represented by a out int gl_FragStencilRefARB built-in.

VK_EXT_shader_demote_to_helper_invocation

This extension was created to help with matching the HLSL discard instruction in SPIR-V by adding a demote keyword. When using demote in a fragment shader invocation it becomes a helper invocation. Any stores to memory after this instruction are suppressed and the fragment does not write outputs to the framebuffer.

VK_KHR_shader_clock

This extension allows the shader to read the value of a monotonically incrementing counter provided by the implementation. This can be used as one possible method for debugging by tracking the order of when an invocation executes the instruction. It is worth noting that the addition of the OpReadClockKHR alters the shader one might want to debug. This means there is a certain level of accuracy representing the order as if the instructions did not exists.

VK_KHR_shader_non_semantic_info

Promoted to core in Vulkan 1.3

This extension exposes SPV_KHR_non_semantic_info which adds the ability to declare extended instruction sets that have no semantic impact and can be safely removed from a module.

VK_KHR_shader_terminate_invocation

Promoted to core in Vulkan 1.3

This extension adds the new instruction OpTerminateInvocation to provide a disambiguated functionality compared to the OpKill instruction.

VK_KHR_workgroup_memory_explicit_layout

This extension provides a way for the shader to define the layout of Workgroup Storage Class memory. Workgroup variables can be declared in blocks, and then use the same explicit layout decorations (e.g. Offset, ArrayStride) as other storage classes.

One use case is to do large vector copies (e.g. uvec4 at at a time) from buffer memory into shared memory, even if the shared memory is really a different type (e.g. scalar fp16).

Another use case is a developers could potentially use this to reuse shared memory and reduce the total shared memory consumption using something such as the following:

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

The explicit layout support and some form of aliasing is also required for layering OpenCL on top of Vulkan.

VK_KHR_zero_initialize_workgroup_memory

Promoted to core in Vulkan 1.3

This extension allows OpVariable with a Workgroup Storage Class to use the Initializer operand.

For security reasons, applications running untrusted content (e.g. web browsers) need to be able to zero-initialize workgroup memory at the start of workgroup execution. Adding instructions to set all workgroup variables to zero would be less efficient than what some hardware is capable of, due to poor access patterns.