Im Großen und Ganzen lautet die Antwort "es kommt darauf an". Die Art und Weise, wie Partikel-Updates gesendet werden, unterscheidet sich erheblich von der Art und Weise, wie Sie eine Reihe von GPU-Modellen senden.
Scheitelpunkt- / Indexpuffer
Im allgemeinen Sinne wird heutzutage jedoch alles mit einem VBO ( Vertex Buffer Object ) erledigt . Die alte Sofortmodus-API ( glBegin
/ glEnd
) ist wahrscheinlich nur als Fat Wrapper um das interne Vertex-Puffersystem des Treibers implementiert.
Erstellen Sie für statische Objekte ein statisches VBO und füllen Sie es mit Ihren Scheitelpunktdaten. Wenn einer der Scheitelpunkte gemeinsam genutzt wird (normalerweise), möchten Sie wahrscheinlich auch einen Indexpuffer erstellen. Dies reduziert die Notwendigkeit, dieselben Daten mehr als einmal zu senden, um Netze zu ändern (möglicherweise Einsparung bei der Übertragungsbandbreite) und bei der Verarbeitung (Einsparung von Vertex-Shading-Zeit). Beachten Sie, dass Sie beim Erstellen von indizierten und nicht indizierten Zeichnungen mit unterschiedlichen Funktionen zeichnen.
Machen Sie dasselbe für dynamische Objekte, allerdings mit einem dynamischen Satz von Puffern.
Erweiterte Hinweise
Bei größeren Teilen wie Gelände werden Sie das Netz wahrscheinlich nicht in mehrere Teile aufteilen. Es ist eine enorme Verschwendung, die GPU dazu zu bringen, hundert Millionen Dreiecke zu rendern, wenn nur zweihunderttausend davon sichtbar sind, insbesondere wenn sie unsortiert sind und viele Überzieh- und verschwendete Fragment-Shader-Aufrufe vorliegen. Teilen Sie das Netz in große Stücke auf und rendern Sie dann nur diejenigen, die sich in der Ansicht befinden, frustrierend. Es gibt auch verschiedene fortgeschrittenere Keulungstechniken, mit denen Sie Brocken aussortieren können, die sich möglicherweise im Frustrum befinden, sich aber vollständig hinter einem Hügel oder Gebäude oder etwas anderem befinden. Es ist gut, den Countdown für Draw Calls niedrig zu halten, aber es besteht ein Gleichgewicht zwischen dem Minimieren von Draw Calls und dem Minimieren des Zeichnens versteckter Geometrie (das Sie für Ihre spezifische App / Hardware finden müssen).
Eines der wichtigsten Dinge, die Sie bei einem GPU-Puffer beachten sollten, ist, dass Sie nicht darauf schreiben können, während die GPU daraus liest. Sie müssen dem Treiber mitteilen, dass es in Ordnung ist, die alte Kopie des Puffers zu verwerfen (wenn dies erledigt ist) und Ihnen eine neue zu geben (wenn die alte beschäftigt ist). Es gibt natürlich lange Zeit keine Funktion von OpenGL, dies zu tun (jetzt gibt es InvalidateBufferData für GL 4.3 und einige ältere Implementierungen als Erweiterung). Vielmehr gibt es ein nicht standardmäßiges, aber allgemeines Verhalten, das die meisten Treiber implementieren. Gehen Sie folgendermaßen vor, um den Puffer zu verwerfen, bevor Sie ihn aktualisieren:
glBindBuffer(GL_ARRAY_BUFFER, my_vbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);
Natürlich ändern GL_ARRAY_BUFFER
und GL_DYNAMIC_DRAW
auf die entsprechenden Werte für den Puffer. Statische Puffer werden nicht aktualisiert (oder sollten es auch nicht sein), sodass Sie sich wahrscheinlich keine Gedanken über das Verwerfen eines solchen Puffers machen müssen.
Beachten Sie, dass die Verwendung möglicherweise schneller glBufferData
oder glBufferSubData
schneller ist glMapBuffer
. Es hängt wirklich vom Treiber und der Hardware ab. PC-Hardware der aktuellen Generation wird wahrscheinlich am schnellsten sein, glBufferData
aber testen Sie, um sicher zu sein.
Eine andere Technik ist die Verwendung von Instanzen . Mit der Instanz können Sie einen einzelnen Zeichenaufruf ausführen, der mehrere Kopien der Daten in einem Scheitelpunkt- / Indexpuffer zeichnet. Wenn Sie beispielsweise 100 identische Steine hätten, würden Sie sie alle auf einmal zeichnen wollen, anstatt 100 unabhängige Ziehungen durchzuführen.
Bei der Instanz müssen Sie die Daten pro Instanz in einem anderen Puffer ablegen (wie die Objektposition jedes Einzelnen). Dies kann entweder eine sein , gleichförmige Puffer ( konstante Puffer in D3D Terminologie) oder einen Texturpuffer oder ein Pro-Vertex - Instanz - Attribut. Auch hier kommt es darauf an, was schneller ist. Attribute pro Instanz sind wahrscheinlich schneller und definitiv viel einfacher zu verwenden, aber viele gängige GL-Implementierungen werden immer noch nicht unterstützt. glBindingAttribDivisor
Sie müssen also prüfen, ob sie verfügbar sind und ob sie wirklich schneller sind (einige ältere Treiber haben die Instanzierung durch Anhängen emuliert Puffer und es endete langsamer, Instanzen auf sie zu verwenden, als unabhängige Draw-Aufrufe zu tätigen, und es gibt keine Standardmethode, um herauszufinden ... die Freuden der Verwendung von OpenGL).
Es gibt auch Algorithmen zur Optimierung des Scheitelpunktcaches , bei denen Scheitelpunkte / Indizes in Ihren Puffern so angeordnet werden, dass sie mit dem Scheitelpunktcache auf modernen GPUs abgespielt werden. Eine GPU führt den Shader nur für einen Scheitelpunkt aus und speichert ihn dann im Scheitelpunkt-Cache. Möglicherweise muss er jedoch zu früh entfernt werden, um Platz für andere Scheitelpunkte zu schaffen. (Angenommen, zwei Dreiecke teilen sich einen Scheitelpunkt, aber es werden 100 andere Dreiecke zwischen ihnen gezeichnet. Der gemeinsame Scheitelpunkt wird wahrscheinlich zweimal vom Scheitelpunkt-Shader verschwenderisch ausgewertet.)
Einige dieser Funktionen erfordern eine ausreichend neue Version von GL oder GLES. GLES2 unterstützte beispielsweise keine Instanz.
Immer Profil
Wenn Sie sich erneut für die Leistung interessieren, testen Sie jede mögliche Methode und finden Sie heraus, welche für Ihre App auf Ihrer Zielhardware schneller ist. Unterschiedliche Hardware / Treiber verschiedener Hersteller sind nicht nur unterschiedlich, sondern einige ganze Hardwareklassen unterscheiden sich von Natur aus. Eine typische mobile GPU ist ein ganz anderes Tier als eine typische diskrete Desktop-GPU. Techniken, die bei einem "am besten" sind, sind bei einem anderen nicht unbedingt am besten.
Seien Sie immer skeptisch, wenn es um Leistung geht .