为什么有些命令只能在渲染过程之外记录?

时间:2019-05-14 06:58:38

标签: vulkan

我不知道它是API功能(我几乎可以肯定不是)还是GPU细节,但是为什么例如vkCmdWaitEvents可以记录在渲染过程的内部和外部,但是vkCmdResetEvent只能在外面录制吗?其他命令也是如此。

2 个答案:

答案 0 :(得分:7)

特别是在事件设置方面,他们对渲染过程模型与基于图块的渲染器之间的交互方式造成了破坏。

回想一下the whole point of the complexity of the render pass model is to service the needs of tile-based renderers (TBRs).,当TBR遇到一系列复杂的子通道时,想要执行它们的方式如下。

它为所有子通道的所有渲染命令一次完成所有顶点处理阶段,并将结果顶点数据存储在缓冲区中以备后用。然后,对于每个图块,它对与该图块的构建有关的图元上的每个子遍执行光栅化阶段。

请注意,这是理想情况;特定的事情会使它在不同程度上失败,但是即使那样,它也可能成批失败,在这种情况下您可以执行渲染过程的多个子过程,如下所示。

因此,假设您要在子通道中间设置一个事件。好吧...什么时候真正发生?请记住,在所有前面的命令完成之后,set-event命令实际上会设置事件。在TBR中,如果一切按上述步骤进行,什么时候设置?理想情况下,整个渲染通道的所有顶点处理都应该在任何光栅化之前进行,因此设置事件必须在完成顶点处理之后进行。并且所有栅格化处理都是在逐个图块的基础上进行的,无论哪个基元与该图块重叠都进行处理。由于渲染过程是零散的,因此很难知道单个渲染命令何时完成。

因此,在整个渲染通道完成后,设置事件调用才可能发生。这显然不是很有用。

另一种选择是使发出ckCmdSetEvent调用的行为从根本上重塑实现如何构建整个渲染过程。将子通道分解为事件发生之前的事件和事件发生之后的事件。

但是VkRenderPass如此大而复杂的原因,VkPipeline必须引用渲染过程的特定子过程的原因,以及vkCmdPipelineBarrier在渲染过程中的原因pass要求您指定子通道自相关性,这样TBR实施可以知道 upfront 在何时何地必须打破理想的TBR呈现方案。有一个函数可以在不事先通知的情况下引入这种分解,因此会违反该想法。

此外,Vulkan的设计宗旨是,如果必须以非常低效的方式实施某些事情,那么要么直接做不到,要么API确实使它看起来真的很低效。 vkCmd(Re)SetEvent无法在TBR硬件上的渲染过程中有效地实现,因此您不能在此期间进行。

请注意,vkCmdWaitEvents没有此问题,因为系统知道等待正在等待渲染过程的外部。因此,只是某个特定阶段需要等待事件完成。如果在等待的顶点阶段,那么在该命令的处理开始时设置等待就足够了。如果是分段阶段,则可以在所有光栅化处理的开始时插入等待。这不是处理它的最有效方法,但是由于所有顶点处理均已执行,因此到那时设置事件的可能性就很大。


对于其他类型的命令,请记住,渲染过程中发生的所有事情的依赖图都是在内部 VkRenderPass中定义的。子通道依赖图在那里。您甚至不能在渲染过程中发出普通的vkCmdPipelineBarrier,除非该子过程在子过程相关性图中具有 explicit 自相关性。

那么,如果您不能等待在该子通道或下一个子通道中完成操作,那么在子通道的中间发出计算着色器调度或内存传输操作有什么好处?如果您不能等待操作结束,则无法使用其结果。而且,如果您无法使用其结果...您也可以在渲染通过之前发布它。

不能拥有其他依赖项的原因可以追溯到TBR。依赖图是渲染过程的不可分割的部分,以允许TBR预先了解子过程之间的关系。这样一来,他们就可以知道是否可以构建理想的渲染器,以及何时何地可能出现故障。

由于渲染过程的TBR模型使这种等待变得不切实际,因此没有必要发布这样的命令。

答案 1 :(得分:2)

因为renderpass是一种特殊的构造,它意味着仅将工作集中在帧缓冲区上。

此外,每个子通道都可以并行运行,除非它们之间有明确的依赖性。

这会影响它们如何与其他子通道中的其他指令同步。

执行复制操作会占用内存总线,并且会使依赖它的渲染工作停止。在渲染通道内部执行此操作会创建一个较大的gpu气泡,可以通过将其放在外面并确保在启动渲染通道时完成将其轻松解决。

某些硬件还具有与图形硬件分开的专用复印单元,因此在它们之间进行的同步越少越好。