glClear失败,返回GL_FRAMEBUFFER_UNDEFINED

glClear fails with GL_FRAMEBUFFER_UNDEFINED

本文关键字:FRAMEBUFFER UNDEFINED GL 返回 失败 glClear      更新时间:2023-10-16

我正在将OpenGL/Qt升级到OSX Lion,并有一些新错误需要帮助解决。我莫名其妙地在GLClear上得到了GL_FRAMEBUFFER_UNDEFINED。

我已经阅读了所有可能的原因,似乎没有什么能与之相匹配。为了帮助追踪问题,我在我们使用的一些常见检查代码中添加了以下两行:

glCheckFramebufferStatus(GL_FRAMEBUFFER);
glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);

这是OpenGL Trace,首先是应用程序的开始:

1: 0x01021b06 glGenBuffers(1, 0x11c461c0); 
2: 0x01021b06 glGenBuffers(1, 0x11c4616c);  
3: 0x01021b06 glGenBuffers(1, 0x11c46118); 
4: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
5: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
6: 0x01021b06 glGetError(); returns: GL_NO_ERROR  
7: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
8: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
9: 0x01021b06 glGetError(); returns: GL_NO_ERROR  
10: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
11: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED

正如您所看到的,两个调用最终都是glCheckFramebufferStatusEXT,我认为这是OSX正在做的事情。不过,我不知道为什么没有帧缓冲区。

现在,这是错误发生时的轨迹:

29842: 0x01021b06 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_ONE); 
29843: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29844: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29845: 0x01021b06 glGetError(); returns: GL_NO_ERROR  
29846: 0x01021b06 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 32, 32, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0x11c3d010); 
29847: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29848: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29849: 0x01021b06 glGetError(); returns: GL_NO_ERROR  
29850: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29851: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29852: 0x01021b06 glGetError(); returns: GL_NO_ERROR  
29853: 0x01021b06 glGetError(); returns: GL_NO_ERROR  
29854: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29855: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29856: 0x01021b06 glGetError(); returns: GL_NO_ERROR  
29857: 0x01021b06 glClearColor(0, 0, 0, 1); 
29858: 0x01021b06 glGetError(); returns: GL_NO_ERROR  
29859: 0x01021b06 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_UNDEFINED  
29860: 0x01021b06 glClear(GL_COLOR_BUFFER_BIT); 
29861: 0x01021b06 glGetError(); returns: GL_INVALID_FRAMEBUFFER_OPERATION

正如您所看到的,我们得到了GL_NO_ERROR,一直到GlClear,它都失败了。

我不知道如何解决这个问题——我应该收集什么信息来解决它?

我在OS X 10.8中使用NSOpenGLView时也看到了这个错误,但我找到了一个解决方法。如果glCheckFrameBufferStatus返回GL_FRAMEBUFFER_UNDEFINED,则表示帧缓冲区未实例化。顺便说一句,请确保您正在调用[[self-openGLContext]setView:self],以确保上下文具有与其关联的可绘制区域。

我的发现是,如果NSOpenGLView的任何祖先(1)具有层(即通过setWantsLayer支持层或托管层)或(2)使用Interface Builder初始化,那么NSOpenGLView帧缓冲区将不会初始化!我不得不修改我的OpenGLView及其祖先,使其不使用层,也不被接口生成器分配,以便以编程方式实例化NSOpenGLView。(通过编程实例化,我的意思是openGLView是使用[[NSOpenGLVIew alloc]init..]创建的。)

你说得完全正确。Apple glFullScreenglClear之后立即获得glGetError。开始时发生一次,然后再次从窗口切换到全屏。

这似乎是由Mac OS X附带的GL实现问题引起的。显然,在调用glClear时,帧缓冲区还没有准备好使用。

http://lists.apple.com/archives/mac-opengl/2012/Jul/msg00038.html

它已经作为一个bug被提交到苹果的bug追踪器上,雷达号为11974524。

我发现,如果在glClear之后调用一次glGetError(),一切都会正常工作,这会重置OpenGL错误标志。

注意:以下仅适用于10.9(可能还有10.8,我还没有测试)。对于较新的osx版本,绘制OpenGL内容的唯一真正受支持的方法是使用CAOpenGLLayer(NSOpenGLView在技术上仍然有效,但存在vsync不起作用、渲染故障等缺陷)

除此之外,@Amber Dixon似乎在资金上是对的,因为这是一个层支持OpenGL视图的问题[在10.9(-10.8?)]。我们有几种方法可以创建它们,有些方法比其他方法更容易出错,所以让我们来看看它们。

下面我有一个简单的OpenGL示例的片段来绘制一个三角形,将NSOpenGLView子类化。您会注意到它是有层支持的,在10.9.5上,它的工作方式与它应该的完全一样。

#import <Cocoa/Cocoa.h>
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGL/gl3.h>
@interface MyOpenGLView : NSOpenGLView
@end
@implementation MyOpenGLView
+ (BOOL)canDrawOpenGL {
    return YES;
}
- (void)prepareOpenGL {
    // Set up OpenGL context and other properties
    [super prepareOpenGL];
    [[self openGLContext] makeCurrentContext];
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
- (void)drawRect:(NSRect)dirtyRect {
    // Clear the view with the clear color
    glClear(GL_COLOR_BUFFER_BIT);
    
    // Set up the vertex data for the triangle
    GLfloat vertices[] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };
    
    // Set up the vertex buffer object (VBO)
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    // Set up the vertex shader
    const GLchar* vertexSource =
    "#version 150n"
    "in vec3 position;"
    "void main() {"
    "   gl_Position = vec4(position, 1.0);"
    "}";
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexSource, NULL);
    glCompileShader(vertexShader);
    
    // Set up the fragment shader
    const GLchar* fragmentSource =
    "#version 150n"
    "out vec4 outColor;"
    "void main() {"
    "   outColor = vec4(1.0, 1.0, 1.0, 1.0);"
    "}";
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
    glCompileShader(fragmentShader);
    
    // Set up the shader program
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glBindFragDataLocation(shaderProgram, 0, "outColor");
    glLinkProgram(shaderProgram);
    glUseProgram(shaderProgram);
    
    // Set up the vertex array object (VAO)
    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    
    // Draw the triangle
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    // Clean up
    glDeleteProgram(shaderProgram);
    glDeleteShader(fragmentShader);
    glDeleteShader(vertexShader);
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    
    // Swap the buffers to display the rendered image
    [[self openGLContext] flushBuffer];
}
@end
int main(int argc, const char * argv[]) {
    // Set up the application and window
    @autoreleasepool {
        NSApplication *app = [NSApplication sharedApplication];
        NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 400)
            styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask
            backing:NSBackingStoreBuffered
            defer:NO];
        
        // Enable layer-backing for the window
        [window setBackingType:NSBackingStoreBuffered];
        
        // Create an NSOpenGLPixelFormat object with double buffering enabled
        NSOpenGLPixelFormatAttribute attribs[] = {
            NSOpenGLPFADoubleBuffer,
            NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
            0
        };
        NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
        
        // Create an NSOpenGLContext object with the pixel format
        NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
        
        // Set the pixel format and context for the view
        MyOpenGLView *glView = [[MyOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400) pixelFormat:pixelFormat];
        [glView setOpenGLContext:glContext];
        
        // Create a CAOpenGLLayer instance and set it as the layer for the view
    
        [glView setWantsLayer: YES];
        [glView.layer setBackgroundColor:[NSColor blueColor].CGColor];
        
        // Set up the window and run the application
        [window setContentView:glView];
        [window makeFirstResponder:glView];
        [window makeKeyAndOrderFront:nil];
        [app run];
        
    }
    return 0;
}

然而,也有一些有趣的地方需要注意。首先,请注意,NSOpenGLView有一个setOpenGLContext:方法,有一种设置像素格式的方法(通过setPixelFormat:或在构造函数中)。你可能想知道为什么这是必要的,因为后者可以从前者派生而来。事实上,这是一个微妙的陷阱。如果你把线路改成这样的

MyOpenGLView *glView = [[MyOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)];

现在没有传入pixelFormat(但稍后仍显式设置openGLContext),您会发现它在setWantsLayer: YES时失败,但在setWantsLayer: NO时工作正常。

事实证明,当你请求一个支持层的上下文时,NSOpenGLView实际上从像素格式(如果没有设置,则使用默认的像素格式)创建了一个全新的上下文,而不是遵守你传入的上下文。这可以从反汇编中看出

void * -[NSOpenGLView openGLContext](void * self, void * _cmd) {
    rbx = self;
    if ([self _isLayerBacked] != 0x0) {
            rax = [rbx _layerBackedOpenGLContext];
    }
    else {
            rax = rbx->_openGLContext;
            if (rax == 0x0) {
                    r14 = [NSOpenGLContext alloc];
                    rax = rbx->_pixelFormat;
                    if (rax == 0x0) {
                            rax = [rbx class];
                            rax = [rax defaultPixelFormat];
                    }
                    objc_assign_ivar([r14 initWithFormat:rax shareContext:0x0], rbx, *_OBJC_IVAR_$_NSOpenGLView._openGLContext);
                    [rbx->_openGLContext makeCurrentContext];
                    rax = rbx->_openGLContext;
            }
    }
    return rax;
}

也就是说,视图具有单独的_layerBackedOpenGLContext:_surfaceBackedOpenGLContext,并且每当切换setWantsLayer时,都会创建并分配一个全新的上下文。

另一个问题出现在下面的片段中。现在,让我们直接将NSView子类化,并自己绑定NSOpenGLContext,而不是将NSOpenGLView子类化。根据苹果公司的说法,没有任何迹象表明NSOpenGLView是任何东西;特别的";除了NSView的一个方便的包装器,它负责初始化和管理上下文,所以你希望它很简单:

#import <Cocoa/Cocoa.h>
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGL/gl3.h>
@interface MyOpenGLView : NSView
@property (nonatomic, strong) NSOpenGLContext *openGLContext;
@end
@implementation MyOpenGLView
+ (BOOL)canDrawOpenGL {
    return YES;
}
- (void)prepareOpenGL {
    // Set up OpenGL context and other properties
    [[self openGLContext] makeCurrentContext];
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
- (void)drawRect:(NSRect)dirtyRect {
    
    [[self openGLContext] setView: self];
    [[self openGLContext] makeCurrentContext];
    
    // Same calls as before, elided for brevity
    
    // Swap the buffers to display the rendered image
    [[self openGLContext] flushBuffer];
}
@end
int main(int argc, const char * argv[]) {
    // Set up the application and window
    @autoreleasepool {
        NSApplication *app = [NSApplication sharedApplication];
        NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 400)
            styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask
            backing:NSBackingStoreBuffered
            defer:NO];
        
        // Enable layer-backing for the window
        [window setBackingType:NSBackingStoreBuffered];
        
        // Create an NSOpenGLPixelFormat object with double buffering enabled
        NSOpenGLPixelFormatAttribute attribs[] = {
            NSOpenGLPFADoubleBuffer,
            NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
            0
        };
        NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
        
        // Create an NSOpenGLContext object with the pixel format
        NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
        
        // Set the pixel format and context for the view
        MyOpenGLView *glView = [[MyOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)];
        [glView setOpenGLContext:glContext];
        
    
        [glView setWantsLayer: YES];
        [glView.layer setBackgroundColor:[NSColor blueColor].CGColor];
        
        // Set up the window and run the application
        [window setContentView:glView];
        [window makeFirstResponder:glView];
        [window makeKeyAndOrderFront:nil];
        [app run];
        
    }
    return 0;
}

然而,当你运行这个(在10.9上)时,你会发现它根本不起作用。但是设置setWantsLayer: NO确实有效!更有趣的是,它在较新的osx版本上运行良好,因此代码本身可能没有错。当您从drawRectangle中打印出[self _layerBackedOpenGLContext]的值时,您会发现上下文确实设置正确。那是什么呢?

比较两个例子中的[self layer]的结果可以提示我们答案:当我们对NSOpenGLView进行子类化时,该层被设置为NSOpenGLLayer。但在后者中,该层仍然是默认的NSView层。(您可能还感兴趣的是,在较新的osx版本上,两者的底层都是NSCGLSurface,如本文所述)。因此,无论是有意还是无意,当您使用绑定的OpenGLContext创建自己的层支持的NSView时,上下文实际上从未绑定到层,这可能解释了您看到的错误。


编辑:所以事实证明,如果你想自己重新实现一个层支持的NSOpenGLView并正确执行,你就有责任提供支持的CALayer(如前所述,即使你在10.10+上没有这样做,它也能工作,但这是未定义的行为)。此外,您不能设置自己的上下文,必须使用层提供的上下文。

#import <Cocoa/Cocoa.h>
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGL/gl3.h>
@interface MyLayer : NSOpenGLLayer
@property (atomic, strong) NSOpenGLContext *myContext;
@end
@implementation MyLayer
- (BOOL)canDrawInCGLContext:(CGLContextObj)ctx pixelFormat:(CGLPixelFormatObj)pf
        forLayerTime:(CFTimeInterval)t displayTime:(const CVTimeStamp *)ts
{
    return YES;
}
- (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask {
    return [self openGLPixelFormat];
}
-(NSOpenGLPixelFormat*) openGLPixelFormat {
    return [[NSOpenGLPixelFormat alloc] initWithCGLPixelFormatObj: CGLGetPixelFormat([self.myContext CGLContextObj])];
}
@end
@interface MyOpenGLView : NSView
@property (atomic, strong) MyLayer *myLayer;
@end
@implementation MyOpenGLView
+ (BOOL)canDrawOpenGL {
    return YES;
}
- (MyLayer *)makeBackingLayer {
    self.myLayer = [[MyLayer alloc] init];
    self.myLayer.view = self;
    self.myLayer.asynchronous = NO;
    return self.myLayer;
}
- (void)prepareOpenGL {
    // Set up OpenGL context and other properties
    [[self.myLayer openGLContext] makeCurrentContext];
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
- (void)drawRect:(NSRect)dirtyRect {
//  self.layer.openGLContext = self.openGLContext;
    [[self.myLayer openGLContext] setView: self];
    [[self.myLayer openGLContext] makeCurrentContext];
    
    NSLog(@"%@", self.myLayer);
    NSLog(@"%@", self.layer);
    NSLog(@"%@", [self.myLayer openGLContext]);
    NSLog(@"%@", [self.myLayer myContext]);
    // <snip triangle>
    glFlush();
    
    
    // Swap the buffers to display the rendered image
    [[self.myLayer openGLContext] flushBuffer];
    [self.myLayer setNeedsDisplay];
    [self setNeedsDisplay: YES];
}
@end
int main(int argc, const char * argv[]) {
    // Set up the application and window
    @autoreleasepool {
        NSApplication *app = [NSApplication sharedApplication];
        NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 400)
            styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask
            backing:NSBackingStoreBuffered
            defer:NO];
        
        // Enable layer-backing for the window
        [window setBackingType:NSBackingStoreBuffered];
        
        // Create an NSOpenGLPixelFormat object with double buffering enabled
        NSOpenGLPixelFormatAttribute attribs[] = {
            NSOpenGLPFADoubleBuffer,
            NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
            0
        };
        NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
        
        // Create an NSOpenGLContext object with the pixel format
        NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
        NSLog(@"%@", glContext);
        
        // Set the pixel format and context for the view
        MyOpenGLView *glView = [[MyOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)];     
    
        [glView setWantsLayer: YES];
        glView.myLayer.myContext = glContext;
        [glView.myLayer setBackgroundColor:[NSColor blueColor].CGColor];
        
        
        // Set up the window and run the application
        [window setContentView:glView];
        [window makeFirstResponder:glView];
        [window makeKeyAndOrderFront:nil];
        [app run];
        
    }
    return 0;
}

我在这里遇到了同样的问题。在试用了苹果自己的GLFullscreen示例应用程序后,我注意到这个错误只有在使用Mac OSX SDK 10.7时才会显现出来,如果我切换到10.6,它就会神奇地消失。

你可以在这里找到GLFullscreen。

可能您需要在10.7中以另一种方式设置帧缓冲区。如果我发现10.6在某种程度上限制了我,我可能会对此进行调查。在那之前,我非常乐意忽略这个问题。