Graphics 使用围栏清理命令缓冲区,同时同步交换链映像
假设我有一个由Graphics 使用围栏清理命令缓冲区,同时同步交换链映像,graphics,vulkan,Graphics,Vulkan,假设我有一个由n图像组成的交换链,我允许k“飞行中的帧”。我通过一组信号量ImageAvailablesMaphore和renderFinishedSemaphore以及围栏imageInFlight确保vkAcquireNextImageKHR和vkQueueSubmit和vkQueuePresentKHR之间的正确同步,就像在以下地方做的那样: 需要设置围栏,以确保在GPU使用完相应的映像之前不再使用信号量。因此,需要在vkQueueSubmit中指定此围栏 另一方面,我正在创建独立于“飞行
n
图像组成的交换链,我允许k
“飞行中的帧”。我通过一组信号量ImageAvailablesMaphore
和renderFinishedSemaphore
以及围栏imageInFlight
确保vkAcquireNextImageKHR
和vkQueueSubmit
和vkQueuePresentKHR
之间的正确同步,就像在以下地方做的那样:
需要设置围栏,以确保在GPU使用完相应的映像之前不再使用信号量。因此,需要在vkQueueSubmit
中指定此围栏
另一方面,我正在创建独立于“飞行中的帧”的命令缓冲区。它们是“一次性提交”命令缓冲区。因此,一旦提交,我会将它们添加到“待删除”列表中。我需要知道GPU何时完成此列表中命令缓冲区的执行
但我无法在vkQueueSubmit
中指定另一个围栏。我怎样才能解决这个问题
我允许k
“飞行中的帧”
这就是你的答案。要为“帧”提供命令缓冲区的每个线程都应该有若干倍的k
命令缓冲区。他们应该以环形缓冲方式使用它们。这些命令缓冲区应该从临时分配池中创建。当他们从环形缓冲区中选取最近使用最少的CB时,他们应该在记录到它之前重置它
在过去的k
th帧(使用围栏)完成之前,不启动下一帧的任何工作,以确保没有线程尝试重置仍在使用的CB
如果出于某种原因,你根本无法告诉你的线程什么是
k
,你仍然需要告诉他们一些事情。当你开始处理线程时,你需要告诉他们还有多少帧在战斗中。这使他们能够对照这个帧数检查环形缓冲区的大小。如果环缓冲区中的元素数小于帧数,则环缓冲区中最旧的CB不在使用中。否则,它将不得不从池中分配一个新的CB,并将其推入环形缓冲区。您可以为此使用时间轴信号量。您可以在此处深入阅读有关它们的信息:
时间轴信号量携带一个特定的值,而不是有信号或无信号。您感兴趣的函数是vkGetSemaphoreCounterValue,它允许您在不阻塞的情况下读取信号量的值
要创建时间轴信号量,只需将VkSemaphoreCreateInfo的pNext值设置为VkSemaphoreTypeCreateInfo,如下所示
VkSemaphoreTypeCreateInfo
timelineCreateInfo{VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO};
timelineCreateInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
timelineCreateInfo.initialValue = 0;
VkSubmitInfo的pNext值需要设置为VkTimelineSemaphoreSubmitInfo
VkTimelineSemaphoreSubmitInfo timelineInfo{VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO};
timelineInfo.signalSemaphoreValueCount = 1;
timelineInfo.pSignalSemaphoreValues = &signalValue;
在命令缓冲区完成后,信号量的值将是您将signalValue设置为的任何值。之后,您可以使用以下命令查询值:
uint64_t value;
vkGetSemaphoreCounterValue(device, semaphore, &value);
所以假设您将signalValue设置为1,这里的值将是1或0,这是我们在VkSemaphoreTypeCreateInfo中作为初始值给出的信号量。之后,您可以安全地删除一次性命令缓冲区
注意:时间轴信号实际上是围栏和二进制信号的半替换,应该是您使用的主要同步原语。我认为唯一需要二进制信号量的函数是vkAcquireSwapchainImage。(a)那么,在单个线程中,对于每个帧
I=0,…,k-1,a[I]
我们有a[I]
命令缓冲区cb[I][0],…,cb I][a[I]-1]
,都是从公共瞬态分配池分配的?或者您是否建议为每个i
使用专用池?(b) 您建议在每个命令缓冲区使用一次后重置整个池?(c)在任何情况下,这似乎表明每个命令缓冲区恰好对应一个交换链。但是如果我有一个命令缓冲区,它使用来自多个交换链的图像,或者它不仅渲染成交换链图像,而且渲染成纹理,会发生什么呢?我真的很想知道你的反应;特别是对于(c)。@0xbadf00d:(a)和(b)太笼统,无法提供答案,因为它们完全取决于您在每个线程中正在做什么以及您计划如何做。(c)有点离题。如果将“帧”定义为渲染到单个swapchain图像,则这就是您需要处理的内容。如果将“帧”定义为渲染到两个交换链图像,则这就是您需要处理的内容。关键是您需要正确地传达允许新框架使用的数据。谢谢您的回答。过去我已经看过时间轴信号量,但有两个问题阻止我使用它们:(a)它们只是一个设备扩展,因此可能不受GPU支持。(b) 正如在博客文章中提到的,“Vulkan的窗口系统集成API还不支持时间轴信号量”。因此,我不知道如何使用它们来同步vkAcquireSwapchainImage
和vkQueueSubmit
.a)它们是Vulkan 1.2和任何支持它们的GPU的核心功能。b) 您可以将它们与二进制信号量组合。您所要做的就是将用于vkAcquireSwapchainImage的信号量添加到waitsemaphores列表中,并在timeline sbmit信息中添加伪值。感谢您的评论。我还不清楚的是:时间轴信号如何帮助我解决问题?我试图实现的哪一方面是普通信号灯/围栏不可能实现的?我真的对你的回答感兴趣。
uint64_t value;
vkGetSemaphoreCounterValue(device, semaphore, &value);