OpenGL/Shader/Objekte Rendern/Performance



  • Hi! 🙂

    Ich spiele gerade etwas mit OpenGL und Shadern rum, und habe jetzt ein paar generelle Fragen dazu.
    Bisher sah mein Plan eigentlich so aus, dass jedes Objekt bereits beim Erstellen seine Textur(en?), Shader, etc. bekommt. Bei einem render() Aufruf aktiviert das Objekt dann selbstständig seinen Kram. Logisch gesehen erschien mir das recht elegant. Ich bekomme jetzt allerdings schon mit ~5000 Objekten starke Performanceprobleme. (Auf der CPU, Grafikkarte hat kaum was zu tun. System: Q6600 + GeForce GTX 460)

    Einige Tests haben ergeben, dass vor allem glUseProgram() und glUniformXYZ() Aufrufe sehr viel Zeit benötigen. Ich habe z.B. einfach mal ein Programm geschrieben, bei dem der Renderloop im Wesentlichen aus 5000 glUseProgram() Aufrufen besteht und bekomme da, bei ~222 FPS, bereits eine CPU-Auslastung von ~12%. (Ohne den Loop sind es 250 FPS und 0% Auslastung, gemessen wurde mit Fraps bzw. dem TaskManager.)

    Ist das normal? Ich hätte jetzt eigentlich damit gerechnet, dass es üblich ist, sehr oft zwischen Shadern zu wechseln bzw. uniform Variablen oft zu ändern, sodass diese Dinge sehr schnell ablaufen?

    Muss ich mein Design in dieser Hinsicht vielleicht noch einmal komplett überdenken? Das mit den Shadern bekomme ich vielleicht noch in den Griff, aber bei den glUniform Aufrufen bin ich etwas ratlos, denn zumindest die Rotation/Position des Objekts muss ich ja übergeben.

    Oder sind 5000 Objekte einfach unrealistisch und viel mehr, als man üblicherweise in einem Spiel hat? (Ich rendere momentan einfach nur Quader.)

    Falls jemand mit mehr Erfahrung mir hier etwas auf die Sprünge helfen könnte, oder einen Artikel verlinkt, wäre ich ihm sehr dankbar.



  • Ich kenn mich mit OpenGL nicht aus , aber

    Oder sind 5000 Objekte einfach unrealistisch und viel mehr, als man üblicherweise in einem Spiel hat? (Ich rendere momentan einfach nur Quader.)

    Das Problem hatte ich auch schonmal.
    Bei mir wars dann so , das ich für jeden quader einen Aufruf gemacht habe.
    Ich hab das dann im Forum gemerkt , und da hat mir einer gesagt as man das mit einer renderloop machen kann, also 5000 objekte mit einem call.Hatte auch keinen lagg oder so, also war ganz praktisch(war mit irrlicht3D).

    Oder , was dein problem vielleicht in genereller hinsicht lösen könnte ist Frustum Culling .Ich weiß nicht ob es das bei OpenGL gibt.
    Wie das ganz genau geht weiß ich auch nicht , hab mich noch nicht rangewagt , in meinem buch wird das etwa mit tessellation gleichgesetzt , also eher Fortgeschritten->Profi.Naja , im Beispiel sach das so aus , also ob objekte nicht nur nicht gerendert werden , wenn man sieh nicht ansieht , sondern garnicht erstellt werden(Vermutung).

    Hier mal was ich gefunden habe http://www.rastertek.com/dx11tut16.html

    Ist halt für DX11

    😉

    Edit : In einem Spieleprogrammierer Forum hab ich gelesen , das viele einem gesamt shader erstellen , der alles beinhaltet , um nicht hin und her wechseln zu müssen



  • 7xCore schrieb:

    (war mit irrlicht3D).

    Das dürfte recht wenig mit meinem Problem zu tun haben. Zudem geht es ja darum 5000 unabhängige Objekte zu rendern, dass ich mit tricksen 5000 Quader hinbekomme ist schon klar.

    7xCore schrieb:

    In einem Spieleprogrammierer Forum hab ich gelesen , das viele einem gesamt shader erstellen , der alles beinhaltet , um nicht hin und her wechseln zu müssen

    Das wäre ja schon mal ein Hinweis. Link? Welche Begründungen führen die für dieses Verhalten an?


  • Mod

    aufrufe um shader zu setzen sind recht teuer, um konstanten zu setzen sind auch noch recht teuer, danach folgen vertex daten, texturen, render states...
    grundsaetzelich gilt, sowenig wie moeglich zu aendern, ansonsten kann es viel cpu zeit kosten die jeweiligen daten fuer einen drawcall zu preparieren z.b. kann es sein dass ein shader compiliert werden muss.
    es kostet auch auf der gpu was, da die pipeline eventuell leerlaufen muss bis ein neues shader-program gestartet werden kann.

    es gibt aber keine pauschalformel wie man das am besten loest. grundsaetzlich sollte man so gut wie moeglich cullen (frustum+occlusion), alles in wenige batches stecken und die so anordnen, dass moeglichst optimal gezeichnet wird.



  • rapso schrieb:

    grundsaetzlich sollte man so gut wie moeglich cullen (frustum+occlusion),

    Nun, das überlasse ich dann OpenGL, oder? Ich meine, ich muss die Position eines Vertex ja eh im Shader errechnen, bevor ich überhaupt weiß ob ich da was Zeichnen muss oder nicht?

    (Halt glEnable(GL_CULL_FACE);)

    rapso schrieb:

    alles in wenige batches stecken und die so anordnen, dass moeglichst optimal gezeichnet wird.

    Das hört sich für mich eher danach an, dass ich eben keine 5000 unabhängigen Objekte zeichnen kann, sondern nur 10 - 50 gänzlich verschiedene, die ich dann eben sortieren muss. Allerdings stellt sich immer noch die Frage, wie ich das dann mit den Positionen/Rotationen der Objekte mache, ich sehe eigentlich keinen Weg, diese Matrix nicht vor jedem Objekt neu zu übergeben.



  • cooky451 schrieb:

    rapso schrieb:

    grundsaetzlich sollte man so gut wie moeglich cullen (frustum+occlusion),

    Nun, das überlasse ich dann OpenGL, oder? Ich meine, ich muss die Position eines Vertex ja eh im Shader errechnen, bevor ich überhaupt weiß ob ich da was Zeichnen muss oder nicht?

    Ja, einzelne Dreiecke cullen lässt du OpenGL machen. Aber ganze Objekte cullst du auf der CPU. Stichwort: Frustrum Culling 😉

    cooky451 schrieb:

    rapso schrieb:

    alles in wenige batches stecken und die so anordnen, dass moeglichst optimal gezeichnet wird.

    Das hört sich für mich eher danach an, dass ich eben keine 5000 unabhängigen Objekte zeichnen kann, sondern nur 10 - 50 gänzlich verschiedene, die ich dann eben sortieren muss. Allerdings stellt sich immer noch die Frage, wie ich das dann mit den Positionen/Rotationen der Objekte mache, ich sehe eigentlich keinen Weg, diese Matrix nicht vor jedem Objekt neu zu übergeben.

    Genau aus diesen Gründen wurde Instancing erfunden 😉
    5000 Objekte sind normalerweise nicht unbedingt ein Problem. 5000 Objekte mit 5000 verschiedenen Shadern sind evtl. ein Problem. Aber ich glaub kaum, dass du 5000 verschiedene Shader hast...


  • Mod

    cooky451 schrieb:

    rapso schrieb:

    grundsaetzlich sollte man so gut wie moeglich cullen (frustum+occlusion),

    Nun, das überlasse ich dann OpenGL, oder? Ich meine, ich muss die Position eines Vertex ja eh im Shader errechnen, bevor ich überhaupt weiß ob ich da was Zeichnen muss oder nicht?

    (Halt glEnable(GL_CULL_FACE);)

    wie dot sagte, das ueberlaesst du nicht opengl, opengl leitet nur deine commandos an die hardware weiter, die drawcalls die du aufrufst, sind aufrufe die dich eben kosten. wenn du siehst dass 5000mal shader setzen ein problem ist, musst du verhindern dass du das so oft machst.

    rapso schrieb:

    alles in wenige batches stecken und die so anordnen, dass moeglichst optimal gezeichnet wird.

    Das hört sich für mich eher danach an, dass ich eben keine 5000 unabhängigen Objekte zeichnen kann, sondern nur 10 - 50 gänzlich verschiedene, die ich dann eben sortieren muss. Allerdings stellt sich immer noch die Frage, wie ich das dann mit den Positionen/Rotationen der Objekte mache, ich sehe eigentlich keinen Weg, diese Matrix nicht vor jedem Objekt neu zu übergeben.

    wie ich schon sagte, musst du die aufrufe minimieren, es gibt spiele die 5000 drawcalls fluessig darstellen, du solltest aber nicht jeden drawcall fuer sich sehen (und alle daten setzen), sondern alle redundanz sein lassen. oft ist es so dass du x-mal das selbe objekt nur an verschiedenen stellen zeichnest, dann setzt du halt nur die world matrix neu, alles andere fasst du nicht an.

    ist nur wirklich die frage, ob du das brauchst, hat dein spiel echt schon 5000 verschiedene objekte? es dauert ein wenig bis die von artists in guter qualitaet erstellt wurden 😉



  • dot schrieb:

    Ja, einzelne Dreiecke cullen lässt du OpenGL machen. Aber ganze Objekte cullst du auf der CPU. Stichwort: Frustrum Culling 😉

    Hm.. ok. Dann muss ich also annahmen über die Größe der Objekte machen, so wie ich das verstehe? (Ich frage dann also die logische Position des Objekts ab und rate dann, ob irgendein realer Punkt überhaupt in mein Sichtfeld guckt? Denn wenn ich das für jeden Punkt mache, müsste ich ja so wie der der Shader selbst jeden Punkt einzeln berechnen. Das fühlt sich irgendwie sehr doppelt gemoppelt an. Vielleicht kann man das ja auch gleich mit Collision Detection und Bounding Boxes verbinden, hm..)

    cooky451 schrieb:

    Genau aus diesen Gründen wurde Instancing erfunden 😉

    Den Begriff habe ich auch gerade gefunden und versuche mir gerade ein Konzept dafür zu backen, sodass ich gleichartige Objekte in einem Rutsch rendern kann.

    Zur Kommunikation mit den Shadern habe ich allerdings nur dreckige Hacks gefunden, von denen der beste noch war, die Matritzen-Arrays über einen Texturpuffer in den Shader zu schmuggeln. Ist das ernst gemeint oder finde ich einfach nicht die richtigen Quellen? 😕

    rapso schrieb:

    ist nur wirklich die frage, ob du das brauchst, hat dein spiel echt schon 5000 verschiedene objekte? es dauert ein wenig bis die von artists in guter qualitaet erstellt wurden 😉

    Hehe momentan habe ich genau 0 ernsthafte Objekte. Ich bin gerade so dabei mir ein Framework zu erstellen, um OpenGL etwas kennen zu lernen. Ich habe jetzt halt nur gemerkt, dass ich da ein Performanceproblem habe und dass ich das vielleicht erst mal lösen sollte, sonst muss ich hinterher vielleicht die ganze Struktur ändern.



  • cooky451 schrieb:

    Hm.. ok. Dann muss ich also annahmen über die Größe der Objekte machen, so wie ich das verstehe? (Ich frage dann also die logische Position des Objekts ab und rate dann, ob irgendein realer Punkt überhaupt in mein Sichtfeld guckt? Denn wenn ich das für jeden Punkt mache, müsste ich ja so wie der der Shader selbst jeden Punkt einzeln berechnen. Das fühlt sich irgendwie sehr doppelt gemoppelt an. Vielleicht kann man das ja auch gleich mit Collision Detection und Bounding Boxes verbinden, hm..)

    Das Ziel ist, mit möglichst wenig Aufwand, möglichst viel Unnötiges rausfiltern zu können. Jeden Punkt zu testen fällt da raus, weil der Aufwand im Vergleich zum Gewinn viel zu hoch ist. Aber mit einfachen Techniken wie z.B. Octrees lassen sich oft recht schnell und einfach schon große Teile einer Szene rausfiltern.



  • Octrees sind auch ein gutes Stichwort, werde ich mir mal angucken. Nachdem es wohl offensichtlich nicht üblich ist, so viele sehr verschiedene Objekte zu haben (wie lässt man dann eigentlich irgendetwas zersplittern?) möchte ich mich jetzt erst mal auf das "Instancing" konzentrieren. Dazu möchte ich noch einmal meine Frage von vorhin hervorheben:

    cooky451 schrieb:

    Zur Kommunikation mit den Shadern habe ich allerdings nur dreckige Hacks gefunden, von denen der beste noch war, die Matritzen-Arrays über einen Texturpuffer in den Shader zu schmuggeln. Ist das ernst gemeint oder finde ich einfach nicht die richtigen Quellen? 😕

    Ich habe dazu diesen Artikel gelesen: http://sol.gfxile.net/instancing.html

    Tut mir Leid, falls die Frage irgendwie überflüssig erscheint, aber ich bin immer noch etwas ungläubig, dass man da so üble Hacks verwenden muss.

    (Ich erhoffe mir von Instancing sehr viel, da das die einzige Möglichkeit zu sein scheint, sehr viele Objekte zu rendern ohne jedes Mal eine Transformationsmatrix übergeben zu müssen. Ich würde dann ja vor dem Rendercall in einem Rutsch alle Zustände aller Objekte übergeben. Die Frage ist nur, wie man das am besten macht. Arrays scheinen in GLSL ja nicht so toll zu sein.)



  • cooky451 schrieb:

    Hi! 🙂
    ... bei dem der Renderloop im Wesentlichen aus 5000 glUseProgram() Aufrufen besteht...

    Wieviele verschiedene Shader hast du? Am besten jeden Shader pro Frame nur einmal binden, wenn möglich. Und auch glUseProgramm nicht aufrufen, wenn sich der Shader nicht ändert - bindest du bspw. Shader1, und bindest dann irgendwie nochmal Shader1, obwohl bis dahin kein anderer Shader gebunden wurde, dann erleidest du dadurch trotzdem deutliche Performance-Einbrüche - also selber merken welcher Shader gerade aktiv ist.



  • Ja.. danke, dass du mir helfen möchtest, aber hast du dir den ganzen Thread mal durchgelesen? 😉


Anmelden zum Antworten