如何正确使用多个交错缓冲区的描述符集?
How to correctly use decriptor sets for multiple interleaving buffers?
我有一个统一的缓冲区,应该每帧更新一次。为了避免大停顿,我想创建 3 个缓冲区(按我的帧缓冲区的数量),每帧应该交错(0-1-2-0-1-2-...)。但我不明白如何创建描述符并绑定它们。我现在就是这样做的:
- 我创建了一个
VkDescriptorSetLayout
,其中指定我想在某个着色器阶段的绑定位置 0 使用统一缓冲区。
- 我为统一缓冲区创建了一个大小为 3 个描述符的
VkDescriptorPool
。
- 接下来我需要分配描述符集,但是这里我需要多少个描述符集?我只有一个
VkDescriptorSetLayout
,我希望得到一个 VkDescriptorSet
。
- 接下来我需要更新创建的描述符集。因为我的着色器中只有一个绑定 (0),所以我只能在
VkDescriptorBufferInfo
中使用一个缓冲区,它将传递给 VkWriteDescriptorSet
,然后传递给 vkUpdateDescriptorSets
。但是其他两个缓冲区呢?在哪里指定它们?
- 当我需要记录命令时,我需要绑定我的描述符集。但直到现在我有一个描述符集,它只为一个缓冲区更新。其他人呢?
我是否需要创建 3 个 VkDescriptorSetLayout
- 每个帧一个?接下来我是否需要使用相应的缓冲区分配和更新相应的描述符集?在此之后我需要创建 3 个不同的命令缓冲区,我应该在其中指定相应的描述符集。
看起来工作量很大 - 数据几乎相同 - 所有绑定、状态保持不变,只有缓冲区发生变化。
这一切听起来很混乱,所以请不要犹豫澄清。
描述符集布局定义描述符集的内容 - 给定集包含哪些类型的资源(描述符)。当您需要多个描述符集和一个统一缓冲区时,您可以使用相同的布局创建所有这些描述符集(布局只是一种描述,一种规范)。这样你只要告诉 driver: "Hey, driver! Give me 3 descriptor sets. But all of them should be exactly the same".
但是因为它们是从相同的布局创建的,并不意味着它们必须包含相同的资源句柄。所有这些(在你的情况下)都必须包含一个统一的缓冲区。但是这个统一缓冲区将使用什么资源取决于你。所以每个描述符集都可以用单独的缓冲区更新。
现在当你想在三个连续的帧中一个接一个地使用 3 个缓冲区时,你可以通过几种不同的方式来实现:
- 您可以设置一个描述符。然后在每一帧中,在你开始准备命令缓冲区之前,你用下一个缓冲区更新描述符集。但是当您更新描述符集时,它不能被任何已提交(且尚未完成)的命令缓冲区使用。所以这将需要额外的同步并且与使用单个缓冲区没有太大区别。这样你也不能"pre-record"命令缓冲区。
- 您可以设置一个描述符。要更改其内容(在其中使用不同的缓冲区),您可以通过 VK_KHR_descriptor_update_template 扩展中添加的函数来更新它。它允许将描述符更新记录在命令缓冲区中,因此同步应该更容易一些。它应该允许您 pre-record 命令缓冲区。但它需要一个扩展来支持,所以在不支持它的平台上它不是一个选项。
- 你可能想到的方法 - 你可以有 3 个独立的描述符集。所有这些都使用具有统一缓冲区的相同布局进行分配。然后你用不同的缓冲区更新每个描述符集(你可以使用第一个缓冲区和第一个描述符集,第二个缓冲区和第二个描述符集,第三个缓冲区和第三个描述符集)。现在在记录命令缓冲区期间,当您想使用第一个缓冲区时,您只需绑定第一个描述符集。在下一帧中,您只需绑定第二个描述符集,依此类推。
方法 3 可能是最容易实现的,因为它不需要描述符同步(如果您有同步,则只需要每个 frame-level 同步)。它还允许您 pre-record 命令缓冲区,并且不需要启用任何其他扩展。但正如您所指出的,它需要创建和管理更多资源。
不要忘记您需要创建一个足够大的描述符池以包含 3 个统一缓冲区,但同时您还必须指定您要从中分配 3 个描述符集(每个描述符一个统一缓冲区设置).
您可以在 Intel 的 API without Secrets: Introduction to Vulkan - Part 6 教程中阅读有关描述符集的更多信息。
关于您的问题:
Do I need to create 3 VkDescriptorSetLayout - one for every frame?
否 - 一个布局就足够了(只要所有描述符集在相同的绑定中包含相同类型的资源。
Next do I need to allocate and update corresponding descriptor set
with a corresponding buffer?
根据选项 3 - 是。
And after this do I need to create 3 different command buffers where I
should specify corresponding descriptor set.
这取决于您 re-record 命令是每帧缓冲还是您 pre-record 预先缓冲它们。通常每帧命令缓冲区 re-recorded。但是由于只有一个命令缓冲区需要等到提交完成,您可能需要为每个帧设置一组命令缓冲区,这些缓冲区对应于您的帧缓冲区图像(和描述符集)。因此,在第 0 帧中,您使用命令缓冲区 #0(或为第 0 帧保留的多个命令缓冲区)。在第 1 帧中,您使用命令缓冲区 #1 等
现在您可以为给定帧记录命令缓冲区,并在记录期间提供与给定帧关联的描述符集。
我有一个统一的缓冲区,应该每帧更新一次。为了避免大停顿,我想创建 3 个缓冲区(按我的帧缓冲区的数量),每帧应该交错(0-1-2-0-1-2-...)。但我不明白如何创建描述符并绑定它们。我现在就是这样做的:
- 我创建了一个
VkDescriptorSetLayout
,其中指定我想在某个着色器阶段的绑定位置 0 使用统一缓冲区。 - 我为统一缓冲区创建了一个大小为 3 个描述符的
VkDescriptorPool
。 - 接下来我需要分配描述符集,但是这里我需要多少个描述符集?我只有一个
VkDescriptorSetLayout
,我希望得到一个VkDescriptorSet
。 - 接下来我需要更新创建的描述符集。因为我的着色器中只有一个绑定 (0),所以我只能在
VkDescriptorBufferInfo
中使用一个缓冲区,它将传递给VkWriteDescriptorSet
,然后传递给vkUpdateDescriptorSets
。但是其他两个缓冲区呢?在哪里指定它们? - 当我需要记录命令时,我需要绑定我的描述符集。但直到现在我有一个描述符集,它只为一个缓冲区更新。其他人呢?
我是否需要创建 3 个 VkDescriptorSetLayout
- 每个帧一个?接下来我是否需要使用相应的缓冲区分配和更新相应的描述符集?在此之后我需要创建 3 个不同的命令缓冲区,我应该在其中指定相应的描述符集。
看起来工作量很大 - 数据几乎相同 - 所有绑定、状态保持不变,只有缓冲区发生变化。
这一切听起来很混乱,所以请不要犹豫澄清。
描述符集布局定义描述符集的内容 - 给定集包含哪些类型的资源(描述符)。当您需要多个描述符集和一个统一缓冲区时,您可以使用相同的布局创建所有这些描述符集(布局只是一种描述,一种规范)。这样你只要告诉 driver: "Hey, driver! Give me 3 descriptor sets. But all of them should be exactly the same".
但是因为它们是从相同的布局创建的,并不意味着它们必须包含相同的资源句柄。所有这些(在你的情况下)都必须包含一个统一的缓冲区。但是这个统一缓冲区将使用什么资源取决于你。所以每个描述符集都可以用单独的缓冲区更新。
现在当你想在三个连续的帧中一个接一个地使用 3 个缓冲区时,你可以通过几种不同的方式来实现:
- 您可以设置一个描述符。然后在每一帧中,在你开始准备命令缓冲区之前,你用下一个缓冲区更新描述符集。但是当您更新描述符集时,它不能被任何已提交(且尚未完成)的命令缓冲区使用。所以这将需要额外的同步并且与使用单个缓冲区没有太大区别。这样你也不能"pre-record"命令缓冲区。
- 您可以设置一个描述符。要更改其内容(在其中使用不同的缓冲区),您可以通过 VK_KHR_descriptor_update_template 扩展中添加的函数来更新它。它允许将描述符更新记录在命令缓冲区中,因此同步应该更容易一些。它应该允许您 pre-record 命令缓冲区。但它需要一个扩展来支持,所以在不支持它的平台上它不是一个选项。
- 你可能想到的方法 - 你可以有 3 个独立的描述符集。所有这些都使用具有统一缓冲区的相同布局进行分配。然后你用不同的缓冲区更新每个描述符集(你可以使用第一个缓冲区和第一个描述符集,第二个缓冲区和第二个描述符集,第三个缓冲区和第三个描述符集)。现在在记录命令缓冲区期间,当您想使用第一个缓冲区时,您只需绑定第一个描述符集。在下一帧中,您只需绑定第二个描述符集,依此类推。
方法 3 可能是最容易实现的,因为它不需要描述符同步(如果您有同步,则只需要每个 frame-level 同步)。它还允许您 pre-record 命令缓冲区,并且不需要启用任何其他扩展。但正如您所指出的,它需要创建和管理更多资源。
不要忘记您需要创建一个足够大的描述符池以包含 3 个统一缓冲区,但同时您还必须指定您要从中分配 3 个描述符集(每个描述符一个统一缓冲区设置).
您可以在 Intel 的 API without Secrets: Introduction to Vulkan - Part 6 教程中阅读有关描述符集的更多信息。
关于您的问题:
Do I need to create 3 VkDescriptorSetLayout - one for every frame?
否 - 一个布局就足够了(只要所有描述符集在相同的绑定中包含相同类型的资源。
Next do I need to allocate and update corresponding descriptor set with a corresponding buffer?
根据选项 3 - 是。
And after this do I need to create 3 different command buffers where I should specify corresponding descriptor set.
这取决于您 re-record 命令是每帧缓冲还是您 pre-record 预先缓冲它们。通常每帧命令缓冲区 re-recorded。但是由于只有一个命令缓冲区需要等到提交完成,您可能需要为每个帧设置一组命令缓冲区,这些缓冲区对应于您的帧缓冲区图像(和描述符集)。因此,在第 0 帧中,您使用命令缓冲区 #0(或为第 0 帧保留的多个命令缓冲区)。在第 1 帧中,您使用命令缓冲区 #1 等
现在您可以为给定帧记录命令缓冲区,并在记录期间提供与给定帧关联的描述符集。