Language agnostic Vulkan中更新顶点缓冲区的最普遍正确方法

Language agnostic Vulkan中更新顶点缓冲区的最普遍正确方法,language-agnostic,vulkan,vertex-buffer,Language Agnostic,Vulkan,Vertex Buffer,假设设备内存中有一个顶点缓冲区和一个主机一致且可见的暂存缓冲区。还假设桌面系统有一个离散的GPU(所以是独立的内存)。最后,假设帧间同步正确 我看到了两种更新顶点缓冲区的常用方法: Map+memcpy+取消映射到暂存缓冲区,然后是包含vkCmdCopyBuffer的临时(单命令)命令缓冲区,将其提交到图形队列,等待队列空闲,然后释放临时命令缓冲区。之后,像往常一样将常规帧绘制队列提交到图形队列。这是在(例如,)上使用的代码 与上面类似,只需在暂存缓冲区copy submit之后使用附加信号量发

假设设备内存中有一个顶点缓冲区和一个主机一致且可见的暂存缓冲区。还假设桌面系统有一个离散的GPU(所以是独立的内存)。最后,假设帧间同步正确

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

  • Map+
    memcpy
    +取消映射到暂存缓冲区,然后是包含
    vkCmdCopyBuffer
    的临时(单命令)命令缓冲区,将其提交到图形队列,等待队列空闲,然后释放临时命令缓冲区。之后,像往常一样将常规帧绘制队列提交到图形队列。这是在(例如,)上使用的代码

  • 与上面类似,只需在暂存缓冲区copy submit之后使用附加信号量发出信号,并在常规帧draw submit中等待,从而跳过“wait for idle”命令


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

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

    只有当您将要删除内存分配本身时,才应取消内存映射

    第二,如果您想到了一个让CPU等待队列或设备空闲再继续的想法,那么您想到了一个坏主意,应该使用另一个。您应该等待设备空闲的唯一时间是您要销毁该设备的时间


    不应相信教程代码提供最佳实践。这通常是为了简单,使它容易理解一个概念。简单的Vulkan代码通常会影响性能(如果您不关心性能,就不应该使用Vulkan)

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

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

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

    但是,如果要执行转移,那么您的选择主要是关于提交转移操作的队列(假设硬件有多个队列)。这主要与你愿意忍受多少延迟有关

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


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

    “Map+memcpy+unmap-into-staging buffer”不,只有在您将要删除一致内存时才取消映射一致内存。映射内存中有不止一次的零点。嗯,实际上我对每一帧都会更新的缓冲区都这样做了(保持映射),但保持所有暂存缓冲区也映射也是有意义的。“不应该相信教程代码会给出最佳实践”--当我和你们在一起的时候,我必须在某个地方学习这些,对吗?我有来自LunarG的文档,其中描述了每个单独的函数和各种教程,有助于正确理解这些函数。无论如何,谢谢你,我明白了,放下空闲等待调用,在缓冲区更新提交和渲染命令提交之间使用同步。我将研究事件,我还没有听说过它们,submit调用只使用信号量和围栏。@Blindy:“在缓冲区更新提交和呈现命令提交之间使用同步”我没有提到“提交”。在单个帧中,您不应向同一队列提交多次
    vkQueueSubmit
    不是一个快速函数,因此您应该在每次调用时提交尽可能多的工作。您提到了同步gpu操作(复制缓冲区操作和绘制调用)的信号量,如果您不提交,如何同步它们?只有submit有等待信号量和信号量字段。@Blindy:“只有submit有等待信号量和信号量字段。”submit本身有