OpenGL 4.2 Atomic Counter Demo: Rendering Order of Fragments



OpenGL 4.2 Atomic Counter Demo, GeeXLab



Article menu:



1 – Atomic Counters: Overview

Some weeks ago I found this video showing in which order the fragments are rendered:






Pretty cool. But how does it work? Answer: thanks to the use of atomic counters.

Atomic counters are a new feature of OpenGL 4.2. Atomic counters are available in all stages: vertex, pixel, geometry, tessellation control and evaluation shaders. The complete specification is available here: GL_ARB_shader_atomic_counters.

OpenGL logo



In a word, an atomic counter is memory buffer (actually a buffer object that stores one or several unsigned int) that can be incremented (GLSL built-in function atomicCounterIncrement()) or decremented (GLSL built-in function atomicCounterDecrement()) by a shader.

GPU Caps Viewer, Atomic Counter, OpenGL 4.2
Information about atomic counters in GPU Caps Viewer



One simple but very interesting application of atomic counters is the visualization of the rendering order of fragments. In the pixel shader, an atomic counter is incremented each time the shader is invoked. Then if you convert the counter value to a color, you can quite easily visualize the order in which fragments are rendered. Nice!

Once the buffer object of the atomic counter is initialized and bound (see OpenGL code snippets at the end of the article), we can write to the atomic counter with the GLSL function atomicCounterIncrement().

The following code allows to shows where the ten first fragments are rendered:

#version 420 compatibility
layout(binding=0, offset=0) uniform atomic_uint ac;
void main(void)
{
  uint counter = atomicCounterIncrement(ac);
  if (counter < 10)
    gl_FragColor = vec4(1, 0, 0, 1);
  else
    gl_FragColor = vec4(0, 0, 0, 1);
}



The following images shows these 10 first fragments:

GeeXLab, Atomic Counter demo on GeForce GTX 460
The ten first fragments

GeeXLab, Atomic Counter demo on GeForce GTX 460
Zoom in on the ten first fragments



Now let's see the rendering order of all pixels of a quad (made up of 2 triangles) with the following shader:

#version 420 compatibility
layout(binding = 0, offset = 0) uniform atomic_uint ac;
void main(void)
{
  uint counter = atomicCounterIncrement(ac);
  float r = (counter/255) / 255.f;
  gl_FragColor = vec4(r, 0, 0, 1);
}

On the screen, the dimensions of the quad are 256x256 (65536 pixels to render). First fragments are rendered in black, last fragments are rendered in red. Here is the rendering order of a GeForce GTX 460:

GeeXLab, Atomic Counter demo on GeForce GTX 460
Atomic counter, pixels rendering order on a GeForce GTX 460



And now the rendering order of a Radeon HD 6970:

GeeXLab, Atomic Counter demo on Radeon HD 6970
Atomic counter, pixels rendering order on a Radeon HD 6970



Or do you prefer the rendering order of a Radeon HD 7770:

GeeXLab, Atomic Counter demo on Radeon HD 7770
Atomic counter, pixels rendering order on a Radeon HD 7770



As you can see, the way the pixels are rendered (rasterizer pattern) is quite different. The GeForce draws many small areas and fills both triangles from top to the bottom while the Radeon GPU draws larger zones and fills one triangle from top to bottom and the second from bottom to top. The GPU of HD 6000 seems to draw fragments in group of 32x32 pixels (size of the squares you can see on the screenshot). Radeon HD 7000 GPU looks like more to GeForce GTX 400 rendering.



2 - Atomic Counters: the Demo


The demo for GeeXLab is available in the GLSL_Atomic_Counter/ folder of GeeXLab code sample pack:
Download GeeXLab Code Samples Pack Version 2009.10.02
This demo requires GeeXLab 0.3.3+.


How to run the demo: start GeeXLab and drop the demo file (DEMO_Atomic_Counter.xml) in GeeXLab. That’s all.

GeeXLab, OpenGL 4.2 demo: atomic counter, live coding



3 - Atomic Counters: OpenGL Details

For OpenGL developers, here are some pieces of code that show you how to init and use atomic counters. First thing, the OpenGL code to initialize an atomic counter buffer object:

GLuint ac_buffer = 0;
glGenBuffers(1, &ac_buffer);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, ac_buffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);



Now how to enable the atomic counter buffer object:

glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, ac_buffer);



And to end up, how to reset the value of the atomic counter:

glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, ac_buffer);
GLuint* ptr = (GLuint*)glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint),
                                        GL_MAP_WRITE_BIT | 
                                        GL_MAP_INVALIDATE_BUFFER_BIT | 
                                        GL_MAP_UNSYNCHRONIZED_BIT);
ptr[0] = value;
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0); 




[ Subscribe to Geeks3D latest news by email ]

Geeks3D.com

↑ Grab this Headline Animator