动态选择命令缓冲区更新

问题描述 投票:0回答:1

对于通过 VkQueueSubmit() 提交 Vulkan 渲染命令,我知道大多数渲染器只是每帧重建 cmdbufs,因为这使得处理动态场景变得更简单。 然而,我想挑战自己构建一个渲染器,仅根据需要更新/使 cmdbuf 无效,否则重用它们。

对于 3D 编辑器之类的东西,场景的某些方面会在一段时间内保持不变,还有其他方面,例如即时模式 gui 渲染 (ImGui),它需要每帧更新 gui 通道的 cmdbufs 。没关系。

我构建的机制允许我按需构建 cmd,并重用不改变的 cmdbuf。初始版本要么重用所有以前构建的命令,要么重建它们全部——完全重用或完全重建。但是,我确实放入了脚手架来进行部分场景更新,但它需要尚未提供的额外功能才能完全工作。

最近,我开始致力于隔离 GUI 通道,以便它可以自行更新,而无需重建所有命令。

我的第一个破解是改变框架的通道构图:

 # All these commands are recorded within a primary cmdbuf.
|==================== RenderPass =====================|
| Subpass0(scene) | Subpass1(Gui) | Subpass2(Present) |

进入此:

 # All cmds within 1 primary, except the secondary cmdbuf stuff
|==================== RenderPass =====================|
| Subpass0(scene) | Subpass1(Gui) | Subpass2(Present) |
        |               |                 |            
 In primary cmdbuf,  In secondary,  Back to primary 

在构建该项目时,我意识到以下要求阻止了这样做。

VUID-vkCmdExecuteCommands-内容-06018 如果在渲染通道实例中调用 vkCmdExecuteCommands 从 vkCmdBeginRenderPass 开始,其内容参数必须已设置为
VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS , 或 VK_SUBPASS_CONTENTS_INLINE_AND_SECONDARY_COMMAND_BUFFERS_EXT

这意味着,如果没有扩展名

VK_EXT_nested_command_buffer
,则不可能拥有在主和辅助 cmdbuf 中创建混合子通道的 RenderPass。 RenderPass 必须包含由辅助 cmdbuf 或仅由主 cmdbuf 组成的子通道,但不能同时由两者组成。

我的下一次尝试是将通道组成更改为:

# The renderpasses themselves are written in 1 primary cmdbuf
# There is a vkExecuteCmds() in RenderPass1
||== RenderPass0 ==|| ||== RenderPass1 ==|| ||== RenderPass2 ==|
|  Subpass(scene)  |  |   Subpass(Gui)   |  | Subpass(Present) |
         |                    |                      |          
 In primary cmdbuf,     In secondary,         Back to primary 

在构建这个之前,我尝试阅读规范来看看这是否可行,但我没有找到答案。所以,我决定尝试一下。

这个想法是记录一次主cmdbuf,以及帧的大致组成。 Gui 子通道构建在辅助 cmdbuf 中,位于其自己的 RenderPass 中。然后,在下一帧,辅助 cmdbuf 被重建,但主 cmdbuf 保持不变。

当我尝试运行此程序时,出现此错误:

Validation Error: [ VUID-vkQueueSubmit-pCommandBuffers-00070 ] 

Object 0: handle = 0x24474d05b40, name = CmdBuf2nd:0, type = VK_OBJECT_TYPE_COMMAND_BUFFER; 
Object 1: handle = 0x24474bcdd00, name = CmdBuf:0, type = VK_OBJECT_TYPE_COMMAND_BUFFER; 
| MessageID = 0xaeecdc0b | vkQueueSubmit(): 
pSubmits[0].pCommandBuffers[0] was called in VkCommandBuffer 0x24474bcdd00[CmdBuf:0] 
which is invalid because bound VkCommandBuffer 0x24474d05b40[CmdBuf2nd:0]
 was destroyed or rerecorded. 

The Vulkan spec states: Each element of the pCommandBuffers member of each element of pSubmits must be in the pending or executable state 
(https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.htm

似乎只要辅助cmdbuf发生变化,就必须重新创建主cmdbuf。这就是我在规范中寻找的信息,但找不到。

我认为我还没有尝试过的唯一组合是将子通道记录到辅助cmdbuf,每帧重新记录主cmdbuf,并且可能只每帧重新记录gui辅助cmdbuf,同时保持其他2个子通道完好无损。

我认为规范可以在可重用性业务上更加清晰。

对于那些已经成功尝试过类似事情的人来说,你的方法是什么?

vulkan
1个回答
0
投票

这意味着,如果没有 VK_EXT_nested_command_buffer 扩展,则不可能拥有在主和辅助 cmdbuf 中创建的混合子通道的 RenderPass。 RenderPass 必须包含由辅助 cmdbuf 或仅由主 cmdbuf 组成的子通道,但不能同时由两者组成。

根据 VU 规则的措辞,这是一个合乎逻辑的结论。然而,它与标准的其他方面相矛盾:

vkCmdBeginRenderPass::contents
的可能值,指定如何提供第一个子通道中的命令,包括:

添加了强调。这清楚地表明

vkCmdBeginRenderPass::contents
应该仅适用于第一个子通道。另请注意,
vkCmdNextSubpass
还会获得具有相同含义的
contents
字段:

contents
指定如何提供下一个子通道中的命令,其方式与
vkCmdBeginRenderPass

的相应参数相同

因此,这两件事的明确意图是您最初的用例旨在发挥作用。根据您的需要,特定的子通道可以是内联的或来自辅助 CB。但这仅适用于该子通道。

需要注意的是,该标准的所有这 3 个部分与 Vulkan 1.0 版本相比基本没有变化。所以这种不一致似乎已经存在了一段时间了。

我建议尝试明显的解决方案(将您的

vkCmdExecuteCommands
与相关子通道的
contents
相匹配)并查看验证层是否会抱怨。

© www.soinside.com 2019 - 2024. All rights reserved.