Hopefully this will clear up a few misconceptions, and give you a little bit better understanding of how general purpose shader storage is setup.
What you have to understand is how buffer objects really work in GL. You often hear people distinguish between things like "Vertex Buffer Objects" and "Uniform Buffer Objects". In reality, there is no fundamental distinction – a buffer object is treated the same way no matter what it stores. It is just a generic data store, and it only takes on special meaning while it is bound to a specific point (e.g. GL_ARRAY_BUFFER
or GL_UNIFORM_BUFFER
).
Do not think of special purpose vertex buffers residing on the GPU, think more generally – it is actually unformatted memory that you can read/write if you know the structure. Calls like glVertexAttribPointer (...)
describe the data structure of the buffer object sufficiently for glDrawArrays (...)
to meaningfully pull vertex attributes from the buffer object's memory for each vertex shader invocation.
You need to do the same thing yourself for compute shaders, as demonstrated below. You need to familiarize yourself with the rules discussed in 7.6.2.2 - Standard Uniform Block Layout to fully understand the following data structure.
Description of a vertex data structure using Shader Storage Blocks can be done like so:
// Compute Shader SSB Data Structure and Buffer Definition
struct VtxData {
vec4 vtx_pos; // 4N [GOOD] -- Largest base alignment
vec3 vtx_normal; // 3N [BAD]
float vtx_padding7; // N (such that vtx_st begins on a 2N boundary)
vec2 vtx_st; // 2N [BAD]
vec2 vtx_padding10; // 2N (in order to align the entire thing to 4N)
}; // ^^ 12 * sizeof (GLfloat) per-vtx
// std140 is pretty important here, it is the only way to guarantee the data
// structure is aligned as described above and that the stride between
// elements in verts[] is 0.
layout (std140, binding = 1) buffer VertexBuffer {
VtxData verts [];
};
This allows you to use an interleaved vertex buffer in a compute shader, with the data structure defined above. You have to be careful with data alignment when you do this... you could haphazardly use any alignment/stride you wanted for an interleaved vertex array ordinarily, but here you want to conform to the std140
layout rules. This means using 3-component vectors is not always a wise use of memory; you need things to be aligned on N (float
), 2N (vec2
) or 4N (vec3
/vec4
) boundaries and this often necessitates the insertion of padding and/or clever packing of data. In the example above, you could fit an entire 3-component vector worth of data in all the space wasted by alignment padding.
Pseudo-code showing how the buffer would be created and bound for dual-use:
struct Vertex {
GLfloat pos [4];
GLfloat normal [3];
GLfloat padding7;
GLfloat st [2];
GLfloat padding10 [2];
} *verts;
[... code to allocate and fill verts ...]
GLuint vbo;
glGenBuffers (1, &vbo);
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glBufferData (GL_ARRAY_BUFFER, sizeof (Vertex) * num_verts, verts, GL_STATIC_DRAW);
glVertexAttribPointer (0, 4, GL_FLOAT, GL_FALSE, 48, 0); // Vertex Attrib. 0
glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 48, 16); // Vertex Attrib. 1
glVertexAttribPointer (2, 2, GL_FLOAT, GL_FALSE, 48, 32); // Vertex Attrib. 2
glBindBufferBase (GL_SHADER_STORAGE_BUFFER, 1, vbo); // Buffer Binding 1