2013-02-27 98 views
7

我正在开发基于Tile的OpenGL,C++应用程序。 我添加从应用示例屏幕,这样它会更清楚:VBO比绘制图元的过时方法更慢 - 为什么?

我有Tile类,其中包含的Object秒的阵列。每个瓷砖最多可以存储15个物体 - 其示例是Tile,其上有绿色和黄色方块(两个物体),因此它的绘制为10x10x15 = 1500 Object s(在最糟糕的情况下,因为我没有处理“空” ' 那些)。通常情况下,我的测试中使用了大约600个。 Object有它自己的图形,可以绘制。每个Object一次属于一个Tile,但它可以移动(例如图片中的红色方块)。

Object s背景将会有一个边框,并且它们需要很好的伸缩性,所以我使用9-patch模式绘制它们(它们由9个四边形组成)。

没有图纸Tile s(他们的Object准确),我的申请有600 fps左右。首先,我一直使用过时的方法绘制那些Tile s - 使用glBegin(GL_QUADS)/glEnd()glDisplayList s。由于该绘图,我的表现大幅下降 - 从600320 fps。这是我一直在绘制它们:

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; 
} 

那里可能有太多的代码,但我想展示一切。这个版本的主要内容是使用显示列表和glVertex2i,这是不赞成的。

我认为这种放缓的问题是使用这种过时的方法,我读的速度很慢,所以我决定去VBO。我用this tutorial,并根据它,我改变了我的方法是这样的:

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; 
} 

这是很类似于以前的版本,但不是它正在从存储阵列中的数据创建glDisplayListglVertex2i()我用VBO

但结果让我失望,因为我得到了业绩下滑,而不是刺激,我几乎没有 ~260 fps,我必须指出,在我还没有实现利用纹理这种方法的版本,所以只有四边形现在没有任何纹理绑定到它。

我读了一些文章,发现这可能是这样的原因放慢,并发现可能是由于小VBO的大金额和我也许应该有一个包含各种背景数据的一个VBO代替每个背景分开VBO。但问题在于Object可以四处移动,并且它们具有不同的纹理(并且纹理地图集对我来说不是一个好的解决方案),所以我很难更新那些改变它们状态的变化。目前,当Object s正在更改时,我只是重新创建它的VBO而其余VBO s保持不变。

所以我的问题是 - 我做错了什么?使用较大(〜600)数量的小VBO s是否比使用glVertex2i绘制的过时方法慢?在我的情况下,可能会有什么 - 也许不是最好的,但更好的解决方案?

+1

每秒帧数是一个[可怕的比较度量](http://www.mvps.org/directx/articles/fps_versus_frame_time.htm)。每帧切换至(毫秒)秒。 – genpfault 2013-02-27 15:57:40

+0

@genpfault谢谢,这绝对是好事。但事实是,VBO在这种情况下速度较慢。 – 2013-02-27 16:09:50

+1

一些驱动程序实际上非常擅长编译显示列表。 – JasonD 2013-02-27 16:21:02

回答

3

仅仅因为固定功能的东西是旧的,不推荐使用,并且通常不推荐,并不一定意味着它总是很慢。

缓冲区和着色器等功能的花式“新”(已经有一段时间了)也不例外,这意味着一切都将闪电般快速。

当你将你的绘图包装在显示列表中时,基本上是将一堆操作传递给驱动程序。这实际上为驾驶员提供了一些适当的空间来优化正在发生的事情。它可能很好地将你正在做的大部分事情打包成一个非常高效的预封装的GPU操作。这可能会比在将数据打包到缓冲区并将其发送出去时发生的效率略高一些。

这并不是说我会建议坚持使用旧式界面,但当然我并不感到惊讶,有些情况下它做得很好。

7

从外观上看,你正在用每一帧重新创建VBO。如果你只是想改变数据使用glBufferSubData,因为glBufferData经历了整个,很长的VBO初始化。

如果数据是静态的,只需创建一次VBO,然后重新使用它。

+3

请注意,如果您想拥有一个数据变化很大的VBO,则应该使用GL_DYNAMIC_DRAW作为VBO的数据类型。 – Ethereal 2013-02-27 16:26:46

+1

这看起来正是这个问题。将您的VBO创建代码移动到不同的方法(如'createMesh')中,并且仅在需要新对象(如初始化)时调用它。重新创建VBO的每一帧都没有任何收益。 – ssell 2013-02-27 16:27:55

+1

VBO没有被重新创建每一帧 - 只有当绘图对象的大小正在改变,这很少发生。 – 2013-02-27 16:32:19