当飞行中有多个帧时,同步访问Vulkan中的单个缓冲区的最佳方法是什么?
我是Vulkan的新手,但是我发现同步是最困难的部分。我浏览了Vulkan规范,同步示例(https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples),Vulkan教程(https://vulkan-tutorial.com/)以及许多Stack Overflow帖子。我仍然不确定我是否真的“明白了”。
为帮助我学习,我尝试编写以下代码:
我认为第N帧(0 <= N <飞行中的最大帧数)的命令缓冲区应如下所示:
// Many parameters omitted for brevity
vkCmdCopyBuffer(commandBuffer[N], stagingBuffer[N], storageBuffer, ...);
VkMemoryBarrier barrier = {0};
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(
commandBuffer[N],
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
0,
1,
&barrier,
...
);
// begin render pass
// drawing commands
// end render pass
vkCmdPipelineBarrier(
commandBuffer[N],
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0,
0,
NULL,
...
);
我相信需要第一个流水线屏障,以防止GPU允许顶点着色器在更新时从存储缓冲区读取。
我认为需要第二个流水线屏障,以防止下一帧的vkCmdCopyBuffers
命令执行,直到前一帧的顶点着色器读取存储缓冲区为止。我的理解是这里不需要内存屏障,因为这是“战争危险”(https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples#first-draw-samples-a-texture-in-the-fragment-shader-second-draw-writes-to-that-texture-as-a-color-attachment)。
我的建议正确吗?还是我误解了某些东西?
注意:我知道我目前采取的方法(即使是正确的)也不是最好的方法-例如也许在飞行中为每个帧设置N个存储缓冲区将提供更好的性能。但是,我希望在继续之前先了解一下同步。
非常感谢您Vulkan大师可以提供的任何帮助!
答案 0 :(得分:0)
教程章节的重点是说Hello不是一个“真正的”应用程序。您好,飞行中可以容纳无数帧。但这不是在“真实”应用程序中发生的事情。
驱动程序和图层可能会在栅栏上背负清理工作,这意味着东西不再在飞行中。如果从来没有这样的同步,则元数据可能会堆积。
您可能会在“真实”应用程序中频繁地更新数据,这意味着会经常进行这种同步。另外,您还要检查等待时间,这意味着您将没有N个登台缓冲区(如您建议的那样)-如果以前的每帧数据尚未使用,则建议更新新的缓冲区。到实际使用时,它们已经太老了。再说一次,如果它不是一个交互式应用程序(例如渲染电影),那可能是有道理的。
话虽如此,使飞行中的车架本身并不是真正想要的特性。最好始终保持GPU和CPU都忙(假设有工作要做)。这意味着队列中的每个工作都可以在完成当前工作后立即选择。
您的管道障碍似乎足以同步storageBuffer
。尽管在某些情况下,最好使用专用的传输队列(这意味着不同的同步方案)。并且可能更可取的是使用等效的外部子通道依赖项。
stagingBuffer
需要同时与主机域和设备域同步。
如前所述,可能不需要{个stagingBuffer
中的N个。如果您要更新新的每帧数据,则理想情况下应该已经对旧数据进行了处理(可以用围栏检查)。
您将stagingBuffer
定义为连贯的,因此您无需在主机上执行任何操作,只需编写映射的指针即可。 如果在此之后调用 vkQueueSubmit
,那么所有这些写操作都将由主机写顺序保证隐式同步。
还必须确保在设备仍在读取内存时主机没有开始写入内存。在写这些文章之前,应该有某种栅栏等待。