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

时间:2019-02-18 15:06:15

标签: vulkan

一直到现在,我一直在启动期间创建3D对象。但是现在我需要动态添加它们。我想有什么可以更简单...

当前的主要问题是如何以最快的方式上传新对象的数据,并确定何时上传数据。

这是我的设置:

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

这是我的想法/问题:

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

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

1 个答案:

答案 0 :(得分:5)

  

我相信应该有一个更简单的模式。

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

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

首先需要做出的决定是您是否完全需要分期。仅当设备的DEVICE_LOCAL内存不可映射时才需要分段(用于缓冲区副本)。是的,有(集成的)GPU可让您映射DEVICE_LOCAL内存。如果是这样,那么您可以直接写到需要数据的地方。

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

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

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

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

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

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

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

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

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

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

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

  

但是有多大?

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

欢迎使用显式的低级API。