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

nik*_*ack 2 vulkan

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

现在的主要问题是如何以最快的方式上传新对象的数据并找出数据上传的时间.

这是我的设置:

  • 我正在使用vulkan内存分配器库,所以我是自由形式的内存管理负担.
  • 我打算VkBuffer为每个对象使用一个单独的 - 这样我就不需要管理偏移,对齐,并且添加/删除对象会更容易.

这是我的想法/问题:

  1. 如何上传数据?我希望缓冲区只能是gpu-visible,这意味着我需要一个临时缓冲区.
  2. 如果我使用临时缓冲区,我需要知道数据何时可以在gpu上使用.我不想冲洗管道并等待.我看到的唯一方法是对每个对象使用一个围栏,并且只draw在该围栏准备就绪时调用该命令.
  3. 如果我使用临时缓冲区并希望在短帧期间上传多个对象,我需要以某种方式确保此临时缓冲区的各个部分不被不同的对象覆盖.为此,我需要保持它的大,处理偏移的对齐.但有多大?

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

Nic*_*las 5

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

这是Vulkan; 它是一个明确的低级API."简单"不是它的目标.

总的来说,需要编写Vulkan代码以适应硬件的功能.这是获得性能的最佳方式.

需要做出的第一个决定是你是否需要升级.只有在设备的DEVICE_LOCAL内存不可映射时,才需要暂存(用于缓冲区副本).是的,有(集成)GPU允许您映射DEVICE_LOCAL内存.如果是这种情况,那么您可以直接写入您需要数据的位置.

如果需要暂存,则需要确定硬件是否支持独立的仅传输队列.如果是这样,那么通过使用它可能会获得性能优势.并非所有硬件都支持仅传输队列,因此您的应用程序需要适应.此外,仅传输队列可以限制在这些队列上发生的内存传输的粒度,因此您需要检查您的流策略是否符合特定硬件的限制.

此外,如果没有适当的传输队列,您可以使用第二个计算或图形队列来创建传输队列的效果 ...如果硬件完全支持多个队列.假设您正在利用线程(即:向不同线程上的不同队列发出批量提交),能够在不同队列上提交传输命令和渲染命令是一件好事.

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

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

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

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

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

这种灵活性在于过程的结构.它的结构与多队列版本的工作方式类似.在这两种情况下,一个单独的线程需要将某些东西传递给渲染提交线程(在一种情况下,信号量;在另一种情况下,一个CB和一个事件).渲染提交线程需要做一些事情来等待"某些东西",但不会中断构建渲染命令本身的过程(在一种情况下,你只需更改批处理以等待信号量;在另一种情况下,你插入等待事件的CB).

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

但事情就是这样:所有的升级案例都不是性能友好的.它们存在问题,因为在传输操作正在进行时您无法执行任何操作.情况就是这样,因为......你正在尝试从你正在写入的同一帧中读取内存.那很糟糕.

您应该努力延迟渲染任何未完成加载的对象.或者换句话说,您希望在需要之前加载新对象的数据,而不是在需要它们的同一帧上.这就是流媒体系统所做的事情:他们先发制人地加载即将需要的数据,但现在却不是.

但有多大?

只有您和您的用例才能回答这个问题.如果你使用固定大小的块(你应该尽可能地做),那么它就相当容易:你的临时缓冲区应该是一个或两个流媒体块.如果您的渲染系统更灵活,对更高级别的代码施加一些限制,那么您的临时缓冲区和流式传输系统需要更加灵活.对此没有正确的答案; 它完全取决于它是如何被使用的.

欢迎使用显式的低级API.