VBO 何时比“简单”的 OpenGL 基元 (glBegin()) 更快?

Posted

技术标签:

【中文标题】VBO 何时比“简单”的 OpenGL 基元 (glBegin()) 更快?【英文标题】:When are VBOs faster than "simple" OpenGL primitives (glBegin())? 【发布时间】:2010-09-30 15:23:58 【问题描述】:

在听说顶点缓冲区对象 (VBO) 多年后,我终于决定尝试使用它们(我的东西通常对性能并不重要,显然......)

我将在下面描述我的实验,但长话短说,我发现“简单”直接模式(glBegin()/glEnd())、顶点数组(CPU 端)和 VBO( GPU 端)渲染模式。我试图理解为什么会这样,以及在什么情况下我可以期望看到 VBO 显着超过其原始(双关语)祖先。

实验细节

为了实验,我生成了一个包含大量点的(静态)3D 高斯云。每个点都有与之关联的顶点和颜色信息。然后我在连续的帧中围绕云旋转相机,形成一种“轨道”行为。同样,这些点是静态的,只有眼睛在移动(通过 gluLookAt())。数据在任何渲染之前生成一次并存储在两个数组中以用于渲染循环。

对于直接渲染,整个数据集在单个 glBegin()/glEnd() 块中渲染,循环包含对 glColor3fv() 和 glVertex3fv() 的单个调用。

对于顶点数组和 VBO 渲染,整个数据集通过一个 glDrawArrays() 调用来渲染。

然后,我只需在一个紧凑的循环中运行它一分钟左右,然后使用高性能计时器测量平均 FPS。

性能结果##

如上所述,我的台式机(XP x64、8GB RAM、512 MB Quadro 1700)和笔记本电脑(XP32、4GB 内存、256 MB Quadro NVS 110)的性能无法区分。然而,它确实随着点数的增加而按预期缩放。显然,我也禁用了 vsync。

笔记本电脑运行的具体结果(使用 GL_POINTS 渲染):

glBegin()/glEnd():

1K 点 --> 603 FPS 10K 点 --> 401 FPS 100K 点 --> 97 FPS 1M 点 --> 14 FPS

顶点数组(CPU 端):

1K 点 --> 603 FPS 10K 点 --> 402 FPS 100K 点 --> 97 FPS 1M 点 --> 14 FPS

顶点缓冲对象(GPU 端):

1K 点 --> 604 FPS 10K 点 --> 399 FPS 100K 点 --> 95 FPS 1M 点 --> 14 FPS

我用 GL_TRIANGLE_STRIP 渲染了相同的数据并且得到了类似的无法区分(尽管由于额外的光栅化而比预期的要慢)。如果有人想要,我也可以发布这些数字。 .

问题

什么给了? 我必须做些什么才能实现 VBO 承诺的性能提升? 我错过了什么?

【问题讨论】:

也许 nVidia 的驱动程序在你背后优化?问题是不可能单独测试...... 我也有这个想法。我还认为,也许 Quadro 与 GeForce(即专业与游戏玩家卡)可能与此有关。 他们如何优化 glBegin()/glEnd() 版本? @Shy:出于好奇,你为什么要编辑这个问题? @Drew Hall:您可以在某处发布程序或来源吗?我想试一试(也许观察一两分钟的云。) @aib:抱歉,我想发布代码,但我无法发布代码。我会看看我是否可以在某处发布一个可执行文件或至少一个屏幕截图。 粘贴执行绘制的代码——glBegin() 和直到 glEnd() 的所有代码——以及创建 VBO 的代码。不管它有多老,都能找到它的底部会很好。 【参考方案1】:

优化 3D 渲染有很多因素。 通常有4个瓶颈:

CPU(创建顶点、APU 调用等) 总线(CPUGPU 传输) 顶点(固定函数管道执行上的顶点着色器) 像素(填充、片段着色器执行和 rops)

您的测试给出了不正确的结果,因为您在最大化顶点或像素吞吐量的同时拥有大量 CPU(和总线)。 VBO 用于降低 CPU(更少的 api 调用,与 CPU DMA 传输并行)。由于您不受 CPU 限制,因此它们不会给您任何收益。这是优化 101。例如,在游戏中 CPU 变得很宝贵,因为它需要用于 AI 和物理等其他事物,而不仅仅是用于发出大量的 api 调用。很容易看出,将顶点数据(例如 3 个浮点数)直接写入内存指针比调用将 3 个浮点数写入内存的函数要快得多——至少可以节省调用的周期。

【讨论】:

我的理解是 Vertex Arrays (GL 1.1) 用于降低 CPU(最小化函数调用),而 VBO 在此基础上也可以降低总线活动。我认为我的实验将是总线绑定(或简单的 glBegin() 绘图的 CPU 绑定),但我想我错了。你能评论吗?谢谢! vbos 只会在您的几何图形是静态的(跨帧重用)时降低总线活动。还要确保只在这种情况下标记它们写入。进一步阅读:developer.nvidia.com/object/using_VBOs.html @starmole:很好的链接。我将 VBO 标记为 STATIC_DRAW,但没有将它们标记为 WRITE_ONLY(我没有使用 glMapBuffer())。我会做出改变,看看会发生什么。感谢您的提示! @starmole:我尝试将映射设置为 WRITE_ONLY(实际上,尝试了所有三种模式),但没有任何效果。在 glBufferData 之后立即进行快速映射/取消映射——我这样做对吗?还是一头雾水... :( @Drew Hall:奇怪。以下是游戏引擎中的一些其他经验测量,它正在评估从顶点数组到 VBO 的移动:glest.org/glest_board/index.php?topic=6260.msg65644#msg65644【参考方案2】:

通过阅读红皮书,我记得有一段文字指出 VBO 可能更快取决于硬件。一些硬件优化了这些,而另一些则没有。您的硬件可能没有。

【讨论】:

谢谢。很难看出将数据驻留在卡上并不总是更快(即使没有对驱动程序进行重大的额外优化),但我想我很难让我的代码成为“总线绑定”。 @Will Mc(再次):也很难想象 Nvidia 在实施 VBO 优化方面不会处于最前沿。他们似乎更有可能(也)找到了一种优化通往我的直接路径的方法。【参考方案3】:

假设我没记错,我的 OpenGL 老师,在 OpenGL 社区中很有名,他说他们在静态几何上更快,这将在典型的游戏中渲染很多时间,这将是桌椅和小型静态实体。

【讨论】:

【参考方案4】:

可能缺少一些东西:

    这是一个疯狂的猜测,但您的笔记本电脑的卡可能根本没有这种操作(即模拟它)。

    您是在将数据复制到 GPU 的内存(通过 glBufferData(GL_ARRAY_BUFFERGL_STATIC_DRAWGL_DYNAMIC_DRAW 参数)还是在内存中使用指向主(非 GPU)数组的指针?(这需要每帧都复制它,因此性能很慢)

    您是否将 indices 作为另一个通过 glBufferDataGL_ELEMENT_ARRAY_BUFFER 参数发送的缓冲区传递?

如果这三件事都做好了,性能提升是很大的。 对于 Python (v/pyOpenGl),它在比 100 个元素大的数组上快大约 1000 倍, C++ 快 5 倍,但在 50k-10m 个顶点的数组上。

这是我对 c++ (Core2Duo/8600GTS) 的测试结果:

 pts   vbo glb/e  ratio
 100  3900  3900   1.00
  1k  3800  3200   1.18
 10k  3600  2700   1.33
100k  1500   400   3.75
  1m   213    49   4.34
 10m    24     5   4.80

因此,即使是 10m 的顶点,它也是正常的帧速率,而使用 glB/e 它是缓慢的。

【讨论】:

【参考方案5】:

14Mpoints/s 并不是很多。很可疑我们可以看到绘制的完整代码以及初始化吗? (将 14M/s 与 Slava Vishnyakov 获得的 240M/s (!) 进行比较)。更令人怀疑的是,它在 1K 绘制时下降到 640K/s(与他的 3.8M/s 相比,这看起来被 ~3800 SwapBuffers 所限制,无论如何)。

我敢打赌,测试并不能衡量你认为它衡量的内容。

【讨论】:

以上是关于VBO 何时比“简单”的 OpenGL 基元 (glBegin()) 更快?的主要内容,如果未能解决你的问题,请参考以下文章

多个网格、多个 VBO、多个 VAO、OpenGL 4.1

OpenGL中的动态VBO

OpenGL - 从 DisplayList 转换为使用 VBO

C++ 和 OpenGL,VBO 顶点与骨骼信息交错

OpenGL显示列表比立即模式慢?

使用 VBO 在 OpenGL 中渲染人群