Simple Mandelbrot fractal for OSX and iOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 
 
 

200 lines
6.4 KiB

//
//
// 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];
}
- (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