为什么基于Dear ImGui的渲染器这么慢?

时间:2018-08-03 11:29:21

标签: c++ opengl optimization 2d renderer

我已经完成了基于Dear ImGui DrawList渲染2d对象的类,因为借助索引向量动态数组,它可以绘制许多不同的对象变体,并且仍然保持良好的优化状态。亲爱的ImGui可以在调试模式下以36fps和70MB的速度渲染3万个未填充的矩形,而无需使用抗锯齿功能(我的电脑)。我的非常有限的版本在调试模式下以〜3 fps和〜130MB的速度绘制了3万个未填充的矩形。

class Renderer
{
public:
    Renderer();
    ~Renderer();

    void Create();

    void DrawRect(float x, float y, float w, float h, GLuint color, float thickness);

    void Render(float w, float h);

    void Clear();

    void ReserveData(int numVertices, int numElements);

    void CreatePolygon(const Vector2* vertices, const GLuint verticesCount, GLuint color, float thickness);

    GLuint vao, vbo, ebo;
    GLShader shader;

    Vertex* mappedVertex = nullptr;     
    GLuint* mappedElement = nullptr,   
            currentVertexIndex = 0;

    std::vector<Vertex> vertexBuffer;  
    std::vector<GLuint> elementBuffer; 
    std::vector<Vector2> vertices;     

};

const char* vtx =
R"(

#version 460 core

layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;

out vec3 v_position;
out vec4 v_color;

uniform mat4 projection;

void main()
{
    gl_Position = projection * vec4(a_position, 1.0);

    v_color = a_color;
}

)";

const char* frag =
R"(
#version 460 core

layout (location = 0) out vec4 outColor;

in vec4 v_color;

void main()
{
    outColor = v_color;
}
)";

void Renderer::Clear()
{
    vertexBuffer.resize(0);
    elementBuffer.resize(0);
    vertices.resize(0);
    mappedVertex = nullptr;
    mappedElement = nullptr;
    currentVertexIndex = 0;
}

void Renderer::Create()
{
    glGenBuffers(1, &vbo);
    glGenBuffers(1, &ebo);

   shader.VtxFromFile(vtx);
   shader.FragFromFile(frag);
}

void Renderer::DrawRect(float x, float y, float w, float h, GLuint color,     float thickness)
{
    // Add vertices
    vertices.push_back({ x, y });
    vertices.push_back(Vector2(x, y + w));
    vertices.push_back(Vector2( x, y ) + Vector2(w, h));
    vertices.push_back(Vector2(x + w, y));
    // Create rect
    CreatePolygon(vertices.data(), vertices.size(), color, thickness);
}

void Renderer::Render(float w, float h)
{
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    shader.UseProgram();
    shader.UniformMatrix4fv("projection", glm::ortho(0.0f, w, 0.0f, h));

    GLuint elemCount = elementBuffer.size();

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, position));
    glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (const void*)offsetof(Vertex, position));

    glBufferData(GL_ARRAY_BUFFER, vertexBuffer.size() * sizeof(Vertex), vertexBuffer.data(), GL_STREAM_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementBuffer.size() * sizeof(GLuint), elementBuffer.data(), GL_STREAM_DRAW);

    const unsigned short* idxBufferOffset = 0;

    glDrawElements(GL_TRIANGLES, elemCount, GL_UNSIGNED_INT, idxBufferOffset);

    idxBufferOffset += elemCount;

    glDeleteVertexArrays(1, &vao);

    glDisable(GL_BLEND);
}

void Renderer::CreatePolygon(const Vector2* vertices, const GLuint     verticesCount, GLuint color, float thickness)
{
    // To create for example unfilled rect, we have to draw 4 rects with small sizes
    // So, unfilled rect is built from 4 rects and each rect contains 4 vertices ( * 4) and 6 indices ( *6)
    ReserveData(verticesCount * 4, verticesCount * 6);

    for (GLuint i = 0; i < verticesCount; ++i)
    {
        const int j = (i + 1) == verticesCount ? 0 : i + 1;

        const Vector2& position1 = vertices[i];
        const Vector2& position2 = vertices[j];

        Vector2 difference = position2 - position1;

        difference *= difference.Magnitude() > 0 ? 1.0f / difference.Magnitude() : 1.0f;

        const float dx = difference.x * (thickness * 0.5f);
        const float dy = difference.y * (thickness * 0.5f);

        mappedVertex[0].position = Vector2(position1.x + dy, position1.y - dx);
        mappedVertex[1].position = Vector2(position2.x + dy, position2.y - dx);
        mappedVertex[2].position = Vector2(position2.x - dy, position2.y + dx);
        mappedVertex[3].position = Vector2(position1.x - dy, position1.y + dx);

        mappedVertex[0].color = color;
        mappedVertex[1].color = color;
        mappedVertex[2].color = color;
        mappedVertex[3].color = color;

        mappedVertex += 4;

        mappedElement[0] = currentVertexIndex;
        mappedElement[1] = currentVertexIndex + 1;
        mappedElement[2] = currentVertexIndex + 2;
        mappedElement[3] = currentVertexIndex + 2;
        mappedElement[4] = currentVertexIndex + 3;
        mappedElement[5] = currentVertexIndex;

        mappedElement += 6;
        currentVertexIndex += 4;
    }

    this->vertices.clear();
}

void Renderer::ReserveData(int numVertices, int numElements)
{
    currentVertexIndex = vertexBuffer.size();

    // Map vertex buffer
    int oldVertexSize = vertexBuffer.size();
    vertexBuffer.resize(oldVertexSize + numVertices);
    mappedVertex = vertexBuffer.data() + oldVertexSize;

    // Map element buffer
    int oldIndexSize = elementBuffer.size();
    elementBuffer.resize(oldIndexSize + numElements);
    mappedElement = elementBuffer.data() + oldIndexSize;
}


int main()
{
    //Create window, init opengl, etc.
    Renderer renderer;
    renderer.Create();
    bool quit=false;
    while(!quit) {
        //Events
        //Clear color bit

        renderer.Clear();

        for(int i = 0; i < 30000; ++i)
            renderer.DrawRect(100.0f, 100.0f, 50.0f, 50.0f, 0xffff0000, 1.5f);

        renderer.Render(windowW, windowH);        

        //swap buffers
    }
    return 0;
}

为什么这么慢? 如何使它更快,更省内存?

2 个答案:

答案 0 :(得分:0)

该代码中最大的瓶颈似乎是您的分配永远不会在帧之间摊销,因为您正在清除缓冲区容量而不是重复使用它们,从而导致大量重新分配/复制(如果您使用Log2(n)可能会重新分配/复制)向量实现会增长2倍。尝试使用.resize(0)更改.clear()调用,也许当事情不使用时,您可以对.clear()进行更多的延迟/稀有调用。

  

是在调试模式还是在释放模式?由于内存检查,向量的调试速度非常慢。分析应始终在Release中进行。

如果您打算在Debug / Unoptimized模式下使用和使用您的应用程序,则应该在Release和Debug / Unoptimized模式下进行性能分析。现代C ++普遍存在的“零成本抽象”谎言是,使用调试器非常麻烦,因为大型应用程序不再在“调试”模式下以正确的帧速率运行。理想情况下,您应始终在调试模式下运行所有​​应用程序。帮自己提高生产力,还可以对最坏的情况进行一些性能分析/优化。

祝您学习顺利! :)

答案 1 :(得分:0)

解决方案

  1. 我不再使用std :: vector了。我改为使用ImVector(也可能是您自己的实现)
  2. 我直接将位置设置为Vector2.x / .y