## 9.7. Specialization Constants

Specialization constants are a mechanism whereby constants in a SPIR-V module can have their constant value specified at the time the `VkPipeline` is created. This allows a SPIR-V module to have constants that can be modified while executing an application that uses the Vulkan API.

| Note
Specialization constants are useful to allow a compute shader to have its local workgroup size changed at runtime by the user, for example.

Each instance of the `VkPipelineShaderStageCreateInfo` structure contains a parameter `pSpecializationInfo`, which can be `NULL` to indicate no specialization constants, or point to a `VkSpecializationInfo` structure.

The `VkSpecializationInfo` structure is defined as:

    typedef struct VkSpecializationInfo {
        uint32_t                           mapEntryCount;
        const VkSpecializationMapEntry*    pMapEntries;
        size_t                             dataSize;
        const void*                        pData;
    } VkSpecializationInfo;

- `mapEntryCount` is the number of entries in the `pMapEntries` array.
- `pMapEntries` is a pointer to an array of `VkSpecializationMapEntry` which maps constant IDs to offsets in `pData`.
- `dataSize` is the byte size of the `pData` buffer.
- `pData` contains the actual constant values to specialize with.

`pMapEntries` points to a structure of type `VkSpecializationMapEntry`.

The `VkSpecializationMapEntry` structure is defined as:

    typedef struct VkSpecializationMapEntry {
        uint32_t    constantID;
        uint32_t    offset;
        size_t      size;
    } VkSpecializationMapEntry;

- `constantID` is the ID of the specialization constant in SPIR-V.
- `offset` is the byte offset of the specialization constant value within the supplied data buffer.
- `size` is the byte size of the specialization constant value within the supplied data buffer.

If a `constantID` value is not a specialization constant ID used in the shader, that map entry does not affect the behavior of the pipeline.

In human readable SPIR-V:

    OpDecorate %x SpecId 13 ; decorate .x component of WorkgroupSize with ID 13
    OpDecorate %y SpecId 42 ; decorate .y component of WorkgroupSize with ID 42
    OpDecorate %z SpecId 3  ; decorate .z component of WorkgroupSize with ID 3
    OpDecorate %wgsize BuiltIn WorkgroupSize ; decorate WorkgroupSize onto constant
    %i32 = OpTypeInt 32 0 ; declare an unsigned 32-bit type
    %uvec3 = OpTypeVector %i32 3 ; declare a 3 element vector type of unsigned 32-bit
    %x = OpSpecConstant %i32 1 ; declare the .x component of WorkgroupSize
    %y = OpSpecConstant %i32 1 ; declare the .y component of WorkgroupSize
    %z = OpSpecConstant %i32 1 ; declare the .z component of WorkgroupSize
    %wgsize = OpSpecConstantComposite %uvec3 %x %y %z ; declare WorkgroupSize

From the above we have three specialization constants, one for each of the x, y & z elements of the WorkgroupSize vector.

Now to specialize the above via the specialization constants mechanism:

    const VkSpecializationMapEntry entries[] =
    {
        {
            13,                             // constantID
            0 * sizeof(uint32_t),           // offset
            sizeof(uint32_t)                // size
        },
        {
            42,                             // constantID
            1 * sizeof(uint32_t),           // offset
            sizeof(uint32_t)                // size
        },
        {
            3,                              // constantID
            2 * sizeof(uint32_t),           // offset
            sizeof(uint32_t)                // size
        }
    };

    const uint32_t data[] = { 16, 8, 4 }; // our workgroup size is 16x8x4

    const VkSpecializationInfo info =
    {
        3,                                  // mapEntryCount
        entries,                            // pMapEntries
        3 * sizeof(uint32_t),               // dataSize
        data,                               // pData
    };

Then when calling `vkCreateComputePipelines`, and passing the `VkSpecializationInfo` we defined as the `pSpecializationInfo` parameter of `VkPipelineShaderStageCreateInfo`, we will create a compute pipeline with the runtime specified local workgroup size.

Another example would be that an application has a SPIR-V module that has some platform-dependent constants they wish to use.

In human readable SPIR-V:

    OpDecorate %1 SpecId 0  ; decorate our signed 32-bit integer constant
    OpDecorate %2 SpecId 12 ; decorate our 32-bit floating-point constant
    %i32 = OpTypeInt 32 1   ; declare a signed 32-bit type
    %float = OpTypeFloat 32 ; declare a 32-bit floating-point type
    %1 = OpSpecConstant %i32 -1 ; some signed 32-bit integer constant
    %2 = OpSpecConstant %float 0.5 ; some 32-bit floating-point constant

From the above we have two specialization constants, one is a signed 32-bit integer and the second is a 32-bit floating-point.

Now to specialize the above via the specialization constants mechanism:

    struct SpecializationData {
        int32_t data0;
        float data1;
    };

    const VkSpecializationMapEntry entries[] =
    {
        {
            0,                                    // constantID
            offsetof(SpecializationData, data0),  // offset
            sizeof(SpecializationData::data0)     // size
        },
        {
            12,                                   // constantID
            offsetof(SpecializationData, data1),  // offset
            sizeof(SpecializationData::data1)     // size
        }
    };

    SpecializationData data;
    data.data0 = -42;    // set the data for the 32-bit integer
    data.data1 = 42.0f;  // set the data for the 32-bit floating-point

    const VkSpecializationInfo info =
    {
        2,                                  // mapEntryCount
        entries,                            // pMapEntries
        sizeof(data),                       // dataSize
        &data,                              // pData
    };

It is legal for a SPIR-V module with specializations to be compiled into a pipeline where no specialization info was provided. SPIR-V specialization constants contain default values such that if a specialization is not provided, the default value will be used. In the examples above, it would be valid for an application to only specialize some of the specialization constants within the SPIR-V module, and let the other constants use their default values encoded within the OpSpecConstant declarations.