【问题标题】:Why Dear ImGui based renderer is so slow?为什么基于 Dear ImGui 的渲染器这么慢?
【发布时间】:2018-08-03 11:29:20
【问题描述】:

我已经完成了基于 Dear ImGui DrawList 渲染 2d 对象的类,因为它可以绘制许多不同的对象变体,这要归功于索引向量动态数组并且仍然保持良好的优化。亲爱的 ImGui 可以渲染 30k 未填充的矩形,同时在调试模式下具有 ~36fps 和 ~70MB,无需抗锯齿(我的电脑)。我的非常有限的版本绘制了 30k 未填充的矩形,同时在调试模式下具有 ~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;
}

为什么这么慢? 我怎样才能使它更快和更少的内存消耗?

【问题讨论】:

  • 整个数据处理看起来并不理想。鉴于每当调用 resize 时,可能会复制整个矢量内容,因此对每个顶点都这样做似乎不是一个好主意。
  • 我也不太确定为未填充的矩形绘制四个四边形是个好主意。 (这绝对不是记忆明智的)。为什么不在片段着色器的开放区域中绘制完整的矩形并丢弃片段?此外,仅将一个包含 x、y、宽度、高度的 vec4 发送到顶点着色器并在几何着色器中创建四边形可能是一种选择。
  • 我的意思是:手动调整向量大小然后通过原始点访问它们不是一个好主意。直接使用 push_back。
  • The whole data handling looks suboptimal. Given that whenever resize is called, the whole vector content might be copied, it doesn't seem a good idea to do that for every vertex. 我不太明白,你能不能换个方式解释一下? 2. 这是另一个绘制调用和另一个着色器。该渲染器不仅适用于未填充的矩形。它也适用于填充、圆圈等。
  • 所以,我刚刚将其更改为推回(我之前尝试过,因为使用 ben 制作游戏制作了有关调试渲染器的视频,并且他推回而不是通过原始指针访问)并且它成功了更差。 1fps 有时 2, ~2 秒白屏等待加载数据。内存下降到90,但最终结果...

标签: c++ opengl optimization 2d renderer


【解决方案1】:

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

处于调试模式还是发布模式?由于内存检查,向量的调试速度非常慢。分析应始终在 Release 中完成。

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

祝您学习顺利! :)

【讨论】:

  • 只有析构函数调用.clear()(所以现在只在应用程序的末尾)和CreatePolygon(...)方法的末尾。我刚刚在该方法中将vertices.clear() 更改为vertices.resize(0)。所以只有 .resize() 东西,性能还是一样的。 Do yourself a productivity favour and ALSO do some profiling/optimization for your worse case我现在就去做。
  • @Omar std::vector::clear 不会减少容量,在这种情况下它与 .resize(0) 没有什么不同。
  • 使用标准向量后最大的瓶颈是这样设置位置:mappedVertex[0].position = Vector2(...)。相反,我应该这样做:mappedVertex[0].position.x = ; mappedVertex[0].position.y = 等等...... Fps 从 ~16 增长到 ~32。感谢您的回信和很大的帮助和解释!
  • 你们知道如何将这篇文章标记为已解决吗?
  • @Scouteeer 写下你自己的答案,解释你做了什么,然后按旁边的绿色勾号。
【解决方案2】:

解决方案

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

【讨论】:

  • 比较你在完全优化的“发布”版本中的表现会很有趣。在 ImDrawList 上进行的许多小优化都有效地避免了非优化构建中的开销。
  • @Omar 我还没有完全优化它,我遇到了一些向量类问题。但在发布模式下:Maximum Optimization (Favor Speed) (/O2)Any Suitable (/Ob2)Yes (/Oi)Favor fast code (/Ot)No (/Oy-)NoYes (/GL) - 我在渲染 30k 未填充的矩形时获得了 200 fps 的帧率。而你的在我的电脑上大约有 300 个。我必须找到这个 fps 杀手并将其从我的代码中删除。如果你愿意,什么时候完成,我可以在某个地方给你回信。
猜你喜欢
  • 1970-01-01
  • 2012-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
  • 2014-12-29
  • 2014-01-20
相关资源
最近更新 更多