【问题标题】:Most generally correct way of updating a vertex buffer in Vulkan在 Vulkan 中更新顶点缓冲区的最正确的方法
【发布时间】:2020-06-03 20:24:20
【问题描述】:

假设设备内存中有一个顶点缓冲区和一个主机连贯且可见的暂存缓冲区。还假设一个带有独立 GPU 的桌面系统(因此独立的内存)。最后,假设正确的帧间同步。

我看到了两种更新顶点缓冲区的通用方法:

  1. Map + memcpy + unmap 到暂存缓冲区,然后是包含vkCmdCopyBuffer 的临时(单命令)命令缓冲区,将其提交到图形队列并等待队列空闲,然后释放瞬态命令缓冲区。之后像往常一样将常规帧绘制队列提交到图形队列。这是https://vulkan-tutorial.com 上使用的代码(例如this .cpp file)。

  2. 与上述类似,只是在暂存缓冲区复制提交后使用附加信号量发出信号,并在常规帧绘制提交中等待,从而跳过“等待空闲”命令。

#2 对我来说有点意思,我反复阅读不要在 Vulkan 中执行任何“等待空闲”操作,因为它使 CPU 与 GPU 同步,但我从未见过它用于任何在线教程或示例。如果顶点缓冲区必须相对频繁地更新,专业人士通常会怎么做?

【问题讨论】:

  • "Map + memcpy + unmap 到暂存缓冲区" 不,只有在你要删除它时才取消映射相干内存。多次映射内存是零点。
  • 嗯,我实际上是为我每帧更新的缓冲区这样做(保持映射),但保持所有暂存缓冲区也映射是有意义的。

标签: language-agnostic vulkan vertex-buffer


【解决方案1】:

首先,如果您分配了一致的内存,那么您几乎可以肯定这样做是为了从 CPU 访问它。这需要映射它。 Vulkan 不是 OpenGL;不要求在使用之前取消映射内存 (and OpenGL doesn't even have that requirement anymore)。

只应永远在您即将删除内存分配本身时完成取消映射内存。

其次,如果您想到的想法涉及让 CPU 在继续之前等待队列或设备空闲,那么您想出了一个坏主意,应该使用其他的。您应该等待设备空闲的唯一时间是您想要销毁设备时。

不应相信教程代码可以提供最佳实践。它通常旨在简单,以便于理解一个概念。简单的 Vulkan 代码通常会妨碍性能(如果您不关心性能,则不应该使用 Vulkan)。

无论如何,在 Vulkan 中做大多数事情都没有“最普遍正确的方法”。有很多绝对不正确的方法,但没有“通常这样做”的建议。 Vulkan 是一个低级的显式 API,其结果是您需要将 Vulkan 的工具应用于您的特定情况。并且可能在不同的硬件上配置文件。

例如,如果您每帧都生成全新的顶点数据,那么最好看看实现是否可以直接从相干内存中读取顶点数据,这样就根本不需要暂存缓冲区。是的,读取可能会更慢,但整个过程可能比传输后读取要快。

再一次,它可能不会。在某些硬件上它可能更快,而在其他硬件上则更慢。并且某些硬件可能不允许您对任何具有顶点输入使用情况的缓冲区使用相干内存。即使允许,您也可以在传输过程中进行其他工作,因此 GPU 在读取传输数据之前花费最少的时间等待。并且一些硬件有一个小的设备本地内存池,您可以直接从 CPU 写入;此内存适用于此类流媒体应用程序。

但是,如果您要进行暂存,那么您的选择主要是关于您在哪个队列上提交传输操作(假设硬件有多个队列)。这主要与您愿意忍受多少延迟有关。

例如,如果您正在为大型地形系统传输数据,那么可能需要一两帧才能在 GPU 上使用顶点数据。在这种情况下,您应该寻找一个替代的仅传输队列,在该队列上执行从暂存缓冲区到主内存的复制。如果这样做,则需要确保使用最终结果的后续命令与该队列同步,这需要通过信号量来完成。

如果您处于低延迟情况下,正在传输的数据需要在此帧中使用,那么最好将两者都提交到同一个队列。您可以使用事件而不是信号量来同步它们。但是您也应该努力在传输和渲染操作之间放置一些不相关的工作,以便您可以利用操作中的某种程度的并行性。

【讨论】:

  • “教程代码不应该被信任来提供最佳实践”——当我和你在一起时,我必须在某个地方学习这些,对吗?我有来自 LunarG 的文档,其中描述了每个单独的函数和各种教程,这些教程有助于正确看待这些。无论如何,谢谢,我明白了,放弃空闲等待调用并在缓冲区更新提交和渲染命令提交之间使用同步。我会调查事件,我还没有听说过,提交调用只使用信号量和栅栏。
  • @Blindy:“在缓冲区更新提交和渲染命令提交之间使用同步”我没有提到“提交”。您不应该在单个帧中多次提交到同一个队列。 vkQueueSubmit 不是一个快速函数,因此您应该在每次调用时提交尽可能多的工作。
  • 你提到了信号量来同步gpu操作(复制缓冲区操作和绘图调用),如果你不提交你怎么同步它们?只有提交有等待信号量和信号量字段。
  • @Blindy: "只有提交有等待信号量和信号量字段。" 提交本身没有; within 提交的批次可以在信号量上发出信号/等待。您可以提交多个批次。我试图表达的观点是,如果你将传输和渲染命令发送到同一个队列,你不应该在不同的时间“提交”它们。一次性提交,可能分批提交。
  • 如果我理解正确,您说的是vkQueueSubmitvkQueuePresentKHR,对吗?我不相信我在文档中看到了提交与批量提交的区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-01
  • 1970-01-01
相关资源
最近更新 更多