我什么时候应该调用glDeleteBuffers()?

时间:2015-01-14 06:53:00

标签: opengl

我有以下工作代码,但我并不相信我会以安全的方式调用glDeleteBuffers。在实践中它起作用(至少现在),但从我读过的内容来看,我认为它不应该起作用。

GLuint vao_id;
glGenVertexArrays(1, &vao_id);
glBindVertexArray(vao_id);

GLuint VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);

    //Alternate position <<----

//Unbind the VAO
glBindVertexArray(0);

//Current position <<----
glDeleteBuffers(1, &VBO);

我在解除VAO绑定后,正在调用glDeleteBuffers。我已经尝试在标记的替代位置调用它 - 在我设置属性指针后立即调用它。然而这引起了崩溃 - 我的猜测是因为当我进行绘制调用时,没有数据被绘制,因为我删除了它。

让我感到困惑的是,它现在有了它的作用。我担心a)我不太清楚当缓冲区被删除时会发生什么,以及b)它只是偶然发生并且可能会意外中断。

据我所知,调用glDeleteBuffers会删除数据,因此不应该绘制任何数据 - 但有。所以我的另一个想法是,当我重新绑定VAO时,数据会被恢复,尽管这对我来说没有多大意义,因为我无法推断数据将从哪里恢复。

如果我正确使用glDeleteBuffer,有人可以告诉我吗?如果不是应该调用的地方(我猜测一旦不再需要绘制数据,可能在程序结束时)。

3 个答案:

答案 0 :(得分:14)

您所看到的是明确定义的行为。以下是与此相关的规范的关键部分(强调添加)。

从第34节开始; 5.1.2删除对象的自动解除绑定&#34;在OpenGL 4.5规范中:

  

当删除缓冲区,纹理或渲染缓冲区对象时,从当前上下文中绑定的任何绑定点解除绑定从容器对象的任何附件中分离出来绑定到当前上下文,如DeleteBuffers,DeleteTextures和DeleteRenderbuffers所述。

和&#34; 5.1.3删除的对象和对象名称生命周期&#34;:

  

删除缓冲区,纹理,采样器,渲染缓冲区,查询或同步对象时,其名称会立即变为无效(例如标记为未使用),但基础对象将不会删除直到不再使用

     

如果满足以下任何条件,则使用缓冲区,纹理,采样器或渲染缓冲区对象:

     

该对象附加到任何容器对象

     

...

VAO被视为&#34;容器对象&#34;对于这种情况下的VBO。因此,只要在VAO中引用VBO,并且不删除VAO本身,VBO就会保持活动状态。这就是您最终使用glDeleteBuffers()的代码版本的原因。

但是,如果VAO当前已绑定,并且您删除了VBO,则它将自动从VAO中解除绑定。因此,它不再被VAO引用,而是立即删除。这适用于您在glDeleteBuffers()之后立即致电glVertexAttribPointer()的情况。

在任何情况下,id(aka name)立即变为无效。因此,您将无法再次绑定它,例如修改数据。

如果你更深入地研究这些规格,有一些警告。例如,如果删除缓冲区,并且它仍处于活动状态,因为它仍然由VAO引用,则缓冲区的名称可用于新缓冲区。这意味着您基本上有两个具有相同名称的缓冲区,这可能会导致一些令人困惑的行为。

部分由于这个原因,我个人不会为你想要继续使用的对象调用glDelete*()。但其他人喜欢尽快致电glDelete*()

答案 1 :(得分:2)

我想在单独的答案中强调@Onyxite 在已接受答案的第一条评论中指出的内容。这让我很抓狂,我花了好几个小时来追踪这个问题。

AMD Windows 驱动程序有一个 BUG,如果您在解除绑定所有引用的 VAO 后删除 VBO,它将删除缓冲区及其底层对象,因此不会绘制任何内容。这可能会导致黑屏,或者 OpenGL 无法绘制该部分 VAO。

因此,考虑到这一点,问题的答案是:

即使按照 OpenGL 规范正确,您也不应该调用 glDeleteBuffers(),直到您要实际删除引用该缓冲区的 VAO。

因此,您应该遵循 Reto Korandi 的建议,不要为要继续使用的对象调用 glDelete*()

答案 2 :(得分:0)

您提到的位置不能正确调用glDeleteBuffer,因为直到您还没有渲染该对象。我认为,如果在呈现对象均值之后调用u是函数,则在调用glDrawArrayglDrawIndex之后调用它会更好。

如果您先删除缓冲区,然后再调用绘制,则您可能不得不面对崩溃问题。因为draw调用会尝试访问您之前删除的缓冲区。