VK_KHR_maintenance8

本提案详细说明并解决了 VK_KHR_maintenance8 扩展解决的问题。

1. 问题陈述

随着时间的推移,需要创建一个维护扩展来处理一组小的特性,这些特性本身都不足以构成一个完整的扩展。

以下是本提案中考虑的问题列表

  • 允许在深度/模板附件和“匹配”颜色附件之间进行复制

  • 允许在 vkMergePipelineCaches 中隐式同步 dstCache

  • 要求在进行队列族所有权转移时,src/dst 同步范围能够工作

  • 支持纹理采样和获取操作中的 Offset(作为 ConstOffset 的替代方案)图像操作数

  • 使用 OpSRem 和 OpSMod 的 SPIR-V 定义,使这些操作为负操作数产生明确定义的结果

  • 在从 3D 图像闪存到其他图像类型时,放宽图层限制

  • 为 VkMemoryBarrier2、VkBufferMemoryBarrier2 和 VkImageMemoryBarrier2 添加额外的 64 个访问标志的空间

2. 提案

此扩展引入的项目是

2.1. 支持纹理采样和获取操作中的 Offset(作为 ConstOffset 的替代方案)图像操作数

ConstOffset 仅允许常量偏移。在 maintenance8 之前,仅对 gather 操作支持 Offset。这是 D3D12 分层的问题。

2.2. 允许在深度/模板附件和“匹配”颜色附件之间进行复制

D3D12 允许例如 R32F 和 D32F 之间的传输复制。提供此功能可以改进分层 D3D12 实现。

2.3. vkMergePipelineCaches 中的显式同步

vkMergePipelineCaches 函数要求其 dstCache 参数在外部同步。目前,可能同时从不同线程调用 vkMergePipelineCaches 的应用程序必须在管道缓存周围实现锁定,即使该管道缓存是在没有 VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT 的情况下创建的,也要对 vkCreate*Pipelines 进行锁定。这可能对性能不利,特别是当 vkMergePipelineCaches 很少发生时。

在创建管道缓存时可以使用新的 VK_PIPELINE_CACHE_CREATE_INTERNALLY_SYNCHRONIZED_MERGE_BIT_KHR 标志来指示当用作 vkMergePipelineCachesdstCache 参数时,管道创建函数不需要外部同步。此标志与 VK_PIPELINE_CACHE_CREATE_EXTERNALLY_SYNCHRONIZED_BIT 互斥。

2.4. 队列族所有权转移的有意义的阶段

此扩展添加了一个新的依赖标志,指示在执行队列族所有权转移时,两个阶段掩码现在都有意义

    VK_DEPENDENCY_QUEUE_FAMILY_OWNERSHIP_TRANSFER_USE_ALL_STAGES_BIT_KHR = 0xTBD,

VkDependencyFlags 中包含 VK_DEPENDENCY_QUEUE_FAMILY_OWNERSHIP_TRANSFER_USE_ALL_STAGES_BIT_KHR 时,指定队列族所有权转移的缓冲区和图像内存屏障将使用两个同步范围。

如果没有这个新标志,每个队列上的操作只能通过使用 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT 来同步,这可能导致许多实现上的设备完全停顿。指定此标志后,可以使用阶段标志来同步这些操作,就像其他任何同步操作一样。

保持目标阶段与源阶段相同通常可以最大限度地减少过度同步,因此建议这样做,但它们没有必要匹配。类似地,两个队列中的阶段掩码也没有必要匹配。

3. 示例

以下代码执行颜色附件的队列族所有权转移,从图形队列转移到计算队列作为存储图像

图形队列释放操作
VkImageMemoryBarrier2 imageMemoryBarrier = {
    .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
    .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
    .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
    .srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
    .oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
    .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    .srcQueueFamilyIndex = graphicsQueueFamilyIndex,
    .dstQueueFamilyIndex = computeQueueFamilyIndex,
    .image = ...,
    .subresourceRange = ...};

VkDependencyInfo dependencyInfo = {
    .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
    .dependencyFlags = VK_DEPENDENCY_QUEUE_FAMILY_OWNERSHIP_TRANSFER_USE_ALL_STAGES_BIT_KHR,
    .imageMemoryBarrierCount = 1,
    .imageMemoryBarrier = &imageMemoryBarrier,
}

vkCmdPipelineBarrier2(graphicsCommandBuffer, &dependencyInfo);
图形队列提交信息
VkSemaphoreSubmitInfo semaphoreSignalInfo = {
    .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
    .semaphore = transferSemaphore,
    .stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT }; // Do not signal the semaphore until color attachment output completes

VkCommandBufferSubmitInfo graphicsCommandBufferInfo = {
    .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
    .commandBuffer = graphicsCommandBuffer };

VkSubmitInfo2 submitInfo = {
    .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
    .commandBufferInfoCount = 1,
    .pCommandBufferInfos = &graphicsCommandBufferInfo,
    .signalSemaphoreInfoCount = 1,
    .pSignalSemaphoreInfos = &semaphoreSignalInfo,

vkQueueSubmit2(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
计算队列获取操作
VkImageMemoryBarrier2 imageMemoryBarrier = {
    .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
    .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
    .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
    .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
    .oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
    .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
    .srcQueueFamilyIndex = graphicsQueueFamilyIndex,
    .dstQueueFamilyIndex = computeQueueFamilyIndex,
    .image = ...,
    .subresourceRange = ...};

VkDependencyInfo dependencyInfo = {
    .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
    .dependencyFlags = VK_DEPENDENCY_QUEUE_FAMILY_OWNERSHIP_TRANSFER_USE_ALL_STAGES_BIT_KHR,
    .imageMemoryBarrierCount = 1,
    .imageMemoryBarrier = &imageMemoryBarrier,
}

vkCmdPipelineBarrier2(computeCommandBuffer, &dependencyInfo);
计算队列提交信息
VkSemaphoreSubmitInfo semaphoreWaitInfo = {
    .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
    .semaphore = transferSemaphore,
    .stageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT };  // Do not execute compute shader stages in the destination until semaphore is signaled.

VkCommandBufferSubmitInfo computeCommandBufferInfo = {
    .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
    .commandBuffer = computeCommandBuffer };

VkSubmitInfo2 submitInfo = {
    .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
    .waitSemaphoreInfoCount = 1,
    .pWaitSemaphoreInfos = &semaphoreWaitInfo,
    .commandBufferInfoCount = 1,
    .pCommandBufferInfos = &computeCommandBufferInfo,

vkQueueSubmit2(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);

4. 问题

4.1. 新的 VK_DEPENDENCY_QUEUE_FAMILY_OWNERSHIP_TRANSFER_USE_ALL_STAGES_BIT_KHR 标志是否与原始的同步命令一起工作?

它将专门与 vkCmdPipelineBarrier 一起工作,因为它具有依赖标志参数。当与此命令一起使用时,同步范围的用法与指定 QFOT 时以相同的方式进行同步。

4.2. 当进行队列族所有权转移时,源队列和目标队列之间指定的阶段是否需要匹配?

无论是是否存在新的 VK_DEPENDENCY_QUEUE_FAMILY_OWNERSHIP_TRANSFER_USE_ALL_STAGES_BIT_KHR 标志,都没有要求获取和释放之间的任何阶段掩码需要匹配。