buffern von opengl states
-
Ich habe bei einigen Projekten gesehen dass bestimmte OpenGL States als variablen gebuffert wurden.
Beispiel:
bool texture2dState = false; void setTexture2d(bool b) { if(b == texture2dState) return; if(b) { glEnable(TEXTURE_2D); } else { glDisable(TEXTURE_2D); } texture2dState = b; }
Kostet die Statusveränderungen (Und Statusveränderungen die gar keine sind, da bereits der gleiche Wert gesetzt ist) wirklich soviel Performance?
Ich bastle mir gerade einige Hilfsbibliotheken für OpenGL und überlege ob es sich lohnt alle Werte in einer "Virtuellen Status Klasse" zwischen zu halten, die nur direkt vor Zeichenoperationen alle wirklich geänderten Werte setzt?
-
Renundante State Changes sind teuer. Kommt aber drauf an, was man ändert. Änderst du als Beispiel glBlendFunc ohne dass es nötig ist, ist durchaus sehr teuer. Der Treiber muss ja erstmal im Context lesen welchen Status das momentan hat, dann deine Funktionen testen ob die in Ordnung sind (die Enums) etc. Ist es am Ende das selbe, kann das durchaus Performance kosten. Sind es nur einige wenige Calls im ganzen Programm, ist das verzeihbar, wenn das Calls jeden Frame sind, kann das einen riesen Unterschied machen. Wenn du also das maximum an Performance rauskitzeln willst, ist es durchaus ratsam den State zu mirrorn.
Was man wirklich vermeiden sollte während des renderns sind glGet Funktionen, denn diese zwingen OpenGL alle gebufferten Kommandos sofort auszuführen.
Das kann am Ende in Microlags und Tearing resultieren.
Aber heutzutage sind diese Calls bei weitem nicht mehr so teuer wie früher, aber trotzdem zu vermeiden.
Als Profiler ist btw glDEBug sehr gut, der zeigt dir da schöne Statistiken an und wenns nen Debug Build ist sogar die Zeilennummer dazu. Das Programm ist kostenlos.
-
Äh. Hm.
Man kann denke ich davon ausgehen, dass State-Changes in OpenGL Implementierungen zumindest eine "if (not changed) return" Optimierung haben. Oder sogar automatisch "delayed" werden (also verzögert bis man etwas macht wo der State überhaupt relevant ist).
Vor allem bei trivialen on/off oder enum States.
Was man dadurch natürlich trotzdem gewinnt sind die Kosten eines oder mehrerer Funktionsaufrufe, und ggf. die Kosten des Wechsels Usermode->Kernelmode (und wieder zurück). Letzteres wäre durchaus spürbar, wobei ich hoffe dass die meisten OpenGL Implementierungen solche Dinge im Usermode erledigen können.
Es ist also fraglich ob man damit viel rausholen kann.
Wenn es um andere Optimierungen geht wie z.B. das Sortieren von Render-Calls nach Shader/Textur/... sieht die Sache vermutlich anders aus, denn sowas kann die OpenGL Implementierung nicht selbst machen.
(In einigen Fällen wäre eine Optimierung in der Implementierung theoretisch möglich aber vermutlich nicht praktikabel)Soll heissen: ich würde mir erstmal ansehen wie einfach oder schwer es wäre das Sortieren nach Shader/Textur/... zu implementieren, bevor ich das redundante Setzen von States angehe.
-
p.S.: gibt's in OpenGL eigentlich was analog zu D3D State-Blocks?
-
Klar, glEnable/Disable ist nicht so teuer wie glBlendFunc oder andere.
Trotzdem sollte man "leere" Aufrufe vermeiden wenn es geht.
Inwiefern die OpenGL Implementationen hier eine Optimierung haben, wissen wir ja nicht.
Man muss auch sagen, dass viele State Changes Client seitig sind und das sind die billigsten die man kriegen kann.
Server Side changes sind entsprechend teurer.
Kommt also drauf an was man ändert.Dennoch, wenn man viele unnötige Changes macht, dann ist das durchaus spürbar. Je nach Projekt kann es sich schon lohnen die States zu buffern.
-
hustbaer schrieb:
p.S.: gibt's in OpenGL eigentlich was analog zu D3D State-Blocks?
Nur wenn man pre OpenGL3 programmiert. In OpenGL3 ist glPushAttrib und glPopAttrib deprecated afaik.
Zumindest ist es auf den Reference Pages nicht mehr dabei:
http://www.opengl.org/sdk/docs/man3/Aber eine direkte Entsprechung gibt es nicht.
-
Mit meine Bibliothek versuche ich mir vor allem viel Schreibarbeit (und damit auch mögliche Fehler) zu ersparen.
Wenn man diese benutzt kommt es aber oft zum Ausschalten und direktem Wiedereinschalten von States. Nach den Infos hier werde ich auf jeden Fall die meisten States in einer extra Puffer-Klasse zwischenspeichern.
-
Osbios schrieb:
Wenn man diese benutzt kommt es aber oft zum Ausschalten und direktem Wiedereinschalten von States.
Nur damit wir uns richtig verstehen, du meinst sowas
// 1 set(on); set(off); set(on);
und NICHT sowas:
// 2 set(on); set(on);
Der Beispislcode in deinem ersten Beitrag fängt nämlich nur (2) ab.
Um (1) abzufangen müsstest du wirklich wie du schreibst die State-Changes puffern, und dann vor dem nächsten Rendercall gucken ob der gepufferte Wert nicht wieder dem bereits aktiven entspricht.
-
Mich würde interessieren, was genau diese Lib machen soll/macht.
-
Scorcher24 schrieb:
Trotzdem sollte man "leere" Aufrufe vermeiden wenn es geht.
Inwiefern die OpenGL Implementationen hier eine Optimierung haben, wissen wir ja nicht.Naja. Jeder Hersteller will ne schnelle OpenGL Implementierung haben. Daher gehe ich mal davon aus, dass Dinge die so einfach zu optimieren sind auch optimiert werden.
Vor allem wenn die Optimierung so "billig" ist, dass sie auch Programme die sie nicht benötigen würden (weil sie keine redundanten State-Changes machen) kaum ausbremst. Macht IMO keinen Sinn das in jeder OpenGL Anwendung zu wiederholen, wenn man es auch in der OpenGL Implementierung machen kann.
Man muss auch sagen, dass viele State Changes Client seitig sind und das sind die billigsten die man kriegen kann.
Server Side changes sind entsprechend teurer.
Kommt also drauf an was man ändert.So lange sich alles auf der selben Maschine abspielt sollte das doch keinen Unterschied machen, oder? (Also so lange man mit einem "lokalen" Terminal arbeitet, das direkt auf die Grafikkarte zugreifen kann und wird)
-
Server ist in dem Fall die Grafikkarte. Ein Server Side Change wäre als bsp. glBindProgram oder glBindBuffer etc.
-
OK. Wobei ein teurer Server-Side-Change genauso Client seitig in der OpenGL Implementierung gepuffert werden kann, wie man den OpenGL Call in der eigenen Applikation puffern kann.
-
hustbaer:
Ich meinte in meinem letzten Beitrag deinen ersten Fall. Aber allgemein würde auch der ON ON Fall auftreten.Und ja, ich würde mit eine statische Klasse bauen über die ich alle States setzen würde. Und vor wirklichen Rendervorgängen würde ich dann eine Funktion aufrufen die die echten Veränderungen zu OpenGL überträgt.
Scorcher24:
Erstmal nur Grundlagen wie FBOs, VBOs, PBOs... Diese aber etwas handhabbarer.Also z.b.
gl::fbo myFbo; myFbo.init(256, 256); myFbo.createTexture(0); myFbo.activeSlot(0); myFbo.begin(); //... OpenGL rumkritzeln(TM) myFbo.end(); GLuint texture = myFbo.textureId();
finde ich eleganter und weniger Fehleranfälig als die ganzen OpenGL aufrufe. Vor allem weil auch das aktivieren mehrerer rendertargets und das anschließende Erzeugen von Mipmaps etc... alles von der Klasse übernommen wird.
Bei dem VBO teil bin ich noch am basteln. Deshalb kam auch erst meine Frage wegen den State changes auf.
-
Ich bin kein OpenGL Guru, ich arbeite nur schon ne Weile damit. Und ich habe halt viele Diskussionen dazu gelesen und sehr viele Seiten zu OpenGL gewälzt. Manche davon waren etwas älter leider. Aber ich denke dass sehr vieles davon auch heute noch gültig ist. Wie die Implementationen von ATI und NVIDIA hier arbeiten, das wissen wir halt wirklich nicht 100%. Böse Zungen sagen auch, dass die nur in DirectX Calls umgewandelt werden und OpenGL quasi als solches gar nicht vorhanden ist.
Auch der glDEBugger zählt renundante State Changes auf und rät diese zu minimieren. Ich finde man sollte das einfach mal glauben.
http://www.abload.de/img/gebugao6n.png@Osbios
Jo, so eine Klasse habe ich auch:
http://code.google.com/p/nightlight2d/source/browse/trunk/NightLightDLL/NLRenderToTexture.cppIst auch gut sowas zu machen.
Auf der anderen Seite:
Welche OpenGL Version programmierst du? 3D?
Gut, ich mach nur 2D und komme mit mit relativ wenigen State Changes aus.
Ist halt die Frage des nutzens. Schlecht ist es aber auf keinen Fall.
Ich behaupte mal dass ein if ( m_blend_set ) billiger ist als ein glGet.
-
Das meiste war bis jetzt 2d. Da konnte ich noch ohne Probleme mit begin end arbeiten. Ich benutzt Version 2.x OpenGL. Mehr gibt mein alte Karte auch nicht her.
-
Nur mal so ne Frage: Hast du denn ein Performanceproblem?
-
Wenn ich nicht auf solche Sachen achte bestimmt sehr bald.
Bestimmte Sachen sollte man halt von Anfang an beachten, weil man später sonnst alles wieder umbauen muss.
Außerdem kann ich anschließend mittels dieses zwischengeschalteten Puffers erst Performancetest machen und so sehen wie viel es wirklich ausmacht. (Naja, halt auf meinem System)Hier hat leider noch keiner Zahlen zeigen können.
Und wenn es so sehr auf die Performance gehen sollte ist es sehr viel einfacher dieses ganze Problem mittels einer State-Klasse zu handhaben und in den restlichen Lib-Teilen nicht mehr penibel darauf achten zu müssen.
-
dot schrieb:
Nur mal so ne Frage: Hast du denn ein Performanceproblem?
Das war die wichtigste Frage bisher
Warum mit Optimierungen anfangen (bei denen man noch nicht mal weiss, ob sie überhaupt was bringen), wenn keine Performanceprobleme bestehen. Und falls letztere vorhanden sind sollte man erstmal den code identifizieren, der den Flaschenhals darstellt.P.S.: Es gibt auch noch glIsEnabled - nicht alles muss über glGet laufen.
-
Steht glIsEnabled nicht auf der selben Stufe wie glGet? Ist ja nur nen anderes namens-schema für eine Funktion die einen BOOL zurückgibt. Aber am Ende macht sie das selbe: Sie erzwingt alle Kommandos in der Pipeline bevor der Code ausgeführt wird.
http://www.mesa3d.org/brianp/sig97/perfopt.htm#MiscMan sollte solche Funktionen während des Renderns halt vermeiden wo es nur geht und wenn das alles in der Update Funktion machen.
Im Gegensatz zu euch empfinde ich das auch nicht als unnötige Microoptimierung, sondern finde es gut wenn man sich darüber vorher Gedanken macht und gewissenhaft plant. Denn das nachher wieder alles umzuschreiben ist mehr Arbyte.
-
Also ich glaube nicht dass glGet wirklich bremst, dazu müsste eine OpenGL Implementierung schon recht doof sein. Ich denke viele dieser Tips sind einfach nichtmehr wahr, und halten sich nur weil jeder sie weitererzählt ohne es selbst ausprobiert zu haben.
-
Ich könnte mir vorstellen dass der CPU seitige Overhead unter Umständen relevant werden könnte. Vor allem die glBind*() calls haben einen evtl. nicht zu vernachlässigbaren Overhead (nicht umsonst hat NVIDIA eine Extension entwickelt um ohne glBind*() arbeiten zu können). Was reine State Changes angeht so liest man aber eigentlich überall dass das heutzutage praktisch nichtmehr relevant ist (glGet() sowieso nicht, warum sollte das einen Flush verursachen, damit liest du doch nur CPU seitigen API State aus, natürlich gibts Function call Overhead, aber das ist denk ich mal klar). Lediglich Shader und Texture Switches können kosten, der Rest ist wohl eher vernachlässigbar. Viel wichtiger als redundante State Changes vermeiden ist heute wohl vernünftiges Batching. Wie immer bei solchen Dingen: Die Antwort hängt von deiner Anwendung ab, wenn dus genau wissen willst frag deinen Profiler...