I. Aperçu des compteurs atomiques

Image non disponible

Il y a quelque temps, j'ai trouvé une vidéo présentant l'ordre de rendu des fragments :



Sympa ! Mais comment cela fonctionne-t-il ? La réponse est dans l'utilisation des compteurs atomiques (atomic counters).

Les compteurs atomiques sont une nouvelle fonctionnalité d'OpenGL 4.2. Ils sont disponibles à n'importe quelle étape : vertex shader, pixel shader, geometry shader, tessellation control shader et tessellation evaluation shader. La spécification complète est disponible sur OpenGL.org.

Image non disponible

En un mot, un compteur atomique est un tampon mémoire (en fait, un buffer object qui contient un ou plusieurs unsigned int) qui peut être incrémenté (avec la fonction du GLSL : atomicCounterIncrement()) ou décrémenté (avec la fonction GLSL atomicCounterDecrement()) par un shader.

Image non disponible
Information sur les compteurs atomiques dans

Une application simple mais néanmoins intéressante est la visualisation de l'ordre de rendu des fragments. Dans le pixel shader, un compteur atomique est incrémenté à chaque fois que le shader est appelé. Ensuite, si vous convertissez la valeur de ce counter en une couleur, vous pouvez facilement visualiser quel fragment est affiché. Super !

Une fois que le buffer object du compteur atomique est initialisé et lié (voir les morceaux de code OpenGL à la fin de cet article), nous pouvons écrire dans le compteur atomique avec la fonction GLSL atomicCounterIncrement().

Le code suivant permet de voir où les dix premiers fragments sont affichés :

 
Sélectionnez
#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); 
}

L'image suivante montre ces dix premiers fragments :

Image non disponible
Les dix premiers fragments
Image non disponible
Zoom sur les dix premiers fragments

Maintenant, observons l'ordre de rendu des pixels d'un quadrilatère (constitué de deux triangles) avec le shader suivant :

 
Sélectionnez
#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); 
}

Sur l'écran, la dimension du quadrilatère est 256x256 (65536 pixels à afficher). Les premiers fragments sont affichés en noir et les derniers en rouge. Voici l'ordre de rendu sur une Geforce GTX 460 :

Image non disponible
Compteurs atomiques : ordre de rendu des pixels sur Geforce GTX 460

Et voici l'ordre de rendu sur une Radeon HD 6970 :

Image non disponible
Compteurs atomiques : ordre de rendu des pixels sur Radeon HD 6970

Ou si vous préférez sur une Radeon HD 7770 :

Image non disponible
Compteurs atomiques : ordre de rendu des pixels sur Radeon HD 7770

Comme vous pouvez le voir, la façon dont sont affichés les pixels (pattern du rasterizer) diffère. La GeForce dessine plusieurs petites zones et remplit les deux triangles de haut en bas alors que la GPU Radeon dessine des zones plus grandes et remplit le premier triangle de haut en bas et le second dans le sens inverse. La GPU de la Radeon HD 6000 semble dessiner les fragments par groupe de 32x32 pixels (taille des carrés que vous pouvez voir sur la capture d'écran). Le GPU de la Radeon HD 7000 ressemble plus à un rendu de GeForce GTX 400.

II. Compteurs atomiques : la démonstration

La démonstration pour GeeXLab est disponible dans le dossier GLSL_Atomic_Counter/ du pack d'exemples. La démonstration nécessite la version 0.3.3 ou supérieure de GeeXLab.

Pour faire fonctionner la démonstration, déposez le fichier de démo (DEMO_Atomic_Counter.xml) dans GeeXLab. C'est tout.

Image non disponible

III. Compteurs atomiques : détails d'OpenGL

Pour les développeurs OpenGL, voici quelques morceaux de code montrant comment initialiser et utiliser les compteurs atomiques. En premier, le code pour initialiser le buffer object pour les compteurs atomiques :

 
Sélectionnez
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);

Maintenant, l'activation du buffer object des compteurs atomiques :

 
Sélectionnez
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, ac_buffer);

Et pour finir, la réinitialisation la valeur du compteur atomique :

 
Sélectionnez
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); 

IV. Remerciements

Cet article est une traduction autorisée de l'article paru sur Geeks3D.com.

Je tiens à remercier Winjerome et gbdivers pour leur relecture lors de la traduction de cet article, ainsi que ClaudeLELOUP pour sa relecture orthographique.