9.7. 特化常量(Specialization Constants)
特化常量是一种机制,在SPIR-V模块中保有VkPipeline
被创建时指定的常量。 这允许SPIR-V模块保有应用程序运行过程中通过Vulkan API可更改的常量。
注意
计算着色器需要在运行时改变本地工作组大小时,特化常量很有用。 |
VkPipelineShaderStageCreateInfo
的每一个实例包含一个参数pSpecializationInfo
, 可以是 NULL
,表示没有特化常量,或者指向VkSpecializationInfo
。
VkSpecializationInfo
类型数据结构定义如下:
typedef struct VkSpecializationInfo {
uint32_t mapEntryCount;
const VkSpecializationMapEntry* pMapEntries;
size_t dataSize;
const void* pData;
} VkSpecializationInfo;
-
mapEntryCount
是pMapEntries
数组的元素个数。 -
pMapEntries
是一个VkSpecializationMapEntry
数组,每一个元素存储了 constant IDs 和对应的pData
偏移值。 -
dataSize
是pData
buffer 的字节大小。 -
pData
包含了真实的需要特化的constant值。
pMapEntries
指向一个类型为VkSpecializationMapEntry
的数据。
VkSpecializationMapEntry
类型数据结构定义如下:
typedef struct VkSpecializationMapEntry {
uint32_t constantID;
uint32_t offset;
size_t size;
} VkSpecializationMapEntry;
-
constantID
是SPIR-V中特化常量的ID。 -
offset
是提供的数据buffer中特化常量值的起始便宜量(字节单位)。 -
size
是提供的数据buffer中特化常量值的大小(字节单位)。
若 constantID
的值并不是在着色器中使用的特化常量ID,这个map entry将不会影响管线的行为。
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.
9.8. 管线的绑定
一旦管线被创建完成,可以使用一个命令把它绑定到命令缓冲区:
void vkCmdBindPipeline(
VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint,
VkPipeline pipeline);
-
commandBuffer
是管线将要绑定的命令缓冲区。 -
pipelineBindPoint
指定了绑定点,必须是如下的值之一。typedef enum VkPipelineBindPoint { VK_PIPELINE_BIND_POINT_GRAPHICS = 0, VK_PIPELINE_BIND_POINT_COMPUTE = 1, } VkPipelineBindPoint;
specifying whether
pipeline
will be bound as a compute (VK_PIPELINE_BIND_POINT_COMPUTE
) or graphics (VK_PIPELINE_BIND_POINT_GRAPHICS
) pipeline. There are separate bind points for each of graphics and compute, so binding one does not disturb the other. -
pipeline
是将被绑定的管线。
绑定完成后,该绑定影响命令缓冲区中稍后的图形和计算命令,直到新的管线被绑定到绑定点。 绑定到VK_PIPELINE_BIND_POINT_COMPUTE
的管线控制了vkCmdDispatch
and vkCmdDispatchIndirect
的行为。 绑定到VK_PIPELINE_BIND_POINT_GRAPHICS
的管线控制了vkCmdDraw
, vkCmdDrawIndexed
, vkCmdDrawIndirect
, and vkCmdDrawIndexedIndirect
的行为。 其他的命令不受管线状态的影响。