在 vulkan 中同步顶点缓冲区?

Synchronizing vertex buffer in vulkan?

我有一个存储在设备内存和缓冲区中的顶点缓冲区,并且主机可见且主机一致。

为了写入主机端的顶点缓冲区,我将其映射,memcpy 到它并取消映射设备内存。

为了从中读取,我在记录渲染通道期间将顶点缓冲区绑定到命令缓冲区中。这些命令缓冲区在获取、提交和呈现的循环中提交,以绘制每一帧。

目前我在程序启动时向顶点缓冲区写入一次。

然后顶点缓冲区在循环期间保持不变。

我想从主机端修改每帧之间的顶点缓冲区。

我不清楚的是 best/right 将这些主机端写入与设备端读取同步的方法。目前我有一个栅栏和一对信号灯,用于允许在飞行中模拟的每个帧。

每帧:

  1. 我守望先锋

  2. 我重置了围栏

  3. 获取信号量#1。

  4. 队列提交等待信号量 #1 并向信号量 #2 发出信号并向围栏发出信号。

  5. 当前等待信号量#2

这里放置主机端的正确位置在哪里 map/memcpy/unmap 我应该如何将其与设备读取正确同步?

如果您想利用异步 GPU 执行,您希望 CPU 避免 GPU 操作必须停止。因此,永远不要等待 刚刚发布 的批次。同样的事情也适用于内存:你永远不应该希望写入你刚刚提交的 GPU 操作正在读取的内存。

你至少应该双缓冲东西。如果你每帧都改变顶点数据,你应该分配足够的内存来保存该数据的两个副本。不需要进行多次分配,甚至不需要进行多次 VkBuffer(只需使分配和缓冲区更大,然后 select 在绑定时使用哪个存储区域)。当 GPU 命令读取一个存储区域时,您向另一个存储区域写入。

您提交的每个批次都从特定的内存中读取。因此,当 GPU 从该内存中完成读取时,将设置该批次的围栏。因此,如果您想从 CPU 写入内存,则在设置代表该内存读取的 GPU 读取操作的栅栏之前,您无法开始该过程。

但是因为你是这样的双缓冲,所以你要写入的内存的围栏是而不是你上一帧提交的批次的围栏。这是您在 之前 提交框架的批次。由于 GPU 收到该操作已经有一段时间了,因此 CPU 实际等待的可能性要小得多。也就是说,围栏应该已经设置好了。

现在,你不应该在那个栅栏上做文字 vkWaitForFences。您应该检查它是否已设置,如果未设置,请花时间去做其他有用的事情。但是,如果您没有其他有用的事情可以做,那么等待可能就可以了(而不是坐下来进行测试)。

一旦设置了围栏,就知道可以自由写入内存了。


How do I know that the memory I have written to with the memcpy has finished being sent to the device before it is read by the render pass?

你知道,因为记忆是连贯的。这就是 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 在此上下文中的意思 :主机对设备内存的更改对 GPU 可见,而无需显式可见性操作,反之亦然。

嗯……差不多了。

如果你想避免必须使用任何同步,你必须调用 vkQueueSubmit 读取批处理 在你完成 上的内存修改后 [=51] =].如果他们以错误的顺序被调用,那么您将需要一个内存屏障。例如,您可以让批处理的某些部分等待主机设置的事件(通过 vkSetEvent),这会在您完成写入时通知 GPU。因此,您可以在执行内存写入之前提交该批次。但在这种情况下,vkCmdWaitEvents 调用应该包括 HOST 的源阶段掩码(因为那是设置事件的人),并且它应该有一个内存屏障,其源访问标志也包括 HOST_WRITE(因为那是写入内存的人)。

但在大多数情况下,在提交批处理之前只写入内存更容易。这样,您就无需使用 host/event 同步。