【问题标题】:Vulkan - synchronising access to a single BufferVulkan - 同步访问单个缓冲区
【发布时间】:2019-03-18 09:22:51
【问题描述】:

当多个帧在运行时,在 Vulkan 中同步访问单个缓冲区的最佳方法是什么?

我是 Vulkan 的新手,但我发现同步是最难理解的部分。我浏览了 Vulkan 规范、同步示例 (https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples)、Vulkan 教程 (https://vulkan-tutorial.com/) 以及一堆 Stack Overflow 帖子。不过,我仍然不确定我是否真的“明白”了。

为了帮助我学习,我正在尝试编写以下代码:

我认为第 N 帧的命令缓冲区(0

// 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 大师提供的任何帮助!

【问题讨论】:

    标签: c graphics synchronization rendering vulkan


    【解决方案1】:

    教程章节的重点是说 Hello 不是一个“真正的”应用程序。你好可以有无限数量的帧在进行中。但这不是在“真实”应用中会发生的事情。

    驱动程序和层可能会在栅栏上进行清理,这意味着东西不再在飞行中。如果从来没有这样的同步,元数据可能会堆积起来。

    您可能会在“真实”应用中频繁更新数据,这意味着会频繁进行此类同步。此外,您将控制延迟,这意味着您将没有 N 个暂存缓冲区(如您所建议的那样) - 如果尚未使用先前的每帧数据,那么不建议更新新的数据。当它们真正被使用时,它们已经太老了。再说一次,如果它不是一个交互式应用程序(例如渲染电影),它可能是有意义的。

    话虽如此,在飞行中拥有框架本身并不是一个理想的特性。不过,最好让 GPU 和 CPU 始终处于忙碌状态(假设有工作量要完成)。这意味着队列中的每个工作都可以在完成当前工作后立即选择。

    您的管道障碍似乎足以同步storageBuffer。尽管在某些情况下,使用专用传输队列可能更可取(这意味着不同的同步方案)。并且最好使用等效的外部子通道依赖项。

    stagingBuffer 需要与主机和设备域同步。

    如前所述,stagingBuffers 中可能没有 N 个。如果您要更新新的每帧数据,理想情况下旧数据应该已经处理(可以通过栅栏检查)。

    您将stagingBuffer 定义为连贯的,因此您无需在主机上执行任何操作,只需写入映射指针即可。 如果之后调用vkQueueSubmit,那么所有这些写入都将通过主机写入顺序保证隐式同步。

    您还必须确保主机不会在设备仍在读取内存时开始写入内存。在这些写入之前应该有某种 Fence 等待。

    【讨论】:

    • 感谢您的 cmets。我认为 Hello 应用程序专门阻止了无限帧在飞行中,因为它使用了 MAX_FRAMES_IN_FLIGHT 信号量数组?无论如何,我很欣赏更新数据缓冲区的方法可能比我在问题中建议的方法更好 - 我主要想确保我正确理解同步。
    • @Alan 是的,本教程的当前版本是这样做的。我认为使用 Fences(信号量是不够的,因为它们只是 GPU 端的)。 AIS 你的障碍似乎是正确的。 stagingBuffer的Host Domain同步你不提供sn-p,所以我只能泛泛的说一下。
    猜你喜欢
    • 2016-09-25
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多