Difference between revisions of "Shader Storage Buffer Object"
m (Fix spelling, grammar.) |
(→OpenGL usage: Corrections.) |
||
Line 32: | Line 32: | ||
== OpenGL usage == | == OpenGL usage == | ||
To copy an array of variable size to the SSBO | To copy an array of variable size to the SSBO | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | Note that the | + | <source lang="cpp"> |
+ | int data[SOME_SIZE]; | ||
+ | ... | ||
+ | GLuint ssbo; | ||
+ | glGenBuffers(1, &ssbo); | ||
+ | glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); | ||
+ | glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), data, GLenum usage); //sizeof(data) only works for statically sized C/C++ arrays. | ||
+ | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, ssbo); | ||
+ | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // unbind | ||
+ | </source> | ||
+ | |||
+ | Note that the buffer is bound to the [[Layout_Qualifier_(GLSL)#Binding_points|binding index]] 3. It can be assigned to an array in the shader like this: | ||
+ | |||
<source lang="glsl"> | <source lang="glsl"> | ||
− | + | layout(std430, binding = 3) buffer layoutName | |
− | + | { | |
− | + | int data_SSBO[]; | |
− | + | }; | |
</source> | </source> | ||
− | There can only be one array of variable size per SSBO and it has to be the bottommost in the | + | |
+ | There can only be one array of variable size per SSBO and it has to be the bottommost variable in the block definition. So this is possible too: | ||
+ | |||
<source lang="glsl"> | <source lang="glsl"> | ||
layout(std430, binding = 2) buffer anotherLayoutName | layout(std430, binding = 2) buffer anotherLayoutName | ||
Line 57: | Line 63: | ||
}; | }; | ||
</source> | </source> | ||
+ | |||
The data can be assigned via a struct like this: | The data can be assigned via a struct like this: | ||
+ | |||
<source lang="cpp"> | <source lang="cpp"> | ||
struct ssbo_data | struct ssbo_data |
Revision as of 15:00, 1 September 2019
|
||||||||||||
OpenGL Objects
|
A Shader Storage Buffer Object is a Buffer Object that is used to store and retrieve data from within the OpenGL Shading Language.
SSBOs are a lot like Uniform Buffer Objects. Shader storage blocks are defined by Interface Block (GLSL)s in almost the same way as uniform blocks. Buffer objects that store SSBOs are bound to SSBO binding points, just as buffer objects for uniforms are bound to UBO binding points. And so forth.
The major differences between them are:
- SSBOs can be much larger. The OpenGL spec guarantees that UBOs can be up to 16KB in size (implementations can allow them to be bigger). The spec guarantees that SSBOs can be up to 128MB. Most implementations will let you allocate a size up to the limit of GPU memory.
- SSBOs are writable, even atomically; UBOs are uniforms. SSBOs reads and writes use incoherent memory accesses, so they need the appropriate barriers, just as Image Load Store operations.
- SSBOs can have variable storage, up to whatever buffer range was bound for that particular buffer; UBOs must have a specific, fixed storage size. This means that you can have an array of arbitrary length in an SSBO (at the end, rather). The actual size of the array, based on the range of the buffer bound, can be queried at runtime in the shader using the length function on the unbounded array variable.
- SSBO access, all things being equal, will likely be slower than UBO access. SSBOs are generally accessed like buffer textures, while UBO data is accessed through internal shader-accessible memory reads. At the very least, UBOs will be no slower than SSBOs.
Functionally speaking, SSBOs can be thought of as a much nicer interface to Buffer Textures when accessed via Image Load Store.
Shader specification
SSBOs are declared as interface blocks, using the buffer keyword. They have special layout qualifiers for specifying important aspects of them, such a memory layout and binding qualities.
Atomic operations
There are special atomic functions that can be applied to variables in storage blocks (these can also be applied to Compute Shader shared variables). These only operate on uint or int types, but these can be members of aggregates (structs/arrays) or vector elements (ie: you can atomically access uvec3.x).
All of the atomic functions return the original value. The term "nint" can be int or uint.
Adds data to mem.
The mem's value is no lower than data.
The mem's value is no greater than data.
mem becomes the bitwise-and between mem and data.
mem becomes the bitwise-or between mem and data.
mem becomes the bitwise-xor between mem and data.
Sets mem's value to data.
If the current value of mem is equal to compare, then mem is set to data. Otherwise it is left unchanged.
OpenGL usage
To copy an array of variable size to the SSBO
int data[SOME_SIZE];
...
GLuint ssbo;
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), data, GLenum usage); //sizeof(data) only works for statically sized C/C++ arrays.
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // unbind
Note that the buffer is bound to the binding index 3. It can be assigned to an array in the shader like this:
layout(std430, binding = 3) buffer layoutName
{
int data_SSBO[];
};
There can only be one array of variable size per SSBO and it has to be the bottommost variable in the block definition. So this is possible too:
layout(std430, binding = 2) buffer anotherLayoutName
{
int some_int;
float fixed_array[42];
float variable_array[];
};
The data can be assigned via a struct like this:
struct ssbo_data
{
int foo;
float bar[42];
float baz[MY_SIZE];
};
TODO: This section needs to be filled in. |