Ich habe kürzlich OpenGL gelernt. In Spielen müssen wir die Position von Spielobjekten häufig aktualisieren, und sie werden ständig auf dem Bildschirm angezeigt. Das bedeutet, dass wir beim Rendern auch den Scheitelpunktpuffer ziemlich oft aktualisieren müssen.
Im OpenGL-Kontext besteht eine intuitive Möglichkeit darin, glBufferSubData zu verwenden, um die geänderten zu aktualisieren.
Ich habe aber auch online einen Trick namens Orphaning gelesen, der neue Pufferdaten erstellt und die gesamten Scheitelpunktdaten hochlädt.
Auch aufgrund der Kosten für Status-Chagnes und des Hochladens können mehrere glBufferSubData auch mehr kosten.
Hier ist meine Frage,
- Welche Methode ist besser?
- Sind Stände in diesem Fall wirklich wichtig?
- Sind Statusänderungen und Upload-Kosten in diesem Fall wirklich wichtig?
Vielen Dank!
opengl
rendering
optimization
vbo
YiFeng
quelle
quelle
Antworten:
Dies ist eine komplexe Frage mit vielen kleinen Details, die wirklich wichtig sind. Die Leistung variiert je nach Plattform und Anwendung. Sie sollten sich daher auf mögliche Engpässe konzentrieren, bevor Sie in Optimierungen investieren.
Das heißt, erstens gehe ich davon aus, dass Sie Uploads und Updates so weit wie möglich reduzieren sollten, zum Beispiel Instanzen verwenden.
Beachten Sie zweitens, dass GPUs nicht gleichzeitig Puffer übertragen und rendern können, sodass alle OpenGL-Befehle in der Befehlswarteschlange vom Gerät nacheinander verarbeitet werden. Es gibt verschiedene Möglichkeiten, Daten zu kopieren und / oder für die Verwendung durch die GPU verfügbar zu machen.
Es gibt verschiedene Möglichkeiten, Daten zur GPU zu streamen
1-
glBufferData
oderglBufferSubData
MethodeVerwenden
glBufferData
oderglBufferSubData
ist wie memcpy. Wenn Sie einen Zeiger übergeben, wird möglicherweise eine DMA-Operation ausgeführt, da der Speicher möglicherweise im CPU-Speicher fixiert und direkt von der GPU verwendet wird, ohne dass je nach Verwendungsflag (GL_STREAM) eine Speicherübertragung zur GPU erfolgt. Meiner Meinung nach sollten Sie dies zuerst versuchen, da es einfacher zu implementieren ist.2- Abrufen eines Zeigers auf den internen Speicher mit
glMapBuffer
Wenn das oben Genannte nicht gut genug ist
glMapBuffer
, erhalten Sie einen Zeiger auf den internen Speicher, und Sie können diesen Zeiger verwenden, um den Puffer direkt zu füllen. Dies ist gut bei Lese- und Schreibvorgängen für Dateien, da Sie die Dateidaten direkt zuordnen können in den GPU-Speicher anstatt zuerst in einen temporären Puffer zu kopieren. Wenn Sie nicht den gesamten Puffer zuordnen möchten, können SieglMapBufferRange
einen Teil des Puffers zuordnen.Ein Trick besteht darin, einen großen Puffer zu erstellen, die erste Hälfte zum Rendern und die zweite Hälfte zum Aktualisieren zu verwenden.
3- Buffer Orphaning
In Bezug auf das Verwaisen von Puffern kann dies mit glBufferData mit null und denselben Parametern erfolgen. Der Treiber gibt den Speicherblock zurück, sobald er nicht verwendet wird. Und wird beim nächsten glBufferData-Aufruf verwendet (es wird kein neuer Speicher zugewiesen).
Alle genannten Methoden verursachen eine Menge teurer Synchronisierungen. Auch hier können GPUs nicht gleichzeitig Puffer übertragen und rendern.
4-
Unsynchronized Buffers
Die schnellste (und am schwierigsten zu ermittelnde) Methode besteht darin, Puffer ohne Synchronisation zu verwenden, mit denen Sie das
GL_MAP_UNSYNCHRONIZED_BIT
Flag verwendenglMapBufferRange
können. Das Problem besteht darin, dass keine Synchronisierung durchgeführt wird, sodass wir möglicherweise Daten in einen Puffer hochladen, der verwendet wird, und daher alles vermasseln. Sie können mehrere Puffer mit unsynchronem Bit verwenden, um die Arbeit ein wenig zu vereinfachen.quelle
Ich mache es anders. Vielleicht stimmt da etwas nicht. Einige versteckte gefährliche Dinge.
(Ich benutze C # + OpenTK.GameWindow)
Zur Instanziierung des Objekts verwende ich für jede Instanz ein separates Array-Pufferobjekt für den Satz von Modellmatrizen. Im Scheitelpunkt geteilt:
In meinem C # -Code werden Matrizen in einem Float-Array gespeichert
float[] mat4_array
Dann binde ich das Array an das Array Buffer Object:
In jedem Frame werden die Modellmatrizen aktualisiert. Zum Update
mat4_array
rufe ich einfach an:und rendern Sie die Szene. mit
GL.DrawElementsInstanced
.Es gibt keine zusätzlichen OpenGL-Aufrufe und es funktioniert perfekt.
quelle