Pourquoi ce shader de géométrie ralentit-il autant mon programme?


27

J'ai un programme OpenGL et je rend un maillage de terrain. Je déplace les sommets dans le tampon de vertex et je ne les colore pas encore vraiment dans le fragment shader. J'ajoute un shader de géométrie une partie à la fois.

Avant d'ajouter le shader de géométrie, alors que je programmais simplement les étapes d'ombrage des fragments et des sommets du pipeline, j'obtenais des fréquences d'images d'environ 30+. Assez que je n'ai pas remarqué de saccades. Après avoir ajouté le shader de géométrie, j'obtiens environ 5 images par seconde. Pourquoi? Voici l'intégralité du shader de géométrie:

#version 420

layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

void main()
{
    for (int i = 0; i < gl_in.length(); i++)
    {
        gl_Position = gl_in[i].gl_Position;
        EmitVertex();
    }
    EndPrimitive();
}

N'est-ce pas exactement ce que faisait OpenGL sans le shader de géométrie?

Réponses:


40

N'est-ce pas exactement ce que faisait OpenGL sans le shader de géométrie?

Non, ça ne l'est pas. La GS est une étape facultative , pas une étape qui a une valeur par défaut.

Pour qu'OpenGL exécute un shader de géométrie , il doit effectuer ce que l'on appelle un « assemblage primitif ». Lorsque vous effectuez le rendu d'une série de triangles via GL_TRIANGLE_STRIP, OpenGL fera des trucs internes pour convertir tous les 3 sommets adjacents en un triangle individuel, en modifiant l'ordre d'enroulement de manière appropriée.

Normalement, lorsque vous n'utilisez pas de GS, ce processus est effectué une fois. Cependant, lorsque vous utilisez une GS, elle doit être effectuée avant l'exécution de la GS. Mais elle doit également être effectuée après la GS, car une GS peut produire un type primitif totalement différent (par exemple des quads).

Alors maintenant, vous faites en sorte que le système fasse un tas de travail supplémentaire pour rien. Après tout, OpenGL ne peut pas supposer que votre GS ne fait rien (c'est un problème indécidable).

De plus, un certain nombre d'optimisations ne fonctionnent plus en présence d'un GS. Considérez le rendu indexé.

Chaque index d'un tampon de tableau d'éléments produira les mêmes sorties d'un vertex shader. Ainsi, le GPU mettra souvent ces sorties en cache dans un cache post-T&L . S'il voit un index qui est déjà dans le cache, le VS n'est pas exécuté à nouveau; il récupère simplement les données du cache.

Qu'Est-ce que c'est"? "It" est ... l' unité d'assemblage primitif . Oui, cette chose qui s'exécute deux fois lorsque vous utilisez une GS. Les trucs de mise en cache d'index? Cela ne fonctionne que pour les entrées du GS.

Qu'advient-il donc des sorties de la GS? Eh bien, cela dépend du matériel. Mais il doit aller dans une sorte de tampon mémoire. Et c'est là que réside le problème: ce tampon n'est pas du tout indexé. C'est comme une situation glDrawArrays.

Donc, si vous envoyez un tampon d'index de 0, 1, 2, 0, 2, 3, cela se traduirait par 4 sommets dans le cache post-T & L. Mais le tampon de sommets post-GS contient désormais 6 sommets. Le tampon post-GS utilise plus d'espace. Donc, si vous avez du mal à créer des listes ou des bandes de triangle optimisées post-T & L et que vous basculez sur une GS pass-through comme la vôtre, vous avez essentiellement tué environ la moitié de vos gains de performances grâce à cette optimisation.

Ce n'était pas inutile, mais ça fait mal.

À cela s'ajoute le fait que de nombreux GPU de classe GL 3.x (aka: DX10) avaient des tampons post-GS plutôt petits. Plus le tampon est petit, moins vous pouvez activer simultanément GS invocations. Ainsi, votre matériel est efficacement goulot d'étranglement sur la GS. Étant donné que la tessellation est une caractéristique importante du matériel de classe 4.x, la plupart de ces matériels ont des tampons suffisants pour rendre viable l'utilisation de GS plus lourdes.

Ainsi, l'utilisation d'un GS est plus susceptible de goulot d'étranglement de votre traitement de vertex de code. Bien sûr, vous pouvez toujours utiliser cela à votre avantage en complexifiant vos shaders de vertex et de fragments, car il s'agit simplement de performances gratuites à ce stade.

Pour plus d'informations sur les ralentissements induits par GS, lisez cet article .

Voici une règle de base à propos des GS: n'utilisez jamais de GS car vous pensez que cela rendra le rendu plus rapide . Vous devez l'utiliser lorsque cela rend possible ce que vous essayez de faire . Si ce que vous essayez de faire est une optimisation, utilisez autre chose.

Les exceptions générales à cette règle sont les suivantes:


J'essaie de calculer la pente de chaque polygone en prenant sa hauteur la plus élevée et en soustrayant sa hauteur la plus basse. Cependant, si un shader de géométrie me ralentit nécessairement de cette quantité, je pense que je pourrais le faire de manière créative dans le vertex shader.
Avi

1
@Avi note que les points les plus hauts et les plus bas d'un triangle ne vous donneront pas sa pente; vous avez besoin des trois points.
sam hocevar

2
Personnellement, j'ai toujours trouvé l'instanciation plus utile pour les sprites ponctuels qu'une GS.
Maximus Minimus

1
L'exception pour les sprites ponctuels se généralise-t-elle aux shaders de layout(points) in;? Ou est-ce la taille de sortie fixe? Ou peut-être les deux?
Philip
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.