在 vulkan 中同步顶点缓冲区?
Synchronizing vertex buffer in vulkan?
我有一个存储在设备内存和缓冲区中的顶点缓冲区,并且主机可见且主机一致。
为了写入主机端的顶点缓冲区,我将其映射,memcpy 到它并取消映射设备内存。
为了从中读取,我在记录渲染通道期间将顶点缓冲区绑定到命令缓冲区中。这些命令缓冲区在获取、提交和呈现的循环中提交,以绘制每一帧。
目前我在程序启动时向顶点缓冲区写入一次。
然后顶点缓冲区在循环期间保持不变。
我想从主机端修改每帧之间的顶点缓冲区。
我不清楚的是 best/right 将这些主机端写入与设备端读取同步的方法。目前我有一个栅栏和一对信号灯,用于允许在飞行中模拟的每个帧。
每帧:
我守望先锋
我重置了围栏
获取信号量#1。
队列提交等待信号量 #1 并向信号量 #2 发出信号并向围栏发出信号。
当前等待信号量#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 同步。
我有一个存储在设备内存和缓冲区中的顶点缓冲区,并且主机可见且主机一致。
为了写入主机端的顶点缓冲区,我将其映射,memcpy 到它并取消映射设备内存。
为了从中读取,我在记录渲染通道期间将顶点缓冲区绑定到命令缓冲区中。这些命令缓冲区在获取、提交和呈现的循环中提交,以绘制每一帧。
目前我在程序启动时向顶点缓冲区写入一次。
然后顶点缓冲区在循环期间保持不变。
我想从主机端修改每帧之间的顶点缓冲区。
我不清楚的是 best/right 将这些主机端写入与设备端读取同步的方法。目前我有一个栅栏和一对信号灯,用于允许在飞行中模拟的每个帧。
每帧:
我守望先锋
我重置了围栏
获取信号量#1。
队列提交等待信号量 #1 并向信号量 #2 发出信号并向围栏发出信号。
当前等待信号量#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 同步。