Buffer Object

From OpenGL.org
(Redirected from Map Buffer Range)
Jump to: navigation, search

Buffer Objects are OpenGL Objects that store an array of unformatted memory allocated by the OpenGL context (aka: the GPU). These can be used to store vertex data, pixel data retrieved from images or the framebuffer, and a variety of other things.



As with the standard OpenGL object paradigm, this only creates the object's name, the reference to the object. To set up its internal state, you must bind it to the context. You do this using the following API:

 void glBindBuffer(enum target, uint bufferName)


Buffer objects hold a linear array of memory of arbitrary size. This memory must be allocated before it can be uploaded to or used. There are two ways to allocate storage for buffer objects: mutable or immutable. Allocating immutable storage for a buffer changes the nature of how you can interact with the buffer object.

Immutable Storage

Immutable Storage
Core in version 4.5
Core since version 4.4
Core ARB extension ARB_buffer_storage

Much like immutable storage textures, the storage for buffer objects can be allocated immutably. When this is done, you will be unable to reallocate that storage. You may still invalidate it with an explicit invalidation command or through mapping the buffer. But you cannot do the glBufferData(..., NULL)​ trick to invalidate it if the storage is immutable.

To allocate immutable storage for buffer objects, you call this function:

void glBufferStorage(GLenum target​, GLsizeiptr size​, const GLvoid * data​, GLbitfield flags​);

The target​ parameter is just like the one for glBindBuffer; it says which bound buffer to modify. size​ represents how many bytes you want to allocate in this buffer object.

data​ is a pointer to an array of bytes of size​ in length. OpenGL will copy that data into the buffer object upon initialization. You may pass NULL for this parameter; if you do, the initial contents of the buffer will be undefined. You can clear the buffer after allocation if you wish to update it.

The flags​ field sets up a contract between you and OpenGL, describing how you may and may not access the contents of the buffer.

Immutable access methods

Immutable storage buffer objects allow you to establish a contract with OpenGL, specifying how you will access and modify the contents of the buffer object's storage. The flags​ bitfield is a series of bits that describes what restrictions you will operate under, and if you do not abide by these restrictions, you will get an OpenGL Error.

The flags​ bits cover how the user may directly read from or write to the buffer. But this only restricts how the user directly modifies the data store; "server-side" operations on buffer contents are always available.

The following operations are always valid on immutable buffers regardless of the flags​ field:

Here are the available client-side behaviors that you can request and the bits that represent them. Most of these can be set independently from one another; any dependencies are explicitly stated:

Allows the user to read the buffer via mapping the buffer. Without this flag, attempting to map the buffer for reading will fail.
Allows the user to map the buffer for writing. Without this flag, attempting to map the buffer for writing will fail.
Allows the user to modify the contents of the storage with glBufferSubData. Without this flag, attempting to call that function on this buffer will fail.
Allows the buffer object to be mapped in such a way that it can be used while it is mapped. Without this flag, attempting to perform any operation on the buffer while it is mapped will fail. You must use one of the mapping bits when using this bit.
Allows reads from and writes to a persistent buffer to be coherent with OpenGL, without an explicit barrier. Without this flag, you must use an explicit barrier to achieve coherency. You must use GL_PERSISTENT_BIT when using this bit.
A hint that suggests to the implementation that the storage for the buffer should come from "client" memory.

While you are free to use any legal combination of these bits, there are certain uses of buffer objects that lend themselves to certain bitfield combinations.

Pure in-OpenGL buffers: Sometimes, it is useful to have a buffer object that is owned almost entirely by OpenGL processes. The contents of the buffer are written by Compute Shaders, Transform Feedback, or various other mechanisms. And other OpenGL processes read from them, such as via Indirect Rendering, Vertex Specification, and so forth.

In this case, all you really need is the ability to allocate storage of some fixed size. Setting flags​ to 0 is the best way to handle this.

Static data buffers: In some cases, data stored in a buffer object will not be changed once it is uploaded. For example, vertex data can be static: set once and used many times.

For these cases, you set flags​ to 0 and use data​ as the initial upload. From then on, you simply use the data in the buffer. This requires that you have assembled all of the static data up-front.

Image reading buffers: Through Pixel Buffer Objects, it is possible to use a buffer as an intermediary for asynchronous pixel transfer operations. In this case, the purpose of the buffer is just to make the read asynchronous.

You could rely on glGetBufferSubData to do the reading, but mapping the buffer for reading will achieve equal if not superior performance. Therefore, flags​ should be set to GL_MAP_READ_BIT.

Modifiable buffers: Generally speaking, mapping a buffer and writing to it will be equally as efficient as glBufferSubData. And in most cases, it will be much faster, particularly if invalidation and other Buffer Object Streaming techniques are employed.

To cover this, flags​ should be set to GL_MAP_WRITE_BIT. This lets the implementation know that you will not be using glBufferSubData at all.

Mutable Storage

To create mutable storage for a buffer object, you use this API:




The usage parameter can be very confusing.

Buffer Object Usage











  • STATIC: The user will set the data once.
  • DYNAMIC: The user will set the data occasionally.





Data Specification

We have seen that glBufferData can be used to update the data in a buffer object. However, this also reallocates the buffer object's storage. This function is therefore not appropriate for merely updating the contents of the allocated memory (and for immutable storage buffers, it is not possible).

Instead, to update all or part of the data in a buffer object, this API can be used:




Buffer Object
Core in version 4.5
Core since version 4.3
Core ARB extension ARB_clear_buffer_object


void glClearBufferData(GLenum target​​, GLenum internalformat​, GLenum format​, GLenum type​, const void * data​);
void glClearBufferSubData(GLenum target​, GLenum internalformat​, GLintptr offset​, GLsizeiptr size​, GLenum format​, GLenum type​, const void * data​);

glClearBufferData works as glClearBufferSubData, except it operates on the entire buffer's contents. Neither function reallocates the buffer object's storage.

target​ is just like the one for glBindBuffer; it says which bound buffer to be cleared.





Data can be copied from one buffer object to another, or from one region to another (non-overlapping) region of the same buffer. To do this, first bind the source and destination buffers to different target​s. These could be any target, but GL_COPY_READ_BUFFER​ and GL_COPY_WRITE_BUFFER have no special semantics, so they make useful targets for this purpose. When copying within a buffer, bind the same buffer object to both.

Once both are bound, use this function:



Errors will be given if the offset/sizes would cause reading to or writing from locations outside of the respective buffer objects' storage. Also, if the source and destination buffers are the same, an error will be given if the range to be read from overlaps with the range to be written to.





While a buffer is mapped, you can freely unbind the buffer. However, unless you map the buffer persistently, you cannot call any function that would cause OpenGL to read, modify, or write to that buffer while it is mapped. Thus, calling glBufferData is out, as is using any function that would cause OpenGL to read from it (rendering with a VAO that uses it, etc).




The access​ parameter is a bit complicated.





Map Buffer Alignment
Core in version 4.5
Core since version 4.2
Core ARB extension ARB_map_buffer_alignment

The alignment of the pointer returned by mapping functions is important when dealing with types that need highly restricted alignment. For example, SSE intrinsics need 16-byte alignment, while AVX intrinsics need 32-byte alignment.

The base alignment of a buffer object is defined as the alignment of the pointer retrieved by calling glMapBuffer or glMapBufferRange with a zero offset​. The base alignment of a buffer is implementation defined, but it must be no less than GL_MIN_MAP_BUFFER_ALIGNMENT. The smallest this value will be is 64, thus allowing this to work for any strictly aligned type in existence and some that aren't.

The alignment of the pointer returned by glMapBufferRange with a non-zero offset​ is the modulus of the offset​ by the base alignment.

Buffer Corruption

There is one major gotcha when mapping a buffer.




Persistent mapping

Immutable Storage
Core in version 4.5
Core since version 4.4
Core ARB extension ARB_buffer_storage

Normally, if a buffer object is mapped, it cannot be used in a non-mapped fashion. Rendering commands that would read from or write to a mapped buffer will throw an error, as will functions that explicitly read from or write to the buffer.

However, if the buffer is created with immutable storage, and the GL_MAP_PERSISTENT_BIT flag is set when it is created, and the buffer is mapped using the same GL_MAP_PERSISTENT_BIT flag, then the buffer can remain mapped essentially indefinitely. You can have OpenGL use or modify the buffer's storage as you see fit.

Everything has a price. The price of this is that you must deal with the ordering of reads and writes. By mapping the buffer in this fashion, you are taking full responsibility for synchronizing mapped access operations with OpenGL (and with other direct uses of the API).

Write Synchronization: When you use a mapped pointer to write to a buffer, the data you write does not immediately become visible to OpenGL. It only becomes visible to OpenGL when you issue a glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT) call. Alternatively, if you map the buffer with GL_MAP_FLUSH_EXPLICIT_BIT, you can tell OpenGL that specific sections of the mapped range should become visible to OpenGL calls:

void glFlushMappedBufferRange(GLenum target​, GLintptr offset​, GLsizeiptr length​);

The buffer must be currently bound to target​. The offset​ and length​ represent the range within the mapped range to be flushed and made visible to OpenGL.

Read Synchronization: Synchronizing OpenGL writes with user reads is more complex. This is because synchronization requires that two things have happened.

First, after issuing whatever OpenGL commands that will perform those writes, we must issue that memory barrier from before: glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT). This ensures that the memory is coherently visible.

Second, and this must be done after issuing the barrier, you must ensure that both the command to write the data and the barrier have completed their execution. This could be done via a call to glFinish, but a better way would be through the use of a fence sync. Once the fence has completed, the memory can be accessed.

So the steps are:

  1. Send whatever commands will write to the buffer.
  2. Issue the memory barrier: glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT)
  3. Create a fence.
  4. Do something else for a while, so that you don't waste precious CPU time waiting for the GPU to be done.
  5. Wait for the fence sync to complete.
  6. Read from the mapped pointer.

Easy Synchronization: If you use the GL_MAP_COHERENT_BIT when allocating the buffer's storage and you map it with the same bit (note that the persistent bit is required if you use this one), then you don't need most of the above synchronization mechanisms. When you write to the mapped pointer, the data you write will become visible to every OpenGL command issued after the write, automatically.

However, just because you get coherent visibility doesn't mean you get to read something that OpenGL hasn't written yet. So you still need to set up that fence sync to make sure that OpenGL has actually written the data.

Note: Obviously, there's a reason why you don't get the coherent behavior by default. That reason being performance. Possibly; there's evidence that there is no performance drop on some hardware, so you should probably profile it yourself.

Performance Concerns






When a buffer or region thereof is invalidated, it means that the contents of that buffer are now undefined. How OpenGL handles invalidation is up to it. Note that any pending operations on the buffer will still complete. Any pending reads from OpenGL will still get the previous values, and any pending writes will still write their values (though the writes will be discarded since you trashed the buffer).

The idea is that, by invalidating a buffer or range thereof, the implementation will simply grab a new piece of memory to use for any later operations. Thus, while previously issued GL commands can still read the buffer's original data, you can fill the invalidated buffer with new values (via mapping or glBufferSubData) without causing implicit synchronization.

Invalidation of a buffer can happen in one of three ways.

For buffer objects with non-immutable storage, a buffer can be invalidated by calling glBufferData with the exact same size and usage hint as before, and with a NULL data​ parameter. This is an older method of invalidation, and it should only be used when the others are not available.

Invalidation can happen at buffer mapping time by calling glMapBufferRange with the GL_MAP_INVALIDATE_BUFFER_BIT mapping bit. This causes the entire buffer's contents to be invalidated (even if you only mapped part of it). To invalidate only the portion of the buffer being mapped, use GL_MAP_INVALIDATE_RANGE_BIT instead.

Buffer invalidation
Core in version 4.5
Core since version 4.3
Core ARB extension ARB_invalidate_subdata

Invalidation can also be caused explicitly by calling one of the following functions:

void glInvalidateBufferData(GLuint buffer​);






The key to effective streaming is synchronization. Or more specifically, avoiding it at all costs.

General use

Most of the uses of buffer objects involve binding them to a certain target, which other OpenGL operations will detect the bound buffer and then use the data stored in that buffer in some way, either reading or writing values in a well-defined format.


The buffer will be used as a source for vertex data, but the connection is only made when glVertexAttribPointer is called. The pointer field of this function is taken as a byte offset from the beginning of whatever buffer is currently bound to this target.
These are for performing direct writes from asynchronous queries to buffer object memory. If a buffer is bound to GL_QUERY_BUFFER, then all glGetQueryObject[ui64v] function calls will write the result to an offset into the bound buffer object.
An indexed buffer binding for buffers used in Transform Feedback operations.

Binding indexed targets










There is a more limited form of this function, glBindBufferBase, that binds the entire buffer to an index. It simply omits the offset​ and size​ fields, but it has one major difference from glBindBufferRange. If you respecify the data store for the buffer with glBufferData, then all indices where it is bound with glBindBufferBase will take the new size (for any subsequent rendering commands). With glBindBufferRange, the bound locations will not be updated if the buffer's data size is changed.

Note: Respecifying a buffer's data store with a larger or smaller size is generally not a good idea to begin with. You are advised not to rely on the resizing feature of glBindBufferBase.

Multibind and indexed targets

Core in version 4.5
Core since version 4.4
Core ARB extension ARB_multi_bind

An array of buffers can be bound to a contiguous range of indexed binding points for a particular indexed buffer target. This is done with this function (note the "s" in "Buffers"):

void glBindBuffersRange(GLenum target​, GLuint first​, GLsizei count​, const GLuint *buffers​, const GLintptr *offsets​, const GLintptr *sizes​);

target​ is the usual buffer binding target. first​ defines the starting index in target​ to begin binding to. count​ is the number of buffers to bind. Therefore, the indices in the range to bind are on the half-open range [first​, first​ + count​).

buffers​ is an array of count​ buffer object names. This can be NULL; if it is, then this function will bind 0 to all of the binding points in the range. Individual elements of the array can also be 0; these will bind 0 as normal, having the expected effect.

offsets​ and sizes​ are arrays of count​ offsets and sizes for the ranges to be bound to the context. These can be NULL, but only if buffers​ is NULL too.

There is also a glBindBuffersBase version. Note that unlike the non-multibind version, glBindBuffersBase always binds the entire buffer, from beginning to end. You don't provide a per-buffer offset.