Bli*_*ndy 3 language-agnostic vertex-buffer vulkan
假设设备内存中有一个顶点缓冲区,以及一个与主机一致且可见的暂存缓冲区。还假设桌面系统具有独立的 GPU(因此独立的内存)。最后,假设正确的帧间同步。
我看到更新顶点缓冲区的两种常见可能方法:
Map++ memcpyunmap 到暂存缓冲区,然后是包含 的瞬态(单个命令)命令缓冲区vkCmdCopyBuffer,将其提交到图形队列并等待队列空闲,然后释放瞬态命令缓冲区。之后,像往常一样将常规帧绘制队列提交到图形队列。这是https://vulkan-tutorial.com上使用的代码(例如,此 .cpp 文件)。
与上面类似,只是在暂存缓冲区复制提交后使用额外的信号量发出信号,并在常规帧绘制提交中等待,从而跳过“等待空闲”命令。
#2 对我来说有点意义,我反复阅读不要在 Vulkan 中执行任何“等待空闲”操作,因为它使 CPU 与 GPU 同步,但我从未见过它在任何教程或中使用网上的例子。如果顶点缓冲区必须相对频繁地更新,专业人士通常会做什么?
首先,如果您分配了一致的内存,那么您几乎肯定这样做是为了从 CPU 访问它。这需要映射它。Vulkan 不是 OpenGL;不要求内存在使用之前必须取消映射(OpenGL 甚至不再有这个要求)。
仅当您要删除内存分配本身时才应取消内存映射。
其次,如果您想到的想法需要让 CPU 等待队列或设备空闲后再继续,那么您就想出了一个坏主意,应该使用不同的想法。唯一应该等待设备空闲的时间是当您想要销毁该设备时。
不应相信教程代码可以提供最佳实践。它通常旨在简单,使概念易于理解。简单的 Vulkan 代码通常会影响性能(如果您不关心性能,则不应使用 Vulkan)。
无论如何,在 Vulkan 中不存在“最普遍正确的方法”来完成大多数事情。有很多绝对不正确的方法,但没有“通常这样做”的建议。Vulkan 是一个低级、显式的 API,其结果是您需要根据您的具体情况应用 Vulkan 的工具。也许还有不同硬件上的配置文件。
例如,如果您每帧都生成全新的顶点数据,那么最好看看该实现是否可以直接从相干内存读取顶点数据,以便根本不需要暂存缓冲区。是的,读取可能会更慢,但整个过程可能比传输后读取更快。
但话又说回来,也可能不会。在某些硬件上它可能会更快,而在其他硬件上可能会更慢。并且某些硬件可能不允许您对任何具有顶点输入使用情况的缓冲区使用一致内存。即使允许,您也可以在传输过程中执行其他工作,因此 GPU 在读取传输的数据之前花费的等待时间最少。有些硬件有一个小的设备本地内存池,您可以直接从 CPU 写入;该内存适用于此类流媒体应用程序。
但是,如果您打算进行分段,那么您的选择主要是在哪个队列上提交传输操作(假设硬件有多个队列)。这主要与您愿意忍受多少延迟有关。
例如,如果您正在为大型地形系统传输数据,那么如果需要一两帧才能在 GPU 上使用顶点数据,则可能没问题。在这种情况下,您应该寻找替代的仅传输队列来执行从暂存缓冲区到主内存的复制。如果这样做,那么您需要确保使用最终结果的后续命令与该队列同步,这需要通过信号量来完成。
如果您处于低延迟场景,其中正在传输的数据需要使用此帧,那么最好将两者提交到同一队列。您可以使用事件而不是信号量来同步它们。但您还应该努力在传输和渲染操作之间放置某种不相关的工作,以便可以利用操作中某种程度的并行性。