As others have mentioned, SSBOs have much larger storage and supports atomic operations, the accepted answer also mentioned that SSBOs are generic in the sense that they allow users to combine different types. But personally, I just want to point out that I think this is usually BAD, it is not always ideal to use interface blocks or structs in SSBO. Here's an example:
Let's say you have a struct in C++ like this:
struct Foo {
glm::vec4 position;
glm::vec4 velocity;
glm::vec4 padding_and_range; // range is a float padded to a vec4
};
which corresponds to an SSBO buffer in glsl:
struct Foo {
vec4 position;
vec4 velocity;
vec4 padding_and_range; // range is a float padded to a vec4
};
layout(std430, binding = 0) readonly buffer SSBO {
Foo data[];
} foo;
Although the SSBO buffer is able to hold an array of struct Foo
, notice that paddings must be taken into account as per the std430
memory layout, you have to padded your float range
to a vec4
, and then use foo.data[i].padding_and_range.w
to access it. This is error-prone, let alone the waste of memory spaces, especially when your SSBO is large (to be used in a compute shader) and your Foo
struct is complex (needs a lot of paddings). Apart from that, you often need to fill in the buffer data in a loop like this:
Foo* foos = reinterpret_cast<Foo*>(glMapNamedBufferRange(ssbo, offset, size, GL_MAP_READ_BIT));
for (int i = 0; i < n_foos; i++) {
Foo& foo = foos[i];
foo.position = glm::vec4(1.0f);
foo.velocity = glm::vec4(2.0f);
foo.padding_and_range = glm::vec4(glm::vec3(0.0f), 3.5f);
}
glUnmapNamedBuffer(ssbo);
instead of simply writing data to it in one go using glNamedBufferData
or glNamedBufferSubData
.
A better way of handling struct is to store each struct element into a separate SSBO, so that each SSBO buffer array is tightly packed and homogeneous. Even though the performance may not be any better, it helps keep your code clean and more readable. Rarher than using the struct, you would want to use:
layout(std430, binding = 0) buffer FooPosition {
vec4 position[];
};
layout(std430, binding = 1) buffer FooVelocity {
vec4 velocity[];
};
layout(std430, binding = 2) buffer FooRange {
float range[];
};