假设设备内存中有一个顶点缓冲区和一个主机连贯可见的暂存缓冲区。同时假设桌面系统有一个独立的GPU(所以是独立的内存)。最后,假设正确的帧间同步。
我认为更新顶点缓冲区一般有两种可能的方式。
Map + memcpy
+ 解映射到暂存缓冲区,然后是一个瞬时(单个命令)命令缓冲区,其中包含一个 vkCmdCopyBuffer
,提交到图形队列,等待队列空闲,然后释放瞬时命令缓冲区。之后像往常一样将普通的帧绘制队列提交到图形队列。这是在 https:/vulkan-tutorial.com。 (例如: 这个.cpp文件).
与上面类似,只是改用额外的semaphores在暂存缓冲区复制提交后发出信号,在常规的帧绘制提交中等待,从而跳过 "wait-for-idle "命令。
#第2条我觉得挺有道理的,我反复阅读过不要在Vulkan中做任何 "wait-for-idle "的操作,因为它会让CPU与GPU同步,但我从来没有在网上的任何教程或例子中看到过它的使用。如果顶点缓冲区要比较频繁地更新,专业人员一般会怎么做?
首先,如果你分配了相干内存,那么你几乎肯定是为了从CPU访问它。这就需要对其进行映射。Vulkan不是OpenGL;没有要求在使用内存之前必须解除映射。而OpenGL已经没有这个要求了。
解除映射内存只应该 曾经 当你要删除内存分配本身时,就可以进行。
其次,如果你想到的主意是要等待队列或设备空闲后再进行,那么你想出了一个糟糕的主意,应该使用另一个主意。你唯一应该等待设备空闲的时候是你想销毁设备的时候。
不应相信教程代码能给出最佳实践。它的目的往往是为了简单易懂地理解一个概念,这往往会妨碍性能(如果你不关心性能,你就不应该使用Vulkan)。
在任何情况下,在Vulkan中做大多数事情都没有 "最普遍正确的方法"。有很多绝对的 错 方式,但没有 "一般这样做 "的建议。Vulkan是一个低级的、明确的API,其结果是你需要根据你的具体情况应用Vulkan的工具。而且可能在不同的硬件上进行配置文件。
例如,如果你每一帧都要生成全新的顶点数据,那么看看实现是否可以直接从相干内存中读取顶点数据,这样就完全不需要中转缓冲区了。是的,读取的速度可能会慢一些,但整体过程可能会比先传输后读取更快。
但也可能不是。它可能在某些硬件上更快,而在其他硬件上更慢。而且有些硬件可能不允许你对任何有顶点输入用途的缓冲区使用相干内存。而且即使允许,你也可能在传输过程中进行其他工作,因此GPU在读取传输的数据之前花费最少的时间进行等待。
然而如果你要进行暂存,那么你的两个选择主要是关于你在哪个队列上提交传输操作(假设你可以选择)。而这主要与你愿意忍受多少延迟有关。
例如,如果你要为一个大型的地形系统进行流式数据传输,那么如果需要一两帧的顶点数据才能在GPU上使用,那可能是可以的。在这种情况下,你应该寻找一个替代的、只传输的队列来执行从暂存缓冲区到主内存的复制。如果你这样做了,那么你需要确保以后使用最终结果的命令与该队列同步,这将需要通过信号体来完成。
如果你处于低延迟的情况下,被传输的数据需要用到这一帧,那么将两者提交到同一个队列中可能会更好。你可以使用一个事件来同步它们,而不是使用信号体。但是你也应该努力把某种无关的工作放在传输和渲染操作之间,这样你就可以利用某种程度的并行操作。