OpenGL 新纹理在删除后替换旧纹理
OpenGL new textures replacing old ones after deletion
我 运行 遇到了一个关于 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生成的。
我并不是真的在寻找关于效率的建议,理想情况下我想要一个答案,它可以引用关于使用这样的临时纹理进行渲染的 expected/correct 行为的文档,以及可能的常见陷阱方法。
预期的行为很明确。您可以在使用完对象后立即将其删除。在您的情况下,在使用纹理进行绘制调用之后,您可以在这些纹理上调用 glDeleteTextures()
。您不需要采取额外的预防措施。
在幕后,OpenGL 通常会异步执行绘制调用。因此在绘制调用 returns 之后仍将使用纹理。但这不是你的问题。驱动程序负责跟踪和管理对象的生命周期,使它们一直存在,直到它们不再被使用。
我在规范中找到的最清晰的表达是在 OpenGL 4.5 规范的第 28 页:
If an object is deleted while it is currently in use by a GL context, its name is immediately marked as unused, and some types of objects are automatically unbound from binding points in the current context, as described in section 5.1.2. However, the actual underlying object is not deleted until it is no longer in use.
在您的代码中,这意味着驱动程序无法删除纹理,直到 GPU 使用纹理完成绘制调用。
为什么这对你的情况不起作用很难说。一种可能性始终是您的代码中的某些内容无意中比应有的时间更早地删除了纹理。对于复杂的软件架构,这比您想象的要容易得多。例如,一个非常普遍的原因是人们将 OpenGL 对象包装在 C++ 中 类,并在底层 OpenGL 对象仍在使用时让这些 C++ 对象超出范围。
所以您绝对应该仔细检查(例如通过使用调试断点或日志记录)没有在意外时间调用删除纹理的代码。
另一个选项是驱动程序错误。虽然对象生命周期管理并非完全微不足道,但它是如此关键以至于很难想象它会在一个非常基本的情况下被破坏。但这当然是可能的,并且或多或少取决于供应商和平台。
作为变通方法,您可以尝试不删除纹理对象,而是只为相同的对象指定新数据(使用 glTexImage2D()
)。如果纹理大小不变,无论如何只用 glTexSubImage2D()
替换数据可能会更有效。
我 运行 遇到了一个关于 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生成的。
我并不是真的在寻找关于效率的建议,理想情况下我想要一个答案,它可以引用关于使用这样的临时纹理进行渲染的 expected/correct 行为的文档,以及可能的常见陷阱方法。
预期的行为很明确。您可以在使用完对象后立即将其删除。在您的情况下,在使用纹理进行绘制调用之后,您可以在这些纹理上调用 glDeleteTextures()
。您不需要采取额外的预防措施。
在幕后,OpenGL 通常会异步执行绘制调用。因此在绘制调用 returns 之后仍将使用纹理。但这不是你的问题。驱动程序负责跟踪和管理对象的生命周期,使它们一直存在,直到它们不再被使用。
我在规范中找到的最清晰的表达是在 OpenGL 4.5 规范的第 28 页:
If an object is deleted while it is currently in use by a GL context, its name is immediately marked as unused, and some types of objects are automatically unbound from binding points in the current context, as described in section 5.1.2. However, the actual underlying object is not deleted until it is no longer in use.
在您的代码中,这意味着驱动程序无法删除纹理,直到 GPU 使用纹理完成绘制调用。
为什么这对你的情况不起作用很难说。一种可能性始终是您的代码中的某些内容无意中比应有的时间更早地删除了纹理。对于复杂的软件架构,这比您想象的要容易得多。例如,一个非常普遍的原因是人们将 OpenGL 对象包装在 C++ 中 类,并在底层 OpenGL 对象仍在使用时让这些 C++ 对象超出范围。
所以您绝对应该仔细检查(例如通过使用调试断点或日志记录)没有在意外时间调用删除纹理的代码。
另一个选项是驱动程序错误。虽然对象生命周期管理并非完全微不足道,但它是如此关键以至于很难想象它会在一个非常基本的情况下被破坏。但这当然是可能的,并且或多或少取决于供应商和平台。
作为变通方法,您可以尝试不删除纹理对象,而是只为相同的对象指定新数据(使用 glTexImage2D()
)。如果纹理大小不变,无论如何只用 glTexSubImage2D()
替换数据可能会更有效。