如何最佳使用VBOs

13

使用VBO的最快和最灵活(适用于大多数情况)的方式是什么?

我正在开发一个OpenGL应用程序,并希望它能达到最佳性能,所以我需要有人回答这些问题。我读了许多问题和答案,但我觉得有太多我不需要的信息,这让我的脑子混乱了...

  • 我应该使用多少个VBO?
  • 我应该如何创建VBO?
  • 如果数据大小不固定,我应该如何更新VBO的数据?
  • 我应该如何渲染VBO?
  • 对于我不想再渲染的VBO中的数据,我应该如何处理?

2
很多你的问题是没有有效答案的。任何这样的答案都将是硬件特定的。 - Nicol Bolas
2个回答

18
  • 我应该使用多少个vbo?

尽量少。切换VBO会带来一些小的但可测量的成本。通常,您将尝试将相似的数据分组到VBO中。例如,在FPS游戏中,所有不同种类的垃圾、小道具等通常位于同一个或仅有少量的VBO中。

这也取决于绘制批次的大小。渲染少于约100个基元的glDraw...调用是次优的(即使15年前也是如此)。因此,您应尽可能对至少100个基元进行批处理。但是,如果单个网格仅具有,比如20个三角形(低多边形计数道具的实例化或类似),每个三角形都在自己的VBO中,则无法再进行更多批处理。

  • 我应该如何创建vbos?

glGenBuffers → glBindBuffer → glBufferData

更新:您可以将null指针传递给glBufferDatadata参数,以初始化缓冲对象而不设置数据。

  • 如果数据大小不固定,我应该如何更新vbos数据?

创建VBO的粒度比数据大小更粗。您的操作系统对主机侧数据已经这样做了,它被称为分页。此外,如果要使用glMapBuffer,使缓冲对象成为主机页大小的倍数非常适合整个系统。

当前系统上的常规页面大小为4kiB。因此,我会选择VBO大小粒度。 更新:顺便说一句,您可以询问操作系统正在使用哪个页面大小。尽管如此,我会就此提出另一个问题。

使用glBufferSubData更新数据或使用glMapBuffer在主机侧映射内存中修改,然后glUnmapBuffer

如果数据超出缓冲区对象的大小,就需要使用glCopyBufferSubData来创建一个新的、更大的缓冲区,并将其复制。请参见最后一段。
“我该如何渲染VBO?” glBindBuffer → glDraw...
“如果我不想再渲染VBO中的数据应该怎么处理?” 如果数据只占用VBO的一部分,并且与其他数据共享,而且没有耗尽内存,那么可以不访问它。理想情况下,您会保留一个索引,在其中跟踪哪个VBO有哪些部分可用于什么样的任务。这非常像内存管理,特别是被称为对象堆栈(obstacks)的一种方案。
但是,最终可能有必要压缩现有的缓冲区对象。为此,您需要创建一个新的缓冲区对象,将其绑定为写入目标,选择旧的缓冲区对象作为读取目标。然后使用glCopyBufferSubData将内容复制到新的、紧凑的缓冲区对象中。当然,然后您需要更新对缓冲区对象名称(=OpenGL ID)和偏移量的所有引用。
因此,编写一个在OpenGL缓冲区对象之上保持实际类型化数据的薄抽象层是有意义的。

@visDEVion:我并不是说你的VBO应该精确地大小为4kiB,而是4kiB的倍数(即向上取整到下一个4096的倍数)。比如说你需要10kiB的内存,那么你会分配一个14kiB大小的VBO。 - datenwolf
假设你需要10kiB的内存,那么你应该分配一个14kiB大小的VBO。难道不应该是12kiB吗? - Qualphey
@visDEVion:啊,我刚才犯了个错误。是应该是12kiB大小的,没错(对于2kiB页面大小来说,10kiB也许有些合理)。 - datenwolf
有没有操作系统页面大小的同义词?我找不到任何东西。 - Qualphey
@visDEVion:同义词?您想查询系统页面大小吗?这取决于操作系统;对于符合POSIX标准的操作系统(Linux,*BSD,MacOS X),您可以使用sysconf(_SC_PAGESIZE);进行查询。http://linux.die.net/man/3/sysconf - 在Windows中有函数GetSystemInfo http://msdn.microsoft.com/en-us/library/windows/desktop/ms724423(v=vs.85).aspx - datenwolf
显示剩余2条评论

-2

我应该使用多少个VBO?

需要多少就用多少,听起来很傻,但事实就是这样。

如果数据大小不固定,我该如何更新VBO的数据?

使用不同的数据和长度覆盖并渲染相同的VBO。

我应该如何创建VBO?

我应该如何渲染VBO?

请参见VBO教程

我应该如何处理不想再渲染的VBO中的数据?

创建一个新的VBO并将数据复制到其中,或者仅渲染内存中的那部分VBO。

要仅渲染部分,请参见glVertexBufferglTexCoordPointer(根据偏移量计算新指针和新大小)。

编辑1:

使用单个VBO来处理所有内容感觉不太对,因为您必须自己管理新顶点位置/纹理坐标的分配,这会变得非常混乱。

最好将小道具分组到VBO中,并批量处理绘图命令。

我可以使用glBufferSubData向缓冲区添加数据(向大小为x的缓冲区添加100个元素)吗?

不行,因为描述中说更新缓冲对象数据存储的子集,而子集是集合内的较小集合。

编辑2

一个很好的教程是学习现代3D图形编程,但它不是特定于VBO的。


哦,每帧网格变化的数量也有所不同。我是否应该创建一个新的VBO来处理它? - Qualphey
6
不建议推荐 NeHe 的过时教程。 - Nicol Bolas
只需使用glBufferSubData在每帧更新VBO,如果之前的缓冲区太小(当然还要复制数据),则分配一个新的缓冲区。 - Quonux
3
@Quonux: 你觉得http://arcsynthesis.org/gltut这个网站怎么样?它是由Nicol Bolas撰写的。 - datenwolf
3
@Quonux:顺便说一下,你的回答充满了不好的建议。你不应该为每一个小东西创建一个VBO。切换VBO会影响性能。虽然不是很大的影响,但如果你在一个大场景中为每个单独的道具分配一个VBO,这将严重损害性能。 - datenwolf
显示剩余3条评论

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