Atomic Counter

Revision as of 03:41, 5 January 2013 by Alfonse (Talk | contribs) (No longer a stub.)

Jump to: navigation, search
Atomic Counter
Core in version 4.5
Core since version 4.2
Core ARB extension ARB_shader_atomic_counters

An Atomic Counter is a GLSL variable type who's storage comes from a Buffer Object. Atomic counters, as the name suggests, can have atomic memory operations performed on them.


Atomic counters are special variables that can have basic atomic operations performed on them. They can be thought of as a very limited form of buffer image variable.

You should use them instead of Image Load/Store whenever you can fit your needs to their limitations. The main limitations compared to image load/store or Shader Storage Buffer Objects are:

  • Atomic counters can only be unsigned integers.
  • Atomic counters can only be incremented or decremented by 1; no other operations besides reading the value are allowed.
  • Atomic counter memory access is not incoherent. So it follows the same rules as for texture reads, framebuffer writes, and so forth, not the Image Load/Store rules. You do not need to use a glMemoryBarrier to synchronize counter accesses.

Atomic counter variables

There is one atomic counter variable type in GLSL: atomic_uint​. Atomic counters are opaque types; as such, they can only be declared as uniform​s or as a in​ function parameter.

Storage parameters

Atomic Counter variables have special layout settings that define where within a buffer object a particular variable comes from. These are required; there are no alternate methods to set these fields.

Atomic counters use the binding​ as mentioned above. This represents the Buffer Object bound to the given index in the indexed target GL_ATOMIC_COUNTER_BUFFER​. Atomic counters also have an optional offset​ parameter. The offset is the byte offset from the beginning of the range bound to the target to the location where this variable gets its 32-bits of storage.

The offset​ parameter is not required. If it is not specified, then the offset​ will be 4 bytes larger than the offset previously used for that binding​, starting at 0 if none were specified. For example:

layout(binding = 0, offset = 12) uniform atomic_uint one;
layout(binding = 0) uniform atomic_uint two;
layout(binding = 0, offset = 4) uniform atomic_uint three;
layout(binding = 1) uniform atomic_uint four;
layout(binding = 1) uniform atomic_uint five;
layout(binding = 1, offset = 20) uniform atomic_uint six;
layout(binding = 0) uniform atomic_uint seven;

The offsets for these are as follows:

  • one​: 12
  • two​: 16 (12 + 4)
  • three​: 4 (specified)
  • four​: 0 (unused bindings offsets always start with a default of 0).
  • five​: 4
  • six​: 20
  • seven​: 8 (the last value used for binding 0 was 4, so this one gets 8).

Two variables cannot use the same offset​ and binding​. Offsets do not strictly have to be aligned to 4 bytes (but it wouldn't be a bad idea), but two atomic counters cannot overlap in storage.


Atomic counter operations are atomic, as the name suggests. Each read or read/modify/write operation will complete before the next one will begin to execute.

There are only three operations that can be performed on atomic counters:

uint atomicCounter(atomic_uint c​);

Reads the variable, atomicly.

uint atomicCounterIncrement(atomic_uint c​);

Adds one to the value of c​, returning the original value.

uint atomicCounterDecrement(atomic_uint c​);

Subtracts one from the value of c​, returning the original value.

Atomic integer operations that overflow or underflow will wrap around. Decrementing 0 will result in 232-1.

Atomic counter storage

Storage for atomic counters is provided by buffer objects. These buffers are bound to indexed binding points in the context, through the GL_ATOMIC_COUNTER_BUFFERS binding target.

All offsets specified in the shader are relative to the range of buffer object memory bound to that target. So if you use an offset of 1024 bytes in your glBindBufferRange call, and the shader has an offset of 256 bytes, the total offset from the beginning of that buffer will be 1280.


The number of buffer object bindings for atomic counter variables is restricted per-stage. The GL_MAX_*_ATOMIC_COUNTER_BUFFERS enum defines these, where * is the usual stage-specific value. All of these have a minimum requirement of 0 in GL 4.3, except for fragment and computer shaders, which have a minimum of 1. So the minimum OpenGL requires of a conforming implementation is to allow 1 buffer in the fragment or compute stage. Thus, without further research into specific hardware, you should only expect to be able to use atomic counters in fragment or compute shaders.

The total number of atomic counter buffers useable for an entire program is limited as well. This limit is GL_MAX_ATOMIC_COUNTER_BUFFERS. Each use of an atomic counter buffer in each stage counts. So even if two or more stages share the same atomic counter buffer binding index, they both count against the limit.

The actual binding​ parameter may not exceed GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS.

There are also limits on the number of atomic counter variables per-stage. These limits are defined by GL_MAX_*_ATOMIC_COUNTERS. The minimum requirements for GL 4.3 are 0 for every stage except fragment and compute shaders, where up to 8 atomic counters are required to be supported.