glClear失败,返回GL_FRAMEBUFFER_UNDEFINED
glClear fails with GL_FRAMEBUFFER_UNDEFINED
我正在将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 glFullScreen
在glClear
之后立即获得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在某种程度上限制了我,我可能会对此进行调查。在那之前,我非常乐意忽略这个问题。
- OpenGL Framebuffer ColorAttachment to Compute Shader
- 如何在Windows中捕获HDR Framebuffer
- 如何在单独的视口和默认的framebuffer中使用framebuffer
- 使用GDI 从ADB Framebuffer绘制数据
- 是否可以将纹理作为渲染目标附加到默认的FrameBuffer
- C 使用Und und Binary Function对象的C 编译误差
- 在Android NDK上通过JNI从OpenGL ES Framebuffer创建位图图像
- 设置 GL 上下文时'framebuffer'的目的是什么?
- CCRenderTexture 在子线程中崩溃 - "Could not attach texture to framebuffer"
- 在GLFW3的glfwwindowint函数中,对framebuffer的几个组件使用位深的目的是什么?
- 延迟渲染:使用默认framebuffer中Gbuffer的深度缓冲区
- Framebuffer,不完整的纹理附件
- OpenGL FrameBuffer and glViewport
- Render the DepthTexture from a FrameBuffer
- 使用framebuffer对象组合图像
- OpenGL Framebuffer,绘制到纹理
- Android C++ framebuffer ioctl, only with root?
- 纹理和渲染缓冲区在framebuffer对象上共享相同的空间吗?
- 如何将framebuffer对象中的屏幕外数据绘制到QGLWidget
- 使用glReadPixels(..)读取framebuffer时精度较低