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

标签 c++ opengl optimization 2d renderer

我完成了基于 Dear ImGui DrawList 渲染 2d 对象的类,因为它可以绘制许多不同的对象变体,这要归功于索引 vector 动态数组并且仍然保持良好的优化。亲爱的 ImGui 可以渲染 30k 未填充的矩形,同时在 Debug模式下具有 ~36fps 和 ~70MB,没有抗锯齿(我的电脑)。我的非常有限的版本绘制了 30k 未填充的矩形,同时在 Debug模式下具有 ~3 fps 和 ~130MB。

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

为什么这么慢? 我怎样才能使它更快并且占用更少的内存?

最佳答案

该代码中最大的瓶颈似乎是您的分配永远不会跨帧摊销,因为您正在清除缓冲区容量而不是重用它们,导致您进行大量重新分配/复制(如果您的可能是 Log2(n) 重新分配/复制) vector 实现增长了 2 倍)。尝试使用 .resize(0) 更改您的 .clear() 调用,也许您可​​以在未使用的情况下对 .clear() 进行更懒惰/罕见的调用。

In debug or in release mode? Vectors are terribly slow in debug due to memory checking. Profiling should always be done in Release.

如果您打算在调试/未优化模式下使用和处理您的应用程序,则应该在发布和调试/未优化模式下进行分析。现代 C++ 的“零成本抽象”谎言是,它让使用调试器变得很痛苦,因为大型应用程序不再以正确的帧速率在“调试”模式下运行。理想情况下,您应该始终在 Debug模式下运行所有​​应用程序。帮自己提高工作效率,同时针对最坏的情况做一些分析/优化。

祝您学习顺利! :)

关于c++ - 为什么基于 Dear ImGui 的渲染器这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51671642/

相关文章:

c++ - 与模板方法的接口(interface)

c++ - 需要有关编程方法的建议

c++ - 如果一个引用被声明为指向它自己呢?例如。整数& x = x;

linux - 编译器 gcc 编译器标志的文档?

Opengl简单四边形渲染

c++ - 指针困惑

opengl - OpenGL 着色器可以访问渲染前设置的哪些值?

mysql - 用于统计存储的大规模 MySQL 数据库 - 您有什么推荐?

Java方法优化

mysql - 尝试优化执行时间较长的查询