SSBOs, Images & Atomics

SSBOs vs UBOs: SSBOs are much larger (≥ 128 MB vs ~64 KB), writable, support unsized arrays and atomics, but UBO reads may be faster for small uniform data.

27.1 Shader Storage Buffer Objects

SSBOs vs UBOs: SSBOs are much larger (≥ 128 MB vs ~64 KB), writable, support unsized arrays and atomics, but UBO reads may be faster for small uniform data.

struct Particle { vec4 position; vec4 velocity; };      // vec4s keep std430 layout simple

layout (std430, binding = 0) buffer ParticleBuffer {
    float deltaTime;            // fixed-size members first
    uint count;
    Particle particles[];       // unsized array must be LAST
};

void main() {
    uint i = gl_GlobalInvocationID.x;
    if (i >= count) return;
    particles[i].velocity.y -= 9.81 * deltaTime;
    particles[i].position.xyz += particles[i].velocity.xyz * deltaTime;
}
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo);
// Use the same buffer as a vertex buffer for rendering after glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT)

27.2 Image load/store

Fragment shaders can also write images/SSBOs (e.g., order-independent transparency, voxelization, GPU picking).

layout (r32f, binding = 0) uniform image2D uHeight;          // format qualifier required for reading
float h = imageLoad(uHeight, ivec2(x, y)).r;
imageStore(uHeight, ivec2(x, y), vec4(h + 0.1));
imageSize(uHeight);
layout (r32ui, binding = 1) uniform uimage2D uCounter;
uint old = imageAtomicAdd(uCounter, ivec2(x, y), 1u);        // atomic on image texels (r32i/r32ui)

27.3 Atomic operations

layout (std430, binding = 2) buffer Stats { uint visibleCount; uint histogram[256]; };
atomicAdd(visibleCount, 1u);
atomicMin(...); atomicMax(...); atomicAnd(...); atomicOr(...); atomicXor(...);
atomicExchange(x, v); atomicCompSwap(x, compare, v);
// Floating-point atomics need extensions (GL_EXT_shader_atomic_float, NV_shader_atomic_float)
layout (binding = 0, offset = 0) uniform atomic_uint uCount;   // atomic counter buffers
atomicCounterIncrement(uCount);

27.4 Memory coherence rules

Writes from one invocation are not automatically visible to others: use coherent qualifiers and memoryBarrier*() within a dispatch; use glMemoryBarrier() (or Vulkan pipeline barriers) between dispatches/draws.