如何在GL_STREAM_DRAW和GL_DYNAMIC_DRAW之间选择?(这是一个关于IT技术的提问标题)

38
我正在使用OpenGL ES 2.0,但我认为这也与非ES相关:当创建VBO时如何知道要选择什么“用途”?
这个特定的VBO将在完全更新之前被使用1到4次,我不确定我必须选择GL_STREAM_DRAW还是GL_DYNAMIC_DRAW。

5个回答

35

根据 OpenGL API,您应该使用DYNAMIC_DRAW

STREAM
当数据存储内容只会被修改一次并且最多使用几次时,请使用STREAM_DRAW。

STATIC
当数据存储内容只会被修改一次并且多次使用时,请使用STATIC_DRAW。

DYNAMIC
当数据存储内容将被重复修改并且多次使用时,请使用DYNAMIC_DRAW。

请确保使用glBufferSubData()更新VBO。


6
考虑到“至多几次”的措辞,我认为应该使用GL_STREAM_DRAW,因为对我来说1到4次就是“几次”了… - lvella

19

使用标志是一个提示,而不是强制性规定。换句话说:如果你使用了“错误”的标志,事情并不会出错。因此我建议你尝试使用全部三种标志:STATIC_DRAW、STREAM_DRAW和DYNAMIC_DRAW,并选择给你最佳性能的标志——很可能它们会并列第一。


16
这是一个有用的实用技巧,但我仍然想知道这两者在“预期”使用方面的区别。一些GPU可能会利用它。 - Kos
4
当他们创造这个区分时,可能心中已经有了一些可能的优化方案,否则它就没有意义。那么这些优化方案是什么呢? - lvella
@lvella:我认为参考手册已经非常清楚了:http://www.opengl.org/sdk/docs/man/xhtml/glBufferData.xml,在“描述”标题下。 - datenwolf
4
“这使得OpenGL实现能够做出更加智能的决策,从而可能显著影响缓冲对象的性能。” 这句话对我来说非常笼统和难以捉摸。 - lvella
4
@bobobobo说的确实如此。事实上,在许多程序错误地使用它们之后,AMD/ATI驱动程序完全忽略了这个提示并通过分析程序的行为选择最佳策略。如果有疑问,我会默认选择DYNAMIC_DRAW。此外,OpenGL规范对此并不是非常清楚。问题在于,必须指定其中之一的标志。设置glHint的GL_DONT_CARE标志也是有效的,这真的是有意义的。 - datenwolf
显示剩余2条评论

11
除了迄今为止给出的答案之外,虽然它与GLES没有直接关系,但我想粘贴来自ARB_buffer_storage扩展第2期的这一节:

2)新标志不直接映射到glBufferData的参数,并且一个标志不能用另一个标志表示。那有关系吗?

大多数应用程序都会错误地使用usage,而它们只是提示而已。标志是必须遵循的硬性规则。它们具有不同的目的。这里的想法是允许实现不必对应用程序进行猜测并执行更少的跟踪,同时允许应用程序具有更多的控制权。我们根据BufferStorage定义BufferData,其中包含最自由的允许标志(基本上,任何东西都可以),但仍向实现传递提示,以便它继续对应用程序进行猜测。

这些标志的问题始终是每个实现可能对如何优化使用提示建议的不同路径具有不同的想法,每个应用程序似乎都对这些优化工作方式有不同的期望。
NVIDIA的桌面GL驱动程序将在调试配置文件中打印其对缓冲区对象所做的一些决策,特别是如果它们存储在客户端RAM或直接在GPU上。在尝试时,我得到了以下结果:
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) has been mapped in HOST memory.
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) stored in VIDEO memory has been updated.
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) has been mapped in HOST memory.
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) stored in SYSTEM HEAP memory has been updated.
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use SYSTEM HEAP memory as the source for buffer object operations.
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use SYSTEM HEAP memory as the source for buffer object operations.
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) has been mapped WRITE_ONLY in SYSTEM HEAP memory (fast).
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) has been mapped WRITE_ONLY in SYSTEM HEAP memory (fast).
Buffer detailed info: Buffer object 5 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STATIC_DRAW) has been mapped WRITE_ONLY in SYSTEM HEAP memory (fast).

我在这里使用了PBO将纹理更新流式传输到GPU,每帧一个更新,通过映射缓冲区。自然的选择应该是使用GL_STREAM_DRAW,但我指定了GL_STATIC_DRAW。驱动程序给我提供了一些VRAM支持的缓冲区,并为我执行了前两个更新的I/O映射。但是,之后它改变了主意并使用客户端支持的缓冲区——正好给了我如果一开始就要求GL_STREAM_DRAW所得到的结果。我们在这里看到的是上述引用文本中所说的“反复猜测”的一个例子。

所有这些都高度依赖于具体实现。这也是上述GL扩展创建的原因之一——它将使程序员在这些方面拥有更多的控制权。然而,据我所知,这个扩展在OpenGL ES领域中并不可用。


9
如果您经常调用glBufferData()函数,应使用GL_STREAM_DRAW - 该函数会完全刷新缓冲区的内容,使OpenGL能够进行优化,例如在旧数据仍在使用时让OpenGL上传新数据:缓冲对象流
如果您不经常更改缓冲区的内容,则应使用GL_STATIC_DRAW
如果您将局部更改缓冲区内容,例如使用glBufferSubData(),则应使用GL_DYNAMIC_DRAW
选择错误的选项可能会降低性能,但据我所知不会产生其他问题。
这些选项很可能会影响内存的位置 - RAM或VRAM,以及与分配内存的某些策略有关(首先组合写入,然后发送数据,或立即写入)。RAM直接由CPU可写,但从GPU读取(甚至不要考虑修改它)速度较慢,因为它必须通过PCI-e传输。 VRAM是GPU本身上的内存; CPU不能直接写入它(除非最近或您是AMD) - 相反,GPU上的特殊硬件应该批量复制数据到VRAM中,从而实现可写。

2

对于iOS来说,有关VBO的信息可以在苹果开发者网站这里找到。根据他们的文档,GL_DYNAMIC_DRAW和GL_STREAM_DRAW是等价的

但我认为你的解决方案更接近GL_DYNAMIC_DRAW,因为GL_DYNAMIC_DRAW用于需要多次渲染并且内容在渲染循环中发生改变的顶点缓冲区。


实际上,如果您阅读您发布的内容,它会说“在iOS中,GL_DYNAMIC_DRAW和GL_STREAM_DRAW是等效的”。 - Tara
是的,你说得对,但它仍然可能有用于编写平台无关的代码。 - mert
对于GL_STREAM_DRAW,它表示:“GL_STREAM_DRAW用于渲染少量次数后被丢弃的顶点缓冲区。” 我认为4是一个相当小的数字。对于GL_DYNAMIC_DRAW,它表示:“GL_DYNAMIC_DRAW用于在渲染循环期间多次渲染并且其内容会发生更改的顶点缓冲区。” 因此,我认为正确的标志应该是GL_STREAM_DRAW。但是,我不知道“丢弃”在这个上下文中是什么意思。顺便说一句,我可以确认在Nvidia硬件上也没有任何区别。正如其他评论者指出的那样,AMD也忽略了该标志。 - Tara

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