文章目录
两个level的同步:within a single queue和across multiple queues
单个device queue内的同步
vulkan允许我们发送cmdbuffer到queue中,去处理graphics 操作。这个进程被设计成线程友好的,这样我们就可以使用来自任何CPU thread 的cmd buffer提交工作,并最终将它们insert到同一个GPU queue中。这使我们能够在Vulkan运行其命令(通常也是并行的)时执行自己的多线程,计算vetices或加载纹理以最大限度地利用所有CPU核。
请注意,即使在同一queue中,我们的命令也可能依赖于其他命令的完成,它们不需要在同一个cmdbuffer中。命令保证按插入的确切顺序start,但由于它们并行运行,因此不能保证命令按相同的顺序完成。
确保这些cmd正确等待的in-queue工具有:pipeline barriers,events,subpass dependencies;
Barriers
管道障碍指定要等待的数据或渲染管道的哪个阶段,以及要阻止哪个阶段,直到上一个命令中的其他指定阶段完成。
请记住,这些barriers只用于GPU,所以我们无法从在cpu上运行的程序来check when a barrier has been executed;如果我们需要向CPU上的应用程序发回信号,那么可以使用另一个称为fence或event的工具(后面介绍)
两种barriers:
- 执行barriers(execution)
- 内存barriers(memory)
我们可以在a single call中创建一个execution barrier,或者一个execution barrier和一个或多个类型的memory barrier;
execution barriers
当我们想要控制命令流并使用pipeline barriers来强制执行命令(execution)的顺序时,我们可以在Vulkan操作命令之间插入一个barrier,并指定prerequisite 管道阶段,在此期间,前面的cmd需要在继续之前完成。我们还可以指定在此barrier之后应保持的管道阶段。
这些选项是使用vkCmdPipelineBarrier的srcStageMask(barrier的等待阶段)和dstStageMask(之后阶段)参数设置的。因为它们是位标志bit flags,所以我们可以在这些mask中指定多个阶段。srcStageMask在允许dstStageMask中给定的阶段在后续命令中执行之前,标记要在前面的命令中等待的阶段。
对于execution barriers,srcStageMask被扩展为包含逻辑上earlier的stages。类似的,dstStageMask被扩展以包括逻辑上later的stages。在dstStageMask(以及之后)的阶段将等待srcStageMask(以及之前)的阶段完成后,才开始。这足以保护srcstagemak中的stage读取访问和dststagemak中的stage写入访问。
为了避免a common pitfall(陷阱),请注意,stage mask扩展不适用于下面定义的memory barrier。
Memory Barriers
为了提高引擎盖(hood)下的性能,Vulkan在CPU和GPU内核上的快速一级/二级缓存内存和相对较慢的主RAM内存之间使用了一系列缓存机制。
当一个内核写入内存(例如,写入渲染目标RT)时,更新仍然只能存在于缓存中,并且对于准备使用它的另一个核心不可用或不可见。
(之后学到了进行补充)
execution和memory dependency
我们可能需要设置pipeline barrier的一种情况是,在compute shader中写入纹理图像buffer,然后在片段着色器中使用它。
(之后补充)
Events
subpass denpendencies
在设备队列中进行同步的另一种方法是通过subpass依赖关系。这和pipeline barriers很像,但专门用于表示呈现子过程之间的依赖关系,以及renderpass实例内的cmd与实例外的cmd之间的依赖关系(在之前或之后)。
这些可能be a bit tricky(有些棘手),因为它们有很多限制。但是,如果我们在渲染过程中处理数据,比如渲染阴影或反射,或者需要等待外部资源或事件,它们会come in handy(派上用场)。
使用subpass denpendencies,需要注意:
- 对于使用srcAccessMask 和srcAccessMask 指定的附件,它们只包含一个memory barrier。
- subpass可以使用管道阶段标志srcStageMask和dstStageMask等待完成。
- 它们只能向前推进,subpass可以在早期阶段或同一阶段wait,但不能依赖于同一渲染过程中的后期阶段。
多个device queue间的同步
现在我们已经介绍了一些在单个设备队列中设置依赖项的机制,让我们看看如何在不同的队列之间协调同步。
使用:信号量和fence
请注意,vulkan1.2引入了timeline信号量,这是未来信号量的新的首选方法。它们还没有在移动设备上广泛使用,所以我们将首先讨论原始的方法,然后看看新的时间轴信号量如何能够取代这两个原始选项。
信号量
理解和使用信号量的关键在于认识到它们只用于在GPU任务之间跨多个queue进行同步(graphics和present),而不用于在GPU和CPU任务之间进行同步。
如果多个命令正忙于跨核心和线程处理它们的任务,那么一个信号量就像一组命令已经完成的公告。信号量只有在批处理batch中的所有命令完成后才发出信号。
它们保证了implict memory guarantees,所以我们可以访问信号量后面的任何内存,而不需要考虑在它们之间添加memory barrier。
Fences
fence是为GPU到CPU的同步而设计的。
fence可以附加到queue submission,并允许应用程序使用vkGetFenceStatus检查fence状态,或使用vkWaitForFences等待队列完成。
Fences make the same implicit memory guarantee as semaphores, and if we want to present the next frame in a swap buffer, we can use fences to know when to swap and start the render of the next frame.
原文链接:
https://www.khronos.org/blog/understanding-vulkan-synchronization