// // // iOSGLEssentials // // Copied from the Apple GLEssentials // // #import "GLEssentialsGLView.h" #import "Game.h" @import GLKit; @import QuartzCore; @interface GLEssentialsGLView (){ CVDisplayLinkRef displayLink; Game* game; } - (void) initGL; @end @implementation GLEssentialsGLView - (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 GLEssentialsGLView*)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]; // Init our renderer. Use 0 for the defaultFBO which is appropriate for // OSX (but not iOS since iOS apps must create their own FBO) game = [[Game alloc] init]; } - (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]); // Get the view size in Points NSRect viewRectPoints = self.bounds; // Rendering at retina resolutions will reduce aliasing, but at the potential // cost of framerate and battery life due to the GPU needing to render more // pixels. // Any calculations the renderer does which use pixel dimentions, must be // in "retina" space. [NSView convertRectToBacking] converts point sizes // to pixel sizes. Thus the renderer gets the size in pixels, not points, // so that it can set it's viewport and perform and other pixel based // calculations appropriately. // 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]; // If we do not want retina (why wouldn't you want it?). DO: // Points:Pixels is always 1:1 when not supporting retina resolutions // NSRect viewRectPixels = viewRectPoints; // Set the new dimensions in our renderer // [m_renderer resizeWithWidth:viewRectPixels.size.width AndHeight:viewRectPixels.size.height]; 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, viewRectPixels.size.width, 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