着色器
着色器指定在图形和计算管线的相应阶段中为每个顶点、控制点、细分顶点、图元、片段或工作组执行的可编程操作。
图形管线包括顶点着色器执行,这是图元装配的结果,如果启用,则后跟在patch上操作的细分控制和评估着色器、如果启用,则后跟在图元上操作的几何着色器,以及如果存在,则后跟在由光栅化生成的片段上操作的片段着色器。在本规范中,顶点、细分控制、细分评估和几何着色器统称为光栅化前着色器阶段,并在光栅化之前的逻辑管线中发生。片段着色器在逻辑上发生在光栅化之后。
计算管线中仅包含计算着色器阶段。计算着色器在工作组中对计算调用进行操作。
着色器可以从输入变量读取,以及读取和写入输出变量。输入和输出变量可以用于在着色器阶段之间传输数据,或者允许着色器与执行环境中存在的值进行交互。同样,执行环境提供了描述功能的常量。
着色器变量使用着色器中的内置装饰与执行环境提供的输入和输出相关联。以下小节中记录了每个阶段可用的装饰。
着色器对象
着色器可以如管线章节所述,编译并链接到管线对象中,或者如果启用了shaderObject
特性,则可以编译为独立的按阶段着色器对象,这些对象可以在命令缓冲区上彼此独立绑定。与管线不同,着色器对象本质上不与任何特定的状态集绑定。相反,状态在命令缓冲区中动态指定。
每个着色器对象都表示一个已编译的着色器阶段,该阶段可以选择性地与其他一个或多个阶段链接。
着色器对象由 VkShaderEXT
句柄表示
// Provided by VK_EXT_shader_object
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderEXT)
着色器对象创建
着色器对象可以从作为 SPIR-V 提供的着色器代码创建,或者从物理设备特有的不透明、实现定义的二进制格式创建。
要创建一个或多个着色器对象,请调用
// Provided by VK_EXT_shader_object
VkResult vkCreateShadersEXT(
VkDevice device,
uint32_t createInfoCount,
const VkShaderCreateInfoEXT* pCreateInfos,
const VkAllocationCallbacks* pAllocator,
VkShaderEXT* pShaders);
-
device
是创建着色器对象的逻辑设备。 -
createInfoCount
是pCreateInfos
和pShaders
数组的长度。 -
pCreateInfos
是指向 VkShaderCreateInfoEXT 结构的数组的指针。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。 -
pShaders
是指向 VkShaderEXT 句柄数组的指针,将在其中返回生成的着色器对象。
当此函数返回时,无论是否成功,都保证 pShaders
的每个元素都已被 VK_NULL_HANDLE 或有效的 VkShaderEXT
句柄覆盖。
这意味着当着色器创建失败时,应用程序可以通过查找 pShaders
中第一个 VK_NULL_HANDLE 元素来确定返回的错误与哪个着色器相关。这也意味着应用程序可以通过迭代 pShaders
数组并销毁每个不是 VK_NULL_HANDLE 的元素,从而可靠地从失败的调用中清理。
VkShaderCreateInfoEXT
结构定义如下
// Provided by VK_EXT_shader_object
typedef struct VkShaderCreateInfoEXT {
VkStructureType sType;
const void* pNext;
VkShaderCreateFlagsEXT flags;
VkShaderStageFlagBits stage;
VkShaderStageFlags nextStage;
VkShaderCodeTypeEXT codeType;
size_t codeSize;
const void* pCode;
const char* pName;
uint32_t setLayoutCount;
const VkDescriptorSetLayout* pSetLayouts;
uint32_t pushConstantRangeCount;
const VkPushConstantRange* pPushConstantRanges;
const VkSpecializationInfo* pSpecializationInfo;
} VkShaderCreateInfoEXT;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
flags
是一个 VkShaderCreateFlagBitsEXT 的位掩码,描述着 shader 的附加参数。 -
stage
是一个 VkShaderStageFlagBits 值,指定单个 shader 阶段。 -
nextStage
是一个 VkShaderStageFlagBits 的位掩码,指定当 shader 绑定时,可以作为逻辑上下一个绑定阶段的零个或多个阶段。 -
codeType
是一个 VkShaderCodeTypeEXT 值,指定pCode
指向的 shader 代码类型。 -
codeSize
是pCode
指向的 shader 代码的大小(以字节为单位)。 -
pCode
是一个指向用于创建 shader 的 shader 代码的指针。 -
pName
是一个指向以 null 结尾的 UTF-8 字符串的指针,该字符串指定此阶段的 shader 的入口点名称。 -
setLayoutCount
是pSetLayouts
指向的描述符集布局的数量。 -
pSetLayouts
是一个指向 shader 阶段使用的 VkDescriptorSetLayout 对象数组的指针。 -
pushConstantRangeCount
是pPushConstantRanges
指向的推送常量范围的数量。 -
pPushConstantRanges
是一个指向 shader 阶段使用的 VkPushConstantRange 结构数组的指针。 -
pSpecializationInfo
是一个指向 VkSpecializationInfo 结构的指针,如 特殊化常量 中所述,或者为NULL
。
// Provided by VK_EXT_shader_object
typedef VkFlags VkShaderCreateFlagsEXT;
VkShaderCreateFlagsEXT
是一个位掩码类型,用于设置零个或多个 VkShaderCreateFlagBitsEXT 的掩码。
VkShaderCreateInfoEXT 的 flags
成员的可能值,指定如何创建着色器对象,如下:
// Provided by VK_EXT_shader_object
typedef enum VkShaderCreateFlagBitsEXT {
VK_SHADER_CREATE_LINK_STAGE_BIT_EXT = 0x00000001,
// Provided by VK_EXT_shader_object with VK_EXT_subgroup_size_control or VK_VERSION_1_3
VK_SHADER_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT = 0x00000002,
// Provided by VK_EXT_shader_object with VK_EXT_subgroup_size_control or VK_VERSION_1_3
VK_SHADER_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT = 0x00000004,
// Provided by VK_EXT_shader_object with VK_EXT_mesh_shader or VK_NV_mesh_shader
VK_SHADER_CREATE_NO_TASK_SHADER_BIT_EXT = 0x00000008,
// Provided by VK_EXT_shader_object with VK_KHR_device_group or VK_VERSION_1_1
VK_SHADER_CREATE_DISPATCH_BASE_BIT_EXT = 0x00000010,
// Provided by VK_KHR_fragment_shading_rate with VK_EXT_shader_object
VK_SHADER_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_EXT = 0x00000020,
// Provided by VK_EXT_fragment_density_map with VK_EXT_shader_object
VK_SHADER_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT = 0x00000040,
// Provided by VK_EXT_device_generated_commands
VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT = 0x00000080,
} VkShaderCreateFlagBitsEXT;
-
VK_SHADER_CREATE_LINK_STAGE_BIT_EXT
指定着色器链接到同一 vkCreateShadersEXT 调用中创建的所有其他着色器,这些着色器的 VkShaderCreateInfoEXT 结构的flags
包括VK_SHADER_CREATE_LINK_STAGE_BIT_EXT
。 -
VK_SHADER_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT
指定SubgroupSize
可以在任务、网格或计算着色器中变化。 -
VK_SHADER_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT
指定子组大小必须在任务、网格或计算着色器中所有调用都处于活动状态时启动。 -
VK_SHADER_CREATE_NO_TASK_SHADER_BIT_EXT
指定网格着色器必须仅在没有任务着色器的情况下使用。否则,网格着色器必须仅在有任务着色器的情况下使用。 -
VK_SHADER_CREATE_DISPATCH_BASE_BIT_EXT
指定计算着色器可以与具有非零基础工作组的 vkCmdDispatchBase 一起使用。 -
VK_SHADER_CREATE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_EXT
指定片段着色器可以与片段着色率附件一起使用。 -
VK_SHADER_CREATE_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT
指定片段着色器可以与片段密度图附件一起使用。 -
VK_SHADER_CREATE_INDIRECT_BINDABLE_BIT_EXT
指定着色器可以与 设备生成的命令 结合使用。
|
可以使用不同类型的着色器代码创建着色器对象。 VkShaderCreateInfoEXT::codeType
的可能值如下:
// Provided by VK_EXT_shader_object
typedef enum VkShaderCodeTypeEXT {
VK_SHADER_CODE_TYPE_BINARY_EXT = 0,
VK_SHADER_CODE_TYPE_SPIRV_EXT = 1,
} VkShaderCodeTypeEXT;
-
VK_SHADER_CODE_TYPE_BINARY_EXT
指定特定于物理设备的不透明、实现定义的二进制格式的着色器代码。 -
VK_SHADER_CODE_TYPE_SPIRV_EXT
指定 SPIR-V 格式的着色器代码。
二进制着色器代码
可以使用以下命令从着色器对象检索二进制着色器代码:
// Provided by VK_EXT_shader_object
VkResult vkGetShaderBinaryDataEXT(
VkDevice device,
VkShaderEXT shader,
size_t* pDataSize,
void* pData);
-
device
是创建着色器对象的逻辑设备。 -
shader
是要从中检索二进制着色器代码的着色器对象。 -
pDataSize
是一个指向与二进制着色器代码大小相关的size_t
值的指针,如下所述。 -
pData
要么是NULL
,要么是指向缓冲区的指针。
如果 pData
为 NULL
,则着色器对象的二进制着色器代码的大小(以字节为单位)将返回到 pDataSize
中。否则,pDataSize
必须指向一个由应用程序设置为 pData
指向的缓冲区大小(以字节为单位)的变量,并且在返回时,该变量将被实际写入到 pData
的数据量覆盖。如果 pDataSize
小于二进制着色器代码的大小,则不会向 pData
写入任何内容,并且将返回 VK_INCOMPLETE
而不是 VK_SUCCESS
。
当 |
使用 vkGetShaderBinaryDataEXT
检索的二进制着色器代码可以通过在 VkShaderCreateInfoEXT
的 codeType
成员中指定 VK_SHADER_CODE_TYPE_BINARY_EXT
,传递给兼容的物理设备上后续对 vkCreateShadersEXT 的调用。
对于同一个 VkShaderEXT
对象,重复调用此函数返回的着色器代码保证在其生命周期内是不变的。
二进制着色器兼容性
二进制着色器兼容性是指从调用 vkGetShaderBinaryDataEXT 返回的二进制着色器代码可以传递给后续对 vkCreateShadersEXT 的调用,可能在不同的逻辑和/或物理设备上,并且这将导致成功创建功能上等同于最初查询该代码的着色器对象的着色器对象。
从 vkGetShaderBinaryDataEXT 查询的二进制着色器代码不能保证在所有设备上都兼容,但实现必须提供一些兼容性保证。应用程序可以使用以下两种机制之一(或两者兼有)来确定二进制着色器兼容性。
着色器二进制文件的保证兼容性通过从物理设备查询的 VkPhysicalDeviceShaderObjectPropertiesEXT 结构的 shaderBinaryUUID
和 shaderBinaryVersion
成员的组合来表达。从具有特定 shaderBinaryUUID
的物理设备检索的二进制着色器保证与所有报告相同 shaderBinaryUUID
和相同或更高 shaderBinaryVersion
的其他物理设备兼容。
每当实现的新的版本包含任何影响 vkGetShaderBinaryDataEXT 输出的更改时,如果从旧版本检索的二进制着色器代码与新实现保持兼容,则实现应该递增 shaderBinaryVersion
;否则,如果向后兼容性被破坏,则将 shaderBinaryUUID
替换为新值。相对于调用 vkCreateShadersEXT 的设备,从具有匹配的 shaderBinaryUUID
和较低 shaderBinaryVersion
的设备查询的二进制着色器代码可能对于新设备而言不是最优的,这不会改变着色器功能,但它仍然保证可用于成功创建着色器对象。
鼓励实现尽可能在其硬件自然允许的最大程度上在设备和驱动程序版本之间共享 |
除了上述描述的着色器兼容性保证之外,应用程序使用在具有不同或未知 shaderBinaryUUID
和/或更高 shaderBinaryVersion
的设备上创建的二进制着色器代码调用 vkCreateShadersEXT 是有效的。在这种情况下,实现可以使用其选择的任何未指定的方法来确定提供的二进制着色器代码是否可用。如果是,则 vkCreateShadersEXT 必须返回 VK_SUCCESS
,并且保证创建的着色器对象有效。否则,在没有错误的情况下,vkCreateShadersEXT 必须返回 VK_INCOMPATIBLE_SHADER_BINARY_EXT
,以表明提供的二进制着色器代码与该设备不兼容。
绑定着色器对象
一旦创建了着色器对象,就可以使用以下命令将其绑定到命令缓冲区:
// Provided by VK_EXT_shader_object
void vkCmdBindShadersEXT(
VkCommandBuffer commandBuffer,
uint32_t stageCount,
const VkShaderStageFlagBits* pStages,
const VkShaderEXT* pShaders);
-
commandBuffer
是着色器对象将绑定到的命令缓冲区。 -
stageCount
是pStages
和pShaders
数组的长度。 -
pStages
是指向 VkShaderStageFlagBits 值数组的指针,该数组指定每个数组索引一个受pShaders
数组中相应值影响的阶段。 -
pShaders
是指向VkShaderEXT
句柄和/或 VK_NULL_HANDLE 值数组的指针,该数组描述要在pStages
中每个阶段执行的着色器绑定操作。
当绑定链接的着色器时,应用程序可以通过一次或多次调用 vkCmdBindShadersEXT
以任意组合绑定它们(即,链接在一起创建的着色器不需要在同一个 vkCmdBindShadersEXT
调用中绑定)。
可以通过将其在 pShaders
中的值设置为 VK_NULL_HANDLE 来解除绑定到特定阶段的任何着色器对象。如果 pShaders
为 NULL
,则 vkCmdBindShadersEXT
的行为就像 pShaders
是一个包含 stageCount
个 VK_NULL_HANDLE 值的数组(即,绑定到 pStages
中指定阶段的任何着色器都将被解除绑定)。
设置状态
每当使用着色器对象发出绘制命令时,在绘制之前,必须调用适当的 动态状态 设置命令,以在命令缓冲区中设置相关状态。
如果着色器绑定到 VK_SHADER_STAGE_VERTEX_BIT
阶段,则在绘制之前,必须在命令缓冲区中调用以下命令:
如果着色器绑定到 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT
阶段,则在绘制之前,必须在命令缓冲区中调用以下命令:
-
vkCmdSetPatchControlPointsEXT,如果
primitiveTopology
是VK_PRIMITIVE_TOPOLOGY_PATCH_LIST
。
如果着色器绑定到 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT
阶段,则在绘制之前,必须在命令缓冲区中调用以下命令:
如果 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令:
-
vkCmdSetAlphaToOneEnableEXT,如果启用了 alphaToOne 功能。
-
vkCmdSetLineWidth,如果
polygonMode
为VK_POLYGON_MODE_LINE
,或者如果着色器绑定到VK_SHADER_STAGE_VERTEX_BIT
阶段且primitiveTopology
为线拓扑,或者如果输出线图元的着色器绑定到VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT
或VK_SHADER_STAGE_GEOMETRY_BIT
阶段。 -
vkCmdSetDepthCompareOp,如果
depthTestEnable
为VK_TRUE
。 -
vkCmdSetDepthBoundsTestEnable,如果启用了 depthBounds 功能。
-
vkCmdSetDepthBounds,如果
depthBoundsTestEnable
为VK_TRUE
。 -
vkCmdSetDepthBias 或 vkCmdSetDepthBias2EXT,如果
depthBiasEnable
为VK_TRUE
。 -
vkCmdSetDepthClampEnableEXT,如果启用了 depthClamp 功能。
-
vkCmdSetStencilOp,如果
stencilTestEnable
为VK_TRUE
。 -
vkCmdSetStencilCompareMask,如果
stencilTestEnable
为VK_TRUE
。 -
vkCmdSetStencilWriteMask,如果
stencilTestEnable
为VK_TRUE
。 -
vkCmdSetStencilReference,如果
stencilTestEnable
为VK_TRUE
。
如果着色器绑定到 VK_SHADER_STAGE_FRAGMENT_BIT
阶段,并且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令:
-
vkCmdSetLogicOpEnableEXT,如果启用了
logicOp
功能。 -
如果
logicOpEnable
为VK_TRUE
,则必须调用 vkCmdSetLogicOpEXT -
如果绑定了颜色附件,则必须调用 vkCmdSetColorBlendEnableEXT 和 vkCmdSetColorWriteMaskEXT,并且在绘制时,为活动渲染通道实例中的每个颜色附件设置值。
-
如果绑定了颜色附件,且
pColorBlendEnables
中索引指向VK_TRUE
值的每个附件,则必须调用 vkCmdSetColorBlendEquationEXT 或 vkCmdSetColorBlendAdvancedEXT。 -
如果
pColorBlendEnables
中的任何索引为VK_TRUE
,并且pColorBlendEquations
中相同索引的项为包含任意成员值为VK_BLEND_FACTOR_CONSTANT_COLOR
、VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR
、VK_BLEND_FACTOR_CONSTANT_ALPHA
或VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA
的 VkBlendFactor 的VkColorBlendEquationEXT
结构,则必须调用 vkCmdSetBlendConstants。
如果启用了 pipelineFragmentShadingRate
功能,并且着色器绑定到 VK_SHADER_STAGE_FRAGMENT_BIT
阶段,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了 geometryStreams
功能,并且着色器绑定到 VK_SHADER_STAGE_GEOMETRY_BIT
阶段,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了 VK_EXT_discard_rectangles
扩展,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
-
如果
discardRectangleEnable
为VK_TRUE
,则必须调用 vkCmdSetDiscardRectangleModeEXT。 -
如果
discardRectangleEnable
为VK_TRUE
,则必须调用 vkCmdSetDiscardRectangleEXT。
如果启用了 VK_EXT_conservative_rasterization
扩展,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
-
如果
conservativeRasterizationMode
为VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
,则必须调用 vkCmdSetExtraPrimitiveOverestimationSizeEXT。
如果启用了 depthClipEnable
功能,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了 VK_EXT_sample_locations
扩展,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
-
如果
sampleLocationsEnable
为VK_TRUE
,则必须调用 vkCmdSetSampleLocationsEXT。
如果启用了 VK_EXT_provoking_vertex
扩展,且 rasterizerDiscardEnable
为 VK_FALSE
,并且着色器绑定到 VK_SHADER_STAGE_VERTEX_BIT
阶段,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了任何 <features-stippledRectangularLines, stippledRectangularLines
>>、<features-stippledBresenhamLines, stippledBresenhamLines
>> 或 <features-stippledSmoothLines, stippledSmoothLines
>> 功能,且 rasterizerDiscardEnable
为 VK_FALSE
,并且如果 polygonMode
为 VK_POLYGON_MODE_LINE
,或者着色器绑定到 VK_SHADER_STAGE_VERTEX_BIT
阶段并且 primitiveTopology
是线拓扑,或者输出线图元的着色器绑定到 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT
或 VK_SHADER_STAGE_GEOMETRY_BIT
阶段,则在绘制之前,必须在命令缓冲区中调用以下命令
-
如果
stippledLineEnable
为VK_TRUE
,则必须调用 vkCmdSetLineStipple。
如果启用了 depthClipControl
功能,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了 colorWriteEnable
功能,并且着色器绑定到 VK_SHADER_STAGE_FRAGMENT_BIT
阶段,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
-
vkCmdSetColorWriteEnableEXT,为渲染通道实例中活动状态的每个颜色附件设置值。
如果启用了 attachmentFeedbackLoopDynamicState 功能,并且着色器绑定到 VK_SHADER_STAGE_FRAGMENT_BIT
阶段,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了 VK_NV_clip_space_w_scaling
扩展,则在绘制之前,必须在命令缓冲区中调用以下命令
-
如果
viewportWScalingEnable
为VK_TRUE
,则必须调用 vkCmdSetViewportWScalingNV。
如果启用了 depthClamp 和 depthClampControl
功能,且 depthClampEnable
为 VK_TRUE
,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了 VK_NV_viewport_swizzle
扩展,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了 VK_NV_fragment_coverage_to_color
扩展,并且着色器绑定到 VK_SHADER_STAGE_FRAGMENT_BIT
阶段,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
-
如果
coverageToColorEnable
为VK_TRUE
,则必须调用 vkCmdSetCoverageToColorLocationNV。
如果启用了 VK_NV_framebuffer_mixed_samples
扩展,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
-
如果
coverageModulationMode
不是VK_COVERAGE_MODULATION_MODE_NONE_NV
,则必须调用 vkCmdSetCoverageModulationTableEnableNV。 -
如果
coverageModulationTableEnable
为VK_TRUE
,则必须调用 vkCmdSetCoverageModulationTableNV。
如果启用了 coverageReductionMode
功能,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,必须在命令缓冲区中调用以下命令
如果启用了 representativeFragmentTest
功能,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,命令缓冲区中必须已调用以下命令
如果启用了 shadingRateImage
功能,且 rasterizerDiscardEnable
为 VK_FALSE
,则在绘制之前,命令缓冲区中必须已调用以下命令
-
如果
shadingRateImageEnable
为VK_TRUE
,则调用 vkCmdSetViewportShadingRatePaletteNV
如果启用了 exclusiveScissor
功能,则在绘制之前,命令缓冲区中必须已调用以下命令
-
如果
pExclusiveScissorEnables
中的任何值为VK_TRUE
,则调用 vkCmdSetExclusiveScissorNV
状态可以在绑定着色器对象之前或之后的任何时间设置,但所有必需的状态必须在发出绘制命令之前设置。
如果启用了 commandBufferInheritance
功能,则图形和计算状态将从队列中先前执行的命令缓冲区继承。 以这种方式继承的任何有效状态都不需要在当前命令缓冲区中再次设置。
与管线的交互
调用 vkCmdBindShadersEXT 会干扰 pStages
中每个阶段对应的管线绑定点,这意味着先前绑定到这些管线绑定点的任何管线都不再绑定。
如果 VK_PIPELINE_BIND_POINT_GRAPHICS
受到干扰(即,如果 pStages
包含任何图形阶段),则先前绑定的管线未指定为动态的任何图形管线状态将变为未定义,并且必须在使用着色器对象发出绘制命令之前在命令缓冲区中设置。
调用 vkCmdBindPipeline 同样会干扰与 pipelineBindPoint
对应的着色器阶段,这意味着先前绑定到任何这些阶段的任何着色器都不再绑定,即使在创建管线时,某些阶段没有着色器。
着色器模块
着色器模块包含着色器代码和一个或多个入口点。通过在管线创建过程中指定一个入口点,从着色器模块中选择着色器。管线的各个阶段可以使用来自不同模块的着色器。定义着色器模块的着色器代码必须采用 SPIR-V 格式,如适用于 SPIR-V 的 Vulkan 环境附录中所述。
着色器模块由 VkShaderModule
句柄表示
// Provided by VK_VERSION_1_0
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderModule)
要创建着色器模块,请调用
// Provided by VK_VERSION_1_0
VkResult vkCreateShaderModule(
VkDevice device,
const VkShaderModuleCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkShaderModule* pShaderModule);
-
device
是创建着色器模块的逻辑设备。 -
pCreateInfo
是指向 VkShaderModuleCreateInfo 结构的指针。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。 -
pShaderModule
是指向 VkShaderModule 句柄的指针,其中返回生成的着色器模块对象。
如果启用了 |
VkShaderModuleCreateInfo
结构定义为
// Provided by VK_VERSION_1_0
typedef struct VkShaderModuleCreateInfo {
VkStructureType sType;
const void* pNext;
VkShaderModuleCreateFlags flags;
size_t codeSize;
const uint32_t* pCode;
} VkShaderModuleCreateInfo;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
flags
保留供将来使用。 -
codeSize
是pCode
指向的代码的大小,以字节为单位。 -
pCode
是指向用于创建着色器模块的代码的指针。代码的类型和格式由pCode
指向的内存内容确定。
// Provided by VK_VERSION_1_0
typedef VkFlags VkShaderModuleCreateFlags;
VkShaderModuleCreateFlags
是用于设置掩码的位掩码类型,但目前保留供将来使用。
要使用 VkValidationCacheEXT 来缓存着色器验证结果,请将 VkShaderModuleValidationCacheCreateInfoEXT 结构添加到 VkShaderModuleCreateInfo 结构的 pNext
链中,指定要使用的缓存对象。
VkShaderModuleValidationCacheCreateInfoEXT
结构的定义如下
// Provided by VK_EXT_validation_cache
typedef struct VkShaderModuleValidationCacheCreateInfoEXT {
VkStructureType sType;
const void* pNext;
VkValidationCacheEXT validationCache;
} VkShaderModuleValidationCacheCreateInfoEXT;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
validationCache
是验证缓存对象,先前验证尝试的结果将从该对象写入,并且此 VkShaderModule 的新验证结果将写入该对象(如果尚未存在)。
要销毁着色器模块,请调用
// Provided by VK_VERSION_1_0
void vkDestroyShaderModule(
VkDevice device,
VkShaderModule shaderModule,
const VkAllocationCallbacks* pAllocator);
-
device
是销毁着色器模块的逻辑设备。 -
shaderModule
是要销毁的着色器模块的句柄。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。
着色器模块可以在仍在使用其着色器创建的管道时销毁。
着色器模块标识符
着色器模块具有与其关联的唯一标识符。要查询实现提供的标识符,请调用
// Provided by VK_EXT_shader_module_identifier
void vkGetShaderModuleIdentifierEXT(
VkDevice device,
VkShaderModule shaderModule,
VkShaderModuleIdentifierEXT* pIdentifier);
-
device
是创建着色器模块的逻辑设备。 -
shaderModule
是着色器模块的句柄。 -
pIdentifier
是指向返回的 VkShaderModuleIdentifierEXT 的指针。
实现返回的标识符必须仅取决于 shaderIdentifierAlgorithmUUID
以及创建 shaderModule
的 VkShaderModuleCreateInfo 中提供的信息。如果差异不影响管道编译,则实现可以为两个不同的 VkShaderModuleCreateInfo 结构返回相同的标识符。仅当查询标识符的设备的 shaderModuleIdentifierAlgorithmUUID
与使用标识符的设备相同时,标识符在不同的 VkDevice 对象上才有意义。
VkShaderModuleCreateInfo 结构具有与其关联的唯一标识符。要查询实现提供的标识符,请调用
// Provided by VK_EXT_shader_module_identifier
void vkGetShaderModuleCreateInfoIdentifierEXT(
VkDevice device,
const VkShaderModuleCreateInfo* pCreateInfo,
VkShaderModuleIdentifierEXT* pIdentifier);
-
device
是可以从pCreateInfo
创建 VkShaderModule 的逻辑设备。 -
pCreateInfo
是指向 VkShaderModuleCreateInfo 结构的指针。 -
pIdentifier
是指向返回的 VkShaderModuleIdentifierEXT 的指针。
实现返回的标识符必须仅依赖于 shaderIdentifierAlgorithmUUID
和 VkShaderModuleCreateInfo 中提供的信息。如果两个不同的 VkShaderModuleCreateInfo 结构之间的差异不影响管线编译,则实现可以为它们返回相同的标识符。只有当查询标识符的设备与使用该标识符的设备具有相同的 shaderModuleIdentifierAlgorithmUUID
时,标识符在不同的 VkDevice 对象上才有意义。
给定等效的 VkShaderModuleCreateInfo 定义和任何链接的 pNext
结构,vkGetShaderModuleCreateInfoIdentifierEXT 返回的标识符必须等于 vkGetShaderModuleIdentifierEXT 返回的标识符。
VkShaderModuleIdentifierEXT 表示由实现返回的着色器模块标识符。
// Provided by VK_EXT_shader_module_identifier
typedef struct VkShaderModuleIdentifierEXT {
VkStructureType sType;
void* pNext;
uint32_t identifierSize;
uint8_t identifier[VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT];
} VkShaderModuleIdentifierEXT;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
identifierSize
是identifier
中返回的有效数据的大小(以字节为单位)。 -
identifier
是一个指定标识符的不透明数据缓冲区。
任何返回的值超出前 identifierSize
个字节都是未定义的。实现必须返回大于 0 且小于等于 VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT
的 identifierSize
。
如果 identifierSize
相等,并且 identifier
的前 identifierSize
个字节比较相等,则认为两个标识符相等。
对于不同的模块,实现可以返回不同的 identifierSize
。实现应该确保 identifierSize
足够大,可以唯一地定义一个着色器模块。
VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT
是着色器模块标识符的长度(以字节为单位),如 VkShaderModuleIdentifierEXT::identifierSize
中返回的那样。
#define VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT 32U
绑定着色器
在着色器可以使用之前,必须先将其绑定到命令缓冲区。
调用 vkCmdBindPipeline 会绑定与 VkPipelineBindPoint 对应的所有阶段。调用 vkCmdBindShadersEXT 会绑定 pStages
中的所有阶段
下表描述了着色器阶段和管线绑定点之间的关系
着色器阶段 | 管线绑定点 | 行为控制 |
---|---|---|
|
|
所有绘图命令 |
|
|
所有调度命令 |
|
|
vkCmdTraceRaysNV vkCmdTraceRaysKHR 和 vkCmdTraceRaysIndirectKHR |
|
|
|
|
|
所有执行图命令 |
着色器执行
在管线的每个阶段,着色器的多个调用可能同时执行。此外,由不同命令产生的单个着色器的调用可能同时执行。相同着色器类型的调用的相对执行顺序是未定义的。着色器调用可能以与它们所源自的图元被应用程序绘制或调度的顺序不同的顺序完成。但是,片段着色器输出以光栅化顺序写入附件。
不同着色器类型的调用的相对执行顺序在很大程度上是未定义的。但是,当调用其输入是从先前管线阶段生成的着色器时,保证先前阶段的着色器调用已执行足够长的时间,以为所有必需的输入生成输入值。
着色器终止
终止的着色器调用已完成指令的执行。
在入口点执行 OpReturn
,或在任何函数中执行 OpTerminateInvocation
将终止调用。当在任何函数中执行 OpKill
时,实现也可能终止着色器调用;否则它会变成一个辅助调用。
当该阶段的所有调用都已终止时,给定命令的着色器阶段完成执行。
根据实现的不同, |
着色器内存访问排序
着色器读取或写入图像或缓冲区内存的顺序在很大程度上是未定义的。对于某些着色器类型(顶点、细分评估和某些情况下的片段),甚至可能执行加载和存储的着色器调用数量也是未定义的。
特别是,以下规则适用
上述对着色器调用顺序的限制使得在单个图元集中无法实现着色器调用之间的某些形式的同步。例如,让一个调用轮询另一个调用写入的内存,假设另一个调用已启动并将在有限时间内完成其写入。 |
应用程序必须不引起数据竞争。
SPIR-V 的 SubgroupMemory、CrossWorkgroupMemory 和 AtomicCounterMemory 内存语义被忽略。不支持顺序一致的原子操作和屏障,并且 SequentiallyConsistent 被视为 AcquireRelease。应该不使用 SequentiallyConsistent。
着色器输入和输出
数据分别通过具有输入或输出存储类的变量传递到着色器中和从着色器中传出。用户定义的输入和输出通过匹配它们的 Location
装饰在阶段之间连接。此外,数据可以通过使用 BuiltIn
装饰提供的执行环境提供的特殊函数提供或与之通信。
在许多情况下,相同的 BuiltIn
装饰可以在多个着色器阶段中使用,具有相似的含义。装饰为 BuiltIn
的变量的具体行为在以下部分中记录。
任务着色器
任务着色器与网格着色器结合运行,以生成一组图元,这些图元将由图形管道的后续阶段处理。其主要目的是创建可变数量的后续网格着色器调用。
任务着色器通过执行可编程网格着色管道调用。
任务着色器没有固定功能的输入,除了标识特定工作组和调用的变量。在 TaskNV
Execution
Model
中,要创建的网格着色器工作组的数量通过 TaskCountNV
装饰的输出变量指定。在 TaskEXT
Execution
Model
中,要创建的网格着色器工作组的数量通过 OpEmitMeshTasksEXT
指令指定。
任务着色器可以将其他输出写入任务内存,这些内存可以由它创建的所有网格着色器工作组读取。
任务着色器执行
任务工作负载由称为工作组的工作项组组成,并由当前图形管道中的任务着色器处理。工作组是执行相同着色器的一组着色器调用,可能并行执行。任务着色器在 全局工作组 中执行,全局工作组被分成多个 局部工作组,局部工作组的大小可以通过为 LocalSize
或 LocalSizeId
执行模式赋值或通过由 WorkgroupSize
装饰装饰的对象设置。局部工作组中的调用可以通过共享变量与其他局部工作组的成员共享数据,并发出内存和控制流屏障以与其他局部工作组的成员同步。如果子通道在其视图掩码中包含多个视图,则使用 TaskEXT
Execution
Model
的任务着色器可能会为每个视图单独调用。
网格着色器
网格着色器在工作组中运行,以生成一组图元,这些图元将由图形管道的后续阶段处理。每个工作组发出零个或多个输出图元,以及每个输出图元所需的一组顶点及其关联数据。
网格着色器通过执行可编程网格着色管道调用。
网格着色器可用的唯一输入是标识特定工作组和调用的变量,以及如果适用,任务着色器写入任务内存的任何输出,该任务着色器生成了网格着色器的工作组。网格着色器也可以在没有任务着色器的情况下运行。
网格着色器工作组的调用写入输出网格,包括一组具有每个图元属性的图元、一组具有每个顶点属性的顶点,以及一个标识属于每个图元的网格顶点的索引数组。然后,此网格的图元由后续的图形管道阶段处理,其中网格着色器的输出形成与片段着色器的接口。
网格着色器执行
网格工作负载由称为工作组的工作项组构成,并由当前图形管线中的网格着色器处理。工作组是执行相同着色器的着色器调用的集合,可能并行执行。网格着色器在全局工作组中执行,这些工作组被划分为多个局部工作组,其大小可以通过为 LocalSize
或 LocalSizeId
执行模式赋值,或通过使用 WorkgroupSize
修饰的对象来设置。局部工作组内的调用可以通过共享变量与其他局部工作组成员共享数据,并发出内存和控制流屏障以与其他局部工作组成员同步。
全局工作组可以通过 API 显式生成,也可以通过任务着色器的工作创建机制隐式生成。如果子通道在其视图掩码中包含多个视图,则使用 MeshEXT
Execution
Model
的网格着色器可以为每个视图单独调用。
集群剔除着色器
集群剔除着色器通过执行可编程集群剔除着色管线来调用。
集群剔除着色器唯一可用的输入是标识特定工作组和调用的变量。
集群剔除着色器在工作组中操作,以执行基于集群的剔除,并生成零个或多个集群绘制命令,这些命令将由图形管线的后续阶段处理。
集群绘制命令 (CDC) 与 MDI 命令非常相似,工作组中的调用可以发出零个或多个 CDC 以绘制零个或多个可见集群。
顶点着色器
每个顶点着色器调用操作于一个顶点及其相关的顶点属性数据,并输出一个顶点和相关数据。使用原始着色的图形管线必须包含顶点着色器,并且顶点着色器阶段始终是图形管线中的第一个着色器阶段。
顶点着色器执行
对于绘制命令指定的每个顶点,必须至少执行一次顶点着色器。如果子通道在其视图掩码中包含多个视图,则可以为每个视图单独调用着色器。在执行期间,着色器会接收到其被调用的顶点和实例的索引。在顶点着色器中声明的输入变量由实现填充,并使用与正在执行的调用相关的顶点属性的值。
如果同一个顶点在绘制命令中多次指定(例如,通过在索引缓冲区中多次包含相同的索引值),如果实现可以静态地确定顶点着色器调用将产生相同的结果,则实现可以重用顶点着色的结果。
何时以及是否重用顶点着色的结果是与实现相关的,因此顶点着色器将执行多少次也是如此。如果顶点着色器包含存储或原子操作(请参阅 |
细分控制着色器
细分控制着色器用于读取应用程序提供的输入面片并生成输出面片。每个细分控制着色器调用操作于输入面片(在面片中的所有控制点都由顶点着色器处理之后)及其相关数据,并输出输出面片的单个控制点及其相关数据,并且可以输出额外的每个面片数据。输入面片的大小根据VkPipelineTessellationStateCreateInfo的 patchControlPoints
成员在输入汇编中确定。
输入面片也可以使用vkCmdSetPatchControlPointsEXT的 patchControlPoints
参数动态调整大小。
要动态设置每个面片的控制点数,请调用
// Provided by VK_EXT_extended_dynamic_state2, VK_EXT_shader_object
void vkCmdSetPatchControlPointsEXT(
VkCommandBuffer commandBuffer,
uint32_t patchControlPoints);
-
commandBuffer
是将记录命令的命令缓冲区。 -
patchControlPoints
指定每个面片的控制点数。
当使用着色器对象进行绘制时,或当使用VkPipelineDynamicStateCreateInfo::pDynamicStates
中设置的 VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT
创建图形管线时,此命令为后续绘制命令设置每个面片的控制点数。否则,此状态由用于创建当前活动管线的VkPipelineTessellationStateCreateInfo::patchControlPoints
值指定。
输出面片的大小由细分控制着色器或细分评估着色器中指定的 OpExecutionMode
OutputVertices
控制,并且必须在至少一个着色器中指定。输入和输出面片的大小必须都大于零且小于或等于 VkPhysicalDeviceLimits
::maxTessellationPatchSize
。
细分控制着色器执行
对于面片中的每个输出顶点,必须至少调用一次细分控制着色器。如果子通道在其视图掩码中包含多个视图,则可以为每个视图单独调用着色器。
细分控制着色器的输入由顶点着色器生成。每次调用细分控制着色器可以读取任何传入顶点的属性及其关联数据。与给定补丁对应的调用在逻辑上并行执行,相对执行顺序未定义。但是,可以使用 OpControlBarrier
指令通过同步补丁内的调用来提供对执行顺序的有限控制,从而有效地将细分控制着色器执行划分为一组阶段。如果在同一阶段中,一个调用读取了另一个调用写入的每个顶点或每个补丁的输出,或者如果两个调用尝试在单个阶段中将不同的值写入相同的每个补丁的输出,则细分控制着色器将读取未定义的值。
片段着色器
片段着色器作为图形管道中的片段操作调用。每个片段着色器调用对单个片段及其关联数据进行操作。除了少数例外,片段着色器无法访问与任何其他片段关联的任何数据,并且被认为与其他片段关联的片段着色器调用隔离执行。
计算着色器
计算着色器通过_调度命令调用。通常,它们可以访问与作为图形管道一部分执行的着色器阶段类似的资源。
计算工作负载由称为工作组的工作项组组成,并由当前计算管道中的计算着色器处理。工作组是执行相同着色器的一组着色器调用,可能会并行执行。计算着色器在全局工作组中执行,全局工作组被划分为多个本地工作组,其大小可以通过为 LocalSize
或 LocalSizeId
执行模式赋值或通过 WorkgroupSize
修饰的对象来设置。本地工作组中的调用可以通过共享变量与其他本地工作组成员共享数据,并发出内存和控制流屏障以与其他本地工作组成员同步。
光线生成着色器
光线生成着色器类似于计算着色器。其主要目的是使用管道跟踪光线指令(例如 OpTraceRayKHR
)执行光线跟踪查询并处理结果。
光线生成着色器执行
每个光线跟踪调度执行一个光线生成着色器。它在着色器绑定表中的位置(详见着色器绑定表)通过 pRaygenShaderBindingTable
参数直接传递到 vkCmdTraceRaysKHR,或通过 raygenShaderBindingTableBuffer
和 raygenShaderBindingOffset
参数传递到 vkCmdTraceRaysNV。
最近命中着色器
最近命中着色器拥有对相应交点着色器生成的属性的只读访问权限,并且可以读取或修改射线载荷。它们还可以访问一些系统生成的值。最近命中着色器可以调用管线跟踪射线指令来递归跟踪射线。
未命中着色器
未命中着色器可以访问射线载荷,并且可以通过管线跟踪射线指令跟踪新射线,但是由于它们不与交点关联,因此不能访问属性。
插值修饰符
片段着色器接口中 Input
存储类中的变量是从正在光栅化的图元指定的值插值而来的。
插值修饰符可以存在于预光栅化着色器的输入和输出变量中,但对执行的插值没有影响。 |
未修饰的输入变量将根据正在光栅化的图元类型进行透视校正插值。线和多边形的插值方式与图元的裁剪坐标相同。如果存在 NoPerspective
修饰符,则对线和多边形使用线性插值。对于点,由于只有一个顶点,因此输入值永远不会被插值,而是采用为单个顶点写入的值。
如果输入变量上存在 Flat
修饰符,则该值不会被插值,而是直接从触发顶点获取其值。片段着色器输入(有符号或无符号整数、整数向量或任何双精度浮点类型)必须使用 Flat
修饰符。
输入变量的插值是在被着色的片段区域内实现定义的某个位置执行的。该位置进一步约束如下:
-
如果使用
Centroid
修饰符,则用于该变量的插值位置必须也落在正在光栅化的图元的边界内。 -
如果使用
Sample
修饰符,则用于该变量的插值位置必须是当前片段着色器调用正在着色的样本的位置。 -
如果使用 1 的样本计数,则插值位置必须位于片段区域的中心。
由于 |
如果输入变量上存在 PerVertexKHR
修饰符,则该值不会被插值,而是可以在数组中使用来自所有输入顶点的值。数组的每个索引对应于生成片段的图元的顶点之一。
如果输入变量上存在 CustomInterpAMD
修饰符,则不能直接访问该值;而是必须使用扩展指令 InterpolateAtVertexAMD
从输入顶点获取值。
静态使用
SPIR-V 模块使用 OpVariable
指令在内存中声明一个全局对象,这将导致指向该对象的指针 x
。如果 SPIR-V 模块中特定入口点的调用树包含一个包含 x
作为 id
操作数的指令的函数,则该入口点称为静态使用该对象。着色器入口点也静态使用在其接口中显式声明的任何变量。
作用域
作用域描述了一组着色器调用,其中每个这样的集合都是一个作用域实例。每个调用属于一个或多个作用域实例,但每个作用域最多属于一个作用域实例。
给定作用域实例中调用之间的可用操作有所不同,较小的作用域通常能够执行更多操作,并且具有更高的效率。
跨设备
在 Vulkan 实例中执行的所有调用都属于单个跨设备作用域实例。
虽然 CrossDevice
作用域在 SPIR-V 中定义,但在 Vulkan 中是不允许的。API 同步命令可以用于在设备之间进行通信。
设备
在单个设备上执行的所有调用构成一个设备作用域实例。
如果启用了vulkanMemoryModel
和vulkanMemoryModelDeviceScope
功能,则此作用域在 SPIR-V 中由 Device
Scope
表示,它可以用作屏障和原子操作的 Memory
Scope
。
如果同时启用了shaderDeviceClock
和vulkanMemoryModelDeviceScope
功能,则将 Device
Scope
与 OpReadClockKHR
指令一起使用将从在同一设备作用域实例中的调用之间一致的时钟读取。
没有在 SPIR-V 中同步这些调用的执行的方法,这只能通过 API 同步原语来完成。
在设备组中不同设备上执行的调用在单独的设备作用域实例中运行。
队列族
给定队列族中队列执行的调用形成一个队列族作用域实例。
如果启用了 vulkanMemoryModel
特性,则此作用域在 SPIR-V 中标识为 QueueFamily
Scope
;否则,标识为 Device
Scope
,它可以用作屏障和原子操作的 Memory
Scope
。
如果启用了 shaderDeviceClock
特性,但未启用 vulkanMemoryModelDeviceScope
特性,则将 Device
Scope
与 OpReadClockKHR
指令一起使用将从同一队列族作用域实例中调用之间一致的时钟读取。
没有在 SPIR-V 中同步这些调用的执行的方法,这只能通过 API 同步原语来完成。
队列族作用域实例中的每个调用必须在相同的设备作用域实例中。
命令
作为单个命令(例如 vkCmdDispatch 或 vkCmdDraw)的结果而执行的任何着色器调用都形成一个命令作用域实例。对于 drawCount
大于 1 的间接绘制命令,来自单独绘制的调用位于单独的命令作用域实例中。对于光线追踪着色器,调用组是由单个跟踪光线命令生成的给定着色器阶段的着色器调用集合的实现定义的子集。
在命令作用域实例中跨调用进行通信没有特定的 Scope
。由于这在 API 级别有一个清晰的边界,因此此处的协调可以在 API 中执行,而不是在 SPIR-V 中执行。
命令作用域实例中的每个调用必须在相同的队列族作用域实例中。
图元
作为单个图元光栅化的结果而执行的任何片段着色器调用都形成一个图元作用域实例。
在图元作用域实例中跨调用进行通信没有特定的 Scope
。
任何生成的辅助调用都包含在此作用域实例中。
图元作用域实例中的每个调用必须在相同的命令作用域实例中。
任何使用 Flat
修饰的输入变量在图元作用域实例中都是统一的。
着色器调用
在一个或多个光线追踪执行模型中执行的任何与着色器调用相关的调用都形成一个着色器调用作用域实例。
ShaderCallKHR
Scope
可用作屏障和原子操作的 Memory
Scope
。
着色器调用作用域实例中的每个调用必须在相同的队列族作用域实例中。
工作组
本地工作组是一组调用,它们可以使用 Workgroup
存储类中的内存相互同步和共享数据。
Workgroup
Scope
可以用作屏障和原子操作的 Execution
Scope
和 Memory
Scope
。
本地工作组中的每个调用必须在相同的命令作用域实例中。
只有任务、网格和计算着色器定义了工作组 - 其他着色器类型不能使用工作组功能。对于定义了工作组的着色器,此调用集合形成SPIR-V 规范中定义的调用组。
当使用 Workgroup
存储类声明的变量显式布局时(因此它们也使用 Block
修饰),所消耗的存储量是最大 Block 变量的大小,不计算末尾的任何填充。使用 Workgroup
存储类声明的非 Block 变量所消耗的存储量是实现定义的。但是,所消耗的存储量可能不会超过通过以下方式获得的最大块大小:如果所有使用 Workgroup
存储类声明的活动非 Block 变量按照标准存储缓冲区布局规则,并按任意顺序连续取得最小有效偏移量,并且在此计算中将 Boolean
值视为 32 位整数值(这相当于使用 GLSL std430 布局规则)。
子组
子组(请参阅 SPIR-V 1.3 修订版 1 规范第 2 节的“控制流”小节)是一组调用,它们可以相互有效地同步和共享数据。
如果启用了 shaderSubgroupClock
特性,则将 Subgroup
Scope
与 OpReadClockKHR
指令一起使用将从同一子组中的调用之间一致的时钟读取。
对于定义了工作组的着色器,子组中的每个调用必须在相同的本地工作组中。
在其他着色器阶段,子组中的每个调用必须在相同的设备作用域实例中。
只有支持子组操作的着色器阶段定义了子组。
不能保证子组是没有定义工作组的着色器中单个命令的子集。保证对于给定命令或子命令统一的值可能对于子组不统一,反之亦然。因此,应用程序在处理混合均匀性时必须小心。 一个比较常见的例子是尝试使用子组操作来优化对每次绘制数据的访问。
这可以通过尝试优化着色器以仅为每个子组执行一次加载来完成。但是,如果实现将多次绘制打包到单个子组中,则来自具有不同 drawID 的绘制的调用现在将从错误的调用接收数据。应用程序应该依赖于实现自动执行这种优化(如果实现可以执行),而不是试图强制执行。 |
四边形
四边形作用域实例由四个着色器调用组成。
在片段着色器中,四边形作用域实例中的每个调用都由相邻帧缓冲区位置 (xi, yi) 中的调用组成,其中
-
i 是调用在作用域实例中的索引。
-
w 和 h 是片段在 x 和 y 轴上覆盖的像素数。
-
对于所有参与的调用,w 和 h 都是相同的。
-
(x0) = (x1 - w) = (x2) = (x3 - w)
-
(y0) = (y1) = (y2 - h) = (y3 - h)
-
每次调用都具有相同的层和采样索引。
在网格着色器、任务着色器或计算着色器中,如果指定了 DerivativeGroupQuadsKHR
执行模式,则四边形范围实例中的每次调用都由局部调用 ID 相邻的调用组成 (xi, yi),其中
-
i 是四边形范围实例中调用的索引。
-
(x0) = (x1 - 1) = (x2) = (x3 - 1)
-
(y0) = (y1) = (y2 - 1) = (y3 - 1)
-
x0 和 y0 是 2 的整数倍。
-
每次调用都具有相同的 z 坐标。
在网格着色器、任务着色器或计算着色器中,如果指定了 DerivativeGroupLinearKHR
执行模式,则四边形范围实例中的每次调用都由局部调用索引相邻的调用组成 (li),其中
-
i 是四边形范围实例中调用的索引。
-
(l0) = (l1 - 1) = (l2 - 2) = (l3 - 3)
-
l0 是 4 的整数倍。
在所有着色器中,四边形范围实例中的每次调用都由相邻子组调用索引中的调用组成 (si),其中
-
i 是四边形范围实例中调用的索引。
-
(s0) = (s1 - 1) = (s2 - 2) = (s3 - 3)
-
s0 是 4 的整数倍。
四边形范围实例中的每次调用必须在同一个 子组中。
在片段着色器中,四边形范围实例中的每次调用必须在同一个 图元范围实例中。
片段着色器、网格着色器、任务着色器和计算着色器都定义了四边形范围实例。如果支持 quadOperationsInAllStages
限制,则任何支持子组操作的着色器阶段也都有定义的四边形范围实例。
片段互锁
片段互锁范围实例是由片段着色器调用根据其帧缓冲区位置 (x,y,layer,sample) 形成的,由单个 子通道 内的命令执行。
所包含的特定调用集会根据执行模式的不同而有所不同,如下所示
-
如果使用
SampleInterlockOrderedEXT
或SampleInterlockUnorderedEXT
执行模式,则仅包含具有相同帧缓冲区位置 (x,y,layer,sample) 的调用。 -
如果使用
PixelInterlockOrderedEXT
或PixelInterlockUnorderedEXT
执行模式,则还会包括具有不同采样 ID 的片段。 -
如果使用
ShadingRateInterlockOrderedEXT
或ShadingRateInterlockUnorderedEXT
执行模式,则还会包括来自相邻帧缓冲区位置的片段。着色率图像 或 片段着色率 确定这些片段。
只有具有上述执行模式之一的片段着色器才具有定义的片段互锁范围实例。
在片段互锁范围实例中,跨调用的通信没有特定的 Scope
值。但是,这被隐式地用作 OpBeginInvocationInterlockEXT
和 OpEndInvocationInterlockEXT
的内存范围。
片段互锁范围实例中的每次调用必须在同一个 队列族范围实例中。
调用
最小的范围是单个调用;这在 SPIR-V 中由 Invocation
Scope
表示。
片段着色器调用必须在 图元范围实例中。
在具有已定义片段互锁范围的片段着色器中的调用必须在 片段互锁范围实例中。
在具有已定义工作组的着色器中的调用必须在 本地工作组中。
在具有已定义子组范围的着色器中的调用必须在 子组中。
在具有已定义四边形范围的着色器中的调用必须在 四边形范围实例中。
所有阶段中的所有调用必须在 命令范围实例中。
组操作
组操作由范围实例内的多个调用执行;其中每个调用都参与计算结果。这为特定范围实例中的调用之间的有效通信提供了一种机制。
组操作都采用一个 Scope
,用于定义要操作的所需 范围实例。只有 Subgroup
范围可以用于这些操作;subgroupSupportedOperations
限制定义了哪些类型的操作可以使用。
基本组操作
基本组操作包括使用 OpGroupNonUniformElect
、OpControlBarrier
、OpMemoryBarrier
和原子操作。
可以使用 OpGroupNonUniformElect
来选择单个调用为整个组执行任务。只有组中 ID 最低的调用才会返回 true
。
内存模型附录定义了屏障和原子操作的执行方式。
投票组操作
投票组操作允许组内的调用跨组比较值。启用的投票类型为
-
所有活动组调用是否都同意某个表达式为真?
-
是否有任何活动组调用将某个表达式计算为真?
-
所有活动组调用是否具有相同的表达式值?
这些操作与控制流结合使用时非常有用,因为它们允许开发人员检查组内的条件是否匹配,并在这些情况下选择可能更快的代码路径。 |
算术组操作
算术组操作允许调用在组中执行扫描和缩减。支持的运算符有加、乘、最小、最大、与、或、异或。
对于缩减,组中的每个调用都将获得这些运算符应用于组中所有值的累积结果。对于独占扫描,组中的每个调用都将获得这些运算符应用于组中索引较低的调用中的所有值的累积结果。包含扫描与独占扫描相同,只是累积结果包括应用于当前调用中值的运算符。
这些运算符的应用顺序取决于实现。
四元组组操作
四元组组操作 (OpGroupNonUniformQuad*
) 是一种特殊的组操作,仅在四元组范围实例上运行。尽管这些指令确实包含一个 Scope
参数,但此范围始终被覆盖;在其执行范围中仅包含四元组范围实例。
静态执行 OpGroupNonUniformQuadBroadcast
或 OpGroupNonUniformQuadSwap
的片段着色器必须启动足够的调用以确保其正确运行;如果需要,会为光栅化片段未覆盖的帧缓冲区位置启动额外的辅助调用。
对于 OpGroupNonUniformQuadBroadcast
,此值等于 Index
。对于 OpGroupNonUniformQuadSwap
,它等于每个参与调用使用的隐式 Index
。
导数操作
导数操作计算表达式 P 相对于调用 x 和 y 坐标的偏导数。
导数操作作用于一组称为导数组的调用,如SPIR-V 规范中所定义。
计算导数时假设 P 在导数组内是分段线性且连续的。
以下控制流限制适用于导数操作:
-
如果指定了
QuadDerivativesKHR
执行模式,任何导数操作的动态实例必须在当前四元组范围实例内统一的控制流中执行。 -
如果未指定
QuadDerivativesKHR
执行模式:-
显式导数指令(
OpDPdx*
、OpDPdy*
和OpFwidth*
)的动态实例必须在导数组内统一的控制流中执行。 -
隐式导数操作的动态实例可以在导数组内不统一的控制流中执行,但结果是未定义的。
-
静态执行导数操作的片段着色器必须启动足够的调用以确保其正确运行;如果需要,会为光栅化片段未覆盖的帧缓冲区位置启动额外的辅助调用。
在网格、任务或计算着色器中,应用程序有责任确保启动足够的调用。 |
导数操作计算其结果为四元组中调用之间 P 的结果之差。对于精细导数操作 (OpDPdxFine
和 OpDPdyFine
),DPdx(Pi) 的值计算为:
-
DPdx(P0) = DPdx(P1) = P1 - P0
-
DPdx(P2) = DPdx(P3) = P3 - P2
并且 DPdy(Pi) 的值计算为:
-
DPdy(P0) = DPdy(P2) = P2 - P0
-
DPdy(P1) = DPdy(P3) = P3 - P1
其中 i 是每个调用的索引,如四元组中所述。
粗略导数操作 (OpDPdxCoarse
和 OpDPdyCoarse
) 以大致相同的方式计算其结果,但可能只计算两个值而不是四个(每个 DPdx 和 DPdy 一个),无论原始调用如何,都重用相同的结果。如果实现这样做,它应该使用为 P0 描述的精细导数计算。
导数值是在片段之间而不是像素之间计算的。如果计算中涉及的片段着色器调用覆盖多个像素,则这些操作会覆盖更广泛的区域,从而产生更大的导数值。反过来,这将导致为使用导数的图像采样操作选择更粗糙的 LOD。 应用程序可能需要在使用多像素片段时考虑这一点;如果需要像素导数,应用程序应使用显式导数操作并将结果除以每个维度中片段的大小,如下所示:
其中 w 和 h 是四元组中片段的大小,DPdx(Pn)' 和 DPdy(Pn)' 是像素导数。 |
OpDPdx
和 OpDPdy
的结果可以计算为精细导数或粗略导数,实现优先选择最有效的方法。实现必须在这两者之间一致地选择粗略或精细。
执行 OpFwidthFine
、OpFwidthCoarse
或 OpFwidth
等效于执行相应的 OpDPdx*
和 OpDPdy*
指令,取结果的绝对值并将它们相加。
执行 OpImage*Sample*ImplicitLod
指令等效于执行 OpDPdx
(Coordinate
) 和 OpDPdy
(Coordinate
),并将结果作为 Grad
操作数 dx
和 dy
传递。
预计使用采样函数的 |
辅助调用
协同矩阵
协同矩阵类型是一种 SPIR-V 类型,其中矩阵的存储和执行的计算分布在范围实例中的调用之间。 这些类型为实现如何在优化矩阵乘法方面提供了自由。
SPIR-V 定义了类型和指令,但未指定有关哪些大小/组合有效的规则,并且预期不同的实现可能支持不同的大小。
要枚举支持的协同矩阵类型和操作,请调用
// Provided by VK_KHR_cooperative_matrix
VkResult vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR(
VkPhysicalDevice physicalDevice,
uint32_t* pPropertyCount,
VkCooperativeMatrixPropertiesKHR* pProperties);
-
physicalDevice
是物理设备。 -
pPropertyCount
是一个指向整数的指针,该整数与可用或查询的协同矩阵属性的数量有关。 -
pProperties
是NULL
或指向 VkCooperativeMatrixPropertiesKHR 结构数组的指针。
如果 pProperties
为 NULL
,则可用协同矩阵属性的数量将在 pPropertyCount
中返回。 否则,pPropertyCount
必须指向应用程序设置为 pProperties
数组中元素数量的变量,并且在返回时,该变量将被实际写入 pProperties
的结构数量覆盖。 如果 pPropertyCount
小于可用协同矩阵属性的数量,则最多将写入 pPropertyCount
个结构,并且将返回 VK_INCOMPLETE
而不是 VK_SUCCESS
,以指示并非返回了所有可用的协同矩阵属性。
要枚举其他支持的协同矩阵类型和操作,请调用
// Provided by VK_NV_cooperative_matrix2
VkResult vkGetPhysicalDeviceCooperativeMatrixFlexibleDimensionsPropertiesNV(
VkPhysicalDevice physicalDevice,
uint32_t* pPropertyCount,
VkCooperativeMatrixFlexibleDimensionsPropertiesNV* pProperties);
-
physicalDevice
是物理设备。 -
pPropertyCount
是一个指向整数的指针,该整数与可用或查询的协同矩阵属性的数量有关。 -
pProperties
是NULL
或指向 VkCooperativeMatrixFlexibleDimensionsPropertiesNV 结构数组的指针。
如果 pProperties
为 NULL
,则可用的灵活维度属性的数量将在 pPropertyCount
中返回。 否则,pPropertyCount
必须指向应用程序设置为 pProperties
数组中元素数量的变量,并且在返回时,该变量将被实际写入 pProperties
的结构数量覆盖。 如果 pPropertyCount
小于可用的灵活维度属性的数量,则最多将写入 pPropertyCount
个结构,并且将返回 VK_INCOMPLETE
而不是 VK_SUCCESS
,以指示并非返回了所有可用的灵活维度属性。
如果不支持 cooperativeMatrixFlexibleDimensions
功能,则实现必须声明零个属性。
要枚举支持的协同矩阵类型和操作,请调用
// Provided by VK_NV_cooperative_matrix
VkResult vkGetPhysicalDeviceCooperativeMatrixPropertiesNV(
VkPhysicalDevice physicalDevice,
uint32_t* pPropertyCount,
VkCooperativeMatrixPropertiesNV* pProperties);
-
physicalDevice
是物理设备。 -
pPropertyCount
是一个指向整数的指针,该整数与可用或查询的协同矩阵属性的数量有关。 -
pProperties
是NULL
或指向 VkCooperativeMatrixPropertiesNV 结构数组的指针。
如果 pProperties
为 NULL
,则可用协同矩阵属性的数量将在 pPropertyCount
中返回。 否则,pPropertyCount
必须指向应用程序设置为 pProperties
数组中元素数量的变量,并且在返回时,该变量将被实际写入 pProperties
的结构数量覆盖。 如果 pPropertyCount
小于可用协同矩阵属性的数量,则最多将写入 pPropertyCount
个结构,并且将返回 VK_INCOMPLETE
而不是 VK_SUCCESS
,以指示并非返回了所有可用的协同矩阵属性。
每个 VkCooperativeMatrixPropertiesKHR 或 VkCooperativeMatrixPropertiesNV 结构都描述了矩阵乘/加运算 (OpCooperativeMatrixMulAddKHR
或 OpCooperativeMatrixMulAddNV
) 支持的类型的单个组合。 乘法可以用以下变量和类型(在 SPIR-V 伪代码中)描述
%A is of type OpTypeCooperativeMatrixKHR %AType %scope %MSize %KSize %MatrixAKHR
%B is of type OpTypeCooperativeMatrixKHR %BType %scope %KSize %NSize %MatrixBKHR
%C is of type OpTypeCooperativeMatrixKHR %CType %scope %MSize %NSize %MatrixAccumulatorKHR
%Result is of type OpTypeCooperativeMatrixKHR %ResultType %scope %MSize %NSize %MatrixAccumulatorKHR
%Result = %A * %B + %C // using OpCooperativeMatrixMulAddKHR
%A is of type OpTypeCooperativeMatrixNV %AType %scope %MSize %KSize
%B is of type OpTypeCooperativeMatrixNV %BType %scope %KSize %NSize
%C is of type OpTypeCooperativeMatrixNV %CType %scope %MSize %NSize
%D is of type OpTypeCooperativeMatrixNV %DType %scope %MSize %NSize
%D = %A * %B + %C // using OpCooperativeMatrixMulAddNV
具有这些维度的矩阵乘法称为 MxNxK 矩阵乘法。
VkCooperativeMatrixPropertiesKHR
结构的定义如下
// Provided by VK_KHR_cooperative_matrix
typedef struct VkCooperativeMatrixPropertiesKHR {
VkStructureType sType;
void* pNext;
uint32_t MSize;
uint32_t NSize;
uint32_t KSize;
VkComponentTypeKHR AType;
VkComponentTypeKHR BType;
VkComponentTypeKHR CType;
VkComponentTypeKHR ResultType;
VkBool32 saturatingAccumulation;
VkScopeKHR scope;
} VkCooperativeMatrixPropertiesKHR;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
MSize
是矩阵A
、C
和Result
中的行数。 -
KSize
是矩阵A
中的列数和矩阵B
中的行数。 -
NSize
是矩阵B
、C
、Result
中的列数。 -
AType
是矩阵A
的组件类型,类型为 VkComponentTypeKHR。 -
BType
是矩阵B
的组件类型,类型为 VkComponentTypeKHR。 -
CType
是矩阵C
的组件类型,类型为 VkComponentTypeKHR。 -
ResultType
是矩阵Result
的组件类型,类型为 VkComponentTypeKHR。 -
saturatingAccumulation
指示OpCooperativeMatrixMulAddKHR
的SaturatingAccumulation
操作数是否必须存在。如果为VK_TRUE
,则SaturatingAccumulation
操作数必须存在。如果为VK_FALSE
,则SaturatingAccumulation
操作数必须不存在。 -
scope
是所有矩阵类型的范围,类型为 VkScopeKHR。
如果某些类型比其他类型更受偏爱(例如,为了性能),它们应该在 vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR 枚举的列表中较早出现。
列表中至少有一个条目的 MSize
、KSize
和 NSize
的所有值必须是 2 的幂。
如果不支持 cooperativeMatrixWorkgroupScope
功能,则 scope
必须为 VK_SCOPE_SUBGROUP_KHR
。
VkCooperativeMatrixFlexibleDimensionsPropertiesNV
结构定义如下:
// Provided by VK_NV_cooperative_matrix2
typedef struct VkCooperativeMatrixFlexibleDimensionsPropertiesNV {
VkStructureType sType;
void* pNext;
uint32_t MGranularity;
uint32_t NGranularity;
uint32_t KGranularity;
VkComponentTypeKHR AType;
VkComponentTypeKHR BType;
VkComponentTypeKHR CType;
VkComponentTypeKHR ResultType;
VkBool32 saturatingAccumulation;
VkScopeKHR scope;
uint32_t workgroupInvocations;
} VkCooperativeMatrixFlexibleDimensionsPropertiesNV;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
MGranularity
是矩阵A
、C
和Result
中行数的粒度。行数必须是该值的整数倍。 -
KGranularity
是矩阵A
中的列数和矩阵B
中的行数的粒度。列/行数必须是该值的整数倍。 -
NGranularity
是矩阵B
、C
、Result
中列数的粒度。列数必须是该值的整数倍。 -
AType
是矩阵A
的组件类型,类型为 VkComponentTypeKHR。 -
BType
是矩阵B
的组件类型,类型为 VkComponentTypeKHR。 -
CType
是矩阵C
的组件类型,类型为 VkComponentTypeKHR。 -
ResultType
是矩阵Result
的组件类型,类型为 VkComponentTypeKHR。 -
saturatingAccumulation
指示OpCooperativeMatrixMulAddKHR
的SaturatingAccumulation
操作数是否必须存在。如果为VK_TRUE
,则SaturatingAccumulation
操作数必须存在。如果为VK_FALSE
,则SaturatingAccumulation
操作数必须不存在。 -
scope
是所有矩阵类型的范围,类型为 VkScopeKHR。 -
workgroupInvocations
是支持此值组合时本地工作组中的调用次数。
VkCooperativeMatrixFlexibleDimensionsPropertiesNV
不是显式枚举支持的大小列表,而是声明了大小粒度,其中矩阵必须是声明大小的倍数。M 和 K 粒度适用于 Use
为 MatrixA
的矩阵的行和列,K 和 N 适用于 Use
为 MatrixB
的矩阵的行和列,M 和 N 适用于 Use
为 MatrixAccumulator
的矩阵的行和列。
对于给定的类型组合,如果支持多个工作组大小,则可能存在多个具有不同粒度的 VkCooperativeMatrixFlexibleDimensionsPropertiesNV
结构。
所有粒度值必须是 2 的幂。
不同的 A/B 类型可能需要不同的粒度,但共享相同的累加器类型。在这种情况下,具有累加器类型的矩阵的受支持粒度将是声明的最小粒度。 |
VkCooperativeMatrixPropertiesNV
结构定义如下:
// Provided by VK_NV_cooperative_matrix
typedef struct VkCooperativeMatrixPropertiesNV {
VkStructureType sType;
void* pNext;
uint32_t MSize;
uint32_t NSize;
uint32_t KSize;
VkComponentTypeNV AType;
VkComponentTypeNV BType;
VkComponentTypeNV CType;
VkComponentTypeNV DType;
VkScopeNV scope;
} VkCooperativeMatrixPropertiesNV;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
MSize
是矩阵 A、C 和 D 中的行数。 -
KSize
是矩阵 A 中的列数和矩阵 B 中的行数。 -
NSize
是矩阵 B、C、D 中的列数。 -
AType
是矩阵 A 的组件类型,类型为 VkComponentTypeNV。 -
BType
是矩阵 B 的组件类型,类型为 VkComponentTypeNV。 -
CType
是矩阵 C 的组件类型,类型为 VkComponentTypeNV。 -
DType
是矩阵 D 的组件类型,类型为 VkComponentTypeNV。 -
scope
是所有矩阵类型的范围,类型为 VkScopeNV。
如果某些类型比其他类型更受偏爱(例如,为了性能),它们应该在 vkGetPhysicalDeviceCooperativeMatrixPropertiesNV 枚举的列表中较早出现。
列表中至少有一个条目的 MSize
、KSize
和 NSize
的所有值必须是 2 的幂。
VkScopeKHR 的可能值包括:
// Provided by VK_KHR_cooperative_matrix
typedef enum VkScopeKHR {
VK_SCOPE_DEVICE_KHR = 1,
VK_SCOPE_WORKGROUP_KHR = 2,
VK_SCOPE_SUBGROUP_KHR = 3,
VK_SCOPE_QUEUE_FAMILY_KHR = 5,
// Provided by VK_NV_cooperative_matrix
VK_SCOPE_DEVICE_NV = VK_SCOPE_DEVICE_KHR,
// Provided by VK_NV_cooperative_matrix
VK_SCOPE_WORKGROUP_NV = VK_SCOPE_WORKGROUP_KHR,
// Provided by VK_NV_cooperative_matrix
VK_SCOPE_SUBGROUP_NV = VK_SCOPE_SUBGROUP_KHR,
// Provided by VK_NV_cooperative_matrix
VK_SCOPE_QUEUE_FAMILY_NV = VK_SCOPE_QUEUE_FAMILY_KHR,
} VkScopeKHR;
或等效值
// Provided by VK_NV_cooperative_matrix
typedef VkScopeKHR VkScopeNV;
-
VK_SCOPE_DEVICE_KHR
对应于 SPIR-VDevice
范围。 -
VK_SCOPE_WORKGROUP_KHR
对应于 SPIR-VWorkgroup
范围。 -
VK_SCOPE_SUBGROUP_KHR
对应于 SPIR-VSubgroup
范围。 -
VK_SCOPE_QUEUE_FAMILY_KHR
对应于 SPIR-VQueueFamily
范围。
所有枚举值都与相应的 SPIR-V 值匹配。
VkComponentTypeKHR 的可能值包括:
// Provided by VK_KHR_cooperative_matrix
typedef enum VkComponentTypeKHR {
VK_COMPONENT_TYPE_FLOAT16_KHR = 0,
VK_COMPONENT_TYPE_FLOAT32_KHR = 1,
VK_COMPONENT_TYPE_FLOAT64_KHR = 2,
VK_COMPONENT_TYPE_SINT8_KHR = 3,
VK_COMPONENT_TYPE_SINT16_KHR = 4,
VK_COMPONENT_TYPE_SINT32_KHR = 5,
VK_COMPONENT_TYPE_SINT64_KHR = 6,
VK_COMPONENT_TYPE_UINT8_KHR = 7,
VK_COMPONENT_TYPE_UINT16_KHR = 8,
VK_COMPONENT_TYPE_UINT32_KHR = 9,
VK_COMPONENT_TYPE_UINT64_KHR = 10,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_FLOAT16_NV = VK_COMPONENT_TYPE_FLOAT16_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_FLOAT32_NV = VK_COMPONENT_TYPE_FLOAT32_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_FLOAT64_NV = VK_COMPONENT_TYPE_FLOAT64_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_SINT8_NV = VK_COMPONENT_TYPE_SINT8_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_SINT16_NV = VK_COMPONENT_TYPE_SINT16_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_SINT32_NV = VK_COMPONENT_TYPE_SINT32_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_SINT64_NV = VK_COMPONENT_TYPE_SINT64_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_UINT8_NV = VK_COMPONENT_TYPE_UINT8_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_UINT16_NV = VK_COMPONENT_TYPE_UINT16_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_UINT32_NV = VK_COMPONENT_TYPE_UINT32_KHR,
// Provided by VK_NV_cooperative_matrix
VK_COMPONENT_TYPE_UINT64_NV = VK_COMPONENT_TYPE_UINT64_KHR,
} VkComponentTypeKHR;
或等效值
// Provided by VK_NV_cooperative_matrix
typedef VkComponentTypeKHR VkComponentTypeNV;
-
VK_COMPONENT_TYPE_FLOAT16_KHR
对应于 SPIR-VOpTypeFloat
16。 -
VK_COMPONENT_TYPE_FLOAT32_KHR
对应于 SPIR-VOpTypeFloat
32。 -
VK_COMPONENT_TYPE_FLOAT64_KHR
对应于 SPIR-VOpTypeFloat
64。 -
VK_COMPONENT_TYPE_SINT8_KHR
对应于 SPIR-VOpTypeInt
8 0/1。 -
VK_COMPONENT_TYPE_SINT16_KHR
对应于 SPIR-VOpTypeInt
16 0/1。 -
VK_COMPONENT_TYPE_SINT32_KHR
对应于 SPIR-VOpTypeInt
32 0/1。 -
VK_COMPONENT_TYPE_SINT64_KHR
对应于 SPIR-VOpTypeInt
64 0/1。 -
VK_COMPONENT_TYPE_UINT8_KHR
对应于 SPIR-VOpTypeInt
8 0/1。 -
VK_COMPONENT_TYPE_UINT16_KHR
对应于 SPIR-VOpTypeInt
16 0/1。 -
VK_COMPONENT_TYPE_UINT32_KHR
对应于 SPIR-VOpTypeInt
32 0/1。 -
VK_COMPONENT_TYPE_UINT64_KHR
对应于 SPIR-VOpTypeInt
64 0/1。
验证缓存
验证缓存对象允许重用内部验证的结果,包括在单个应用程序运行期间和多次运行之间。通过在创建受支持的 Vulkan 对象时传递相同的验证缓存对象来实现在单次运行中的重用。通过在一次应用程序运行中检索验证缓存内容、保存内容,并在后续运行中使用它们来预初始化验证缓存来实现跨应用程序运行的重用。验证缓存对象的内容由验证层管理。应用程序可以管理验证缓存对象消耗的主机内存,并控制从验证缓存对象中检索的数据量。
验证缓存对象由 VkValidationCacheEXT
句柄表示。
// Provided by VK_EXT_validation_cache
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkValidationCacheEXT)
要创建验证缓存对象,请调用
// Provided by VK_EXT_validation_cache
VkResult vkCreateValidationCacheEXT(
VkDevice device,
const VkValidationCacheCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkValidationCacheEXT* pValidationCache);
-
device
是创建验证缓存对象的逻辑设备。 -
pCreateInfo
是指向 VkValidationCacheCreateInfoEXT 结构的指针,该结构包含验证缓存对象的初始参数。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。 -
pValidationCache
是指向 VkValidationCacheEXT 句柄的指针,将在其中返回结果验证缓存对象。
应用程序可以使用 |
创建后,验证缓存可以通过将此对象添加到 VkShaderModuleCreateInfo 结构的 pNext
链传递给 vkCreateShaderModule
命令。如果 VkShaderModuleValidationCacheCreateInfoEXT 对象包含在 VkShaderModuleCreateInfo::pNext
链中,并且其 validationCache
字段不是 VK_NULL_HANDLE,则实现将查询它以寻找可能的重用机会并使用新内容更新它。在这些命令中使用验证缓存对象是内部同步的,并且同一验证缓存对象可以在多个线程中同时使用。
实现应该尽一切努力将任何临界区限制在对缓存的实际访问上,这预计会比 |
VkValidationCacheCreateInfoEXT
结构定义如下
// Provided by VK_EXT_validation_cache
typedef struct VkValidationCacheCreateInfoEXT {
VkStructureType sType;
const void* pNext;
VkValidationCacheCreateFlagsEXT flags;
size_t initialDataSize;
const void* pInitialData;
} VkValidationCacheCreateInfoEXT;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
flags
保留供将来使用。 -
initialDataSize
是pInitialData
中的字节数。如果initialDataSize
为零,则验证缓存最初将为空。 -
pInitialData
是一个指向先前检索的验证缓存数据的指针。如果验证缓存数据与设备不兼容(如下定义),则验证缓存最初将为空。如果initialDataSize
为零,则忽略pInitialData
。
// Provided by VK_EXT_validation_cache
typedef VkFlags VkValidationCacheCreateFlagsEXT;
VkValidationCacheCreateFlagsEXT
是用于设置掩码的位掩码类型,但目前保留供将来使用。
可以使用以下命令合并验证缓存对象
// Provided by VK_EXT_validation_cache
VkResult vkMergeValidationCachesEXT(
VkDevice device,
VkValidationCacheEXT dstCache,
uint32_t srcCacheCount,
const VkValidationCacheEXT* pSrcCaches);
-
device
是拥有验证缓存对象的逻辑设备。 -
dstCache
是要将结果合并到的验证缓存的句柄。 -
srcCacheCount
是pSrcCaches
数组的长度。 -
pSrcCaches
是指向验证缓存句柄数组的指针,这些句柄将合并到dstCache
中。合并后,将包括dstCache
的先前内容。
合并操作的详细信息取决于实现,但实现应该合并指定的验证缓存的内容并修剪重复的条目。 |
可以使用以下命令从验证缓存对象检索数据
// Provided by VK_EXT_validation_cache
VkResult vkGetValidationCacheDataEXT(
VkDevice device,
VkValidationCacheEXT validationCache,
size_t* pDataSize,
void* pData);
-
device
是拥有验证缓存的逻辑设备。 -
validationCache
是要从中检索数据的验证缓存。 -
pDataSize
是指向与验证缓存中的数据量相关的值的指针,如下所述。 -
pData
要么是NULL
,要么是指向缓冲区的指针。
如果 pData
为 NULL
,则在 pDataSize
中返回可以从验证缓存中检索的最大数据大小(以字节为单位)。否则,pDataSize
必须指向应用程序设置为由 pData
指向的缓冲区大小(以字节为单位)的变量,并且在返回时,该变量将被实际写入 pData
的数据量覆盖。如果 pDataSize
小于验证缓存可以检索的最大大小,则最多将 pDataSize
字节写入 pData
,并且 vkGetValidationCacheDataEXT
将返回 VK_INCOMPLETE
而不是 VK_SUCCESS
,以指示没有返回所有验证缓存。
写入 pData
的任何数据都有效,并且可以作为传递给 vkCreateValidationCacheEXT
的 VkValidationCacheCreateInfoEXT 结构的 pInitialData
成员提供。
除非在两次调用之间调用了修改缓存内容的命令,否则两次使用相同参数调用 vkGetValidationCacheDataEXT
必须检索相同的数据。
应用程序可以存储从验证缓存检索的数据,并使用这些数据(可能在应用程序的将来运行中)来填充新的验证缓存对象。但是,验证的结果可能取决于供应商 ID、设备 ID、驱动程序版本以及设备的其它详细信息。为了使应用程序能够检测到先前检索的数据何时与设备不兼容,写入 pData
的初始字节必须是包含以下成员的标头
偏移量 | 大小 | 含义 |
---|---|---|
0 |
4 |
整个验证缓存标头的长度(以字节为单位),以字节流的形式写入,最小有效字节在前 |
4 |
4 |
一个 VkValidationCacheHeaderVersionEXT 值,以字节流的形式写入,最小有效字节在前 |
8 |
|
一个层提交 ID,以 UUID 的形式表示,唯一标识用于生成这些验证结果的验证层版本。 |
前四个字节编码整个验证缓存头部的长度,以字节为单位。此值包括头部中的所有字段,包括验证缓存版本字段和长度字段的大小。
接下来的四个字节编码验证缓存版本,如 VkValidationCacheHeaderVersionEXT 所述。验证缓存的消费者应该使用缓存版本来解释缓存头部的其余部分。
如果 pDataSize
小于存储此头部所需的空间,则不会写入任何内容到 pData
中,并且会将零写入到 pDataSize
。
由 vkGetValidationCacheDataEXT 返回的头部中第二组四个字节的可能值,编码验证缓存版本,如下所示:
// Provided by VK_EXT_validation_cache
typedef enum VkValidationCacheHeaderVersionEXT {
VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1,
} VkValidationCacheHeaderVersionEXT;
-
VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT
指定验证缓存的版本一。
要销毁验证缓存,请调用:
// Provided by VK_EXT_validation_cache
void vkDestroyValidationCacheEXT(
VkDevice device,
VkValidationCacheEXT validationCache,
const VkAllocationCallbacks* pAllocator);
-
device
是销毁验证缓存对象的逻辑设备。 -
validationCache
是要销毁的验证缓存的句柄。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。
CUDA 模块
创建 CUDA 模块
CUDA 模块必须包含一些内核代码,并且必须至少公开一个函数入口点。
CUDA 模块由 VkCudaModuleNV
句柄表示。
// Provided by VK_NV_cuda_kernel_launch
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCudaModuleNV)
要创建 CUDA 模块,请调用:
// Provided by VK_NV_cuda_kernel_launch
VkResult vkCreateCudaModuleNV(
VkDevice device,
const VkCudaModuleCreateInfoNV* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkCudaModuleNV* pModule);
-
device
是创建着色器模块的逻辑设备。 -
pCreateInfo
是指向 VkCudaModuleCreateInfoNV 结构的指针。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。 -
pModule
是指向 VkCudaModuleNV 句柄的指针,其中返回生成的 CUDA 模块对象。
创建 CUDA 模块后,应用程序可以创建函数入口点,该入口点必须引用模块中的一个函数。
VkCudaModuleCreateInfoNV
结构的定义如下:
// Provided by VK_NV_cuda_kernel_launch
typedef struct VkCudaModuleCreateInfoNV {
VkStructureType sType;
const void* pNext;
size_t dataSize;
const void* pData;
} VkCudaModuleCreateInfoNV;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
可以是NULL
,或者可以是指向扩展此结构的结构的指针。 -
dataSize
是pData
数组的长度。 -
pData
是指向 CUDA 代码的指针。
创建 CUDA 函数句柄
CUDA 函数由 VkCudaFunctionNV
句柄表示。然后,可以使用指向 global
函数的句柄从命令缓冲区发出内核启动(即调度)。请参阅调度 CUDA PTX 内核的命令。
// Provided by VK_NV_cuda_kernel_launch
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCudaFunctionNV)
要创建 CUDA 函数,请调用:
// Provided by VK_NV_cuda_kernel_launch
VkResult vkCreateCudaFunctionNV(
VkDevice device,
const VkCudaFunctionCreateInfoNV* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkCudaFunctionNV* pFunction);
-
device
是创建着色器模块的逻辑设备。 -
pCreateInfo
是指向 VkCudaFunctionCreateInfoNV 结构的指针。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。 -
pFunction
是指向 VkCudaFunctionNV 句柄的指针,其中返回生成的 CUDA 函数对象。
VkCudaFunctionCreateInfoNV
结构定义如下
// Provided by VK_NV_cuda_kernel_launch
typedef struct VkCudaFunctionCreateInfoNV {
VkStructureType sType;
const void* pNext;
VkCudaModuleNV module;
const char* pName;
} VkCudaFunctionCreateInfoNV;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
module
是函数所在的 CUDA VkCudaModuleNV 模块。 -
pName
是一个以 null 结尾的 UTF-8 字符串,其中包含此阶段的着色器入口点名称。
销毁 CUDA 函数
要销毁 CUDA 函数句柄,请调用
// Provided by VK_NV_cuda_kernel_launch
void vkDestroyCudaFunctionNV(
VkDevice device,
VkCudaFunctionNV function,
const VkAllocationCallbacks* pAllocator);
-
device
是销毁函数的逻辑设备。 -
function
是要销毁的 CUDA 函数的句柄。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。
销毁 CUDA 模块
要销毁 CUDA 着色器模块,请调用
// Provided by VK_NV_cuda_kernel_launch
void vkDestroyCudaModuleNV(
VkDevice device,
VkCudaModuleNV module,
const VkAllocationCallbacks* pAllocator);
-
device
是销毁着色器模块的逻辑设备。 -
module
是要销毁的 CUDA 模块的句柄。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。
读取 CUDA 模块缓存
上传 PTX 内核代码后,模块会编译代码以生成二进制缓存,其中包含设备执行所需的所有必要信息。 可以读取回此缓存以供以后使用,例如加速进一步执行的初始化。
要获取 CUDA 模块缓存,请调用
// Provided by VK_NV_cuda_kernel_launch
VkResult vkGetCudaModuleCacheNV(
VkDevice device,
VkCudaModuleNV module,
size_t* pCacheSize,
void* pCacheData);
-
device
是销毁函数的逻辑设备。 -
module
是 CUDA 模块。 -
pCacheSize
是一个指针,其中包含要复制到pCacheData
中的字节数 -
pCacheData
是一个指针,指向要复制二进制缓存的缓冲区
如果 pCacheData
为 NULL
,则二进制缓存的大小(以字节为单位)将返回到 pCacheSize
中。 否则,pCacheSize
必须指向由应用程序设置为缓冲区大小的变量(以字节为单位),该缓冲区由 pCacheData
指向,并且在返回时,变量将被实际写入 pCacheData
的数据量覆盖。如果 pCacheSize
小于二进制着色器代码的大小,则不会向 pCacheData
写入任何内容,并且将返回 VK_INCOMPLETE
而不是 VK_SUCCESS
。
然后,可以将返回的缓存用于以后初始化 CUDA 模块,方法是在使用 vkCreateCudaModuleNV 时,发送此缓存而不是 PTX 代码。
给定整个编译和验证不是必要的,使用二进制缓存而不是原始 PTX 代码应该显著加快 CUDA 模块的初始化速度。 与 VkPipelineCache 一样,二进制缓存取决于特定的实现。 应用程序必须假设缓存上传可能在许多情况下失败,因此可能必须准备好在必要时回退到原始 PTX 代码。 通常,如果在从 PTX 生成缓存和使用此缓存之间使用相同的设备驱动程序和架构,则缓存可能会成功。 如果是新的驱动程序版本或使用不同的设备架构,则此缓存可能会失效。 |
限制
CUDA 和 Vulkan 不在相同的配置中使用设备。必须考虑以下限制
-
无法从 Vulkan 读取或写入全局参数。与 PTX 内核共享资源或发送值的唯一方法是将其作为函数的参数传递。有关更多详细信息,请参见 CUDA 内核和 Vulkan 之间的资源共享。
-
不支持对模块 PTX 外部的函数进行任何调用。
-
Vulkan 禁用了一些着色器/内核异常,这可能会破坏依赖于异常的 CUDA 内核。
-
提交到 Vulkan 的 CUDA 内核受限于共享内存量,可以从物理功能中查询。它可能小于 CUDA 可以提供的内存量。
-
CUDA 指令级抢占 (CILP) 不起作用。
-
此扩展中 CUDA 统一内存不起作用。
-
不支持 CUDA 动态并行性。
-
vk*DispatchIndirect
不可用。