I. Aperçu des compteurs atomiques▲
Il y a quelque temps, j'ai trouvé une vidéo présentant l'ordre de rendu des fragments :
Cliquez pour lire la vidéo
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.
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.
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 :
#
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 :
Maintenant, observons l'ordre de rendu des pixels d'un quadrilatère (constitué de deux triangles) avec le shader suivant :
#
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 :
Et voici l'ordre de rendu sur une Radeon HD 6970 :
Ou si vous préférez sur une 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.
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 :
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 :
glBindBufferBase
(
GL_ATOMIC_COUNTER_BUFFER, 0
, ac_buffer);
Et pour finir, la réinitialisation la valeur du compteur atomique :
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.