Unterschied in glDrawArrays und glDrawElements

12

Während ich mich bei OpenGL ES erfrischte, stieß ich auf glDrawArraysund glDrawElements.

Ich verstehe, wie sie verwendet werden und verstehe, warum sie unterschiedlich sind.

Was ich nicht zu verstehen scheine, ist, dass ich nicht sehe, wie glDrawElementsDraw Calls gespeichert werden können (das Speichern von Draw Calls ist eine Beschreibung, die in den meisten Büchern, die ich gelesen habe, erwähnt wird, daher erwähne ich sie hier).

Stellen Sie sich ein einfaches Szenario vor, in dem ich versucht habe, ein Quadrat mit zwei Dreiecken zu zeichnen.

Ich würde einen Satz von 6 Eckpunkten benötigen, die glDrawArrayswährend der Verwendung verwendet glDrawElementswerden. Ich würde nur 4 zusätzlich zu einem Indexarray mit 6 Elementen benötigen.

In Anbetracht des oben Gesagten verstehe ich Folgendes nicht:

  1. Wie könnten glDrawElementsDraw-Aufrufe gespeichert werden, wenn das Index-Array (6 Indizes, im Falle eines Quadrats) weiterhin zum Indizieren in mein Vertex-Array mit 4 Elementen (6-mal) verwendet werden muss? Mit anderen Worten, bedeutet dies, glDrawElementsdass immer noch insgesamt 6 Draw Calls erforderlich sind, genau wie glDrawArrays?
  2. Wie würde man Platz glDrawElementssparen, wenn man noch zwei Arrays haben müsste, nämlich eines für die Eckpunkte und eines für die Indizes?
  3. Wie viele Zeichenaufrufe werden beim Zeichnen eines Quadrats aus 2 Dreiecken glDrawElements(4 Elemente im Scheitelpunktarray und 6 Elemente im Indexarray) und glDrawArrays(6 Elemente nur im Scheitelpunktarray) einzeln benötigt?

Vielen Dank.

Unheilig
quelle

Antworten:

15

Jeder glDraw * ist ein Draw-Aufruf.

1 glDrawArrays ist 1 Draw Call.
1 glDrawElements ist 1 Draw Call.

Es spielt keine Rolle (was die Anzahl der Draw Calls betrifft), wie viele Eckpunkte oder Indizes Sie verwenden. 1 glDraw * ist 1 Draw Call.

Die einfachen Fälle des Zeichnens von Quads (vorausgesetzt, die von Ihnen verwendete GL-Version hat Quads nicht entfernt) oder Dreiecke sind kein gutes Beispiel für den Vergleich dieser Quadrate, da eine Liste von Quads oder eine Liste von Dreiecken mit einem einzigen Aufruf gezeichnet werden kann. und glDrawArrays erscheinen effizienter, da sie keinen zusätzlichen Aufwand für die Indizierung haben.

Nehmen wir einen etwas komplexeren Fall, der jedoch für ein realistischeres Szenario repräsentativ ist. Stellen Sie sich vor, Sie haben ein Modell, das aus mehreren Dreiecksstreifen und Lüftern besteht:

glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);

In diesem Beispiel erhalten Sie mit glDrawArrays insgesamt 6 Zeichenaufrufe für das Modell. Sowohl Streifen als auch Lüfter können jedoch problemlos in indizierte Dreiecke konvertiert werden. Fügen Sie also Indizes hinzu, und es wird:

glDrawElements (GL_TRIANGLES, .....);

Das ist ein Draw Call für das gesamte Modell.


Die Vorteile von glDrawElements gegenüber glDrawArrays sind mehr als nur das Speichern von Speicher, was die naive Art der Messung ist. Es besteht die Möglichkeit, Speicherplatz zu sparen, wenn Scheitelpunkte wiederverwendet werden können, da ein Index normalerweise kleiner als ein Scheitelpunkt ist (selbst wenn Sie viele Indizes im Gleichgewicht haben, sind sie kleiner). Weitere Vorteile sind jedoch:

  • Reduzierte Anzahl von Draw Calls. Jeder Draw-Aufruf verursacht einen gewissen Overhead durch das Überprüfen des Status, das Einrichten von Dingen usw., und ein Großteil dieses Overheads geschieht auf der CPU-Seite. Durch die Reduzierung der Anzahl der Draw Calls vermeiden wir einen Großteil dieses Overheads.

  • Vertex-Wiederverwendung. Dies ist viel mehr als nur Speicherplatzersparnis. Ihre GPU verfügt wahrscheinlich über einen Hardware-Vertex-Cache, in dem kürzlich transformierte Vertices gespeichert werden können. Wenn derselbe Scheitelpunkt erneut eingeht und sich im Cache befindet, kann er aus dem Cache wiederverwendet werden, anstatt erneut transformiert werden zu müssen. Ihre Hardware überprüft den Cache durch Vergleichen von Indizes. In OpenGL-Begriffen können Sie den Cache also nur mit glDrawElements verwenden.


Um Ihre spezifischen Fragen zu beantworten:

  1. Wie könnte glDrawElements Draw Calls speichern ...? In dem von Ihnen verwendeten Beispiel ist dies nicht der Fall. Jeder hat einen Draw Call. Wie ich oben diskutiere, ist dieses Beispiel ein sehr schlechtes Beispiel für den Vergleich der beiden.

  2. Wie würde die Verwendung von glDrawElements Platz sparen ...? Weil Indizes kleiner als Eckpunkte sind. Ein 16-Bit-Index besteht aus zwei Bytes, ein Positions- / Farb- / Texcoord-Scheitelpunkt aus 24 Bytes. 6 Eckpunkte sind 144 Bytes, 4 Eckpunkte plus 6 Indizes sind 108 Bytes.

  3. Beim Zeichnen eines Quadrats aus 2 Dreiecken ...? Eins und eins. Ein glDraw * -Aufruf ist ein Draw-Aufruf. Die Anzahl der verwendeten Eckpunkte oder Indizes ist für diese Messung irrelevant. Aber ich muss noch einmal betonen, dass dies ein sehr schlechtes Beispiel für den Vergleich der beiden ist.


Und nur um die Sache ein wenig zu komplizieren, während glDrawElements mit Dreiecken der optimale Pfad auf Desktop-GPUs ist, kann es auf Mobilgeräten sehr unterschiedlich sein. Einige mobile GPUs bevorzugen möglicherweise glDrawArrays mit GL_TRIANGLE_STRIP (in diesem Fall würden Sie entartete Dreiecke hinzufügen, um Grundelemente zu verketten), und ich habe noch nicht einmal fortgeschrittenere Themen wie primitiven Neustart oder Multi-Draw angesprochen.

Maximus Minimus
quelle
Vielen Dank für Ihr Feedback. Ich nehme mir nur ein wenig Zeit, um zu lesen, was Sie geteilt haben. Ich wollte Sie nur wissen lassen, dass ich Ihre Antwort bestätigt habe. Danke noch einmal.
Unheilig
In Ihrem Beispiel für die Konvertierung von 6 DrawTriangleFans- und DrawTriangleStrips-Aufrufen in 1 einzelnen DrawTriangles-Aufrufe. Verbessert dies wirklich die Leistung? Nach meinem Verständnis ist das Zeichnen eines Dreiecksstreifens aus 100 Dreiecken viel effizienter als das Zeichnen von 100 Dreiecken einzeln, und der Vorteil würde leicht jede Strafe ausgleichen, die durch die Verwendung von 6 Funktionsaufrufen anstelle von 1 entsteht.
Samuel Li
@SamuelLi 6-zu-1 wird keine große Verbesserung sein, es soll lediglich das Prinzip veranschaulichen. Wie bereits erwähnt, sind Streifen in der Regel kein Leistungsgewinn gegenüber indizierten Listen auf Desktop-Hardware nach 1998. Überprüfen Sie das Datum auf jedem Dokument, das Ihnen die Verwendung von Streifen empfiehlt.
Maximus Minimus
Verstanden, danke für die Klarstellung! @ Maximus Minimus
Samuel Li