GLSL memoryBarrier Function Guarantees Explained
In OpenGL Shading Language (GLSL), the memoryBarrier()
function acts as a comprehensive ordering control for memory
transactions issued by a single shader invocation. This article details
the specific guarantees provided by memoryBarrier() across
all supported GLSL memory spaces, how it affects the ordering of memory
reads and writes, its interaction with the coherent
qualifier, and how it differs from execution synchronization barriers
like barrier().
The Core Guarantee: Ordering of Memory Transactions
The primary guarantee of memoryBarrier() is compile-time
and run-time ordering, not execution synchronization.
When an invocation calls memoryBarrier(), the GLSL
specification guarantees that:
- All memory writes issued by that invocation prior to calling
memoryBarrier()will complete and be committed to memory before any memory reads or writes issued after the call tomemoryBarrier()are executed. - Memory operations cannot be reordered by the compiler or the GPU memory architecture across the barrier boundary.
memoryBarrier() does not stall or pause other shader
invocations, nor does it guarantee that other invocations have reached
the same point in execution. It only controls the relative order of
memory operations initiated by the calling invocation.
Memory Spaces Covered by memoryBarrier()
Calling memoryBarrier() serves as an umbrella operation.
It provides the combined ordering guarantees of all specialized GLSL
memory barriers in a single call, covering the following memory
spaces:
- Buffer Variables (SSBOs): Guarantees ordering for
all reads and writes to Shader Storage Buffer Objects (equivalent to
memoryBarrierBuffer()). - Image Variables: Guarantees ordering for memory
operations initiated through image functions such as
imageStore(),imageLoad(), andimageAtomic*()(equivalent tomemoryBarrierImage()). - Atomic Counters: Guarantees ordering for atomic
counter operations such as
atomicCounterIncrement()andatomicCounterDecrement()(equivalent tomemoryBarrierAtomicCounter()). - Shared Variables: In compute shaders, it guarantees
ordering for accesses to variables declared with the
sharedstorage qualifier (equivalent tomemoryBarrierShared()).
Because it spans all these spaces simultaneously, a write to an SSBO before the barrier is guaranteed to complete before an image read after the barrier is executed, preventing cross-resource race conditions within the invocation.
Inter-Invocation
Visibility and the coherent Qualifier
For writes ordered by memoryBarrier() to become visible
to other invocations, the targeted resources must be declared with the
coherent memory qualifier (which is the default for atomic
counters).
- Without
coherent: Writes may remain in local invocation caches, and subsequent reads by other invocations may read stale cached values, regardless of memory barriers. - With
coherent:memoryBarrier()forces pending writes from the calling invocation out of local caches toward shared visibility, and ensures that subsequent reads by the invocation invalidate stale cache entries to observe newly committed data.
memoryBarrier() vs.
barrier()
It is vital to distinguish memoryBarrier() from
barrier():
memoryBarrier(): Affects only memory access ordering within the calling invocation. It can be called inside conditional code and non-uniform control flow without causing undefined behavior or GPU deadlocks.barrier(): An execution barrier for compute shaders and tessellation control shaders. It forces all invocations in the local workgroup to pause until all invocations reach the barrier, while also ordering shared and memory operations. UnlikememoryBarrier(),barrier()must be encountered uniformly by all invocations in a workgroup.