|(91 intermediate revisions by 9 users not shown)|
'''Buffer Objects''' is the general term for unformatted linear memory allocated by the OpenGL context. These can be used to store [[Vertex Buffer Objects|vertex data]], pixel data retrieved from images or the framebuffer, and a variety of other things.
== Versioning ==
were initially introduced in the ARB_vertex_buffer_object extension. They have been part of core OpenGL since version 1. 5. This discussion will pertain specifically to the core version of buffer objects, though it is essentially identical to the extension version.
of buffer objects
it is to
While buffer objects themselves are relatively old, some of the uses of them are much more recent.
buffer are , the .
Buffer Objects are [[OpenGL Objects]]; they therefore follow all of the rules of regular OpenGL objects. To create a buffer object, you call <code>glGenBuffers</code>. Deleting them uses <code>glDeleteBuffers</code>. These use the standard Gen/Delete paradigm as most OpenGL objects.
As with the standard OpenGL object paradighm, this only creates the object's name, the reference to the object. To actually create the object itself, you must bind it to the context. You do this using the following API:
with this the s:
<code>void glBindBuffer( enum ''target '', uint ''bufferName'') </code>
void (target, )
The <code>target </code> defines how you intend to use this binding of the buffer object. When you're just creating and/or filling the buffer object with data, the target you use doesn't matter much. It matters more when you intend to tell OpenGL to use the data in the buffer in some way.
targetthe buffer . the target. to the in the .
To create the actual memory storage for a buffer object, you use this API:
buffer object , this
<code>void glBufferData(enum ''target'', sizeiptr ''size'', const void *''data'', enum ''usage'')</code>
The <code>target</code> parameter is just like the one for <code>glBindBuffer</code>; it says which bound buffer to modify. <code>size</code> represents how many bytes you want to allocate in this buffer object.
is the bufferto you buffer .
While it technically does not matter how you bind the buffer for creations vs. how you use it (create it with one target, unbind and bind it for use as another target) this is not recommended if you can at all help it. OpenGL implementations can, and will, make assumptions based on what target you used to create a buffer object, so don't try to confuse them.
The <code>data</code> parameter is a pointer to user memory that will be copied into the buffer object's data store. If this value is NULL, then no copying will occur, and the buffer object's data will be undefined.
that buffer .
The <code>usage</code> parameter can be very confusing.
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.
Buffer Objects are OpenGL Objects; they therefore follow all of the rules of regular OpenGL objects. To create a buffer object, you call glGenBuffers. Deleting them uses glDeleteBuffers. These use the standard Gen/Delete paradigm as most OpenGL objects.
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)
The target defines how you intend to use this binding of the buffer object. When you're just creating, filling the buffer object with data, or both, the target you use doesn't technically matter.
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.
|Core in version
|Core since version
|Core ARB extension
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:
, 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.
To create mutable storage for a buffer object, you use this API:
void glBufferData(enum target, sizeiptr size, const void *data, enum usage)
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.
The data parameter is a pointer to user memory that will be copied into the buffer object's data store. If this value is NULL, then no copying will occur, and the buffer object's data will be undefined.
The usage parameter can be very confusing.
Buffer Object Usage
Buffer objects are general purpose memory storage blocks allocated by OpenGL. They are intended to be used in a great many ways. To give the implementation great flexibility in exactly what a particular buffer object's data store will be, so as to better optimize performance, the user is required to give usage hints. These provide a general description as to how exactly the user will be using the buffer object.
There are two independent parts to the usage pattern: how the user will be reading/writing from/to the buffer, and how often the user will be changing it relative to the use of the data.
There are two ways for data to cause a change in the data contents of a buffer object. One way is for the user to explicitly upload some binary data. The other way is for the user to issue GL commands that cause the buffer to be filled in. For example, if you want to have a buffer store the results of a vertex shader computation through the use of transform feedback, the user is not directly changing the buffer information. So this is the latter kind of change.
Similarly, the user can read a buffer's data, using a variety of commands. Or, the user can execute an OpenGL command that causes the GL to read the contents of the buffer and do something based on it. Buffers storing vertex data are read by the GL when rendering.
There are three hints that the user can specify the data. They are all based on what the user will be doing with the buffer. That is, whether the user will be directly reading or writing the buffer's data.
- DRAW: The user will be writing data to the buffer, but the user will not read it.
- READ: The user will not be writing data, but the user will be reading it back.
- COPY: The user will be neither writing nor reading the data.
DRAW is useful for, as the name suggests, drawing. The user is uploading data, but only the GL is reading it. Buffer objects holding vertex data are generally specified as DRAW, though there can be exceptions.
READ is used when a buffer object is used as the destination for OpenGL commands. This could be rendering to a Buffer Texture, using arbitrary writes to buffer textures, doing a pixel transfer into a buffer object, using Transform Feedback, or any other OpenGL operation that writes to buffer objects.
COPY is used when a buffer object is used to pass data from one place in OpenGL to another. For example, you can read image data into a buffer, then use that image data as vertex data in a draw call. Your code never actually sends data to the buffer directly, nor does it read data back. You can also use Transform Feedback to achieve the same thing in a more direct way. You have the feedback data go to a buffer object, then use that buffer object as vertex data. And while the user is causing the buffer to be updated via rendering commands, at no time is the user directly either reading from or writing to the buffer's storage.
There are three hints for how frequently the user will be changing the buffer's data.
- STATIC: The user will set the data once.
- DYNAMIC: The user will set the data occasionally.
- STREAM: The user will be changing the data after every use. Or almost every use.
STREAM is pretty easy to understand: the buffer object's storage will be updated after almost every use. STATIC is pretty easy to understand too. The buffer object's contents will be updated once and never changed.
What is unclear is when DYNAMIC becomes STREAM or STATIC. These are only hints, after all. It is perfectly legal OpenGL code to modify a STATIC buffer after it has been created, or to never modify a STREAM buffer.
Is it better to use STATIC for buffers that are updated very infrequently? Is it better to use DYNAMIC for buffers that get updated frequently, but not at STREAM speed? Is it better to use DYNAMIC for buffers that get partially updated? These are questions that can only be answered with careful profiling. And even then, the answer will only be accurate for that particular driver version from that particular hardware vendor.
In any case, STREAM, STATIC, and DYNAMIC can be matched with READ, DRAW, and COPY in any combination. STREAM_COPY means that you will be doing transform feedback writes (or other kinds of GL-based writes) into the buffer after almost every use; it will not be updated with glBufferSubData or similar functions. STATIC_READ means that you will fill the buffer up from the GL, but you will only do this once.
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:
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data)
The offset parameter is an integer offset into the buffer object where we should begin updating. The size parameter is the number of bytes we should copy out of data. For obvious reasons, data cannot be NULL.
A buffer object's storage can be cleared, in part or in full, to a specific value. These functions work in a similar fashion to Pixel Transfer operations, though with some significant differences:
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.
internalformat must be a sized Image Format, but only of the kind that can be used for buffer textures. This defines how OpenGL will store the data in the buffer object. format and type operate as normal for Pixel Transfer operations.
data is a pointer to a single pixel's worth of data, rather than the rows that are used in actual pixel transfers. So if format is GL_RG and type is GL_UNSIGNED_BYTE, then data should be a pointer to an array of two GLubytes.
This function will copy the given data repeatedly throughout the specified range of the buffer. offset must be a multiple of the byte size defined by the internalformat, as must size.
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 targets. 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:
void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);
readtarget is the buffer you bound the source buffer to. So this is where you get the data to copy from. writetarget is the buffer you bound the destination buffer to. readoffset is the byte offset from the beginning of the source buffer to start reading. writeoffset is the byte offset from the beginning of the destination buffer to start writing to. size is the number of bytes to read.
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.
glBufferSubData is a nice way to present data to a buffer object. But it can be wasteful in performance, depending on your use patterns.
For example, if you have an algorithm that generates data that you want to store in the buffer object, you must first allocate some temporary memory to store that data in. Then you can use glBufferSubData to transfer it to OpenGL's memory. Similarly, if you want to read data back, glGetBufferSubData is perhaps not what you need, though this is less likely. It would be really nice if you could just get a pointer to the buffer object's storage and write directly to it.
You can. To do this, you must map the buffer. This gives you a pointer to memory that you can write to or read from, theoretically, just like any other. When you unmap the buffer, this invalidates the pointer (don't use it again), and the buffer object will be updated with the changes you made to it.
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).
To map a buffer, you call glMapBufferRange. The signature of this function is this:
void *glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
The return value is the pointer to the buffer object's data. The parameters offset and length allow you to specify a particular range within the buffer to map; you do not have to map the entire buffer. The target parameter refers to the particular target that you have the buffer you want to map bound to.
The access parameter is a bit complicated.
You can tell OpenGL what you intend to do with the pointer. If you're just adding new data to the buffer, then the pointer that gets returned isn't something you need to read from. Similarly, it could be your intention to read from this pointer and not change the buffer; OpenGL could do a simple copy from the buffer's memory to scratch memory in the client address space to make reading the buffer faster (buffer memory may not be optimized for reading).
You specify this by setting bitflags in the access parameter. This parameter must have either the GL_MAP_READ_BIT or GL_MAP_WRITE_BIT set; it can have both (ie: the memory will be fit for reading and writing) but it can't have neither. There are a number of other flags that can be set.
Unmapping the buffer is done when you are finished with the pointer and want to let OpenGL know that the buffer is free to be used. This is done with the function glUnmapBuffer. This function takes only the target that the buffer in question is bound to. After calling this function, you should not use the pointer returned in the map call again.
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.
There is one major gotcha when mapping a buffer.
During normal OpenGL operations, the OpenGL specification requires that all data stored in OpenGL objects be preserved. Thus, if something in the operating system or other such things causes video memory to be trashed, the OpenGL implementation must ensure that this data is restored properly.
Mapping is not a normal operation. Because of its low-level nature, these protections have to be relaxed. Therefore, it is possible that, during the time a buffer is mapped, some kind of corruption happens. If this occurs, calling glUnmapBuffer will return GL_FALSE. At that point, the contents of the buffer in question are considered undefined. It may have your data, or it may have random garbage.
How often does this happen? On Microsoft Windows 5.1 (XP) and below, video memory could get trashed anytime an application didn't have input focus. This is why alt-tabbing away from games takes a long time to recover from; the application/OpenGL has to reload all of this data back to video memory. Fortunately, on Windows 6.0 (Vista) and above, this is fixed; Windows itself manages video memory and will ensure that all video memory is retained. Thus, at least theoretically, this should never be a problem on Vista or above machines.
|Core in version
|Core since version
|Core ARB extension
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 flush the written range of memory, and only if you mapped the buffer with GL_MAP_FLUSH_EXPLICIT_BIT:
The buffer must be currently bound to target. The offset and length represent the memory range within the mapped storage to be flushed and made visible to OpenGL.
Also, you must take care not to modify memory that OpenGL is actively using. So if you have issued any commands that read from a particular range of a persistently mapped buffer, you should not modify that range unless you have ensured that those commands have completed their execution. This is best done via a fence, but using multiple buffer regions for multiple frames worth of data is also a good idea. You'll still need a sync object, but you're far less likely to encounter an actual CPU stall.
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 a memory barrier: glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT). This ensures that data OpenGL has written is 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:
- Send whatever commands will write to the buffer.
- Issue the memory barrier: glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT)
- Create a fence.
- Do something else for a while, so that you don't waste precious CPU time waiting for the GPU to be done.
- Wait for the fence sync to complete.
- 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, so you don't need to flush. When you read from a mapped pointer, you don't need the glMemoryBarrier call to ensure that data OpenGL commands wrote to the buffer will become visible to you.
However, just because you get coherent visibility doesn't mean you get to read something that OpenGL hasn't written yet. And the same goes for writing when OpenGL reads. So you still need to set up that fence sync to make sure that OpenGL has actually written the data.
One thing to remember about buffer mapping is this: the implementation is not obligated in any way to give you an actual pointer to the buffer object's memory. It is perfectly capable of giving you a pointer to some memory that OpenGL allocated just for the purpose of mapping, then it will do the copy on its own time.
The other thing to remember is that you should not care. OpenGL likes to give implementations flexibility to make performance optimizations. If mapping a certain buffer gives you some other pointer, and the implementation will do the copy on its own time, you should assume that this is probably the fastest way to work. It's still potentially faster than glBufferSubData, since the copy in glBufferSubData must happen before the function call returns, whereas the copy in the mapping case can happen in a thread the GL spawns. The worst case is that it's no slower than glBufferSubData.
However, you should not use the pointer you are given like any other pointer you might have. If this pointer is a pointer to non-standard memory (uncached or video memory), then writing to it haphazardly can be problematic. If you are attempting to stream data to the buffer, you should always map the buffer only for writing and you should write sequentially. You do not need to write every byte, but you should avoid going backwards or skipping around in the memory.
The purpose in writing sequentially is to be able to use write-combine memory, a feature of some processors (most x86's). It allows sequential writes to uncached memory to flow fairly quickly, compared to random writes to uncached memory.
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.
Invalidation can also be caused explicitly by calling one of the following functions:
glInvalidateBufferData is equivalent to calling glInvalidateBufferSubData with offset as 0 and length as the size of the buffer's storage.
Streaming is the process of frequently uploading data to a buffer object and then using that buffer object in some OpenGL process. Making this as efficient as possible is a delicate operation. Buffer objects provide a number of possible usage patterns for streaming, and which ones work best is not entirely clear. Testing should be done with the hardware of interest to make sure that you get optimal streaming performance.
The key to streaming is parallelism. The OpenGL specification permits an implementation to delay the execution of drawing commands. This allows you to draw a lot of stuff, and then let OpenGL handle things on its own time. Because of this, it is entirely possible that well after you called the rendering function with a buffer object, you might start trying to stream vertex data into that buffer. If this happens, the OpenGL specification requires that the thread halt until all drawing commands that could be affected by your update of the buffer object complete. This obviously misses the whole point of streaming.
The key to effective streaming is synchronization. Or more specifically, avoiding it at all costs.
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.
Here are the various targets for buffer objects and their associated uses:
- 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.
- All rendering functions of the form gl*Draw*Elements* will use the pointer field as a byte offset from the beginning of the buffer object bound to this target. The indices used for indexed rendering will be taken from the buffer object. Note that this binding target is part of a Vertex Array Objects state, so a VAO must be bound before binding a buffer here.
- GL_COPY_READ_BUFFER and GL_COPY_WRITE_BUFFER
- These have no particular semantics. Because they have no actual meaning, they are useful targets for copying buffer object data with glCopyBufferSubData. You do not have to use these targets when copying, but by using them, you avoid disturbing buffer targets that have actual semantics.
- GL_PIXEL_UNPACK_BUFFER and GL_PIXEL_PACK_BUFFER
- These are for performing asynchronous pixel transfer operations. If a buffer is bound to GL_PIXEL_UNPACK_BUFFER, glTexImage*, glTexSubImage*, glCompressedTexImage*, and glCompressedTexSubImage* are all affected. These functions will read their data from the bound buffer object instead of where a client pointer points. Similarly, if a buffer is bound to GL_PIXEL_PACK_BUFFER, glGetTexImage, and glReadPixels will store their data to the bound buffer object instead of where a client pointer points.
- 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.
- This target has no special semantics.
- An indexed buffer binding for buffers used in Transform Feedback operations.
- An indexed buffer binding for buffers used as storage for uniform blocks.
- The buffer bound to this target will be used as the source for the indirect data when performing indirect rendering. This requires OpenGL 4.0 or ARB_draw_indirect.
- An indexed buffer binding for buffers used as storage for atomic counters. This requires OpenGL 4.2 or ARB_shader_atomic_counters
- The buffer bound to this target will be used as the source for indirect compute dispatch operations, via glDispatchComputeIndirect. This requires OpenGL 4.3 or ARB_compute_shader.
- An indexed buffer binding for buffers used as storage for shader storage blocks. This requires OpenGL 4.3 or ARB_shader_storage_buffer_object.
Binding indexed targets
Some buffer targets are indexed, as noted above. This is used for binding a number of buffers that do similar things. For example, a GLSL program can use a number of different uniform buffers.
To bind a buffer object to an indexed location, you may use this function:
void glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size );
This causes the buffer to be bound to the indexed location target at the index location. The only valid values for target are indexed targets (see below).
The valid values for index depend on the kind of target being bound. The valid target values, and their associated index limits, are:
- GL_TRANSFORM_FEEDBACK_BUFFER: limit GL_MAX_TRANSFORM_FEEDBACK_BUFFERS.
- GL_UNIFORM_BUFFER: limit GL_MAX_UNIFORM_BUFFER_BINDINGS.
- GL_ATOMIC_COUNTER_BUFFER: limit GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS.
- GL_SHADER_STORAGE_BUFFER: limit GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS.
The offset is the byte offset into buffer that the should be used for the binding, and the size is how many bytes after this are valid for this use of the buffer object. This allows you to bind subsections of a buffer object. If buffer is zero, then this index is unbound.
Do note that this does not replace standard buffer binding with glBindBuffer. It does in fact bind the buffer to the target parameter, thus unbinding whatever was bound to that target. But usually when you use glBindBufferRange, you are serious about wanting to use the buffer rather than just modify it.
Think of glBindBufferRange as binding the buffer to two places: the particular index and the target. glBindBuffer only binds to the target, not the index.
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.
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
|Core since version
|Core ARB extension
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"):
, 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. Similarly to the non-multibind version, glBindBuffersBase always binds the entire buffer, from beginning to end; you don't provide a per-buffer offset or size.