我是否需要在下一次传输时将所有权*返回*到传输队列?
Do I need to transfer ownership *back* to the transfer queue on next transfer?
我打算使用 vulkan synchronization examples 之一作为参考,了解如何处理不经常更新的统一缓冲区。具体来说,我正在看这个:
vkBeginCommandBuffer(...);
// Submission guarantees the host write being complete, as per
// https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-submission-host-writes
// So no need for a barrier before the transfer
// Copy the staging buffer contents to the vertex buffer
VkBufferCopy vertexCopyRegion = {
.srcOffset = stagingMemoryOffset,
.dstOffset = vertexMemoryOffset,
.size = vertexDataSize};
vkCmdCopyBuffer(
commandBuffer,
stagingBuffer,
vertexBuffer,
1,
&vertexCopyRegion);
// If the graphics queue and transfer queue are the same queue
if (isUnifiedGraphicsAndTransferQueue)
{
// If there is a semaphore signal + wait between this being submitted and
// the vertex buffer being used, then skip this pipeline barrier.
// Pipeline barrier before using the vertex data
// Note that this can apply to all buffers uploaded in the same way, so
// ideally batch all copies before this.
VkMemoryBarrier memoryBarrier = {
...
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TRANSFER_BIT , // srcStageMask
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, // dstStageMask
1, // memoryBarrierCount
&memoryBarrier, // pMemoryBarriers
...);
vkEndCommandBuffer(...);
vkQueueSubmit(unifiedQueue, ...);
}
else
{
// Pipeline barrier to start a queue ownership transfer after the copy
VkBufferMemoryBarrier bufferMemoryBarrier = {
...
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = 0,
.srcQueueFamilyIndex = transferQueueFamilyIndex,
.dstQueueFamilyIndex = graphicsQueueFamilyIndex,
.buffer = vertexBuffer,
...};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TRANSFER_BIT , // srcStageMask
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
1, // bufferMemoryBarrierCount
&bufferMemoryBarrier, // pBufferMemoryBarriers
...);
vkEndCommandBuffer(...);
// Ensure a semaphore is signalled here which will be waited on by the graphics queue.
vkQueueSubmit(transferQueue, ...);
// Record a command buffer for the graphics queue.
vkBeginCommandBuffer(...);
// Pipeline barrier before using the vertex buffer, after finalising the ownership transfer
VkBufferMemoryBarrier bufferMemoryBarrier = {
...
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,
.srcQueueFamilyIndex = transferQueueFamilyIndex,
.dstQueueFamilyIndex = graphicsQueueFamilyIndex,
.buffer = vertexBuffer,
...};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, // dstStageMask
...
1, // bufferMemoryBarrierCount
&bufferMemoryBarrier, // pBufferMemoryBarriers
...);
vkEndCommandBuffer(...);
vkQueueSubmit(graphicsQueue, ...);
}
在这个例子中,我将其简化为:
map updated buffer which is host coherent
perform transfer in transfer queue to device local memory
make sure to put a buffer memory barrier to handle the queue ownership transfer
perform normal draw commands
make sure to put a buffer memory barrier to handle receiving of buffer in queue ownership
然后我必须恢复传输队列再次复制该数据的能力吗? None 的例子似乎提到了它,但我可能错过了。我真的看不出添加另一个缓冲区屏障如何对同一个绘图命令缓冲区起作用,因为它会在下一次提交时停止,即使我没有任何东西要传输,所以我只需要提交另一个命令缓冲区只是为了在我提交下一个转移操作之前排队所有权转移?
即
//begin with transfer ownership
submit(copy)
submit(ownership to graphics)
submit(draw)
submit(ownership to transfer)
submit(copy)
submit(ownership to graphics)
submit(draw)
submit(draw)
submit(draw)
submit(ownership to transfer)
submit(copy)
submit(ownership to graphics)
submit(draw)
如果是这样,我不确定如何处理绘制和传输以及复制和绘制之间的信号量信号。一开始很容易,但后来由于多个飞行帧而变得奇怪,因为绘制提交之间没有依赖关系。基本上我想我需要设置我提交的最近的绘图命令有一个信号量来表示所有权的转移,这将表示复制,这将表示图形的所有权,如果它在一个单独的线程上然后我会检查此副本是否已提交,并要求等待图形传输的所有权并重置已提交的副本检查。但是我不确定没有这种依赖性的下一帧会发生什么,并且可以在按时间顺序排列的前一帧之前完成?
只要您不介意数据变得未定义,您就可以在任何队列系列(无需传输)上使用资源。你仍然需要一个信号量来确保没有内存危险。
旧规格:
NOTE
If an application does not need the contents of a resource to remain valid when transferring from one queue family to another, then the ownership transfer should be skipped.
例子就不提了,因为只是例子
至于同步(这是与 QFOT 不同的问题),作为 vkQueueSubmit
一部分的信号量信号涵盖了之前在 提交顺序 中的所有内容。因此,当您提交副本时,您将让它在信号量上等待最后一次提交的绘制已发出信号。这意味着在复制可以在另一个队列上开始之前,绘制和该队列上的任何先前绘制都已完成。
然后你通过副本发出信号量,并在你提交的下一次抽奖时等待它。这意味着副本完成写入,绘制(和任何后续绘制)在图形队列中读取它之前。
例如:
submit(copy, release ownership from tranfer, semaphore signal)
submit(semaphore wait, acquire ownership to graphics, draw)
submit(draw)
submit(draw)
submit(draw)
submit(draw)
submit(draw, semaphore signal)
submit(semaphore wait, copy, release ownership from tranfer, semaphore signal)
submit(semaphore wait, acquire ownership to graphics, draw)
submit(draw)
submit(draw)
etc
但请注意,上述方法实际上序列化了两种访问,因此它可能不是最佳选择。采用双缓冲(或通常为 N 缓冲)可能会更好。如果你有更多的缓冲区,你可以开始复制到一个而不用担心它已经被其他东西使用了。这意味着复制可以与平局并行进行,这会很棒。
我打算使用 vulkan synchronization examples 之一作为参考,了解如何处理不经常更新的统一缓冲区。具体来说,我正在看这个:
vkBeginCommandBuffer(...);
// Submission guarantees the host write being complete, as per
// https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-submission-host-writes
// So no need for a barrier before the transfer
// Copy the staging buffer contents to the vertex buffer
VkBufferCopy vertexCopyRegion = {
.srcOffset = stagingMemoryOffset,
.dstOffset = vertexMemoryOffset,
.size = vertexDataSize};
vkCmdCopyBuffer(
commandBuffer,
stagingBuffer,
vertexBuffer,
1,
&vertexCopyRegion);
// If the graphics queue and transfer queue are the same queue
if (isUnifiedGraphicsAndTransferQueue)
{
// If there is a semaphore signal + wait between this being submitted and
// the vertex buffer being used, then skip this pipeline barrier.
// Pipeline barrier before using the vertex data
// Note that this can apply to all buffers uploaded in the same way, so
// ideally batch all copies before this.
VkMemoryBarrier memoryBarrier = {
...
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TRANSFER_BIT , // srcStageMask
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, // dstStageMask
1, // memoryBarrierCount
&memoryBarrier, // pMemoryBarriers
...);
vkEndCommandBuffer(...);
vkQueueSubmit(unifiedQueue, ...);
}
else
{
// Pipeline barrier to start a queue ownership transfer after the copy
VkBufferMemoryBarrier bufferMemoryBarrier = {
...
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = 0,
.srcQueueFamilyIndex = transferQueueFamilyIndex,
.dstQueueFamilyIndex = graphicsQueueFamilyIndex,
.buffer = vertexBuffer,
...};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TRANSFER_BIT , // srcStageMask
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
1, // bufferMemoryBarrierCount
&bufferMemoryBarrier, // pBufferMemoryBarriers
...);
vkEndCommandBuffer(...);
// Ensure a semaphore is signalled here which will be waited on by the graphics queue.
vkQueueSubmit(transferQueue, ...);
// Record a command buffer for the graphics queue.
vkBeginCommandBuffer(...);
// Pipeline barrier before using the vertex buffer, after finalising the ownership transfer
VkBufferMemoryBarrier bufferMemoryBarrier = {
...
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,
.srcQueueFamilyIndex = transferQueueFamilyIndex,
.dstQueueFamilyIndex = graphicsQueueFamilyIndex,
.buffer = vertexBuffer,
...};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, // dstStageMask
...
1, // bufferMemoryBarrierCount
&bufferMemoryBarrier, // pBufferMemoryBarriers
...);
vkEndCommandBuffer(...);
vkQueueSubmit(graphicsQueue, ...);
}
在这个例子中,我将其简化为:
map updated buffer which is host coherent
perform transfer in transfer queue to device local memory
make sure to put a buffer memory barrier to handle the queue ownership transfer
perform normal draw commands
make sure to put a buffer memory barrier to handle receiving of buffer in queue ownership
然后我必须恢复传输队列再次复制该数据的能力吗? None 的例子似乎提到了它,但我可能错过了。我真的看不出添加另一个缓冲区屏障如何对同一个绘图命令缓冲区起作用,因为它会在下一次提交时停止,即使我没有任何东西要传输,所以我只需要提交另一个命令缓冲区只是为了在我提交下一个转移操作之前排队所有权转移?
即
//begin with transfer ownership
submit(copy)
submit(ownership to graphics)
submit(draw)
submit(ownership to transfer)
submit(copy)
submit(ownership to graphics)
submit(draw)
submit(draw)
submit(draw)
submit(ownership to transfer)
submit(copy)
submit(ownership to graphics)
submit(draw)
如果是这样,我不确定如何处理绘制和传输以及复制和绘制之间的信号量信号。一开始很容易,但后来由于多个飞行帧而变得奇怪,因为绘制提交之间没有依赖关系。基本上我想我需要设置我提交的最近的绘图命令有一个信号量来表示所有权的转移,这将表示复制,这将表示图形的所有权,如果它在一个单独的线程上然后我会检查此副本是否已提交,并要求等待图形传输的所有权并重置已提交的副本检查。但是我不确定没有这种依赖性的下一帧会发生什么,并且可以在按时间顺序排列的前一帧之前完成?
只要您不介意数据变得未定义,您就可以在任何队列系列(无需传输)上使用资源。你仍然需要一个信号量来确保没有内存危险。
旧规格:
NOTE
If an application does not need the contents of a resource to remain valid when transferring from one queue family to another, then the ownership transfer should be skipped.
例子就不提了,因为只是例子
至于同步(这是与 QFOT 不同的问题),作为 vkQueueSubmit
一部分的信号量信号涵盖了之前在 提交顺序 中的所有内容。因此,当您提交副本时,您将让它在信号量上等待最后一次提交的绘制已发出信号。这意味着在复制可以在另一个队列上开始之前,绘制和该队列上的任何先前绘制都已完成。
然后你通过副本发出信号量,并在你提交的下一次抽奖时等待它。这意味着副本完成写入,绘制(和任何后续绘制)在图形队列中读取它之前。
例如:
submit(copy, release ownership from tranfer, semaphore signal)
submit(semaphore wait, acquire ownership to graphics, draw)
submit(draw)
submit(draw)
submit(draw)
submit(draw)
submit(draw, semaphore signal)
submit(semaphore wait, copy, release ownership from tranfer, semaphore signal)
submit(semaphore wait, acquire ownership to graphics, draw)
submit(draw)
submit(draw)
etc
但请注意,上述方法实际上序列化了两种访问,因此它可能不是最佳选择。采用双缓冲(或通常为 N 缓冲)可能会更好。如果你有更多的缓冲区,你可以开始复制到一个而不用担心它已经被其他东西使用了。这意味着复制可以与平局并行进行,这会很棒。