Updateperformance bei komplexen 3D Animationen


  • Mod

    hustbaer schrieb:

    Hm. Ok, wenn rapso das sagt glaube ich es erstmal. Sorry dass ich hier Blödsinn geschrieben habe! Sieht so aus als müsste ich mich mit dem Thema nochmal näher beschäftigen.

    da empfehle ich sehr
    http://www2.ati.com/developer/gdc/D3DTutorial10_Half-Life2_Shading.pdf



  • Habe nun einige Varianten probiert und möchte Euch die Ergebnisse nicht vorenthalten.
    Allerdings bin ich mir nicht sicher, ob ich den erzielten Framerates ohne weiteres glauben darf 🙄

    Meine Meshdaten liegen erstmal als Punktewolke vor, um die einzelnen Vertices dynamisch ändern zu können.
    Getestet wurden 60501 Vertices auf einer Radeon 9250.

    Die Vertice-Struktur sah so aus:

    struct {
    float nv[3];
    float p[3];
    char color;
    } vertice;
    

    Das Member "color" ist ein Index auf ein Farbfeld, was zum Testen aber nicht genutzt wurde.
    Die Buffer wurden mit glInterleavedArrays definiert, da man sich hiermit einige Aufrufe erspart:

    glInterleavedArrays(GL_N3F_V3F, sizeof(vertice), 0);
    

    glDrawElements ist bei Verwendung von VBOs ab count=42000 gnadenlos in die Knie gegangen, ein Update dauerte dann schlagartig bis zu 5 sec!
    40000 dagegen liefen problemlos.
    Die Abfrage von GL_MAX_ELEMENTS_INDICES ergab 65535, GL_MAX_ELEMENTS_VERTICES liegt bei 2147483647.
    Ohne Verwendung der VBOs gab es überhaupt keine Probleme.
    Nach langem Suchen habe ich eben dieses zusätzliche char-Element als Verursacher herausgefunden...😮
    Konnte jedoch nichts konkretes im Web finden, warum die Graka dies nicht mochte.

    Habe dann das char-Member und sämtliche Dynamik rausgenommen und nur das reine Darstellen der Daten gemessen.
    Zum Vergleich habe ich mehrere Varianten erstellt.
    Bei der "billigsten" sucht man sich per Pointer jeweils 4 nebeneinanderliegende Vertices und erzeugt einen Quad.
    Für glDrawElements wird zusätzlich ein Index-Array erstellt und für glDrawArrays habe ich ein Array erstellt, das die Quads in fortlaufender Reihenfolge enthält.
    Der Nachteil von glDrawArrays ist einerseits, dass jedes (innere) Vertice 4x im Array gelistet wird, andererseits geht damit auch die Flexibilität beim Ändern der einzelnen Vertices verloren.

    Hier nun die Ergebnisse:

    glBegin(GL_QUADS)/glEnd: 31 fps
    glDrawElements mit Indices-Array: 32 fps
    glDrawArrays mit Quad-Array: 41 fps
    glDrawArrays mit Quad-Array und VBOs: 41 fps
    glDrawElements mit Indices-Array und VBOs: 46 fps
    glBegin(GL_QUAD_STRIP)/glEnd: 51 fps
    glBegin(GL_QUAD_STRIP)/glEnd, ohne glClear: 59 fps
    glDrawElements mit VBOs, ohne glClear: 73 fps

    glClear verschlingt somit einiges, da außerhalb der GPU aufgerufen.
    Das Bit GL_DEPTH_BUFFER_BIT nimmt 16 Frames, GL_COLOR_BUFFER_BIT weitere 11.
    Es machte bei der Menge zudem keinen Unterschied, ob ich die Indices mit einem Standard-Pointer oder mit einem STL-Vector übergab.

    Sind die Ergebnisse realistisch oder bin ich auf dem Holzweg?
    Für die theoretische Videofrequenz von 25 fps würde ja schon die langsamste Methode reichen...


  • Mod

    versuch triangles statt quads, graphickarten koennen eigentlich keine quads. es wird jedesmal zu dreiecken konvertiert.
    wenn du glclear testen willst, mach das clear sofort nach dem swapbuffers und falls du einen stencilbuffer angibst, dann clear den auch.
    char color, sowas gibt es hardwaremaessig nicht. und deine struct sollte 16byte alligned sein.
    um sagen zu koennen weshalb dein vbo so lange bem update braucht, muesste man sehen was du machst, da laeuft eindeutig etwas schief.

    wobei ich mir bei einer 9250 auch nicht so sicher bin was die kann, war das nicht nur eine onboard karte?



  • rapso schrieb:

    versuch triangles statt quads, graphickarten koennen eigentlich keine quads. es wird jedesmal zu dreiecken konvertiert.

    Quads haben einen entscheidenen Vorteil: sie können sich krümmen, was bei korrekten Vertex-Normalen saubere Übergänge schafft.
    Ausserdem halbiert es die Anzahl der Objekte, die Indices werden um 1/3 reduziert.
    Habe trotzdem mal Triangles probiert.
    "glDrawElements mit VBOs, ohne glClear" ging mit Triangles statt Quads von 73 auf 74 fps hoch 😉

    rapso schrieb:

    wenn du glclear testen willst, mach das clear sofort nach dem swapbuffers und falls du einen stencilbuffer angibst, dann clear den auch.

    Stencil nutze ich für den Test noch nicht.
    Das glClear hatte ich ja schon rausgenommen, aber danke für den Tip!

    rapso schrieb:

    char color, sowas gibt es hardwaremaessig nicht. und deine struct sollte 16byte alligned sein.

    Der Witz ist, dass es bis 40000 Indizes keine Probleme gibt.
    Dachte auch, das wäre genau der Sinn von Interleaved Arrays...
    Somit sollte es der GPU doch egal sein, oder?

    rapso schrieb:

    um sagen zu koennen weshalb dein vbo so lange bem update braucht, muesste man sehen was du machst, da laeuft eindeutig etwas schief.

    Ist eigentlich nix aussergewöhnliches, hier mal eine kurze Zusammenfassung:

    struct {
    float nv[3];
    float p[3];
    } vertice; 
    
    // ...
    // Mesh Vertices extern berechnen
    // ...
    
    // Function Pointer holen
    glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
    glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
    glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
    glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)wglGetProcAddress("glDrawRangeElements");
    glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
    
    // Indices erzeugen
    indices = new unsigned short[ (mesh.nCountX-1)*(mesh.nCountY-1)*6 ];
    int ii=0;
    for( int x=0; x<(mesh.nCountX-1); x++) {  
      for( int y=0; y<(mesh.nCountY-1); y++) {
    	indices[ii]   = x*mesh.nCountY + y;
    	indices[ii+1] = x*mesh.nCountY + y + 1;
    	indices[ii+2] = (x+1)*mesh.nCountY + y + 1;
    
    	indices[ii+3] = (x+1)*mesh.nCountY + y + 1;
    	indices[ii+4] = (x+1)*mesh.nCountY + y;
    	indices[ii+5] = x*mesh.nCountY + y;
    	ii += 6;
      }
    }
    
    // VBOs erzeugen und Daten kopieren
    glGenBuffersARB(1, &vboId);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, (mesh.nCountX)*(mesh.nCountY)*sizeof(vertice), vertices, GL_STATIC_DRAW_ARB);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
    
    glGenBuffersARB(1, &eboId);
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, eboId);
    glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, (mesh.nCountX-1)*(mesh.nCountY-1)*6*sizeof(unsigned short), indices, GL_STATIC_DRAW_ARB);
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
    
    //später zeichnen
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, eboId);
    glInterleavedArrays(GL_N3F_V3F, 0, 0);
    glDrawElements(GL_TRIANGLES, (mesh.nCountX-1)*(mesh.nCountY-1)*6, GL_UNSIGNED_SHORT, 0);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
    

    rapso schrieb:

    wobei ich mir bei einer 9250 auch nicht so sicher bin was die kann, war das nicht nur eine onboard karte?

    Nein, die Radeon 9250 ist eine 128MB AGP Karte mit recht guter 3D Performance.
    Bei einer Quadro 3500 lief es ähnlich...

    Übrigens:
    den Performance-Einbruch habe ich auch ohne Licht, also bei folgender Konfig:

    glDisable(GL_LIGHTING);
       glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
    

    Mit Licht passiert das nicht:

    glEnable(GL_LIGHTING);
       glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
    

    Tappe hier momentan etwas im Dunkeln 🙄



  • rapso schrieb:

    mach das clear sofort nach dem swapbuffers.
    ...
    und deine struct sollte 16byte alligned sein.

    Hi Rapso,

    habe beides nochmal explizit ausgetestet.
    Hatte mich bislang daran orientiert, Berechnungen direkt nach dem Swappen auszuführen, da ich dies schon öfters gelesen hatte.

    Die Struktur habe ich jetzt mit

    VERTICE __declspec(align(16)) vertice;
    

    deklariert, siehe auch http://msdn2.microsoft.com/en-us/library/83ythb65.aspx

    Das glClear führe ich nun direkt nach dem Swappen aus.
    Beides hat leider nicht die geringste Auswirkung.

    Mal abgesehen davon, dass die finale FPS mich nicht umhaut, so wundere ich mich immer noch sehr über die drastischen Einbrüche bei Verwendung der VBOs unter den vorher beschriebenen Bedingungen (mehr als 40000 Elemente für glDrawElements und im Wire-Modus).

    Gruß,
    Modes



  • Vielleicht reicht über der Anzahl von 40000 VBO's die Kapaizität der Grafikkarte nicht mehr und es muss auf festplatte doer andere Speichermedien ausgelagert werden. Schon mit dem RAM wäre es dann wesentlich langsamer als über den Grafikkarten internen Speicher.

    Oder ich steh schon wieder auf dem Schlacuh und verwechsel etwas.


  • Mod

    modestia schrieb:

    rapso schrieb:

    versuch triangles statt quads, graphickarten koennen eigentlich keine quads. es wird jedesmal zu dreiecken konvertiert.

    Quads haben einen entscheidenen Vorteil: sie können sich krümmen, was bei korrekten Vertex-Normalen saubere Übergänge schafft.

    eigentlich triangulieren das die graphikkarten nur bzw der treiber, du hast also eigentlich garkeine vorteile davon.

    Ausserdem halbiert es die Anzahl der Objekte, die Indices werden um 1/3 reduziert.
    Habe trotzdem mal Triangles probiert.
    "glDrawElements mit VBOs, ohne glClear" ging mit Triangles statt Quads von 73 auf 74 fps hoch 😉

    ich koennte vermuten dass das problem woanders liegt wenn die framerate so ist. 74fps bei 40000 indices? also 60 000 wenn es dreiecke waeren? zufaellig *hehe*
    was ist denn der maximale wert deiner 9250? 5Mio/s klingt nicht so gut, meine psp uebertrifft das (ohne licht etc.)

    rapso schrieb:

    wenn du glclear testen willst, mach das clear sofort nach dem swapbuffers und falls du einen stencilbuffer angibst, dann clear den auch.

    Stencil nutze ich für den Test noch nicht.
    Das glClear hatte ich ja schon rausgenommen, aber danke für den Tip!

    wenn du einen stencilbuffer setzt, selbst wenn du den nicht benutzt, musst du den beim clear vom zbuffer mit clearen wenn es schnell gehen soll.

    rapso schrieb:

    char color, sowas gibt es hardwaremaessig nicht. und deine struct sollte 16byte alligned sein.

    Der Witz ist, dass es bis 40000 Indizes keine Probleme gibt.
    Dachte auch, das wäre genau der Sinn von Interleaved Arrays...
    Somit sollte es der GPU doch egal sein, oder?

    und ab 40 000 indices fuer quads, also 60 000 bei triangles bricht es ein, und das nur wenn das char mit drinnen ist?

    rapso schrieb:

    um sagen zu koennen weshalb dein vbo so lange bem update braucht, muesste man sehen was du machst, da laeuft eindeutig etwas schief.

    Ist eigentlich nix aussergewöhnliches, hier mal eine kurze Zusammenfassung:

    ...glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, (mesh.nCountX-1)*(mesh.nCountY-1)*6*sizeof(unsigned short), indices, GL_STATIC_DRAW_ARB);
    ...
    

    ich glaube zum updaten sollte man GL_DYNAMIC_DRAW benutzen

    Übrigens:
    den Performance-Einbruch habe ich auch ohne Licht, also bei folgender Konfig:

    glDisable(GL_LIGHTING);
       glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
    

    Mit Licht passiert das nicht:

    glEnable(GL_LIGHTING);
       glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
    

    Tappe hier momentan etwas im Dunkeln 🙄

    vielleicht bist du ja auch nicht von den vertices her limitiert. zum benchmarken von vertices solltest du das objekt so klein wie moeglich auf dem screen haben. des weiteren lohnen sich garkeine benchmarks wenn du im window mode bist, vor allem clear ist dannn vermutlich viel langsammer.
    zudem solltest du checken ob du nicht mit VSync testest, denn wenn dein monitor nur 75hz darstellst, wirst du nie ueber diese fps kommen.


  • Mod

    modestia schrieb:

    rapso schrieb:

    mach das clear sofort nach dem swapbuffers.
    ...
    und deine struct sollte 16byte alligned sein.

    Hi Rapso,

    habe beides nochmal explizit ausgetestet.
    Hatte mich bislang daran orientiert, Berechnungen direkt nach dem Swappen auszuführen, da ich dies schon öfters gelesen hatte.

    welche berechnungen?

    gut ist

    logic
    zeichnen
    zwap
    clear
    
    logic
    zeichnen
    swap
    clear
    ...
    

    viele neigen zu

    logic
    clear
    zeichnen
    swap
    ..
    

    zudem, wie gesagt, in fullscreen testen ;).
    desweiteren, color cleart wohl kein spiel. man ueberzeichnet eh alles, und fuer den z+stencilbuffer gibtb es meist einen 'zero cycle clear', also quasi umsonst.

    Die Struktur habe ich jetzt mit

    VERTICE __declspec(align(16)) vertice;
    

    deklariert, siehe auch http://msdn2.microsoft.com/en-us/library/83ythb65.aspx

    Das glClear führe ich nun direkt nach dem Swappen aus.
    Beides hat leider nicht die geringste Auswirkung.

    nein, darum geht es nicht 😉
    es geht darum dass in einem array die vertices immer 16byte (oder zumindestens 8byte) alligned sind.
    wenn also sizeof(Vertex) 16, 32, 48... ist, gibst du der graphikkarte optimales futter. wenn du einen char einbaust, also dann ganz krumme werte hast bei den addressen der vertices, kommt eine gpu damit absolut nicht klar.
    Ist wie SSE bei CPUs, alligned load ist sehr viel schneller als unalligned.

    Mal abgesehen davon, dass die finale FPS mich nicht umhaut, so wundere ich mich immer noch sehr über die drastischen Einbrüche bei Verwendung der VBOs unter den vorher beschriebenen Bedingungen (mehr als 40000 Elemente für glDrawElements und im Wire-Modus).

    wiremodus kann durchaus langsammer als fillmodus sein ;). skalier das objekt einfach ganz klein zum testen. und wie gesagt, vsync aus und fullscreen 😉



  • rapso schrieb:

    welche berechnungen?

    z.B. die Berechnungen, die das Mesh bzw. einzelne Vertices dynamisch aktualisieren und die beweglichen Objekte neu positionieren.

    rapso schrieb:

    viele neigen zu

    logic
    clear
    zeichnen
    swap
    ..
    

    Ähem...an diesem Gerücht ist bestimmt was dran 😃

    rapso schrieb:

    zudem, wie gesagt, in fullscreen testen ;).

    DAS bringt in der Tat was, bin ohne glClear auf 106 fps gekommen 👍
    Ähnliche Raten bekomme ich (nun logischerweise) auch, wenn ich das Windowsfenster auf ca. 600x400 Pixel setze.
    Denke, hier bin ich einfach zu gierig nach Pixeln 🤡

    rapso schrieb:

    desweiteren, color cleart wohl kein spiel. man ueberzeichnet eh alles, und fuer den z+stencilbuffer gibtb es meist einen 'zero cycle clear', also quasi umsonst.

    Aha, also lieber einen blauen Himmel als Hintergrund zeichnen als "clearen"?
    Beim Stencil bin ich noch nicht, werde ich aber mal testen...

    rapso schrieb:

    nein, darum geht es nicht 😉
    es geht darum dass in einem array die vertices immer 16byte (oder zumindestens 8byte) alligned sind.

    Dachte, genau das soll ja die "align" Methode vorgaukeln.
    Hatte aber das CHAR aus dem Struct schon rausgenommen, so dass es hier kein Unterschied mehr machte.

    Komme jetzt der Sache schon näher, danke schonmal!



  • glFlush() vor dem swappen kann in einigen fällen und treiberabhängig einen großen geschwindigkeitsvorteil bringen 🙂

    edit: SwapBuffers() impliziert eigentlich glFlush(), nichtsdestotrotz hat es aus irgendeinem mir unerfindlichem grund ein steigerung der framerate eines projektes von mir um 250% gebracht. vielleicht ist der treiber verbuggt :S


Anmelden zum Antworten