2013-02-27 2 views
7

Ich arbeite an einer Tile-basierten OpenGL, C++ - Anwendung. Ich füge Beispielbild aus der Anwendung, so dass es klar ist:VBOs langsamer als veraltete Methode zum Zeichnen von Primitiven - warum?

Ich habe Tile Klasse, die ein Array von Object s enthält. Jede Kachel kann bis zu 15 Objekte speichern - das Beispiel ist Tile mit grünem und gelbem Quadrat darauf (zwei Objekte), also ist es 10x10x15 = 1500 Object s zu zeichnen (im schlimmsten Fall, weil ich 'leer' nicht handhabe) ' Einsen). Normalerweise ist es weniger, in meinen Tests verwende ich ungefähr 600 von ihnen. Object hat seine eigene Grafik, die gezeichnet werden kann. Jede Object gehört zu einer Zeit zu einer Tile, aber sie kann verschoben werden (wie zum Beispiel rote Quadrate im Bild).

Object s Hintergründe werden eine Grenze haben und sie müssen gut skalierbar sein, also verwende ich 9-Patch-Muster, um sie zu zeichnen (sie sind aus 9 Quads).

Ohne Zeichnung Tile s (ihre Object s um genau zu sein), meine Anwendung hat um 600 fps.

Zuerst habe ich veraltete Methode wurde unter Verwendung dieser Tile s zu ziehen - glBegin(GL_QUADS)/glEnd() und glDisplayList s verwenden. Ich hatte einen großen Leistungsabfall aufgrund dieser Zeichnung - von 600 bis 320 fps. Dies ist, wie ich sie gezeichnet habe:

bool Background::draw(const TPoint& pos, int width, int height) 
{ 
    if(width <= 0 || height <= 0) 
     return false; 
    //glFrontFace(GL_CW); 
    glPushMatrix(); 
    glTranslatef((GLfloat)pos.x, (GLfloat)pos.y, 0.0f);  // Move background to right direction 
    if((width != m_savedWidth) || (height != m_savedHeight)) // If size to draw is different than the one saved in display list, 
     // then recalculate everything and save in display list 
    { 
     // That size will be now saved in display list 
     m_savedWidth = width; 
     m_savedHeight = height; 

     // If this background doesn't have unique display list id specified yet, 
     // then let OpenGL generate one 
     if(m_displayListId == NO_DISPLAY_LIST_ID) 
     { 
      GLuint displayList; 
      displayList = glGenLists(1); 
      m_displayListId = displayList; 
     } 

     glNewList(m_displayListId, GL_COMPILE); 

     GLfloat texelCentersOffsetX = (GLfloat)1/(2*m_width); 

     // Instead of coordinates range 0..1 we need to specify new ones 
     GLfloat maxTexCoordWidth = m_bTiling ? (GLfloat)width/m_width : 1.0; 
     GLfloat maxTexCoordHeight = m_bTiling ? (GLfloat)height/m_height : 1.0; 

     GLfloat maxTexCoordBorderX = (GLfloat)m_borderWidth/m_width; 
     GLfloat maxTexCoordBorderY = (GLfloat)m_borderWidth/m_height; 

     /* 9-cell-pattern 

     ------------------- 
     | 1 | 2 | 3 | 
     ------------------- 
     | |   | | 
     | 4 | 9 | 5 | 
     | |   | | 
     ------------------- 
     | 6 | 7 | 8 | 
     ------------------- 

     */ 

     glBindTexture(GL_TEXTURE_2D, m_texture);    // Select Our Texture 

     // Top left quad [1] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(0.0, maxTexCoordBorderY); 
      glVertex2i(0, 0 + m_borderWidth); 

      // Top left 
      glTexCoord2f(0.0, 0.0); 
      glVertex2i(0, 0); 

      // Top right 
      glTexCoord2f(maxTexCoordBorderX, 0.0); 
      glVertex2i(0 + m_borderWidth, 0); 

      // Bottom right 
      glTexCoord2f(maxTexCoordBorderX, maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + m_borderWidth); 
     glEnd(); 

     // Top middle quad [2] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + m_borderWidth); 

      // Top left 
      glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, 0.0); 
      glVertex2i(0 + m_borderWidth, 0); 

      // Top right 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, 0.0); 
      glVertex2i(0 + width - m_borderWidth, 0); 

      // Bottom right 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth); 
     glEnd(); 

     // Top right quad [3] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth); 

      // Top left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, 0.0); 
      glVertex2i(0 + width - m_borderWidth, 0); 

      // Top right 
      glTexCoord2f(1.0, 0.0); 
      glVertex2i(0 + width, 0); 

      // Bottom right 
      glTexCoord2f(1.0, maxTexCoordBorderY); 
      glVertex2i(0 + width, 0 + m_borderWidth); 
     glEnd(); 

     // Middle left quad [4] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(0.0, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0, 0 + height - m_borderWidth); 

      // Top left 
      glTexCoord2f(0.0, maxTexCoordBorderY); 
      glVertex2i(0, 0 + m_borderWidth); 

      // Top right 
      glTexCoord2f(maxTexCoordBorderX, maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + m_borderWidth); 

      // Bottom right 
      glTexCoord2f(maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth); 
     glEnd(); 

     // Middle right quad [5] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth); 

      // Top left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth); 

      // Top right 
      glTexCoord2f(1.0, maxTexCoordBorderY); 
      glVertex2i(0 + width, 0 + m_borderWidth); 

      // Bottom right 
      glTexCoord2f(1.0, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width, 0 + height - m_borderWidth); 
     glEnd(); 

     // Bottom left quad [6] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(0.0f, 1.0); 
      glVertex2i(0, 0 + height); 

      // Top left 
      glTexCoord2f(0.0f, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0, 0 + height - m_borderWidth); 

      // Top right 
      glTexCoord2f(maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth); 

      // Bottom right 
      glTexCoord2f(maxTexCoordBorderX, 1.0); 
      glVertex2i(0 + m_borderWidth, 0 + height); 
     glEnd(); 

     // Bottom middle quad [7] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, 1.0); 
      glVertex2i(0 + m_borderWidth, 0 + height); 

      // Top left 
      glTexCoord2f(maxTexCoordBorderX + texelCentersOffsetX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth); 

      // Top right 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth); 

      // Bottom right 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX - texelCentersOffsetX, 1.0); 
      glVertex2i(0 + width - m_borderWidth, 0 + height); 
     glEnd(); 

     // Bottom right quad [8] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, 1.0); 
      glVertex2i(0 + width - m_borderWidth, 0 + height); 

      // Top left 
      glTexCoord2f((GLfloat)1.0 - maxTexCoordBorderX, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth); 

      // Top right 
      glTexCoord2f(1.0, (GLfloat)1.0 - maxTexCoordBorderY); 
      glVertex2i(0 + width, 0 + height - m_borderWidth); 

      // Bottom right 
      glTexCoord2f(1.0, 1.0); 
      glVertex2i(0 + width, 0 + height); 
     glEnd(); 

     GLfloat xTexOffset; 
     GLfloat yTexOffset; 

     if(m_borderWidth > 0) 
     { 
      glBindTexture(GL_TEXTURE_2D, m_centerTexture);  // If there's a border, we have to use 
      // second texture now for middle quad 
      xTexOffset = 0.0;         // We are using another texture, so middle middle quad 
      yTexOffset = 0.0;         // has to be texture with a whole texture 
     } 
     else 
     { 
      // Don't bind any texture here - we're still using the same one 

      xTexOffset = maxTexCoordBorderX;     // But it implies using offset which equals 
      yTexOffset = maxTexCoordBorderY;     // maximum texture coordinates 
     } 

     // Middle middle quad [9] 
     glBegin(GL_QUADS); 
      // Bottom left 
      glTexCoord2f(xTexOffset, maxTexCoordHeight - yTexOffset); 
      glVertex2i(0 + m_borderWidth, 0 + height - m_borderWidth); 

      // Top left 
      glTexCoord2f(xTexOffset, yTexOffset); 
      glVertex2i(0 + m_borderWidth, 0 + m_borderWidth); 

      // Top right 
      glTexCoord2f(maxTexCoordWidth - xTexOffset, yTexOffset); 
      glVertex2i(0 + width - m_borderWidth, 0 + m_borderWidth); 

      // Bottom right 
      glTexCoord2f(maxTexCoordWidth - xTexOffset, maxTexCoordHeight - yTexOffset); 
      glVertex2i(0 + width - m_borderWidth, 0 + height - m_borderWidth); 
     glEnd(); 

     glEndList(); 
    } 

    glCallList(m_displayListId); // Now we can call earlier or now created display list 

    glPopMatrix(); 

    return true; 
} 

Es gibt wahrscheinlich zu viel Code dort, aber ich wollte alles zeigen. Die Hauptsache an dieser Version ist die Verwendung von Displaylisten und glVertex2i, die veraltet sind.

Ich dachte, das Problem einer solchen Verlangsamung war die Verwendung dieser veralteten Methode, die ich lese ziemlich langsam ist, so entschied ich mich für VBO zu gehen. Ich habe verwendet this tutorial und nach änderte ich meine Methode wie folgt:

bool Background::draw(const TPoint& pos, int width, int height) 
{ 
    if(width <= 0 || height <= 0) 
     return false; 

    glPushMatrix(); 
    glTranslatef((GLfloat)pos.x, (GLfloat)pos.y, 0.0f);    // Move background to right direction 
    if((width != m_savedWidth) || (height != m_savedHeight))  // If size to draw is different than the one saved in display list, 
                    // then recalculate everything and save in display list 
    { 
     // That size will be now saved in display list 
     m_savedWidth = width; 
     m_savedHeight = height; 

     GLfloat texelCentersOffsetX = (GLfloat)1/(2*m_width); 

     // Instead of coordinates range 0..1 we need to specify new ones 
     GLfloat maxTexCoordWidth = m_bTiling ? (GLfloat)width/m_width : 1.0; 
     GLfloat maxTexCoordHeight = m_bTiling ? (GLfloat)height/m_height : 1.0; 

     GLfloat maxTexCoordBorderX = (GLfloat)m_borderWidth/m_width; 
     GLfloat maxTexCoordBorderY = (GLfloat)m_borderWidth/m_height; 

     /* 9-cell-pattern, each number represents one quad 

     ------------------- 
     | 1 | 2 | 3 | 
     ------------------- 
     | |   | | 
     | 4 | 9 | 5 | 
     | |   | | 
     ------------------- 
     | 6 | 7 | 8 | 
     ------------------- 

     */ 

     /* How vertices are distributed on one quad made of two triangles 

     v1 ------ v0 
     |  /| 
     | / | 
     |/  | 
     v2 ------ v3 

     */ 

     GLfloat vertices[] = { 
           // Top left quad [1] 
           m_borderWidth, 0, 0,       // v0 
           0, 0, 0,          // v1  
           0, m_borderWidth, 0,       // v2    

           0, m_borderWidth, 0,       // v2 
           m_borderWidth, m_borderWidth, 0,    // v3 
           m_borderWidth, 0, 0,       // v0 

           // Top middle quad [2] 
           width-m_borderWidth, 0, 0,      // v0 
           m_borderWidth, 0, 0,       // v1 
           m_borderWidth, m_borderWidth, 0,    // v2 

           m_borderWidth, m_borderWidth, 0,    // v2 
           width-m_borderWidth, m_borderWidth, 0,   // v3 
           width-m_borderWidth, 0, 0,      // v0 

           // Top right quad [3] 
           width, 0, 0,         // v0 
           width-m_borderWidth, 0, 0,      // v1 
           width-m_borderWidth, m_borderWidth, 0,   // v2 

           width-m_borderWidth, m_borderWidth, 0,   // v2 
           width, m_borderWidth, 0,      // v3 
           width, 0, 0,         // v0 

           // Middle left quad [4] 
           m_borderWidth, m_borderWidth, 0,    // v0 
           0, m_borderWidth, 0,       // v1 
           0, height-m_borderWidth, 0,      // v2 

           0, height-m_borderWidth, 0,      // v2 
           m_borderWidth, height-m_borderWidth, 0,   // v3 
           m_borderWidth, m_borderWidth, 0,    // v0 

           // Middle right quad [5] 
           width, m_borderWidth, 0,      // v0 
           width-m_borderWidth, m_borderWidth, 0,   // v1 
           width-m_borderWidth, height-m_borderWidth, 0, // v2 

           width-m_borderWidth, height-m_borderWidth, 0, // v2 
           width, height-m_borderWidth, 0,     // v3 
           width, m_borderWidth, 0,      // v0 

           // Bottom left quad [6] 
           m_borderWidth, height-m_borderWidth, 0,   // v0 
           0, height-m_borderWidth, 0,      // v1 
           0, height, 0,         // v2 

           0, height, 0,         // v2 
           m_borderWidth, height, 0,      // v3 
           m_borderWidth, height-m_borderWidth, 0,   // v0 

           // Bottom middle quad [7] 
           width-m_borderWidth, height-m_borderWidth, 0, // v0 
           m_borderWidth, height-m_borderWidth, 0,   // v1 
           m_borderWidth, height, 0,      // v2 

           m_borderWidth, height, 0,      // v2 
           width-m_borderWidth, height, 0,     // v3 
           width-m_borderWidth, height-m_borderWidth, 0, // v0 

           // Bottom right quad [8] 
           width, height-m_borderWidth, 0,     // v0 
           width-m_borderWidth, height-m_borderWidth, 0, // v1 
           width-m_borderWidth, height, 0,     // v2 

           width-m_borderWidth, height, 0,     // v2 
           width, height, 0,        // v3 
           width, height-m_borderWidth, 0,     // v0 

           // Middle middle quad [9] 
           width-m_borderWidth, m_borderWidth, 0,   // v0 
           m_borderWidth, m_borderWidth, 0,    // v1 
           m_borderWidth, height-m_borderWidth, 0,   // v2 

           m_borderWidth, height-m_borderWidth, 0,   // v2 
           width-m_borderWidth, height-m_borderWidth, 0, // v3 
           width-m_borderWidth, m_borderWidth, 0   // v0 
          }; 

     copy(vertices, vertices + 162, m_vCoords);    // 162, because we have 162 coordinates 


     int dataSize = 162 * sizeof(GLfloat); 
     m_vboId = createVBO(m_vCoords, dataSize); 

    } 

    // bind VBOs for vertex array 
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_vboId);   // for vertex coordinates 

    glEnableClientState(GL_VERTEX_ARRAY);     // activate vertex coords array 
     glVertexPointer(3, GL_FLOAT, 0, 0);      
     glDrawArrays(GL_TRIANGLES, 0, 162); 
    glDisableClientState(GL_VERTEX_ARRAY);     // deactivate vertex array 

    // bind with 0, so, switch back to normal pointer operation 
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, NO_VBO_ID); 

    glPopMatrix(); 

    return true; 
} 

Es ist ganz ähnlich wie vorherige Version, aber statt glDisplayList und glVertex2i() I VBO verwendet, die in einem Array gespeichert von Daten erstellt werden .

Aber Ergebnissen enttäuscht mich, weil ich Leistungsabfall bekam statt Auftrieb bekam ich kaum ~260 fps und ich muss, dass bei dieser Methode Version beachten Sie habe ich noch nicht die Verwendung von Texturen implementiert, so gibt es nur Quads für jetzt ohne jede Textur, die daran gebunden ist.

Ich habe ein paar Artikel gelesen, um herauszufinden, was könnte der Grund für eine solche Verlangsamung sein und herausgefunden, dass es wegen der großen Menge an kleinen VBO s ist und ich hätte wahrscheinlich eine VBO enthält alle Hintergründe Daten statt separate VBO für jeden Hintergrund. Aber das Problem ist, dass Object s sich bewegen können und sie haben unterschiedliche Texturen (und Textur Atlas ist keine gute Lösung für mich), so dass es schwierig für mich wäre, diese Änderungen für diejenigen zu aktualisieren, die ihren Zustand geändert haben. Für den Moment, wenn Object s geändert wird, erstelle ich einfach VBO und der Rest VBO s unberührt bleiben.

Also meine Frage ist - was mache ich falsch?Ist die Verwendung von größeren (~ 600) Anzahl von kleinen VBO s wirklich langsamer als veraltete Methode des Zeichnens mit glVertex2i? Und was könnte - vielleicht nicht die beste, aber bessere - Lösung in meinem Fall sein?

+1

Frames pro Sekunde ist eine [schreckliche komparative Metrik] (http://www.mvps.org/directx/articles/fps_versus_frame_time.htm). Wechseln Sie zu (Milli-) Sekunden pro Frame. – genpfault

+0

@genpfault Danke, es ist definitiv gut das zu wissen. Tatsache ist, dass VBO in diesem Fall langsamer ist. –

+1

Einige Treiber sind tatsächlich sehr gut beim Erstellen von Anzeigelisten. – JasonD

Antwort

3

Nur weil das fixed-function-Zeug alt ist, veraltet ist und im Allgemeinen nicht empfohlen wird, heißt das nicht unbedingt, dass es immer langsam ist.

Noch ist die Phantasie "neu" (es ist schon eine Weile her) Funktionalität mit Puffern und Shadern und dergleichen bedeutet, dass alles blitzschnell sein wird.

Wenn Sie Ihre Zeichnung in eine Anzeigeliste umbrechen, übergeben Sie im Grunde eine Reihe von Operationen an den Treiber. Dies gibt dem Fahrer ein gutes Stück Spielraum, um zu optimieren, was passiert. Es kann sehr gut das meiste von dem, was Sie tun, zu einem ziemlich effizienten Paket von GPU-Vorgängen verpacken. Das kann durchaus etwas effizienter sein als das, wenn Sie Ihre Daten in Puffer packen und senden Sie sie ab.

Das ist nicht zu sagen, dass ich empfehlen würde, mit der alten Stil-Schnittstelle zu bleiben, aber sicherlich bin ich nicht überrascht, dass es Fälle gibt, in denen es einen guten Job macht.

7

Mit dem Aussehen, Sie erstellen die VBO mit jedem Rahmen neu. Wenn Sie nur die Daten ändern wollen, verwenden Sie glBufferSubData, da glBufferData die ganze, langwierige VBO-Initialisierung durchläuft.

Wenn die Daten statisch sind, erstellen Sie den VBO nur einmal, und verwenden Sie ihn dann erneut.

+3

Beachten Sie, dass Sie, wenn Sie einen VBO mit Daten haben möchten, die sich stark ändern, GL_DYNAMIC_DRAW als Datentyp für den VBO verwenden sollten. – Ethereal

+1

Dies scheint genau das Problem zu sein. Verschieben Sie Ihren VBO-Erstellungscode in eine andere Methode (z. B. createMesh), und rufen Sie ihn nur auf, wenn ein neues Objekt benötigt wird (z. B. Initialisierung). Bei der Wiederherstellung der VBO verliert jeder Frame einen Gewinn, wenn er verwendet wird. – ssell

+1

VBO wird nicht bei jedem Frame neu erstellt - nur wenn die Größe des Zeichnungsobjekts geändert wird, was sehr selten passiert. –

Verwandte Themen