glBufferStorage和glBufferData有什么区别?

时间:2015-01-07 02:26:27

标签: opengl

  

glBufferStorage为缓冲区创建一个新的不可变数据存储   当前绑定到目标的对象。数据存储的大小是   由大小指定。如果初始数据可用,则其地址可以   提供数据。否则,创建未初始化的数据   存储,数据应为NULL。

不可变意味着我不能改变它吗?但是,“未初始化的数据”将毫无意义。

但它并非真正不可变,因为我们可以指定GL_DYNAMIC_STORAGE_BIT​

那么glBufferStorage和glBufferData有什么区别?

2 个答案:

答案 0 :(得分:17)

您知道,这与glTexStorage* (...)背后的原理相同。实际上,您与API签订了一份合同,该合同规定您永远不会被允许更改对象的某些属性,作为交换,这将赋予对象不可变状态,并允许您执行通常无法使用它的操作。

纹理视图是一个有趣的例子,其中不可变纹理的内部图像数据可以在多个纹理对象之间共享,甚至可以重新解释其格式/维度(例如 1个2D数组纹理切片可以像普通的2D纹理一样共享和使用。)

对于顶点缓冲区,不可变存储会打开一类性能优化(例如持久映射内存),如果您可以随时更改缓冲区的大小,则无法实现。您创建一个永远不会更改其大小的缓冲区,但您仍然可以使用glBufferSubData* (...)命令随时向其发送新数据,或者在内存映射时写入缓冲区。

使用glBufferData (...),您可以在同一个对象上多次调用该命令,它将孤立旧内存并分配新存储。使用glBufferStorage (...),缓冲区的大小设置为对象的生命周期(不可变),并且一旦分配不可变,再次调用GL_INVALID_OPERATION是错误的(glBufferStorage (...))。

简而言之,数据存储(存储特性)是不可变的,而不是实际的数据。

答案 1 :(得分:8)

我相信[https://www.opengl.org/registry/specs/ARB/buffer_storage.txt]中的这句话显示了关键:

  

OpenGL长期以来一直支持缓冲区对象作为存储数据的方法       可用于获取顶点属性,纹理的像素数据,       制服和其他元素。在未扩展的GL中,缓冲区数据存储       是可变的 - 也就是说,它们可能会被取消分配或调整大小       正在使用中。 GL_ARB_texture_storage扩展添加了不可变存储       用于纹理对象(随后并入OpenGL 4.2)。       此扩展进一步将不可变存储的概念应用于       缓冲对象。如果实现意识到缓冲区的不变性,       它可以做出某些假设或特别适用       优化以提高性能或可靠性。

这些提到可变缓冲区可能已经解除分配或调整大小,并且来自glBufferData,它带来了可变缓冲区。但是glBufferStorage将向您展示创建不可变缓冲区的能力。

这里的关键是“不可变”'意味着您将来无法调整大小或取消分配,但这并不意味着您无法在其中写入/读取数据。

[编辑] 我认为附加一些样本也很好,这可以使规范中的单词更容易理解,:))

  • glBufferData有时您可能会遇到“缓冲孤儿”字样,通常您会看到类似的调用(还有一些其他方法可以像GL_MAP_INVALIDATE_BUFFER_BIT这样做缓冲孤儿等等。):
    glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, size, 0, GL_STREAM_DRAW); GLubyte* ptr = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_WRITE_BIT); Foo(ptr, size); glUnmapBuffer(GL_ARRAY_BUFFER);
  • glBufferStorage不允许您取消分配[请注意0中的glBufferData参数,但它会保留Persistent-mapped Buffer的内存,通常你会看到用法如下:
    glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferStorage(GL_ARRAY_BUFFER, size, data, GL_MAP_PRESISTENT_BIT|GL_MAP_COHERENT_BIT); GLubyte* ptr = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_PRESISTENT_BIT|GL_MAP_COHERENT_BIT); Foo(ptr, size);
    请注意,ptr只保留在相同缓冲区的地址上,这意味着缓冲区会在内存中保留,并且您不需要取消映射,直到您真的不需要它

由于