【问题标题】:OpenGL new textures replacing old ones after deletionOpenGL新纹理在删除后替换旧纹理
【发布时间】:2015-08-31 14:33:10
【问题描述】:

我在使用 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 缓冲我的绘图调用有关, 当它们被实际处理时,纹理被删除了?但这对我来说真的没有意义,为什么在删除以前的纹理之后绘制其他东西会导致这种行为?

更多上下文

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

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

【问题讨论】:

  • 如果你这边没有奇怪的错误(除了到目前为止粘贴的代码),这似乎是一个驱动程序错误。您使用的是哪个驱动程序?您是否有机会使用另一个 GL 实现来测试您的代码?
  • 您好,我已经更新了适用于windows的nvidia驱动程序,这次我完全忘记考虑驱动程序的错误了,有机会我会尽快在其他硬件上进行测试,谢谢。
  • 删除tex1和tex2后尝试glFinish会怎样?

标签: c++ winapi opengl textures


【解决方案1】:

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

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

我在规范中找到的最清楚的表达是在 OpenGL 4.5 规范的第 28 页:

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

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

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

因此,您绝对应该仔细检查(例如通过使用调试断点或日志记录)是否在意外时间调用了删除纹理的代码。

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

作为一种解决方法,您可以尝试不删除纹理对象,而只为相同的对象指定新数据(使用glTexImage2D())。如果纹理大小没有改变,那么无论如何只用glTexSubImage2D()替换数据可能会更有效。

【讨论】:

  • 我的代码实际上是程序化的,没有使用复杂的创建或销毁,它基本上是我粘贴的确切代码,只是在大约 4 个不同的函数调用中。不过,感谢您提供预期的行为和对规范的参考。
  • 我想补充一点,我发现了我的具体情况的问题,纹理没有被过早删除或类似的东西,但是包含它们的数组在它们被删除后并没有更新其使用的计数器绘制,所以在那之后的任何调用都试图重新绘制这些纹理多次,在它们被删除之后(doh!),知道行为得到了很好的定义和支持,让我固执地不放弃调试它,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-02
  • 2011-05-19
相关资源
最近更新 更多