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

Vertex batches (geometry groups) and maximum VBO (vertex buffer) size

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

对我来说,这是关于该主题的 2 篇主要有趣的文章:

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(例如 10 Mo)更重要的 VBO 有那么糟糕吗?

2) 对于顶点字节总大小大于4Mo的网格,我们如何处理?我需要将几何图形分成几批吗?

3) 一个批次是否不可避免地存储一个唯一的 VBO 或多个批次可以存储在一个 VBO 中? (这是两种不同的方式,但第一种似乎是正确的选择)。你同意吗?

根据作者的说法,每个批处理一个唯一的 VBO,最大尺寸在 1 到 4 Mo 之间,整个 VBO 必须只包含共享相同 material 和转换信息的顶点数据。因此,如果我必须使用不同的 material 对另一个网格进行批处理(这样顶点就不能与现有的 bathes 合并),我必须创建一个新的批次并实例化一个新的 vbo。

所以根据作者的说法,我的第二种方法是不正确的:不建议将多个批次存储到一个 VBO 中。

Does the maximum byte size mentionned above is an absolute rule ? Is it so bad to allocate VBO with a byte size more important than 4Mo (for example 10 Mo) ?

没有

那是一条(非常)古老的信息,在现代硬件上不一定有效。

导致 4MB 建议的问题与驱动程序能够管理内存有关。如果你分配的内存比 GPU 多,它就需要分页进出。如果您为缓冲区对象使用较小的块,则驱动程序可以更轻松地选择整个缓冲区进行分页(因为它们目前未被使用)。

不过,这并没有那么重要。为了提高性能,您可以做的最好的事情就是 避免 完全超出内存限制。分页进出会影响性能。

所以不用担心。请注意,我已将此 "advice" 从 Wiki 中删除。

So according to the author my second method is not correct : it's not adviced to store several batches into a single VBO.

我认为您混淆了 "batch" 和 "mesh" 这个词。但这是完全可以理解的;您阅读的那篇文档的作者似乎也没有意识到其中的区别。

出于本次讨论的目的,"mesh" 是用单个 rendering command 渲染的东西,它在概念上与您要渲染的其他东西是分开的。网格以特定状态渲染。

一个 "batch" 指的是一个或多个 可以 使用单独的渲染命令渲染的网格。但是,为了提高性能,您使用技术让它们全部使用 same 渲染命令进行渲染。这就是一个批次的全部。

"Batching" 是获取一系列网格并使其有可能将它们作为批处理渲染的过程。 Instanced rendering 是批处理的一种形式;每个实例都是一个单独的 "mesh",但是您通过一次渲染调用渲染了很多实例。他们使用实例计数来获取每个实例的状态数据。

批处理采用实例渲染以外的多种形式。批处理通常发生在艺术家级别。虽然 modeller/texture 美工人员可能希望将一个角色拆分成单独的部分,每个部分都有自己的纹理和材质,但图形程序员告诉他们将它们保持为一个单一的网格,可以使用相同的 textures/materials .

有了更好的硬件,可以减少批处理规则。使用 array textures,您可以为每个网格指定一个特定的 ID,用于选择在获取纹理时使用的数组层。这允许艺术家为这些角色提供更多纹理变化,而无需将批处理分成多个渲染调用。 Ubershaders 是另一种形式,其中着色器使用该 ID 来决定如何进行照明而不是(或除了)纹理获取。

您所引用的人所说的批处理是......好吧,非常困惑。

What do you think about that?

嗯,坦率地说,我认为您的第二个 link 中的人应该被忽略。他的代码的第一行:class Batch sealed 不是有效的 C++。这是 Microsoft 的一些 C++/CX 发明,在这种情况下很好。但是他试图将其作为纯 C++ 传递出去;那是不好

我对他的代码质量也不是特别满意。他经常自相矛盾。例如,他谈到了能够分配合理大小的内存块的重要性,这样驱动程序就可以更自由地移动东西。但是他的 GuiVertex class 臃肿得要命 。它使用完整的 16 个字节,四个浮点数,仅用于 colors。 4 个字节(作为规范化的无符号整数)就足够了。类似地,他的纹理坐标是浮点数,而短裤(作为无符号规范化整数)对于他的用例来说没问题。这将使他的每个顶点成本从 32 个字节减少到 10 个字节;这不仅仅是 3:1 减少。

当您使用合理大小的顶点数据时,4MB 会更长。最好的部分是什么?他 link 编辑了 OpenGL Wiki 页面 tells you to do exactly this. 但他没有这样做。

更不用说,他显然已经为 GUI 编写了这个批处理管理器(正如他的 GuiVertex 类型所暗示的)。然而,GUI 可能是游戏开发中 最不友好的 批处理渲染方案。您经常需要更改状态,例如绑定纹理、当前程序(是否从纹理读取)、混合模式、剪刀盒等。

现在有了现代 GPU,肯定有办法使 GUI 渲染器更加批处理友好。但他从不谈论它们。他没有提到使用 gl_ClipDistance 作为使用每个顶点数据进行剪刀盒的方法的技术。他没有谈论 ubershader 的使用,他的顶点格式也没有提供允许这样的事情的 ID。

如前所述,批处理就是不让对象之间的状态发生变化。但他完全关注顶点状态。他没有谈论纹理、程序等。他没有谈论允许多个对象成为同一批次的一部分同时仍然具有单独变换的技术。

他的 class 不能真正用于任何不能只是单个网格的批处理。