OpenGL新纹理在删除后取代旧纹理

OpenGL new textures replacing old ones after deletion

本文关键字:纹理 删除 取代 新纹理 OpenGL      更新时间:2023-10-16

我遇到了一些令人困惑的问题与OpenGL,它是相当简单,但我没能找到任何直接相关的信息。

我想做什么

我每帧创建几个新的纹理,创建后立即绑定它们,使用它们进行绘制,然后删除它们。

问题

如果我在使用后立即删除每个纹理,那么最后一个要绘制的纹理将替换之前的纹理(但它们的不同几何形状应该如此)。如果我在所有绘制完成后批量删除,它会像预期的那样工作,但如果我在删除纹理后进行任何绘制调用,则最后一次绘制调用中使用的纹理会替换旧的纹理(可能是一些常见的永久精灵纹理)。

调试结果

我尝试过使用glFlush(),它似乎根本没有做任何事情,根本没有删除纹理给出正确的行为,也没有在删除纹理和调用SwapBuffers()作品之间绘制任何东西。

代码

这不是我的代码看起来像什么,但这是相关部分归结为:

int Tex1, Tex2, Tex3;
glGenTextures(1, &Tex1);
glBindTexture(GL_TEXTURE_2D, Tex1);
// ... Fill Texture with data, set correct filtering etc.
glDrawElements(GL_TRIANGLES, ...); // Using Tex1

glGenTextures(1, &Tex2);
glBindTexture(GL_TEXTURE_2D, Tex2);
// ... Fill Texture with data, set correct filtering etc.
glDrawElements(GL_TRIANGLES, ...); // Using Tex2
// I delete some textures here.
glDeleteTextures(1, &Tex1);
glDeleteTextures(1, &Tex2);
// If I comment out this section, everything works correctly
// If I leave it in, this texture replaces Tex1 and Tex2, but
// the geometry is correct for each geometry batch.
glGenTextures(1, &Tex3);
glBindTexture(GL_TEXTURE_2D, Tex3);
// ... Fill Texture with data, set correct filtering etc.
glDrawElements(GL_TRIANGLES, ...); // Using Tex3 
glDeleteTextures(1, &Tex3);
// ...
SwapBuffers();

我怀疑这可能与OpenGL缓冲我的draw调用有关,当它们真正被处理的时候,纹理被删除了吗?这对我来说并没有什么意义,为什么在删除之前的纹理后绘制其他会导致这种行为?

更多上下文

生成的纹理是文本字符串,可能会或可能不会改变每帧,现在我为每个字符串每帧创建新的纹理,然后渲染纹理并在之后丢弃它。位图数据由Windows GDI生成。

我不是真的在寻找效率方面的建议,理想情况下我想要一个答案,可以引用使用这样的临时纹理渲染的预期/正确行为的文档,以及这种方法可能存在的常见问题。

预期的行为是明确的。您可以在使用完对象后立即删除它们。在你的情况下,在你使用纹理进行绘制调用之后,你可以在这些纹理上调用glDeleteTextures()。你方不需要采取额外的预防措施。

在底层,OpenGL通常会异步执行draw调用。所以在draw调用返回后纹理仍然会被使用。但这不是你的问题。驱动程序负责跟踪和管理对象的生命周期,以保持它们的存在,直到它们不再被使用。

我在OpenGL 4.5规范的第28页找到了最清晰的表达:

如果一个对象在当前被GL上下文使用时被删除,它的名称立即被标记为未使用,并且某些类型的对象会自动从当前上下文的绑定点解除绑定,如5.1.2节所述。但是,实际的底层对象直到不再使用时才会被删除。

在你的代码中,这意味着驱动程序不能删除纹理,直到GPU完成使用纹理的绘制调用。

为什么这在你的情况下不起作用很难说。有一种可能性是,代码中的某些内容总是无意中提前删除了纹理。对于复杂的软件架构,这比您想象的要容易得多。例如,一个非常流行的原因是人们将OpenGL对象包装在c++类中,并让这些c++对象超出作用域,而底层OpenGL对象仍在使用中。

所以你绝对应该仔细检查(例如通过使用调试断点或日志记录),删除纹理的代码不会在意外的时间被调用。

另一个选项是驱动程序错误。虽然对象生命周期管理并非完全无关紧要,但它是如此重要,以至于很难想象它在非常基本的情况下会被破坏。但这当然是可能的,或多或少取决于供应商和平台。

作为一个解决方案,你可以尝试不删除纹理对象,而只指定新的数据(使用glTexImage2D())为相同的对象。如果纹理大小不变,用glTexSubImage2D()替换数据可能会更有效。