对于通过 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个子通道完好无损。
我认为规范可以在可重用性业务上更加清晰。
对于那些已经成功尝试过类似事情的人来说,你的方法是什么?
这意味着,如果没有 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
相匹配)并查看验证层是否会抱怨。