方法A)一个描述符集和多个缓冲区(每个对象一个缓冲区)。在渲染之前,仅将描述符集绑定到管道一次。使用vkCmdPushDescriptorSetKHR将缓冲区绑定到绘制调用之间的唯一描述符集。
方法B)多个描述符集(每个对象一个描述符集)。 (使用多个或单个缓冲区,这并不重要。如果使用单个缓冲区,我认为它被称为动态统一缓冲区,而我要做的就是在绑定描述符集时指定偏移量。)绑定一个或多个缓冲区描述符集仅在渲染前设置一次。使用vkCmdBindDescriptorSets将匹配的描述符集绑定到绘制调用之间的管道。
我已经习惯了DX12,所以方法A看起来更自然,因为据我了解,Vulkan中的vkCmdPushDescriptorSetKHR就像DX12中的SetGraphicsRootConstantBufferView一样,因为它们都将缓冲区绑定到管道,并且此操作被其他命令缓冲。但是Vulkan确实感觉有点慢,主要是因为1)它不是根描述符,2)Vulkan不使用GPU地址,并且3)需要描述符集写操作。
#1 楼
您最终想要做的是在两次绘制调用之间更改一些状态,该状态允许选择在渲染过程中使用的一组只读数据。有很多技术可以使用。推常量可用于为存储在SSBO中的任意大小的数组提供索引。实际上,对于不同的状态频率(每个帧与每个对象),单个推入常数可以用于为多个SSBO提供多个索引。高16位可以用于一个频率,低16位可以用于另一个频率。
这听起来可能比其他方法慢,因为您正在访问全局数据而不是实际的UBO。但是在某些硬件上,它不会改变任何东西。例如,AMD硬件实际上没有用于UBO的任何专用硬件,因此它们将UBO实施为只读SSBO。即使在具有专用UBO的硬件上,您正在读取的状态数据也可能会被快速缓存,因为所有调用都将从数组中读取相同的值。因此,除非您动用SSBO缓存,否则这不会对性能产生太大影响。
给定
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
类型,也存在动态UBO描述符。基本上,这个想法是,当您调用vkCmdBindDescriptorSets
时,可以为任何动态描述符提供一个动态偏移量。偏移量表示从附加到该描述符的缓冲区到要获取的UBO数据所在的字节偏移量。因此,您将所有数据都放在一个大缓冲区中,并且可以通过调用具有不同动态偏移量的vkCmdBindDescriptorSets
来选择用于特定渲染调用的特定数据。现在,
vkCmdBindDescriptorSets
似乎比设置push constant更重磅。但是该实现具有所有必需的信息,以便能够看到您正在使用与该命令缓冲区中的上一个调用相同的描述符,从而不执行该操作的实际部分,只需将动态偏移量应用于任何动态描述符。因此,该部分就不必担心。推描述符主要用于纹理/采样器/图像绑定,它们没有像动态偏移那样方便的机制,它可以让您快速切换哪个图像/您正在使用的采样器。话虽如此,即使对于UBO / SSBO,推送描述符也是一个有用的概念。它们是否比动态描述符快要进行概要分析。
原则上的缺点是显而易见的:push描述符是扩展。每个Vulkan 1.0实施都必须具有动态UBO偏移量和推送常数。推送描述符甚至不是Vulkan的可选功能。它们对于获得KHR扩展足够重要,但是它们并没有作为可选功能加入Vulkan 1.1。这不是理论上的问题:NVIDIA和Intel似乎在台式机上支持它,但没有官方的AMD驱动程序。
毕竟,这正是它们的用途。
评论
$ \ begingroup $
这正是我所需要的。非常感谢!
$ \ endgroup $
– ACskyline
19年6月13日在3:47
评论
“与DX12相比,资源绑定过程似乎有点复杂。”老实说,D3D12似乎更复杂。 Vulkan只是“描述符”和“描述符集”;这一切都非常简单和规则。我仍然没有发现“根描述符”是什么,或者它与非“根”描述符的关系。“我没有考虑推送常数,因为我想要一种通用方法,我也可以将其应用于其他种类的统一缓冲区(即,每遍,每帧,每场景等)。”为什么不能将推常量也用于这些常量呢?另外,如果效率如此重要,则应使用能够为当前任务提供最佳性能的工具。不是最“普遍”的。