多线程渲染与命令池

Multi-thread rendering vs command pools

:

After all, being able to build command buffers in parallel is one of the selling points of Vulkan.

规范(5.1 命令池)(强调我的):

Command pools are application-synchronized, meaning that a command pool must not be used concurrently in multiple threads. That includes use via recording commands on any command buffers allocated from the pool, as well as operations that allocate, free, and reset command buffers or the pool itself.

当涉及到并行记录时,这不会扼杀命令池的全部用途吗?如果你打算并行记录,那么你最好不要为每个线程设置一个单独的池,不是吗?

如果您预先记录从同一个池(在一个线程中)分配的所有命令缓冲区,然后并行执行它们,我会理解的。这具有摊销资源创建成本以及并行执行的优势。但是,并行记录和命令池似乎不太匹配。

我个人不知道您为什么不预先录制所有内容。那么为什么需要并行构建命令缓冲区呢?那么您真的需要为每个线程使用一个池吗?

If you intend to record in parallel, then you would better be off having a separate pool for each thread, isn't that right?

我看不出每个线程都有一个单独的池 "kills the whole purpose of command pools when it comes to recording in parallel"。事实上,它有很大帮助,因为每个线程都可以根据需要管理自己的命令池。

考虑一下描述符池和命令池之间的结构差异。使用描述符池,您基本上可以准确地告诉它您将从中分配什么。 VkDescriptorPoolCreateInfo 提供详细信息,允许实现预先准确分配每个池要使用的内存量。而且你不能从描述符池中分配比这更多的东西。

相比之下,VkCommandPoolCreateInfo 包含……什么都没有。哦,你告诉它命令缓冲区可以是主要的还是次要的。你说命令缓冲区是否会经常重置或持久化。还有其他一些事情。但除此之外,您对命令缓冲区的内容只字不提。您甚至不提供有关您将分配多少缓冲区的信息。

描述符池是固定的:根据需要分配,但不超过构建时设置的数量。命令缓冲区旨在非常动态:根据您的特定用例的需要分配。

将其视为每个池都有自己的 malloc/free。由于用户被迫同步对池及其缓冲区的访问,这意味着每个 vkCmd* 函数在分配内存时都不需要这样做。这使得命令构建更快。 有助于 线程化。当一个线程决定重置它的命令池时,它不必锁定任何互斥体或任何其他类似的东西来做到这一点。

每个线程有一个命令池在概念上没有错。事实上,每个线程有两个(双缓冲)更有意义。

I don't personally know why you wouldn't just pre-record everything.

因为您不是在制作静态技术演示。

I guess this comes from lack of experience, but I imagined the parallel-recording would look like "threads 2-N record secondary command buffers, thread 1 calls all of them in one primary command buffer", in which case there is only one command buffer per thread. That was why I said it kills the purpose of command pools, because you are only making a single allocation per pool.

这当然是并行记录命令缓冲区的一种可行形式。但是你错过了两件事。

虽然这当然是并行记录的一种形式,但它并不是唯一的一种。如果您正在执行延迟渲染,则为光照通道构建 CB 的线程将比负责(部分)几何通道的线程之一更快地完成其工作。因此,一个设计良好的多线程系统必须根据需要将工作分配给线程,而不是基于一些固定的东西安排。因此,一个单独的线程通常最终会构建多个命令缓冲区。

即使情况并非如此,您也忘记了缓冲。当需要为下一帧构建 CB 时,您不能只覆盖现有的。毕竟,他们可能仍在队列中工作。所以每个线程至少需要两个个CB;当前正在执行的一个和当前正在构建的一个。

即使情况并非如此,命令池也会分配与 CB 关联的所有内存。我将它们类比为 malloc/free 是有原因的。即使您仅将单个 CB 与特定池一起使用,该 CB 的分配(可能由于任何 vkCmd* 函数而发生)永远不必与另一个线程同步这一事实是一件好事。

所以不,这不会以任何方式抑制使用多线程构建 CB 的能力。

If you intend to record in parallel, then you would better be off having a separate pool for each thread, isn't that right?

完全正确。这就是您的规格报价所暗示的。

I would understand it if if you pre-record command buffers allocated all from the same pool (in one thread) and then execute them in parallel.

Vulkan 做得更好。您可以并行预先记录命令缓冲区(从每个线程池分配),然后也并行执行它们(如果您的工作负载有利于这样做)。

I don't personally know why you wouldn't just pre-record everything. So why is building command buffers in parallel so needed?

因为这很难(尤其是当您的应用越来越复杂时)。在某些时候甚至会适得其反(当您将 CmB 扭曲为可预录制时 - 例如,用空占位符绑定填充它,其中 80% 不会被使用)。
不一定"needed",Vulkan只是让你选择你认为最适合你的App(或其中的一部分)的东西。