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

When should I call glDeleteBuffers()?

我有以下工作代码,但我不确定我是否以安全的方式调用 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?如果不是应该调用它的地方(我猜一旦不再需要绘制数据,可能在程序结束时)。

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

来自 OpenGL 4.5 规范中的“5.1.2 自动取消绑定已删除对象”部分:

When a buffer, texture, or renderbuffer object is deleted, it is unbound from any bind points it is bound to in the current context, and detached from any attachments of container objects that are bound to the current context, as described for DeleteBuffers, DeleteTextures, and DeleteRenderbuffers.

和“5.1.3 已删除对象和对象名称生命周期”:

When a buffer, texture, sampler, renderbuffer, query, or sync object is deleted, its name immediately becomes invalid (e.g. is marked unused), but the underlying object will not be deleted until it is no longer in use.

A buffer, texture, sampler, or renderbuffer object is in use if any of the following conditions are satisfied:

the object is attached to any container object

...

在这种情况下,VAO 被视为 VBO 的“容器对象”。因此,只要 VBO 在 VAO 中被引用,并且 VAO 本身没有被删除,VBO 就会保持活动状态。这就是为什么您的代码版本最后带有 glDeleteBuffers() 的原因。

但是,如果当前已绑定 VAO,并且您删除了 VBO,它会自动与 VAO 解除绑定。因此,它不再被 VAO 引用,并立即删除。这适用于您在 glVertexAttribPointer().

之后立即调用 glDeleteBuffers() 的情况

在任何情况下,id(又名名称)都会立即失效。所以你将无法再次绑定它,例如修改数据。

如果您更深入地研究规范,会有一些注意事项。例如,如果你删除了一个缓冲区,它仍然存在,因为它仍然被 VAO 引用,缓冲区的名称可以用于新的缓冲区。这意味着您基本上有两个同名的缓冲区,这可能会导致一些混乱的行为。

部分出于这个原因,我个人不会为您想继续使用的对象调用 glDelete*()。但其他人喜欢尽快打电话给glDelete*()

你提到的位置调用 glDeleteBuffer 是不正确的,因为直到你还没有渲染对象。我认为如果你在渲染对象之后调用函数会更好 mean;s 在调用 glDrawArrayglDrawIndex 之后。

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

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

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

所以,考虑到这一点,问题的答案是:

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

所以你应该听从 Reto Korandi 的建议,不要为你想继续使用的对象调用 glDelete*()