VBO比绘制原语的过时方法慢 - 为什么?

时间:2013-02-27 15:45:14

标签: c++ performance opengl graphics vbo

我正在开发基于Tile的OpenGL,C ++应用程序。 我正在从应用程序添加示例屏幕,以便更清楚:

我有Tile类,其中包含Object个数组。每个瓷砖最多可以存储15个对象 - 其中的例子是Tile,上面有绿色和黄色方块(两个对象),因此要绘制10x10x15 = 1500 Object s(在最坏的情况下,因为我不处理'空'的)。通常它更少,在我的测试中我使用了大约600个。 Object有自己的图形,可以绘制。每个Object一次属于一个Tile,但可以移动它(例如图片中的红色方块)。

Object的背景将具有边框,并且它们需要具有良好的可伸缩性,因此我使用9补丁模式来绘制它们(它们由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;
}

它与以前的版本非常相似,但我使用glDisplayList代替glVertex2i()VBO,而~260 fps是根据存储在数组中的数据创建的。

但结果令我失望,因为我的表现下降而不是提升,我几乎没有  VBO我必须注意,在这个方法版本中我还没有实现纹理的使用,所以现在只有四边形没有绑定任何纹理。

我已经阅读了一些文章,找出可能是这种减速的原因,并发现可能是由于大量的小VBO而且我应该有一个VBO包含所有背景数据,而不是每个背景的单独Object。但问题是Object可以移动并且它们具有不同的纹理(纹理图集对我来说不是一个好的解决方案),因此我很难为那些Object更新这些更改这改变了他们的状态。现在,当VBO s被更改时,我只是重新创建它VBO而其余的VBO保持不变。

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

2 个答案:

答案 0 :(得分:7)

从它的外观来看,你每帧都在重建VBO。如果您只想更改数据,请使用glBufferSubData,因为glBufferData会经历整个冗长的VBO初始化。

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

答案 1 :(得分:3)

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

使用缓冲区和着色器等花哨的“新”(它已经有一段时间了)功能也不一定意味着一切都会闪电般快。

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

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