Davorin
Davorin

Reputation: 1204

NSOpenGLView and CVDisplayLink, no default frame buffer

I have an NSOpenGLView and OpenGL code that works with an NSTimer running in the main loop (calling setNeedsDisplay and drawRect). I would like to use a CVDisplayLink, so I can get a better frame-rate without overdriving the timer. I copied most of the code from apple's OSXGLEssentials example. The display link starts and the callback runs, but nothing is actually draw on screen. glGetError returns GL_INVALID_FRAMEBUFFER_OPERATION.

glCheckFramebufferStatus returns GL_FRAMEBUFFER_UNDEFINED for GL_FRAMEBUFFER, GL_DRAW_FRAMEBUFFER and GL_READ_FRAMEBUFFER.

Info from the documentation:

GL_FRAMEBUFFER_UNDEFINED is returned if target is the default framebuffer, but the default framebuffer does not exist.

Here are the relevant bits of code:

- (void)awakeFromNib {

  NSOpenGLPixelFormatAttribute attributes[] = {
    NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,   // Core Profile !
    NSOpenGLPFADoubleBuffer,
    NSOpenGLPFAAccelerated,
    NSOpenGLPFAColorSize, 24,
    NSOpenGLPFAAlphaSize, 8,
    NSOpenGLPFAAllowOfflineRenderers,
    0
  };

  NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
  NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:format shareContext: nil];
  // [context setView: self];
  [self setPixelFormat: format];
  [self setOpenGLContext: context];

}


- (void)prepareOpenGL {

  [super prepareOpenGL];

  NSOpenGLContext* context = [self openGLContext];
  [context makeCurrentContext];

  // Synchronize buffer swaps with vertical refresh rate
  GLint swapInt = 1;
  [context setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];

  MyDisplay_setup();
  MyDisplay_initScene(_bounds.size.width, _bounds.size.height);

  CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
  CVDisplayLinkSetOutputCallback(displayLink, &displayLinkCallback, (__bridge void *)self);
  CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
  CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, [context CGLContextObj], cglPixelFormat);
  CVDisplayLinkStart(displayLink);

}


static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime,
CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) {

  @autoreleasepool {
    [(__bridge MyView*)displayLinkContext redraw];
  }
  return kCVReturnSuccess;

}


- (void)redraw {

  NSOpenGLContext* context = [self openGLContext];
  [context makeCurrentContext];
  CGLLockContext([context CGLContextObj]);
  MyDisplay_drawScene();
  CGLFlushDrawable([context CGLContextObj]);
  CGLUnlockContext([context CGLContextObj]);

}

Upvotes: 1

Views: 1702

Answers (2)

dreta
dreta

Reputation: 1036

This is an old question, but this problem still persists, so here's my answer. For reference, i don't use Xcode at all, i write code in Vim and compile with Clang, so this is the default behaviour, and nothing to do with the IB. I use only the NSOpenGLView, NSOpenGLContext, and CGDisplayLink for rendering. I have a MacBook Pro (Retina, 15-inch, Mid 2014) running macOS Sierra.

While debugging i found that NSOpenGLContext's view property returned nil for the first few frames after starting the display link. This was enough to corrupt the context if you did any rendering (other than glClear) while the view wasn't attached, and caused the same GL_FRAMEBUFFER_UNDEFINED error.

The easiest way to solve this, i found, was to assign the NSOpenGLView to its NSOpenGLContext after creation like this:

NSOpenGLView *view = ...;

view.openglContext.view = view;

I'm baffled that, apparently, it's necessary to do this even though the NSOpenGLContext is created by the NSOpenGLView, but there it is.

Upvotes: 2

Davorin
Davorin

Reputation: 1204

The trick is to open the View Effects inspector and uncheck the parent View in the Core Animation Layer section.

enter image description here

Upvotes: 0

Related Questions