顶点批次(几何组)和最大VBO(顶点缓冲区)大小

3

我进行了很多关于将顶点数据收集到常被称为批处理的组中的方法的研究。

以下是对该主题最有趣的两篇文章:

https://www.opengl.org/wiki/Vertex_Specification_Best_Practices

http://www.gamedev.net/page/resources/_/technical/opengl/opengl-batch-rendering-r3900

第一篇文章介绍了如何操作VBO的最佳实践(最大大小、格式等)。
第二篇文章展示了一个简单的例子,说明如何使用批处理来管理顶点内存。根据作者的说法,每个批次必须包含一个VBO实例(以及一个VAO),他强烈强调VBO的最大大小在1Mo(1000000字节)到4Mo(4000000字节)之间。第一篇文章也提供了相同的建议。我引用:“根据nVidia的一个文档,1MB到4MB是一个很好的大小。驱动程序可以更容易地进行内存管理。对于所有其他实现(如ATI/AMD、Intel、SiS)也应该是这种情况。”
我有几个问题:
1)上述最大字节大小是否是绝对规则?分配比4Mo(例如10Mo)更重要的字节大小的VBO是否那么糟糕?
2)对于总顶点字节大小大于4Mo的网格,我们该怎么办?我需要将几何图形拆分成多个批次吗?
3)一个批次是否必须作为属性存储一个唯一的VBO,或者可以将多个批次存储在单个VBO中?(这是两种不同的方式,但第一种方式似乎是正确的选择)。你同意吗?
根据作者的说法,每个批次处理一个唯一的VBO,大小在1到4 Mo之间,整个VBO必须仅包含共享相同材质和变换信息的顶点数据。因此,如果我需要将另一个具有不同材质的网格进行批处理(因此无法将顶点与现有批次合并),我必须创建一个新的批次,并实例化一个新的VBO。
因此,根据作者的说法,我的第二种方法是不正确的:不建议将多个批次存储在单个VBO中。
1个回答

5
上述提到的最大字节大小是否是绝对规则?分配比4Mo(例如10Mo)更重要的字节大小的VBO是否那么糟糕?
不是。
这是一个(非常)古老的信息,不一定在现代硬件上有效。
导致4MB建议的问题是关于驱动程序能够管理内存。如果您分配比GPU多的内存,则需要将其分页进出。如果您对缓冲区对象使用较小的块,则驱动程序更容易选择整个缓冲区进行分页出(因为它们当前未被使用)。
然而,这并不太重要。您可以为性能做的最好的事情是完全避免超过内存限制。分页会影响性能。
所以不用担心。请注意,我已经从Wiki中删除了这个“建议”。
根据作者的说法,我的第二种方法是不正确的:不建议将几个批次存储到单个VBO中。
我认为你正在将“批处理”与“网格”混淆。但这是完全可以理解的;您所阅读的文档的作者似乎也没有识别出差异。

在本讨论中,“网格”是指使用单个渲染命令呈现的物体,这个概念与其他需要呈现的物体是相互独立的。网格会使用特定状态进行呈现。

“批处理”是指一个或多个可能需要使用不同渲染命令呈现的网格。但是,为了提高性能,您可以使用技术使它们都能够使用相同的渲染命令进行呈现。这就是批处理的全部内容。

“批处理”是将一系列网格变成可呈现为批处理的过程。实例化渲染是批处理的一种形式;每个实例都是单独的“网格”,但是您正在使用一个渲染调用来呈现大量的实例。它们使用其实例计数来获取其每个实例的状态数据。

批处理除了实例渲染外,还有很多形式。批处理通常发生在艺术家的层面上。虽然建模师/贴图艺术家可能希望将角色分成单独的部分,每个部分都有自己的纹理和材质,但图形程序员告诉他们将它们保持为单个网格,可以使用相同的纹理/材质进行渲染。
随着硬件的改进,批处理规则可以减少。使用数组纹理,您可以为每个网格提供特定的ID,它用于在获取纹理时选择要使用的数组层。这使得艺术家可以为这样的角色提供更多的纹理变化,而不会将批处理分成多个渲染调用。Ubershaders 是另一种形式,其中着色器使用该 ID 来决定如何进行光照,而不是(或除了)纹理获取。
你引用的那个人所谈论的批处理类型... 嗯,非常混乱。
“你对此有什么看法?”
坦率地说,我认为你第二个链接中的人应该被忽略。他代码的第一行:class Batch sealed 不是有效的C++代码。这是微软发明的C++/CX,在那种情况下很好。但他试图将其作为纯C++来使用,那就不好了。
我也不太 impressed 他的代码质量。他经常自相矛盾。例如,他谈到了能够分配合理大小的内存块的重要性,以便驱动程序可以更自由地移动事物。但他的GuiVertex类非常臃肿。它使用了16个字节,四个浮点数,仅用于颜色。4个字节(作为规范化的无符号整数)就足够了。同样,他的纹理坐标是浮点数,而对于他的用例来说,shorts(作为无符号规范化整数)就足够了。这将把每个顶点的成本从32字节降至10字节,这是超过3:1的减少。

合理使用顶点数据可以使4MB的数据更加充分利用。最好的部分是?OpenGL Wiki页面告诉你要做这个。但他没有这样做。

更不用说,他显然已经为GUI编写了此批处理管理器(由他的GuiVertex类型所暗示)。然而,在游戏开发中,GUI可能是最不适合批量渲染的场景。您经常需要更改状态,例如绑定纹理,当前程序(是否从纹理读取),混合模式,剪切框等。

现代GPU现在有许多方法可以使GUI渲染器更加批量友好。但他从未谈论过它们。他没有提到使用gl_ClipDistance作为使用每个顶点数据进行剪切框的方法。他没有谈论ubershader的使用,也没有他的顶点格式提供ID,可以允许这种事情发生。

如前所述,批处理是关于对象之间没有状态更改的。但他完全专注于顶点状态。他没有讨论纹理、程序等。他没有讨论允许多个对象成为同一批次的技术,同时仍然具有单独的变换。
他的类实际上不能用于任何不能只是单个网格的批处理。

非常感谢您提供这个非常有趣的答案 :)!当我写下我的帖子的第一个版本时,我正确地理解了网格和批次之间的区别!我的主要问题是关于VBO的最大大小以及如何在内存中组织我的批次(一个批次是否存储在唯一的VBO中,或者一个VBO可以包含多个批次)。我已经更新了我的帖子,并给出了Sponza网格的示例,提出了3个渲染它的建议!我想知道您对此的看法!非常感谢您的帮助! :) - user1364743
1
@user1364743:好的,你已经将你的问题改成了一个完全不同的问题,这需要一个完全不同的答案。如果你想问那个问题,那很好。这就是“提问”按钮的作用。Stack Overflow是一个问答网站,而不是一个论坛。你不能通过将其附加到现有问题来提出后续问题。 - Nicol Bolas
好的!我刚刚添加了我的帖子。这是链接:https://dev59.com/zpPfa4cB1Zd3GeqPHcBU - user1364743

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接