vulkan - 在运行时添加新3D对象的最佳方法

标签 vulkan

一直以来,我一直在启动过程中创建3D对象。但是现在我需要动态添加它们。我想...更简单的是...

现在的主要问题是如何以最快的方式上载新对象的数据并确定何时上载数据。

这是我的设置:

  • 我正在使用vulkan memory allocator library,所以我没有任何形式的内存管理负担。
  • 我打算为每个对象使用单独的VkBuffer-这样,我就不需要管理偏移量,对齐方式,并且添加/删除对象会更容易。

  • 这是我的想法/问题:
  • 如何上传数据?我只希望该缓冲区是gpu可见的,这意味着我需要一个临时缓冲区。
  • 如果我使用登台缓冲区,则需要知道何时可以在gpu上使用数据。我不想冲洗管道并等待。我看到的唯一方法是对每个对象使用围栏,并且仅在此围栏准备就绪时才调用draw命令。
  • 如果我使用登台缓冲区并想在短帧内上载多个对象,则需要以某种方式确保该登台缓冲区的各个部分不会被其他对象覆盖。为此,我需要将其保持较大,处理偏移量的对齐方式。但是有多大?

  • 我很确定自己太复杂了。我相信应该有一个更简单的模式。你会怎么做?

    最佳答案

    I believe there should be a much simpler pattern.



    是Vulkan;这是一个显式的低级API。 “简单”不是它的目标。

    总体而言,需要编写您的Vulkan代码以适应硬件功能。这是从中获得性能的最佳方法。

    需要做出的第一个决定是您是否完全需要暂存。仅当设备的DEVICE_LOCAL存储器不可映射时才需要暂存(用于缓冲区副本)。是的,有(集成)GPU可让您映射DEVICE_LOCAL内存。如果是这种情况,那么您可以直接将其写入需要数据的位置。

    如果需要分段,则需要确定硬件是否支持独立的仅传输队列。如果是这样,那么使用它可能会获得性能上的好处。并非所有硬件都支持仅传输队列,因此您的应用程序需要进行调整。此外,仅传输队列可能会对在这些队列上进行的内存传输的粒度有所限制,因此您需要检查流策略是否适合该特定硬件的限制。

    另外,如果没有适当的传输队列,则可以使用第二个计算或图形队列来创建传输队列的效果……如果硬件完全支持多个队列。假设您正在利用线程(例如:将批处理的提交发布到不同线程上的不同队列),能够在不同队列上提交传输命令和呈现命令是一件好事。

    如果您能够使用单独的队列进行传输(无论是真正的传输队列还是仅使用单独的计算/图形队列),那么您就可以使用信号量。传输数据的批处理完成时必须发出信号量。这是vkQueueSubmit调用中批处理的一部分。使用已传输数据进行某些处理的主队列上的批处理需要等待该信号量。因此,两个线程都需要使用相同的VkSemaphore对象。并且等待信号量应该只具有全局内存屏障,以使内存可见。

    棘手的部分是:您不能提交等待信号量的批处理,直到发出指示已提交的批处理的提交调用为止。您不必等到完成,而是必须等到传输队列上的vkQueueSubmit调用返回。因此,您需要一种在不同线程之间传输信号的方法,或者您可以只在同一线程上发布两个提交命令。

    如果您不使用第二个队列,那么事情会稍微简单一些。

    您仍然希望在另一个线程上构建传输命令缓冲区本身(以利用线程CB构造)。但是,现在需要将该CB传达给负责提交渲染内容的线程。并且此通信通道需要知道此CB包含传输命令,某些渲染CB进程应等待这些命令。

    最简单,最灵活的方法是构建传输CB,以使最后一个命令是vkCmdSetEvent命令(第一个命令是vkCmdResetEvent,以将其从以前的用法框架中重置)。然后,提交线程仅需要创建一个小的CB,该CB仅包含vkCmdWaitEvenets命令,该命令等待将要设置的传输事件。该命令应发出完整的内存屏障,并且CB应该在传输CB和从传输的数据读取的任何呈现CB之间执行。

    其灵活性在于过程的结构。它的结构类似于多队列版本的工作方式。在这两种情况下,一个单独的线程都需要与渲染提交线程通信(在一种情况下,是一种信号灯;在另一种情况下,是一种CB和一个事件)。渲染提交线程需要做一些事情以等待该“事情”,但又不中断构建渲染命令本身的过程(在一种情况下,您只需更改批处理以等待信号量;在另一种情况下,您可以插入等待事件的CB)。

    如果您想更加了解执行依赖关系,甚至可以让传输操作转发有关哪些管道阶段需要等待该操作的信息。但这主要是一种优化。

    事情是这样的:所有暂存案例都不是性能友好的。它们有问题,因为在传输操作进行期间您无法执行任何操作。就是这种情况,因为...您正试图在与之相同的帧中读取内存。那很糟。

    相反,您应该努力延迟渲染所有未完成加载的对象。或者换种说法,您想在需要新对象之前加载它们的数据,而不是在需要它们的同一帧上。这就是流系统的工作方式:它们抢先加载很快将需要的数据,但现在不是。

    But how big?



    只有您和您的用例才能回答该问题。如果您正在以固定大小的块进行流式传输(在可能的情况下应该这样做),那么这很容易:您的暂存缓冲区的大小应为一个或两个流式传输块。如果您的渲染系统更加灵活,对高级代码没有什么限制,那么登台缓冲区和流式传输系统就需要更加灵活。而且没有正确的答案;这完全取决于它的使用方式。

    欢迎使用显式的低级API。

    关于vulkan - 在运行时添加新3D对象的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54750116/

    相关文章:

    gpu - Vulkan 管道 + 颜色附件

    c++ - GLSL 日志返回未定义的结果

    specifications - 创建子 channel 时什么时候应该使用 VK_ATTACHMENT_UNUSED?

    c++ - 导出 vulkan 内存分配句柄导致设备内存不足

    c++ - 在 Kubuntu 18.10 上链接的 Vulkan SDK 版本 1.1.85.0

    c++ - Vulkan 中命令缓冲区之间的同步

    c++ - 如何在 C++ 项目的 Cmake 文件中添加 Mac OS 框架

    c++ - 使用 typedef 的模板特化

    c++ - 当设置 SDL_WINDOW_VULKAN 标志时,SDL_CreateWindow 失败

    rust - 相交着色器从 SSBO 中读取零