执行图
执行图 提供了一种让应用程序从主机上的单个初始命令动态分派多个操作的方法。为了实现这一点,提供了一个新的执行图管线,它将多个着色器或管线链接在一起,每个着色器或管线都描述了可以在执行图中分派的一个或多个操作。每个链接的管线或着色器都描述了图中的一个执行节点,该节点可以从同一图中的另一个着色器动态分派。这允许应用程序以比通常仅使用 API 命令可能实现的更精细的粒度来描述更丰富的执行拓扑。
管线创建
要创建执行图管线,请调用
// Provided by VK_AMDX_shader_enqueue
VkResult vkCreateExecutionGraphPipelinesAMDX(
VkDevice device,
VkPipelineCache pipelineCache,
uint32_t createInfoCount,
const VkExecutionGraphPipelineCreateInfoAMDX* pCreateInfos,
const VkAllocationCallbacks* pAllocator,
VkPipeline* pPipelines);
-
device
是创建执行图管线的逻辑设备。 -
pipelineCache
要么是 VK_NULL_HANDLE,表示禁用管线缓存;要么是有效的 管线缓存 对象的句柄,在这种情况下,在该命令的持续时间内启用该缓存的使用。 -
createInfoCount
是pCreateInfos
和pPipelines
数组的长度。 -
pCreateInfos
是指向 VkExecutionGraphPipelineCreateInfoAMDX 结构数组的指针。 -
pAllocator
控制主机内存分配,如内存分配章节中所述。 -
pPipelines
是指向 VkPipeline 句柄数组的指针,其中返回生成的执行图管线对象。
管线的创建和返回方式与 多管线创建 所述相同。
VkExecutionGraphPipelineCreateInfoAMDX
结构定义为:
// Provided by VK_AMDX_shader_enqueue
typedef struct VkExecutionGraphPipelineCreateInfoAMDX {
VkStructureType sType;
const void* pNext;
VkPipelineCreateFlags flags;
uint32_t stageCount;
const VkPipelineShaderStageCreateInfo* pStages;
const VkPipelineLibraryCreateInfoKHR* pLibraryInfo;
VkPipelineLayout layout;
VkPipeline basePipelineHandle;
int32_t basePipelineIndex;
} VkExecutionGraphPipelineCreateInfoAMDX;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
flags
是一个 VkPipelineCreateFlagBits 的位掩码,指定如何生成管线。 -
stageCount
是pStages
数组中的条目数。 -
pStages
是指向stageCount
个 VkPipelineShaderStageCreateInfo 结构的数组的指针,描述了要包含在执行图管线中的着色器阶段集合。 -
pLibraryInfo
是指向 VkPipelineLibraryCreateInfoKHR 结构的指针,该结构定义要包含的管线库。 -
layout
是管线以及与管线一起使用的描述符集所使用的绑定位置的描述。 -
basePipelineHandle
是要从中派生的管线。 -
basePipelineIndex
是一个索引,指向要用作从中派生的管线的pCreateInfos
参数。
参数 basePipelineHandle
和 basePipelineIndex
在 管线派生 中有更详细的描述。
创建执行图管线时提供的每个着色器阶段(包括库中的着色器阶段)都与一个名称和一个索引相关联,该名称和索引由其 pNext
链中是否包含 VkPipelineShaderStageNodeCreateInfoAMDX 结构来决定。对于任何图形管线库,只有顶点或网格着色器阶段的名称和索引直接链接到图中作为节点 - 管线中的其他着色器阶段将在这些着色器阶段之后正常执行。任务着色器不能包含在用于绘制节点的图形管线中。
除了着色器名称和索引之外,还会为每个节点生成一个内部“节点索引”,可以使用 vkGetExecutionGraphPipelineNodeIndexAMDX 查询该索引,并且该索引专门用于执行图的初始调度。
VK_SHADER_INDEX_UNUSED_AMDX
是一个特殊的着色器索引,用于指示创建的节点不覆盖索引。在这种情况下,着色器索引通过其他方式确定。它被定义为
#define VK_SHADER_INDEX_UNUSED_AMDX (~0U)
VkPipelineShaderStageNodeCreateInfoAMDX
结构定义如下:
// Provided by VK_AMDX_shader_enqueue
typedef struct VkPipelineShaderStageNodeCreateInfoAMDX {
VkStructureType sType;
const void* pNext;
const char* pName;
uint32_t index;
} VkPipelineShaderStageNodeCreateInfoAMDX;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
pName
是在执行图中创建节点时使用的着色器名称。如果pName
为NULL
,则使用 SPIR-V 中指定的入口点名称作为着色器名称。 -
index
是在执行图中创建节点时使用的着色器索引。如果index
为VK_SHADER_INDEX_UNUSED_AMDX
,则使用原始索引,或者如ShaderIndexAMDX
执行模式所指定,或者如果也未指定,则使用0
。
当包含在 VkPipelineShaderStageCreateInfo 结构的 pNext
链中时,此结构在创建执行图管线时指定节点的着色器名称和着色器索引。如果省略此结构,则着色器名称设置为 SPIR-V 中入口点的名称,着色器索引设置为 0
。
当从另一个着色器分派节点时,名称在管线创建时固定,但索引可以动态设置。通过将多个着色器与相同的名称但不同的索引关联,应用程序可以动态选择要执行的不同节点。应用程序必须确保每个节点都有唯一的名称和索引。
具有相同名称的着色器必须具有相同的类型,例如,计算着色器和图形着色器,或者甚至两个计算着色器,其中一个正在合并,而另一个没有,不能共享相同的名称。 |
要查询执行图中特定节点的内部节点索引,请调用
// Provided by VK_AMDX_shader_enqueue
VkResult vkGetExecutionGraphPipelineNodeIndexAMDX(
VkDevice device,
VkPipeline executionGraph,
const VkPipelineShaderStageNodeCreateInfoAMDX* pNodeInfo,
uint32_t* pNodeIndex);
-
device
是创建executionGraph
的逻辑设备。 -
executionGraph
是要查询内部节点索引的执行图管线。 -
pNodeInfo
是指向 VkPipelineShaderStageNodeCreateInfoAMDX 结构的指针,该结构标识要查询的节点的名称和索引。 -
pNodeIndex
是返回的已标识节点的内部节点索引。
一旦此函数返回,pNodeIndex
的内容将包含已标识节点的内部节点索引。
初始化暂存内存
在执行管线图时,实现可能需要暂存内存来管理调度队列或类似内容,这由应用程序显式管理。
要查询调度执行图所需的暂存空间,请调用
// Provided by VK_AMDX_shader_enqueue
VkResult vkGetExecutionGraphPipelineScratchSizeAMDX(
VkDevice device,
VkPipeline executionGraph,
VkExecutionGraphPipelineScratchSizeAMDX* pSizeInfo);
-
device
是创建executionGraph
的逻辑设备。 -
executionGraph
是要查询暂存空间的执行图管线。 -
pSizeInfo
是指向 VkExecutionGraphPipelineScratchSizeAMDX 结构的指针,该结构将包含所需的暂存大小。
此函数返回后,有关所需暂存空间的信息将在 pSizeInfo
中返回。
VkExecutionGraphPipelineScratchSizeAMDX
结构定义为
// Provided by VK_AMDX_shader_enqueue
typedef struct VkExecutionGraphPipelineScratchSizeAMDX {
VkStructureType sType;
void* pNext;
VkDeviceSize minSize;
VkDeviceSize maxSize;
VkDeviceSize sizeGranularity;
} VkExecutionGraphPipelineScratchSizeAMDX;
-
sType
是一个 VkStructureType 值,用于标识此结构。 -
pNext
是NULL
或指向扩展此结构的结构的指针。 -
minSize
表示调度查询的执行图所需的最小暂存空间。 -
maxSize
表示调度查询的执行图可使用的最大暂存空间。 -
sizeGranularity
表示暂存空间可以从minSize
增加的粒度。
应用程序可以使用大于 minSize
的任意大小的暂存内存来调度图,但是只会使用等于 minSize
+ sizeGranularity
的整数倍的值。更大的值可能会带来更高的性能,最大值为 maxSize
,表示实现可以有效使用的最大内存量。
要初始化特定执行图的暂存内存,请调用
// Provided by VK_AMDX_shader_enqueue
void vkCmdInitializeGraphScratchMemoryAMDX(
VkCommandBuffer commandBuffer,
VkPipeline executionGraph,
VkDeviceAddress scratch,
VkDeviceSize scratchSize);
-
commandBuffer
是将记录命令的命令缓冲区。 -
executionGraph
是要初始化暂存内存的执行图管线。 -
scratch
是要初始化的暂存内存的地址。 -
scratchSize
是要初始化的暂存内存的字节范围。
在调度绑定的执行图管线之前,必须先调用此命令以使用 scratch
。
执行此命令可能会修改范围 [scratch
, scratch
+ scratchSize
) 中的任何内存位置。对该内存范围的访问在具有 VK_ACCESS_2_SHADER_STORAGE_READ_BIT
和 VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT
访问标志的 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT
管线阶段中执行。
如果 scratch
的任何部分被除了 vkCmdDispatchGraphAMDX、vkCmdDispatchGraphIndirectAMDX、vkCmdDispatchGraphIndirectCountAMDX 或具有相同执行图的 vkCmdInitializeGraphScratchMemoryAMDX 之外的任何命令修改,则在再次对其进行调度之前,必须再次为执行图重新初始化。
调度图
执行图的初始调度从主机以与其他任何命令相同的方式完成,并且可以以类似于计算调度命令的方式使用,并提供间接变体。
要记录执行图调度,请调用
// Provided by VK_AMDX_shader_enqueue
void vkCmdDispatchGraphAMDX(
VkCommandBuffer commandBuffer,
VkDeviceAddress scratch,
VkDeviceSize scratchSize,
const VkDispatchGraphCountInfoAMDX* pCountInfo);
-
commandBuffer
是将记录命令的命令缓冲区。 -
scratch
是要使用的暂存内存的地址。 -
scratchSize
是要使用的暂存内存的字节范围。 -
pCountInfo
是指向 VkDispatchGraphCountInfoAMDX 结构的主机指针,该结构定义了将初始执行的节点。
执行此命令时,将执行 pCountInfo
中指定的节点。作为此命令一部分执行的节点在调度后不会以任何方式相互隐式同步。单独调度的图形节点之间没有光栅化顺序保证,尽管单个调度中的各个图元确实遵守光栅化顺序。在执行图之前或之后执行的绘制调用也相对于光栅化顺序的每个图形节点执行。
对于此命令,子结构中的所有设备/主机指针都被视为主机指针,并且在此命令的主机执行期间只读。此命令返回后,不会保留对原始指针的任何引用。
执行此命令可能会修改范围 [scratch
, scratch
+ scratchSize
) 中的任何内存位置。对该内存范围的访问在具有 VK_ACCESS_2_SHADER_STORAGE_READ_BIT
和 VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT
访问标志的 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT
管线阶段中执行。
此命令捕获网格节点的命令缓冲区状态,类似于绘制命令。
要记录使用在设备上读取的节点和负载参数的执行图调度,请调用
// Provided by VK_AMDX_shader_enqueue
void vkCmdDispatchGraphIndirectAMDX(
VkCommandBuffer commandBuffer,
VkDeviceAddress scratch,
VkDeviceSize scratchSize,
const VkDispatchGraphCountInfoAMDX* pCountInfo);
-
commandBuffer
是将记录命令的命令缓冲区。 -
scratch
是要使用的暂存内存的地址。 -
scratchSize
是要使用的暂存内存的字节范围。 -
pCountInfo
是指向 VkDispatchGraphCountInfoAMDX 结构的主机指针,该结构定义了将初始执行的节点。
执行此命令时,将执行 pCountInfo
中指定的节点。作为此命令一部分执行的节点在调度后不会以任何方式相互隐式同步。单独调度的图形节点之间没有光栅化顺序保证,尽管单个调度中的各个图元确实遵守光栅化顺序。在执行图之前或之后执行的绘制调用也相对于光栅化顺序的每个图形节点执行。
对于此命令,子结构中的所有设备/主机指针都视为设备指针,并在设备执行此命令期间读取。这些指针的分配和内容仅需要在设备执行期间有效。所有这些地址都将在 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT
管线阶段使用 VK_ACCESS_2_SHADER_STORAGE_READ_BIT
访问标志读取。
执行此命令可能会修改范围 [scratch
, scratch
+ scratchSize
) 中的任何内存位置。对该内存范围的访问在具有 VK_ACCESS_2_SHADER_STORAGE_READ_BIT
和 VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT
访问标志的 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT
管线阶段中执行。
此命令捕获网格节点的命令缓冲区状态,类似于绘制命令。
要记录所有参数都在设备上读取的执行图调度,请调用
// Provided by VK_AMDX_shader_enqueue
void vkCmdDispatchGraphIndirectCountAMDX(
VkCommandBuffer commandBuffer,
VkDeviceAddress scratch,
VkDeviceSize scratchSize,
VkDeviceAddress countInfo);
-
commandBuffer
是将记录命令的命令缓冲区。 -
scratch
是要使用的暂存内存的地址。 -
scratchSize
是要使用的暂存内存的字节范围。 -
countInfo
是 VkDispatchGraphCountInfoAMDX 结构的设备地址,用于定义将初始执行的节点。
当此命令执行时,会执行 countInfo
中指定的节点。一旦调度,作为此命令一部分执行的节点之间不会以任何方式进行隐式同步。
对于此命令,子结构中的所有指针都将被视为设备指针,并在设备执行此命令期间读取。这些指针的分配和内容仅需要在设备执行期间有效。所有这些地址都将在 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT
管线阶段以 VK_ACCESS_2_SHADER_STORAGE_READ_BIT
访问标志读取。
执行此命令可能会修改范围 [scratch
, scratch
+ scratchSize
) 中的任何内存位置。对该内存范围的访问在具有 VK_ACCESS_2_SHADER_STORAGE_READ_BIT
和 VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT
访问标志的 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT
管线阶段中执行。
VkDeviceOrHostAddressConstAMDX
联合定义为
// Provided by VK_AMDX_shader_enqueue
typedef union VkDeviceOrHostAddressConstAMDX {
VkDeviceAddress deviceAddress;
const void* hostAddress;
} VkDeviceOrHostAddressConstAMDX;
-
deviceAddress
是由 vkGetBufferDeviceAddressKHR 命令返回的缓冲区设备地址。 -
hostAddress
是一个常量主机内存地址。
VkDispatchGraphCountInfoAMDX
结构定义为
// Provided by VK_AMDX_shader_enqueue
typedef struct VkDispatchGraphCountInfoAMDX {
uint32_t count;
VkDeviceOrHostAddressConstAMDX infos;
uint64_t stride;
} VkDispatchGraphCountInfoAMDX;
-
count
是要执行的分派的数量。 -
infos
是 VkDispatchGraphInfoAMDX 结构的平面数组的设备或主机地址。 -
stride
是infos
中连续 VkDispatchGraphInfoAMDX 结构之间的字节步长。
infos
是作为设备指针还是主机指针使用,由使用此结构的命令定义。
VkDispatchGraphInfoAMDX
结构定义为
// Provided by VK_AMDX_shader_enqueue
typedef struct VkDispatchGraphInfoAMDX {
uint32_t nodeIndex;
uint32_t payloadCount;
VkDeviceOrHostAddressConstAMDX payloads;
uint64_t payloadStride;
} VkDispatchGraphInfoAMDX;
-
nodeIndex
是要分派的执行图中的节点索引。 -
payloadCount
是要为指定节点分派的有效负载的数量。 -
payloads
是指向大小等于payloadCount
和payloadStride
的乘积的有效负载平面数组的设备或主机地址指针。 -
payloadStride
是payloads
中连续有效负载之间的字节步长。
payloads
是作为设备指针还是主机指针使用,取决于使用该结构的命令。
着色器入队
执行图中的计算着色器可以使用 OpInitializeNodePayloadsAMDX
来初始化用于调度的节点。以这种方式初始化的任何节点有效负载,一旦着色器完成写入有效负载,就会被入队以进行调度。由于编译器在做出此决定时可能会比较保守,因此着色器可以进一步调用 OpFinalizeNodePayloadsAMDX
以保证不再写入有效负载。
有效负载上的 PayloadNodeNameAMDX
修饰的 Node
Name
操作数标识要入队的节点的着色器名称,而 OpInitializeNodePayloadsAMDX
的 Shader
Index
操作数标识着色器索引。以这种方式标识的节点将按照以下部分中的描述进行调度。
计算节点
添加到执行图中的计算着色器节点,根据是否存在 StaticNumWorkgroupsAMDX
或 CoalescingAMDX
执行模式,执行方式会有所不同。
调度一个未声明 StaticNumWorkgroupsAMDX
或 CoalescingAMDX
执行模式的计算着色器节点,将在每个维度中执行由有效负载的前 12 个字节指定的数量的工作组,这些字节被解释为 VkDispatchIndirectCommand。相同的有效负载将被广播到相同调度中的每个工作组。有效负载中的其他值对执行没有影响。
调度一个具有 StaticNumWorkgroupsAMDX
执行模式的计算着色器节点,将根据 StaticNumWorkgroupsAMDX
执行模式的 x
、y
和 z
size
操作数在每个维度中执行工作组。相同的有效负载将被广播到相同调度中的每个工作组。有效负载中的任何值对执行没有影响。
调度一个具有 CoalescingAMDX
执行模式的计算着色器节点,将入队一个单独的调用以进行执行。实现可以将多个此类调度合并到同一个工作组中,直到达到工作组的大小。可以通过 CoalescedInputCountAMDX
内置变量查询以此方式合并到给定工作组中的调用次数。有效负载中的任何值对执行没有影响。
网格节点
添加到执行图中的图形管线以类似于 vkCmdDrawMeshTasksIndirectEXT 的方式执行,使用与计算着色器相同的有效负载,但会从命令缓冲区捕获一些状态。
当执行图调度记录到命令缓冲区中时,它会捕获以下动态状态,以用于绘制节点
-
VK_DYNAMIC_STATE_VIEWPORT
-
VK_DYNAMIC_STATE_SCISSOR
-
VK_DYNAMIC_STATE_LINE_WIDTH
-
VK_DYNAMIC_STATE_DEPTH_BIAS
-
VK_DYNAMIC_STATE_BLEND_CONSTANTS
-
VK_DYNAMIC_STATE_DEPTH_BOUNDS
-
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT
-
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT
-
VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR
其他状态不会被捕获,并且当在执行图管线中用作库时,必须不能使用其他动态状态创建图形管线。