3
$\begingroup$

The OpenGL SuperBible 7th Edition points out that

the glMemoryBarrier() function supports a bit specifically for synchronizing access to atomic counters with other parts of the OpenGL pipeline

and describes its usage.

The OpenGL Wiki says:

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.

So now I am confused:

Do I (and if so when do I) need to use glMemoryBarrier when using an atomic counter?

$\endgroup$

1 Answer 1

4
$\begingroup$

The post October 22, 2019 version of the OpenGL 4.6 standard gives atomic counters the same incoherency restrictions as image load/store and SSBO operations. You have to use explicit barriers to synchronize access to them and so forth. This is a textual change from older versions of the standard, but it isn't a behavioral change from implementations. The older wording is considered a bug and thus it should be assumed that atomic counters have always worked incoherently.


Note: the following predates the October 22, 2019 update to OpenGL 4.6.

From the OpenGL 4.6 specification, section 7.12: Shader Memory Access:

As described in the OpenGL Shading Language Specification, shaders may perform random-access reads and writes to buffer object memory by reading from, assigning to, or performing atomic memory operation on shader buffer variables, or to texture or buffer object memory by using built-in image load, store, and atomic functions operating on shader image variables. The ability to perform such random-access reads and writes in systems that may be highly pipelined results in ordering and synchronization issues discussed in the sections below.

Notice something missing from that list? It mentions "shader buffer variables", and "shader image variables". But atomic counter variables are not mentioned. And atomic counter variables are a different kind of thing from either of those.

Therefore, that entire section (which is what explains the behavior of glMemoryBarrier) does not apply to atomic counters. Well, except where it specifically says otherwise, but that's only where it defines that helper FS invocations don't have side-effects. Note that even this explicitly calls out atomic counters as being something different from regular buffer or image operations: "stores, atomics, and atomic counter updates".

To lend greater credence to this view, the ARB_atomic_counter_buffer_object extension does not mention barriers at all. It doesn't require ARB_shader_image_load_store as a companion extension either, which is what defines glMemoryBarrier. Indeed, it has no interactions whatsoever with image load/store, while image load/store does have specified interactions with atomic counters.

Given the weight of the evidence in the specifications, I would have to say that the Wiki is right. In the interest of full disclosure, I did write that Wiki article. Though I also did all this research before I wrote it.

Basically, what the API seems to be saying is that buffer updates from atomic counters are treated like buffer updates from feedback operations. The implementation is required to track when you attempt to use that buffer for a read operation, then issue any synchronization needed to make sure that you can read the value. Or if you write to the value in the buffer (like clearing it to a value), then the implementation must automatically synchronize this.

The likely reason the specification permits this for atomic counters but not for image load/store is that the range of data for atomic counters is fixed. SSBOs and image load/store through buffer textures can write to arbitrary parts of the bound range of buffer data. By contrast, atomic counters write to 4 bytes times the number of atomic counter variables used by the shader.

Note however that atomic counters are affected by the in-shader memoryBarrier function. That is, atomic counters are subject to the requirements of, as the Wiki calls it, "Internal Visibility". Granted, that's not very useful most of the time, as the primary use of atomic counters is for when multiple invocations are all updating the same one.


On the other hand:

the glMemoryBarrier() function supports a bit specifically for synchronizing access to atomic counters with other parts of the OpenGL pipeline

The specification describes this particular barrier as:

Memory accesses using shader atomic counter built-in functions issued after the barrier will reflect data written by shaders prior to the barrier. Additionally, atomic counter function invocations after the barrier will not execute until all memory accesses (e.g., loads, stores, texture fetches, vertex fetches) initiated prior to the barrier complete.

What's curious about this is the "all memory accesses" part. This specific wording is only used in two other bits: shader storage and shader image. Other barriers for coherent operations like query buffers and transform feedbacks tend to use the term "all shader writes".

So... I would say that there is confusion at the specification level.

$\endgroup$
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.