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

use*_*743 3 opengl vbo vertex-buffer

我做了很多关于如何将顶点数据收集到通常称为批次的组中的研究。

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

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

http://www.gamedev.net/page/resources/_/technical/opengl/opengl-batch-rendering-r3900
Run Code Online (Sandbox Code Playgroud)

第一篇文章解释了如何操作 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中?(这是两种不同的方式,但第一种似乎是正确的选择)。你同意吗?

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

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

Nic*_*las 6

上面提到的最大字节大小是绝对规则吗?分配比 4Mo(例如 10 Mo)更重要的字节大小的 VBO 是否那么糟糕?

不。

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

导致 4MB 建议的问题是驱动程序是否能够管理内存。如果您分配的内存多于 GPU 的内存,则需要将一些内存调入和调出。如果您对缓冲区对象使用较小的块,则驱动程序可以更轻松地选择整个缓冲区进行页面调出(因为它们目前尚未使用)。

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

所以不用担心。请注意,我已从 Wiki 中删除了此“建议”。

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

我认为您将“批量”一词与“网格”混淆了。但这是完全可以理解的;您阅读的该文档的作者似乎也没有意识到其中的区别。

出于本次讨论的目的,“网格”是使用单个渲染命令渲染的事物,它在概念上与您要渲染的其他事物分开。网格以某种状态渲染。

“批次”是指可以使用单独的渲染命令渲染的一个或多个网格。但是,为了提高性能,您可以使用一些技术来允许使用相同的渲染命令来渲染它们。这就是一批。

“批处理”是获取一系列网格体并使其能够批量渲染的过程。实例化渲染是批处理的一种形式;每个实例都是一个单独的“网格”,但您通过一次渲染调用来渲染大量实例。他们使用实例计数来获取每个实例的状态数据。

除了实例渲染之外,批处理还有多种形式。批处理通常发生在艺术家的层面上。虽然建模者/纹理艺术家可能希望将角色分成单独的部分,每个部分都有自己的纹理和材质,但图形程序员告诉他们将它们保留为可以使用相同纹理/材质渲染的单个网格。

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

你所引用的人正在谈论的那种批处理是......嗯,非常混乱。

你对此有何看法?

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

我对他的代码质量也没有特别印象。他经常自相矛盾。例如,他谈到了能够分配合理大小的内存块的重要性,以便驾驶员可以更自由地移动东西。但他的GuiVertex班级却臃肿得可怕。它使用完整的 16 个字节,四个浮点,仅用于颜色。4 个字节(作为标准化无符号整数)就足够了。类似地,他的纹理坐标是浮点数,而短裤(作为无符号标准化整数)适合他的用例。这会将每个顶点的成本从 32 字节减少到 10 字节;减少幅度超过 3:1。

当您使用合理大小的顶点数据时,4MB 会更长。最好的部分是什么?他链接到的 OpenGL Wiki 页面告诉你要这样做但他不这样做。

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

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

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

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