// // // OSX GLEssentials // // Copied from the Apple GLEssentials // // #import "GLView.h" #import "Game.h" @import GLKit; @import QuartzCore; @interface GLView (){ CVDisplayLinkRef displayLink; Game* game; } - (void) initGL; @end @implementation GLView - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime{ // There is no autorelease pool when this method is called // because it will be called from a background thread. // It's important to create one or app can leak objects. @autoreleasepool { [self drawView]; return kCVReturnSuccess; } } // This is the renderer output callback function static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { CVReturn result = [(__bridge GLView*)displayLinkContext getFrameForTime:outputTime]; return result; } - (void) awakeFromNib{ NSOpenGLPixelFormatAttribute attrs[] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, 0 }; NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; assert(pf); NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil]; assert(context); #ifdef DEBUG // When we're using a CoreProfile context, crash if we call a legacy OpenGL function // This will make it much more obvious where and when such a function call is made so // that we can remove such calls. // Without this we'd simply get GL_INVALID_OPERATION error for calling legacy functions // but it would be more difficult to see where that function was called. CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions); #endif self.pixelFormat = pf; self.openGLContext = context; // Opt-In to Retina resolution self.wantsBestResolutionOpenGLSurface = YES; } - (void) prepareOpenGL{ [super prepareOpenGL]; // Make all the OpenGL calls to setup rendering // and build the necessary rendering objects [self initGL]; // Create a display link capable of being used with all active displays CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); // Set the renderer output callback function CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, (__bridge void *)(self)); // Set the display link for the current renderer CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); // Activate the display link CVDisplayLinkStart(displayLink); // Register to be notified when the window closes so we can stop the displaylink [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:[self window]]; } - (void) windowWillClose:(NSNotification*)notification{ // Stop the display link when the window is closing because default // OpenGL render buffers will be destroyed. If display link continues to // fire without renderbuffers, OpenGL draw calls will set errors. CVDisplayLinkStop(displayLink); } - (void) initGL{ // The reshape function may have changed the thread to which our OpenGL // context is attached before prepareOpenGL and initGL are called. So call // makeCurrentContext to ensure that our OpenGL context current to this // thread (i.e. makeCurrentContext directs all OpenGL calls on this thread // to [self openGLContext]) [[self openGLContext] makeCurrentContext]; // Synchronize buffer swaps with vertical refresh rate GLint swapInt = 1; [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; game = [[Game alloc] init]; CGFloat x = 20; CGFloat y = 20; for (NSString * a in game.actions) { NSButton *myButton = [[NSButton alloc] initWithFrame:NSMakeRect(x, y, 200, 22)]; myButton.buttonType = NSMomentaryLightButton; myButton.bezelStyle = NSRecessedBezelStyle; myButton.title = a; myButton.target = self; myButton.action = @selector(action:); myButton.wantsLayer = YES; [self addSubview: myButton]; y += 22; } } - (void) action:(NSButton*) sender{ [game action:sender.title]; } - (void) reshape{ [super reshape]; // We draw on a secondary thread through the display link. However, when // resizing the view, -drawRect is called on the main thread. // Add a mutex around to avoid the threads accessing the context // simultaneously when resizing. CGLLockContext([self.openGLContext CGLContextObj]); NSRect viewRectPoints = self.bounds; // [NSView convertRectToBacking] converts point sizes to pixel sizes. // viewRectPixels will be larger (2x) than viewRectPoints for retina displays. // viewRectPixels will be the same as viewRectPoints for non-retina displays NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints]; game.windowSize = viewRectPixels.size; CGLUnlockContext([self.openGLContext CGLContextObj]); } - (void)renewGState{ // Called whenever graphics state updated (such as window resize) // OpenGL rendering is not synchronous with other rendering on the OSX. // Therefore, call disableScreenUpdatesUntilFlush so the window server // doesn't render non-OpenGL content in the window asynchronously from // OpenGL content, which could cause flickering. (non-OpenGL content // includes the title bar and drawing done by the app with other APIs) [[self window] disableScreenUpdatesUntilFlush]; [super renewGState]; } - (void) drawRect: (NSRect) theRect{ // Called during resize operations // Avoid flickering during resize by drawiing [self drawView]; } - (void) drawView{ [[self openGLContext] makeCurrentContext]; // We draw on a secondary thread through the display link // When resizing the view, -reshape is called automatically on the main // thread. Add a mutex around to avoid the threads accessing the context // simultaneously when resizing CGLLockContext([[self openGLContext] CGLContextObj]); NSRect viewRectPoints = self.bounds; NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints]; glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, (int)viewRectPixels.size.width, (int)viewRectPixels.size.height); [game update:1/60.0f]; [game draw]; CGLFlushDrawable([[self openGLContext] CGLContextObj]); CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void) dealloc{ // Stop the display link BEFORE releasing anything in the view // otherwise the display link thread may call into the view and crash // when it encounters something that has been release CVDisplayLinkStop(displayLink); CVDisplayLinkRelease(displayLink); // Release the display link AFTER display link has been released } @end