Wird das Ändern einer Textur (Malen darauf) als „Zustandsänderung“ betrachtet?

11

Die Konvention in Grafiken lautet, dass weniger Zustandsänderungen besser sind als mehr Zustandsänderungen (Shader wechseln, Puffer binden, Texturen binden usw.). Bei Texturen ist es schneller, viele Polygone mit einem einzigen Atlas (zum Rendern von Sprites / Text) zu rendern, als für jedes Polygon einzeln eine neue Textur zu binden.

Gilt das, wenn ich ständig über eine Textur male glTexSubImage2D? Ich habe einen Datenstrom (über ein Netzwerk), der verarbeitet wird und dann zeilenweise auf eine Textur gemalt wird. Die Daten werden visuell in einer endlosen Schriftrolle dargestellt.

Wäre es besser, wenn ich auf eine Textur male, die auf einem großen Rechteck gerendert ist (indem ich die gemalten Daten in die Ansicht scrolle)? Die Idee hier ist, dass ich zu einem bestimmten Zeitpunkt eine (oder zwei) Texturen gebunden haben würde, während ich einfach weiter daran male.

Oder sollte ich viele kleine Rechtecke malen (das Rechteck wird erst nach dem Malen sichtbar)? Ich gehe davon aus, dass ich eine Textur pro Rechteck binden würde.

TheBuzzSaw
quelle

Antworten:

11

Das Aktualisieren eines Speicherbereichs im Grafikgerät (eine Textur, ein Puffer usw.) entspricht nicht ganz dem Ändern eines Rendering-Status.

Was eine Änderung des Renderstatus teuer macht, ist der Arbeitsaufwand, den der Treiber leisten muss, um die neuen Status zu validieren und die Pipeline neu zu ordnen. Dies führt höchstwahrscheinlich auch zu einer gewissen Synchronisation zwischen CPU und Grafikgerät. Die zwischen den Geräten übertragene Datenmenge sollte jedoch für eine Statusänderung gering sein (wahrscheinlich nur wenige Befehle).

Bei einer Textur- / Pufferaktualisierung liegen die Hauptkosten dagegen in der Datenübertragung selbst. Theoretisch sollte es keine Synchronisation oder Pipeline-Verzögerungen geben, es sei denn, Sie lesen die Texturdaten nach dem Update zurück in die CPU. Ein weiterer Aspekt sollte jedoch berücksichtigt werden: API-Overhead. Selbst wenn die Datenmenge, die Sie an das Grafikgerät senden, gering ist, werden die Kosten für die Kommunikation mit dem Treiber / Gerät höher als die Kosten für die Datenübertragung, wenn Sie dies häufig genug tun. Dies ist ein weiterer Grund, warum das Stapeln bei der Optimierung eines Renderers so wichtig ist.

In Ihrem Fall wäre es meiner Meinung nach der beste Ansatz, eine Systemspeicherkopie der Textur aufzubewahren, die Sie aktualisieren, wenn neue Daten eintreffen. Setzen Sie ein Dirty-Flag und konsolidieren Sie so viele Updates wie möglich zu einem glTexSubImagefür die gesamte Textur (oder einen großen Teil davon). Sie können auch mit Pixel Buffer Objects spielen und versuchen, eine asynchrone Datenübertragung durchzuführen, um Pipeline-Verzögerungen so weit wie möglich zu reduzieren. Wenn Sie eine Art Doppelpufferung implementieren können, können Sie auf eine Kopie der Textur schreiben, während die andere mit gerendert wird. Dieses Tutorialuntersucht dieses Szenario. Das ist mein intuitiver Ansatz. Ich würde versuchen, die Anzahl der API-Aufrufe zu reduzieren und die Texturaktualisierungen zu "stapeln". Abgesehen davon ist dies sehr spekulativ, und Sie müssten dies profilieren und mit anderen Ansätzen vergleichen, z. B. mit mehreren kleinen Updates, um sicher zu wissen, welcher in Ihrem Anwendungsfall am leistungsfähigsten ist.

Als Randnotiz ist diese Präsentation von NVidia ebenfalls relevant und bietet viele gute Einblicke: Annäherung an den Null-Treiber-Overhead in OpenGL .

glampert
quelle
5
Ich weiß es nicht genau, aber ich würde definitiv vermuten, dass glTexSubImage auf einer Textur, die in den letzten ein oder zwei Frames gerendert wurde, die Pipeline blockiert, da PC-Treiber häufig versuchen, ein oder zwei Frames zu puffern, und dies wahrscheinlich nicht ist wegen eines winzigen Updates Kopien ganzer Texturen erstellen zu wollen. Daher würde ich erwarten, dass eine doppelte oder dreifache Pufferung der Texturen (oder Pixelpufferobjekte) für eine maximale Leistung erforderlich ist.
John Calsbeek