【问题标题】:Why are glDeleteBuffers and glDeleteVertexArrays so slow?为什么 glDeleteBuffers 和 glDeleteVertexArrays 这么慢?
【发布时间】:2019-09-16 15:39:08
【问题描述】:

在我的程序流程的某个时刻,我生成了 0 到 300 个网格,每个网格都像这样:

public Mesh(float[] vertices, byte[] indices, float[] textureCoordinates)
{
    vao = glGenVertexArrays();
    glBindVertexArray(vao);

    vbo = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW);
    glVertexAttribPointer(ShaderProgram.VERTEX_ATTRIB, 3, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(ShaderProgram.VERTEX_ATTRIB);

    tbo = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, tbo);
    glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL_STATIC_DRAW);
    glVertexAttribPointer(ShaderProgram.TCOORD_ATTRIB, 2, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(ShaderProgram.TCOORD_ATTRIB);

    ibo = glGenBuffers();
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW);

    glBindVertexArray(0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

当用户按下按钮时,这些网格需要再次删除,因此我对每个网格对象运行清理方法:

public void cleanup()
{
        glDeleteBuffers(vbo);
        glDeleteBuffers(tbo);
        glDeleteBuffers(ibo);
        glDeleteVertexArrays(vao);
}

问题是我试图以 60 fps 的速度运行,删除 70 个这些网格对象大约需要 30 毫秒(删除其中 110 个需要 75 毫秒)。这会在性能上造成明显的中断,因为一帧最多需要 16 毫秒。

这不是处理 VBO 和 VAO 的正确方法吗?我在另一个问题 (glDeleteBuffers slower than glBufferData) 中读到

glGenBuffers 和 glDeleteBuffers 分别设计为仅在初始化和清理时运行。在运行时调用它们是不好的。

但我不确定如何在不调用上述函数的情况下摆脱这些 VBO 和 VAO。

我曾想过将所有要删除的网格添加到队列中,并且每帧只删除几个,慢慢清空队列,但这感觉不是正确的解决方案。我想到的另一个(可能的)解决方案是使用实例化渲染,但据我所知,当我每帧进行不到 1000 次绘制调用时,非实例化渲染也应该可以正常工作。我的程序在任何时候都不会有超过 1000 个 Mesh 对象,我什至不确定这是否能解决我的问题。

更新:除了从下面的答案指出我完全正确的方向外,我还发现我实际上并没有删除 ~0-300 VBO,而是 48 个因子!难怪表演被扼杀了。因此,如果其他人遇到过同样的问题,请彻底检查您的代码正在执行的 glDeleteBuffers 的数量。

【问题讨论】:

    标签: opengl lwjgl vbo vao


    【解决方案1】:

    你在那里遇到了一些心理障碍。您似乎认为 "per mesn: {one vertex attribute == one VBO}",但这不是它应该如何工作的。您应该做的是使用一个大型 VBO,并将其用作分配块的内存池,每个块都保存一些数据。

    因此,您不仅可以将单个网格的所有顶点属性放入一个公共 VBO,还可以将多个网格放入单个 VBO。

    此外,您似乎在每次渲染迭代时都在创建和删除 VBO 和 VAO。为什么?网格是否在每一帧之间发生显着变化?如果是这样,那一定是癫痫诱发观看;烘焙到几何数据中的网格变形不需要重新创建缓冲区,您只需使用 glBufferSubData 覆盖数据。

    【讨论】:

    • 我只创建和删除每个 VBO/VAO 一次。每一帧都会调用每个 Mesh 对象上的 render 方法,其中包含:glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, 0); glBindVertexArray(0); 每个网格的位置被传递给着色器,我不会为此重新创建 Mesh 对象。
    • 所以基本上你说的是顶点缓冲区、纹理坐标缓冲区和索引缓冲区都可以存储在一个VBO中?并且“您还可以将多个网格放入单个 VBO。”你的意思是实例化渲染?
    • @Thorakh:将多个网格放入单个缓冲区与实例化渲染完全不同。使用实例化渲染,您是使用单个绘图调用的多个实例(应用了不同的转换)中的 same 数据。但是在一个缓冲区中拥有多个网格可以通过提供更长的索引缓冲区和更少的绘制调用来绘制它们,或者通过多个绘制调用来绘制它们。
    • 有道理,谢谢。我会考虑这样做的。如果我有更少的 VBO,我想删除它们也将花费更少的时间,即使每个单独的 VBO 都更大。但是 glDeleteBuffers 应该会很慢吗?
    猜你喜欢
    • 2021-09-27
    • 2012-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-03
    • 2011-12-08
    • 1970-01-01
    • 2016-09-28
    相关资源
    最近更新 更多