glBufferStorage 和 glBufferData 有什么区别?

What is the difference between glBufferStorage and glBufferData?

glBufferStorage creates a new immutable data store for the buffer object currently bound to target​. The size of the data store is specified by size​. If an initial data is available, its address may be supplied in data​. Otherwise, to create an uninitialized data store, data​ should be NULL​.

不可变意味着我不能改变它对吧?但是那样 "uninitialized data" 就毫无意义了。

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

那么glBufferStorage和glBufferData有什么区别呢?

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

OpenGL has long supported buffer objects as a means of storing data that may be used to source vertex attributes, pixel data for textures, uniforms and other elements. In un-extended GL, buffer data stores are mutable - that is, they may be de-allocated or resized while they are in use. The GL_ARB_texture_storage extension added immutable storage for texture object (and was subsequently incorporated into OpenGL 4.2). This extension further applies the concept of immutable storage to buffer objects. If an implementation is aware of a buffer's immutability, it may be able to make certain assumptions or apply particular optimizations in order to increase performance or reliability.

这些提到可变缓冲区可能会被取消分配或调整大小,这来自 glBufferData,它带来了可变缓冲区。但是 glBufferStorage 将向您展示创建不可变缓冲区的能力。

这里的关键是'immutable'意味着你以后不能调整它的大小或取消分配它,但不意味着你不能write/read里面的数据。

[编辑] 我认为附加一些示例也很好,这可以使规范中的文字更容易理解,:)

  • glBufferData 有时你可能会遇到 'buffer orphan' 这样的词,通常你会看到类似的调用(还有一些其他方法可以像 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 不允许您取消分配它 [注意 glBufferData 中的 0 参数,但它通常会保留 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 只是保持在同一个缓冲区的地址上,这意味着缓冲区在内存中持久存在,除非你真的不需要它,否则你不需要取消映射

谢谢

请注意,这与 glTexStorage* (...) 背后的原理相同。实际上,你与 API 签订了一份合同,其中规定你永远不会被允许更改对象的某些属性,作为交换,这将赋予对象不可变的状态,并允许你做一些你通常不能用它做的事情。

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

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

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

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