来自 glFramebufferTexture2DEXT 的 GL_INVALID_VALUE 仅在删除/重新分配纹理之后

Posted

技术标签:

【中文标题】来自 glFramebufferTexture2DEXT 的 GL_INVALID_VALUE 仅在删除/重新分配纹理之后【英文标题】:GL_INVALID_VALUE from glFramebufferTexture2DEXT only after delete/realloc texture 【发布时间】:2012-11-29 19:25:31 【问题描述】:

我有一些 C#(SharpGL-esque)代码将 OpenGL 帧缓冲区处理抽象为简单的“将此纹理设置为‘渲染目标’”调用。当纹理第一次设置为渲染目标时,我为该纹理大小创建了一个具有匹配深度缓冲区的 FBO;然后,FBO/深度缓冲区组合将被重用于所有相同大小的纹理。

我有一个奇怪的错误如下。

应用程序最初运行并呈现良好。但是,如果我增加我的窗口大小,这可能会导致一些代码需要调整其“渲染目标”纹理的大小,它通过 glDeleteTextures() 和 glGenTextures() (然后绑定、glTexImage2D 和 texparams 所以 MIN_FILTER 和 MAG_FILTER 都是 GL_NEAREST )。我观察到这样做时我倾向于返回相同的名称(ID)(因为 GL 重用了刚刚释放的名称)。

然后我们点击下面的代码(对于有点混蛋的类似 GL 的语法表示歉意):

    void SetRenderTarget(Texture texture)
    
        if (texture != null)
        
            var size = (texture.Width << 16) | texture.Height;
            FrameBufferInfo info;
            if (!_mapSizeToFrameBufferInfo.TryGetValue(size, out info))
            
                info = new FrameBufferInfo();
                info.Width = texture.Width;
                info.Height = texture.Height;

                GL.GenFramebuffersEXT(1, _buffer);
                info.FrameBuffer = _buffer[0];

                GL.BindFramebufferEXT(GL.FRAMEBUFFER_EXT, info.FrameBuffer);
                GL.FramebufferTexture2DEXT(GL.FRAMEBUFFER_EXT, GL.COLOR_ATTACHMENT0_EXT, GL.TEXTURE_2D, texture.InternalID, 0);

                GL.GenRenderbuffersEXT(1, _buffer);
                info.DepthBuffer = _buffer[0];
                GL.BindRenderBufferEXT(GL.RENDERBUFFER_EXT, info.DepthBuffer);
                GL.RenderbufferStorageEXT(GL.RENDERBUFFER_EXT, GL.DEPTH_COMPONENT16, texture.Width, texture.Height);
                GL.BindRenderBufferEXT(GL.RENDERBUFFER_EXT, 0);
                GL.FramebufferRenderbufferEXT(GL.FRAMEBUFFER_EXT, GL.DEPTH_ATTACHMENT_EXT, GL.RENDERBUFFER_EXT, info.DepthBuffer);
                _mapSizeToFrameBufferInfo.Add(size, info);
            
            else
            
                GL.BindFramebufferEXT(GL.FRAMEBUFFER_EXT, info.FrameBuffer);
                GL.FramebufferTexture2DEXT(GL.FRAMEBUFFER_EXT, GL.COLOR_ATTACHMENT0_EXT, GL.TEXTURE_2D, texture.InternalID, 0);
            
            GL.CheckFrameBufferStatus(GL.FRAMEBUFFER_EXT);
        
        else
        
            GL.FramebufferTexture2DEXT(GL.FRAMEBUFFER_EXT, GL.COLOR_ATTACHMENT0_EXT, GL.TEXTURE_2D, 0, 0);
            GL.BindFramebufferEXT(GL.FRAMEBUFFER_EXT, 0);
        

        ProjectStandardOrthographic();
    

在所述窗口调整大小之后,GL 从 glFramebufferTexture2DEXT() 调用返回一个 GL_INVALID_VALUE 错误(用 glGetError() 和 gDEBugger 标识)。如果我忽略这一点,glCheckFrameBufferStatus() 稍后会失败并显示 GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT。如果我也忽略这一点,如果我检查它们,我会看到预期的“framebuffer to doubious to do nothing”错误,如果我不检查它们,则会看到黑屏。

我在 NVidia GeForce GTX 550 Ti、Vista 64(32 位应用程序)、306.97 驱动程序上运行。我将 GL 3.3 与 Core 配置文件一起使用。

解决方法和好奇心:如果在重新分配纹理时,我在 glDeleteTextures() 之前使用 glGenTextures() - 为了避免取回相同的 ID - 问题就消失了。我不想这样做,因为它是一个愚蠢的 kluge 并增加了我的内存不足错误的机会。我推测这是因为 GL 在最近的 FBO 中曾经/正在使用纹理,现在已经确定纹理 ID 正在使用或以某种方式不再有效,因此不可接受?也许?

问题后 gDEBugger 显示两个 FBO(原始的具有较小的深度缓冲区和先前的纹理,以及具有较大组合的新的)都附加了相同的纹理 ID。

我尝试在释放之前从帧缓冲区中分离纹理(再次通过 glFramebufferTexture2DEXT),但无济于事(gDEBuffer 反映了更改,但问题仍然存在)。我已经尝试完全取出深度缓冲区。在使用之前,我尝试通过 glGetTexLevelParameter() 检查纹理大小;它确实存在。

【问题讨论】:

【参考方案1】:

这听起来像是 NVIDIA 的 OpenGL 实现中的一个错误。删除对象名称后,该对象名称将变为无效,因此应该是 glGen* 返回的合法候选对象。

您应该提交一份错误报告,并附上重现问题的最小案例。

我不想这样做,因为这很愚蠢,会增加我出现内存不足错误的机会。

不,它没有。 glGenTextures 没有为纹理分配 storage(这是任何真正的 OOM 错误可能来自的地方)。它只创建纹理名称。不幸的是,您必须使用解决方法,但这并不是真正的问题。

【讨论】:

优秀的点,你当然是正确的重新存储分配,我没有想到在代码中将名称创建与提交纹理内存分离,但我当然可以这样做。我希望这可能是一个错误,但我倾向于认为事情是我的错。如果您认为我的方法在重用帧缓冲区+深度缓冲区对池和一组可能定期重新创建和破坏的颜色纹理方面没有什么可怕的明显错误,我是否会过度推断,以期获得最少数量的颜色纹理FBO 覆盖一系列纹理尺寸? 由于此答案有助于实现令人满意的解决方法,并且目前缺乏其他替代方法,因此我将其标记为正确,谢谢。

以上是关于来自 glFramebufferTexture2DEXT 的 GL_INVALID_VALUE 仅在删除/重新分配纹理之后的主要内容,如果未能解决你的问题,请参考以下文章

打开 GL ES 2 - glFramebufferTexture2D 带有不完整的缺少附件错误

OpenGL使用 FrameBufferObject

OSX OpenGL 深度模板组合

将帧缓冲区附加到纹理错误

OpenGL ES 2.0 坐标系

GL_COLOR_ATTACHMENT 有啥作用?