7

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

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

Objects 背景将有一个边框,它们需要很好地扩展,所以我使用 9-patch 模式来绘制它们(它们由 9 个四边形组成)。

如果没有画Tiles(Object准确地说是他们的 s),我的应用程序大约有600 fps.

起初,我一直在使用过时的方法来绘制那些Tiles - 使用glBegin(GL_QUADS)/glEnd()glDisplayLists。由于那次绘图,我的表现大幅下降 - 从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. 我已经使用了本教程,并根据它改变了我的方法,如下所示:

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

它与以前的版本非常相似,但不是glDisplayListandglVertex2i()我使用VBO的是从存储在数组中的数据创建的。

但是结果让我失望了,因为我得到了性能下降而不是提升,我几乎没有 ~260 fps,我必须注意,在这个方法版本中我还没有实现纹理的使用,所以现在只有四边形没有任何纹理绑定到它。

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

所以我的问题是——我做错了什么?使用更大(~600)个小VBOs 真的比过时的绘图方法慢glVertex2i吗?在我的情况下,什么可能是——也许不是最好的,但更好的——解决方案?

4

2 回答 2

7

从外观上看,您正在用每一帧重新创建 VBO。如果您只想更改数据使用glBufferSubData,则需要glBufferData经历整个冗长的 VBO 初始化。

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

于 2013-02-27T16:23:58.367 回答
3

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

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

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

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

于 2013-02-27T17:33:15.543 回答