Uniform Buffer Object
|Core in version||4.5|
|Core since version||3.1|
|Core ARB extension||ARB_Uniform_Buffer_Object|
A Buffer Object that is used to store uniform data for a shader program is called a Uniform Buffer Object. They can be used to share uniforms between different programs, as well as quickly change between sets of uniforms for the same program object.
The term "Uniform Buffer Object" refers to the OpenGL buffer object that is used to provide storage for uniforms. The term "uniform blocks" refer to the GLSL language grouping of uniforms who's storage come from buffer objects.
Uniform buffers have several uses.
Switching between uniform buffer bindings is typically faster than switching dozens of uniforms in a program. Therefore, uniform buffers can be used to quickly change between different sets of uniform data for different objects that share the same program.
Also, uniform buffer objects can typically store more data than non-buffered uniforms. So they can be used to store and access larger blocks of data than unbuffered uniform values.
Lastly, they can be used to share information between different programs. So modifying a single buffer can effectively allow uniforms in multiple programs to be updated.
Each shader stage has a limit on the number of separate uniform buffer binding locations. These are queried using glGetIntergerv with GL_MAX_VERTEX_UNIFORM_BLOCKS, GL_MAX_GEOMETRY_UNIFORM_BLOCKS, or GL_MAX_FRAGMENT_UNIFORM_BLOCKS.
There is also a limitation on the available storage per uniform buffer. This is queried through GL_MAX_UNIFORM_BLOCK_SIZE. This is in basic machine units (ie: bytes).
Buffer objects are associated with a program's uniform block similarly to the way that texture objects are associated with sampler uniforms. The context has GL_MAX_UNIFORM_BUFFER_BINDINGS binding locations for uniform buffers. This value is usually the sum of the maximum uniform block count for all 3 possible stages. This list of uniform binding locations is analogus to the set of texture image unit binding points.
Each active uniform block in GLSL has a corresponding active uniform block in the linked program. You can get the uniform block index (similar to a uniform location) with this function:
GLuint glGetUniformBlockIndex( GLuint program, const char *uniformBlockName );
The uniformBlockName is the name of the uniform block, not the name of the GLSL scope for the uniform. This function returns GL_INVALID_INDEX if the block name could not be found.
The uniform block index is used to set what uniform buffer binding location that this uniform block uses. You can call this function to associate this uniform block with a uniform buffer binding location:
void glUniformBlockBinding( GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding );
This caused the uniformBlockIndex index in the program to be bound to the uniform buffer binding location uniformBlockBinding.
Once the program is bound to the context, all you need to do is bind the appropriate uniform buffer object(s) to those binding locations using glBindBufferRange or glBindBufferBase, as appropriate.
If you did not use the std140 layout for a block, you will need to query the byte offset for each uniform within the block. The OpenGL specification explains the storage of each of the basic types, but not the alignment between types. Struct members, just like regular uniforms, each have a separate offset that must be individually queried.
Retrieving this information uses the standard GLSL uniform querying API.
To get the byte offset from the beginning of the block, use the GL_UNIFORM_OFFSET enum.
Uniform arrays in a buffer are stored sequentially. But they are not guaranteed to be tightly packed. Therefore, for any array uniforms in a block, you must query a stride value with GL_UNIFORM_ARRAY_STRIDE. This stride is the byte distance from the beginning of one element to the beginning of the next.
Vector elements are stored sequentially and tightly packed. How matrices are stored depends on the row/column major status of the matrix. A row major matrix is stored as an array of row vectors, with an array length of the number of columns in the matrix. A column major matrix is stored as an array of column vectors, with an array length of the number of columns in the matrix.
Recall that the row/column major status of the matrix's layout only affects the in-memory layout of the matrices. It changes nothing about how GLSL uses the matrix data. So a mat4x2 is a 4-column, 2-row matrix even if it is defined with row-major as its layout.
Therefore, a column-major mat4x2 is stored in a buffer object as an array of 2 vec4's. A row-major mat4x2 is stored as an array of 4 vec2's.
The storage for vector elements is sequential and tightly packed, as previously mentioned. However, the spacing between vector elements of a matrix is not. It is a value queried with GL_UNIFORM_MATRIX_STRIDE. This stride is the byte distance from the beginning of one vector to the beginning of the next.
Again, the querying is not necessary when using the std140 layout for a uniform block. There, these values are well-defined by the specification. Section 2.11.4 of the OpenGL 3.2 core specification describes this layout in great detail. You can design C structs that obey the layout conventions.
If you bind a uniform buffer with glBindBufferRange, the offset field of that parameter must be a multiple of GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT (this is a global value, not a per-program or per-block one). Thus, if you want to put the data for multiple uniform blocks in a single buffer object, you must make sure that the data for each within that block matches this alignment.
An older form of this functionality exists through the GL_EXT_bindable_uniform. This extension differs in many substantial ways. Essentially, it is a way of providing buffer object storage for a single uniform.
The "single uniform" is allowed to be a struct, array, or array of structs rather than just a single basic type. So one could store multiple component members easily enough.
This extension only provides storage for this uniform. It does not expose layout. Where UBO requires the user to use standard buffer object manipulation to fill the buffer object, bindable uniform uses the same
glUniform API to change the values in the bound uniform.
This also means that sharing buffers is not guaranteed. Implementations can still optimize away unused uniforms, so you cannot guarantee that you can share the same buffer object between different programs. The fact that you have to use the program object API to change the buffer object's data also means that you have to use a program object to change the stored uniform data.
Since the same hardware that supports GL_EXT_bindable_uniform is just as capable of supporting GL_ARB_uniform_buffer_object, and UBO is a core extension, you are advised to prefer UBOs over bindable uniform. This is only problematic on Mac OSX, because (at the time this was written), that platform exposes bindable uniform for appropriate hardware but not UBO or any other GL 3.x extensions.