Warum verlangsamt dieser Geometrie-Shader mein Programm so sehr?

27

Ich habe ein OpenGL-Programm und rendere ein Geländegitter. Ich verschiebe die Vertices im Vertex Buffer und färbe sie im Fragment Shader noch nicht richtig ein. Ich füge Teil für Teil einen Geometrie-Shader hinzu.

Bevor ich den Geometrie-Shader hinzufügte, wurden beim Programmieren der Fragment- und Vertex-Shading-Schritte der Pipeline Frameraten von ca. 30+ ermittelt. Genug, dass ich keine Abnutzungserscheinungen feststellen konnte. Nach dem Hinzufügen des Geometrie-Shaders erhalte ich ungefähr 5 Bilder pro Sekunde. Warum? Dies ist die Gesamtheit des Geometrie-Shaders:

#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();
}

Ist das nicht genau das, was OpenGL ohne den Geometrie-Shader tat?

Avi
quelle

Antworten:

40

Ist das nicht genau das, was OpenGL ohne den Geometrie-Shader tat?

Nein, das ist es nicht. Der GS ist ein optionaler Schritt, kein Schritt mit Standardeinstellung.

Damit OpenGL einen Geometrie-Shader ausführen kann , muss es eine sogenannte " primitive Assembly " ausführen . Wenn Sie eine Reihe von Dreiecken über rendern GL_TRIANGLE_STRIP, wandelt OpenGL alle 3 benachbarten Scheitelpunkte in ein einzelnes Dreieck um und ändert die Wicklungsreihenfolge entsprechend.

Normalerweise wird dieser Vorgang einmal ausgeführt, wenn kein GS verwendet wird. Wenn Sie jedoch eine GS verwenden, muss diese ausgeführt werden, bevor die GS ausgeführt wird. Es muss aber auch nach dem GS durchgeführt werden, da ein GS einen völlig anderen primitiven Typ (zB Quads) ausgeben kann.

Jetzt veranlassen Sie das System im Grunde genommen, eine Menge zusätzlicher Arbeit für nichts zu erledigen. Schließlich kann OpenGL nicht davon ausgehen, dass Ihre GS nichts tut (das ist ein unentscheidbares Problem).

Darüber hinaus funktionieren einige Optimierungen bei Vorhandensein einer GS nicht mehr. Betrachten Sie indiziertes Rendern.

Jeder Index aus einem Elementarray-Puffer erzeugt die gleichen Ausgaben von einem Vertex-Shader. Daher speichert die GPU diese Ausgaben häufig in einem Post-T & L-Cache . Wenn ein Index angezeigt wird, der sich bereits im Cache befindet, wird der VS nicht erneut ausgeführt. Es werden nur Daten aus dem Cache abgerufen.

Was ist es"? "Es" ist ... die primitive Montageeinheit . Ja, das Ding, das zweimal ausgeführt wird, wenn Sie eine GS verwenden. Das Index-Caching-Zeug? Es funktioniert nur für die Eingänge der GS.

Was passiert nun mit den Ausgängen der GS? Nun, das ist hardwareabhängig. Aber es muss in eine Art Speicherpuffer gehen. Und darin liegt das Problem: Dieser Puffer ist überhaupt nicht indiziert. Es ist wie in einer glDrawArrays-Situation.

Wenn Sie also einen Indexpuffer von senden 0, 1, 2, 0, 2, 3, würde dies zu 4 Eckpunkten im Post-T & L-Cache führen. Der Post-GS-Puffer von Vertices enthält jetzt 6 Vertices. Der Post-GS-Puffer benötigt mehr Platz. Wenn Sie sich also die Mühe machen, nach dem Test ordnungsgemäß optimierte Dreieckslisten oder -streifen zu erstellen, und eine Pass-Through-GS wie Ihre umblättern, haben Sie im Grunde genommen etwa die Hälfte Ihres Leistungsgewinns durch diese Optimierung getötet.

Es war nicht nutzlos, aber es tut weh.

Hinzu kommt, dass viele GPUs der GL 3.x-Klasse (auch bekannt als: DX10) eher kleine Post-GS-Puffer hatten. Je kleiner der Puffer, desto weniger GS-Aufrufe können gleichzeitig aktiv sein. Ihre Hardware hat also effektiv Engpässe bei der GS. Da Tessellation ein großes Merkmal von Hardware der 4.x-Klasse ist, verfügen die meisten dieser Hardware über Puffer, die ausreichen, um eine stärkere GS-Nutzung zu ermöglichen.

Die Verwendung eines GS führt daher mit größerer Wahrscheinlichkeit zu Engpässen bei der Verarbeitung von Code-Vertexen. Natürlich können Sie dies immer zu Ihrem Vorteil nutzen, indem Sie Ihre Vertex- und Fragment-Shader komplexer gestalten, da dies zu diesem Zeitpunkt nur eine kostenlose Leistung ist.

Weitere Informationen zu GS-induzierten Verlangsamungen finden Sie in diesem Artikel .

Hier ist eine grundlegende Faustregel für GS: Verwenden Sie niemals eine GS, da Sie denken, dass dies das Rendern beschleunigen wird . Sie sollten es verwenden, wenn es das ermöglicht, was Sie versuchen zu tun . Wenn Sie versuchen, eine Optimierung durchzuführen, verwenden Sie etwas anderes.

Die allgemeinen Ausnahmen sind:

Nicol Bolas
quelle
Ich versuche, die Steilheit jedes Polygons zu berechnen, indem ich seine höchste Höhe nehme und seine niedrigste Höhe subtrahiere. Wenn mich ein Geometrie-Shader jedoch notwendigerweise um diesen Betrag verlangsamt, kann ich dies möglicherweise im Vertex-Shader kreativ tun.
Avi
1
@Avi Beachten Sie, dass der höchste und der niedrigste Punkt in einem Dreieck nicht steil sind. Sie brauchen alle drei Punkte.
Sam Hocevar
2
Persönlich fand ich es immer sinnvoller, Instanzen für Punkt-Sprites zu verwenden als für eine GS.
Maximus Minimus
1
Does the exception for point sprites generalize to shaders of layout(points) in;? Or is it the fixed output size? Or perhaps both?
Philip