如何同步统一缓冲区更新?

How to synchronize uniform buffer updates?

我有许多统一的缓冲区 - 每个帧缓冲区一个。我在 fences 的帮助下保证 cpu 上的更新是安全的,即当我 memcpy 时我确定缓冲区没有被使用。更新后,我正在刷新内存。

现在,如果我理解正确的话,我需要为 gpu 提供新数据——为此,我需要使用障碍。我现在就是这样做的:

VkBufferMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.pNext = nullptr;
barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.buffer = buffer;
barrier.offset = offset;
barrier.size = size;

vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT, 0, 0, nullptr, 1, &barrier, 0, nullptr);

好吧,实际上在我的情况下一切都没有障碍。我需要它吗?

如果我将 barrier.dstAccessMaskdstStageMask 分别更改为 VK_ACCESS_TRANSFER_READ_BITVK_PIPELINE_STAGE_TRANSFER_BIT,一切都会再次正常工作,图层不会抱怨。什么是更好的选择,为什么?

如果我尝试在 vkCmdBeginRenderPass 之后设置障碍,图层会抱怨。所以我把所有的障碍都移到了 vkBeginCommandBuffervkCmdBeginRenderPass 之间。这有多正确?

Well, actually in my case everything works without the barrier.

规范是否说明您需要它?那你需要它。

在处理 Vulkan 时,您不应将 "everything appears to work" 视为您已做对所有事情的标志。

What is the better choice and why?

"better choice" 是正确的。 GPU 没有对该内存进行传输操作;它将它作为统一数据读取。因此,您在屏障中指定的操作必须与此匹配。

层没有抱怨,因为验证层或多或少不可能知道您何时写入了一块内存。因此,他们无法判断您是否正确构建了屏障以使此类写入对 GPU 可用。

If I try to set a barrier after vkCmdBeginRenderPass, the layer complains.

渲染通道内的障碍必须位于 self-dependencies 的子通道内。并且屏障本身必须匹配子通道 self-dependency.

基本上,您所说的那种障碍必须在渲染过程之前发生。


也就是说,仅调用 vkQueueSubmit 会自动在 vkQueueSubmit 之前发出的(正确刷新的)主机写入与提交命令中来自命令缓冲区的这些写入的任何使用之间创建一个内存屏障(当然,还有稍后提交操作中的命令)。

所以你不需要这样的屏障,只要你能确保你在 vkQueueSubmit 从它们读取之前完成你的写入(和任何需要的刷新)。如果你不能保证这一点,你可能应该一直使用 vkCmdWaitEvents 屏障来防止在你完成写入(和刷新)之前尝试读取。