英文:
Do we and why do we need to arrange descriptor sets/slots in an ascending order of update frequency in vulkan
问题
我已经阅读了关于Vulkan中根据更新频率对描述符集进行分组的一些信息。当更新描述符集时,根据相同的更新频率直接更新整个组是有道理的,这肯定有助于性能。
我还阅读了一些关于着色器中不同描述符集/槽的顺序的建议,它们都建议频繁更新的描述符集应该排在后面(即在我的看法中,具有较大的集合ID),但我无法完全理解这一点,并且我通过Google没有找到任何有用的信息,解释为什么我们应该遵循这种顺序。
顺序如下(来自vkguide.dev):
描述符集编号0将用于引擎全局资源,并且每帧绑定一次。描述符集编号1将用于每个通道的资源,并且每个通道绑定一次。描述符集编号2将用于材质资源,描述符集编号3将用于每个对象的资源。这样,内部渲染循环只会绑定描述符集2和3,性能将很高。
以来自vkguide.dev的上述引用为例,我认为如果我们使用0用于每个对象的资源和3用于每帧的资源,总绑定次数是相同的。
是否有人可以分享一些有关这个问题的有用提示?提前感谢您。
我认为这是对Vulkan机制的一种理解,我尝试的唯一事情就是找到关于这个问题的有用信息,但目前我找到的答案都没有完全满足我的需求。
英文:
I've read some information about grouping of the descriptor sets according to update frequency in vulkan. That makes sense when updating the descriptor sets we can directly update the whole group with the same update frequency, it is surely good for performance.
I've also read some recommendations about the orders of different descriptor sets/slots in shader, they all recommend that the sets updated more frequently should be arranged more behind(i.e. in my opinion, with larger set id), but I can't fully understand that and I didn't find any useful information through google why should we follow that order.
The order goes like this:
> The descriptor set number 0 will be used for engine-global resources, and bound once per frame. The descriptor set number 1 will be used for per-pass resources, and bound once per pass. The descriptor set number 2 will be used for material resources, and the number 3 will be used for per-object resources. This way, the inner render loops will only be binding descriptor sets 2 and 3, and performance will be high.
Take the above quote from vkguide.dev as an example, I think the total binding times is the same if we use 0 for per-object resources and 3 for per frame resouces.
Would anyone please share me some useful tips around this question? Thanks in advance.
I think this is an understanding about the vulkan mechanism, the only thing I tried is to find useful information about this and none of the answer I found fully satisfied me currently.
答案1
得分: 4
我认为,如果我们对每个对象的资源使用0,对每帧资源使用3,那么总绑定次数是相同的。
你所说的“总绑定次数”是什么意思?如果你指的是调用 vkCmdBindDescriptorSets
的次数,那么是的,这些调用的次数大致相同。但如果你说的是执行这些调用所需的实际时间,那就是另一回事了。
按照绑定频率的顺序排列描述符集的目的是为了不必绑定太多的集合。
让我们以你提供的网站示例为例。你要渲染两个对象。这些对象在同一个渲染通道中,并使用相同的材质,但它们具有不同的每个对象属性。我说“不同”,并不是指它们仅仅使用不同的资源。我指的是资源的结构不同。一个对象可能使用2个2D纹理,而另一个对象可能使用1个立方体贴图纹理。
重要的是,这两个对象对于集合3具有不同的描述符集布局,但对于集合0、1和2使用相同的布局。
所以你绑定集合0、1和2。然后你绑定集合3并渲染一个对象,然后再次绑定集合3并渲染另一个对象。
现在,让我们反转集合的顺序。所以0是每个对象的,1是每个材质的,等等。
你开始绑定集合0、1、2和3。为什么?因为在此命令缓冲区内,除非之前已经绑定了某些东西到0,否则你不能绑定到1、2或3。你可以在描述符集部分的管线布局兼容性一节中看到这一点。因为我们还没有开始渲染任何东西,所以集合0中没有任何内容,所以我们的第一个描述符集绑定命令必须绑定一些东西到它上面。
然后我们渲染第一个对象。现在我们想绑定一个不同的集合0。然而,我们再次遇到了前面提到的问题。
因为新的描述符集使用与之前不兼容的布局,仅仅绑定集合0将“干扰”上面的所有集合。因此,集合1、2和3变得无效。
因此,我们必须再次绑定集合1、2和3,尽管使用相同的描述符。这会花更多的时间,因为你要绑定更多的东西。实现还必须重新分配这些描述符分配给管线资源的方式。
也就是说,尽管我们的两个对象在技术上在集合1、2和3中使用相同的布局,但Vulkan并不认为这些集合是兼容的,因为它们在集合0上使用不同的布局。在我们的原始版本中,仅仅绑定到集合3是可行的,因为Vulkan从集合0开始确定布局兼容性。这两个布局在集合0、1和2上使用相同的布局,只在集合3上有区别。因此,你可以只改变集合3,而其他集合仍然有效。
将管线布局中的集合视为一堆对象。你可以轻松更改堆栈顶部的对象而不影响下面的对象。但要更改底部的项目需要移除上面的所有项目。
英文:
> I think the total binding times is the same if we use 0 for per-object resources and 3 for per frame resouces.
What do you mean by "total binding times"? If you mean how many calls you make to vkCmdBindDescriptorSets
, then yes, the number of such calls would be roughly equivalent. If however you're talking about the actual time it takes to execute those calls, that's a different matter.
The point of ordering descriptor sets by binding frequency is so that you don't have to bind as many sets.
Let's take the example from the site you linked to. You're rendering two objects. These objects are in the same pass and use the same material, but they have different per-object properties. And when I say "different", I don't mean that they just use different resources. I mean that the structure of the resources are different. One of them might have 2 2D textures, and the other might use 1 cubemap texture.
The point being that these two objects have different descriptor set layouts for set 3, but use the same layouts for sets 0, 1, and 2.
So you bind sets 0, 1, and 2. Then you bind set 3 and render one object, then bind set 3 again and render the other object.
Now, let's reverse the set order. So 0 is per-object, 1 is per-material, etc.
You start by binding sets 0, 1, 2, and 3. Why? Because you cannot bind to 1, 2, or 3 unless something was previously bound (within this command buffer) to 0. You can see this in the section on pipeline layout compatibility in the descriptor set section. Since we haven't started rendering anything yet, nothing is in set 0, so our first descriptor set binding command must bind something to it.
So then we render the first object. Now we want to bind a different set 0. However, we again run afoul of the aforementioned section.
Because the new descriptor set uses a different, incompatible layout from the previous one, binding just set 0 will "disturb" all of the sets above it. Thus sets 1, 2, and 3 become invalid.
Therefore, we must also re-bind sets 1, 2, and 3, despite using the same descriptors. And that's going to take longer because you're binding a bunch more stuff. And the implementation is going to have to redistribute how those descriptors are assigned to the resources for the pipeline.
That is, even though our two objects technically use the same layouts in sets 1, 2, and 3, Vulkan does not consider these sets to be compatible because they use different layouts for set 0. In our original version, binding to just set 3 works because Vulkan starts layout compatibility from set 0. The two layouts use the same layout for sets 0, 1, and 2, and only differ at set 3. Therefore, you can change just set 3 and the other sets remain fine.
Think of the sets within a pipeline layout like a stack of objects. You can easily change the top object on the stack without disturbing the ones under it. But changing the bottom item requires removing all of the ones above it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论