【Vulkan学习记录-基础篇-4】Vulkan中的同步机制

提醒:本文不含任何图,读起来可能有些枯燥,如果对Vulkan中的同步还没有任何了解的,建议先参考后面给出的链接 [1][2][3]

在Vulkan中,对资源读写所需要做的同步是应用程序的职责,Vulkan本身只提供了很少的隐式同步机制,其余的都需要在程序中显式地使用Vulkan中的同步机制来实现。

提交顺序

提交顺序是Vulkan中的一个非常基本的概念,它本身并不具有任何同步的意义,但是不管是Vulkan提供的隐式同步,还是用户要自己实现的显式同步,都要以这个精确的概念为前提。
在Vulkan中,用户需要将命令写入CommandBuffer中,然后把一个或多个CommandBuffer写入到一个或多个VkSubmitInfo中,再把一个或多个VkSubmitInfo传给vkQueueSubmit,让Queue开始执行传入的命令,由此,从高往低,提交顺序为:
1.在CPU上通过多次vkQueueSubmit提交了一系列命令,这些命令的提交顺序为调用vkQueueSubmit从前往后的顺序,即先通过vkQueueSubmit提交的命令一定在后通过vkQueueSubmit提交的命令之前。

2.在同一次vkQueueSubmit中,传入了一个或多个VkSubmitInfo,这些VkSubmitInfo中的命令,按照VkSubmitInfo的下标顺序排列,即在pSubmits所指向的VkSubmitInfo数组中,下标靠前的VkSubmitInfo中所记录的所有命令都在下标靠后的VkSubmitInfo中所记录的所有命令之前。

3.在同一个VkSubmitInfo中,填入了一个或多个CommandBuffer,这些CommandBuffer中的命令的提交顺序为按照这些CommandBuffer的下标顺序,类似2中的顺序。

4.在同一个CommandBuffer中,所记录的命令分为两种:
一是不在RenderPass中的命令,即除去所有在vkCmdBeginRenderPass和vkCmdEndRenderPass之间的命令,这些命令的提交顺序为按照在CPU上写入CommandBuffer时的顺序。
二是在RenderPass中的命令,在RenderPass中的命令,只定义在同一SubPass中的其他命令的提交顺序,这些命令的提交顺序也是按照在CPU上写入CommandBuffer时的顺序。注意,如果几个命令在vkCmdBeginRenderPass和vkCmdEndRenderPass之间,但是它们不在同一SubPass中,那么它们之间是不存在任何提交顺序的。

Vulkan提供的隐式同步

有了提交顺序的概念,就可以定义一些隐式的同步机制,即不需要用户自己去实现,一定会默认遵循的同步。
Spec中提到的隐式同步有:

1.所有的Action类命令(Draw、Transfer、Clear、Copy等)以及显示地使用同步机制的命令(这个在之后会介绍),这些命令在执行VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT时,会遵循提交顺序。即这些命令开始执行的顺序,是严格遵循提交顺序的。(但这并不意味着这些命令结束执行时的顺序会有什么约束,所有的这些命令,到底是哪个先结束,隐式同步并没有严格的规定,也就是说任何一个命令都有可能最先结束。)

2.所有的设置状态类的命令(bind pipelines, descriptor sets, and buffers等),由于它们不需要在GPU上执行,它们只负责设置CPU上相应CommandBuffer的状态,所以它们的执行顺序,遵循它们在CPU上写入CommandBuffer时的顺序。

3.所有的Draw类命令在处理Primitive时,首先遵循提交顺序,即先提交的Draw中的Primitive会先被处理。而在一个Draw内所提交的Primitive,会按照顶点和索引的下标顺序执行。

4.ImageLayout的转移,是通过ImageMemoryBarrier实现的(也是一种显式的同步原语),它们遵循提交顺序,即先提交的先转移。

以上差不多就是Vulkan中所提供的隐式同步了,还有一些细节可以参考Spec6.2中相关内容。
其他的所有需要用到同步的情景(比如写后写问题,写后读问题,读后写问题),都需要手动地通过显式的同步机制来实现。

Vulkan同步的基本概念

在使用同步类命令时,往往会填一些让人不太容易理解的参数,比如看pipeline barrier以及image memory barrier 的参数:

void vkCmdPipelineBarrier(
    VkCommandBuffer                             commandBuffer,
    VkPipelineStageFlags                        srcStageMask,
    VkPipelineStageFlags                        dstStageMask,
    VkDependencyFlags                           dependencyFlags,
    uint32_t                                    memoryBarrierCount,
    const VkMemoryBarrier*                      pMemoryBarriers,
    uint32_t                                    bufferMemoryBarrierCount,
    const VkBufferMemoryBarrier*                pBufferMemoryBarriers,
    uint32_t                                    imageMemoryBarrierCount,
    const VkImageMemoryBarrier*                 pImageMemoryBarriers);
typedef struct VkImageMemoryBarrier {
    VkStructureType            sType;
    const void*                pNext;
    VkAccessFlags              srcAccessMask;
    VkAccessFlags              dstAccessMask;
    VkImageLayout              oldLayout;
    VkImageLayout              newLayout;
    uint32_t                   srcQueueFamilyIndex;
    uint32_t                   dstQueueFamilyIndex;
    VkImage                    image;
    VkImageSubresourceRange    subresourceRange;
} VkImageMemoryBarrier;

可以看到这里面有很多莫名其妙的参数,比如VkPipelineStageFlags、VkAccessFlags、VkImageLayout等,初看时,可能会认为这些东西和同步根本没有任何关系,所谓同步,是想让一个命令的某个过程,一定要等待另一个命令的某个过程完成后才能开始执行,所以这些参数有什么意义呢?

VkPipelineStageFlags
Vulkan中并不提供任何命令级别的同步,即明确地指定某两个命令之间需要满足什么同步。所有需要同步的命令,都会将其执行过程划分为若干个阶段,所有的命令都会在流水线上执行,只是不同类型的命令,它们的阶段划分是不同的。当我们在Vulkan中使用同步机制时,都是以流水线阶段为单位,即某个流水线阶段上执行的所有命令,会在当前阶段暂停,等待另一个流水线阶段上的所有命令在相应的阶段执行完全后,再开始执行。VkPipelineStageFlags就代表流水线阶段,在Spec 6.1.2节中给出了它的所有可能取值,以及对于不同类型命令的流水线划分规则。

VkAccessFlags
Vulkan中的同步不仅控制操作执行的顺序,还要控制缓存的写回,即内存数据的同步。什么意思呢?不管是CPU还是GPU,存储系统都是按级划分的,比如主存、L2 Cache、L1 Cache等等ÿ

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值