Created
September 24, 2016 07:57
-
-
Save davepkennedy/75f0e4c53edc10e31fd4d7fe0ebebbbf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// GLView.h | |
// | |
// Created by Dave Kennedy on 11/09/2016. | |
// | |
// | |
#import <Cocoa/Cocoa.h> | |
#import <GLKit/GLKit.h> | |
@interface GLView : NSView | |
@property (readonly) NSString* appName; | |
@property (readonly) NSString* title; | |
@property (readonly) NSSet* activeKeys; | |
- (void) setup; | |
- (void) resized:(NSSize) size; | |
- (void) renderDisplay; | |
- (NSData*) loadResourceData:(NSString*) name; | |
- (NSString*) loadResourceString:(NSString*) name; | |
- (GLuint) createShader:(GLenum) shaderType | |
withSource:(const char*) source; | |
- (GLuint) loadShader:(GLenum) shaderType | |
named:(NSString*) name; | |
- (void) checkShaderCompiled: (GLuint) shaderIndex; | |
- (void) printLogForShader:(GLuint) shaderIndex; | |
- (void) checkProgramLinked:(GLuint) program; | |
- (void) printLogForProgram:(GLuint) program; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// GLView.m | |
// | |
// Created by Dave Kennedy on 11/09/2016. | |
// | |
// | |
#import "GLView.h" | |
@implementation GLView { | |
CVDisplayLinkRef _displayLink; | |
NSOpenGLContext* _context; | |
NSMutableSet* _activeKeys; | |
double _previousSeconds; | |
int _frameCount; | |
} | |
@synthesize activeKeys = _activeKeys; | |
- (BOOL) acceptsFirstResponder { | |
return YES; | |
} | |
- (NSOpenGLPixelFormat*) pixelFormat { | |
NSOpenGLPixelFormatAttribute attribs[] = | |
{ | |
NSOpenGLPFADoubleBuffer, | |
NSOpenGLPFAAllowOfflineRenderers, // lets OpenGL know this context is offline renderer aware | |
NSOpenGLPFAMultisample, 1, | |
NSOpenGLPFASampleBuffers, 1, | |
NSOpenGLPFASamples, 4, | |
NSOpenGLPFAColorSize, 32, | |
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, // Core Profile is the future | |
0 | |
}; | |
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; | |
if(!pf) | |
{ | |
NSLog(@"Failed to create pixel format."); | |
return nil; | |
} | |
return pf; | |
} | |
- (NSOpenGLContext*) makeContext { | |
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:[self pixelFormat] shareContext:nil]; | |
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions); | |
return context; | |
} | |
void MyDisplayReconfigurationCallBack(CGDirectDisplayID display, | |
CGDisplayChangeSummaryFlags flags, | |
void *userInfo) | |
{ | |
if (flags & kCGDisplaySetModeFlag) | |
{ | |
[((__bridge GLView *)userInfo) update]; | |
} | |
} | |
static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, | |
const CVTimeStamp* now, // Current time | |
const CVTimeStamp* outputTime, // Displayed at time | |
CVOptionFlags flagsIn, // unused | |
CVOptionFlags* flagsOut, // unused | |
void* displayLinkContext) // Context object | |
{ | |
GLView* delegate = (__bridge GLView*)displayLinkContext; | |
[delegate render]; | |
return kCVReturnSuccess; | |
} | |
- (void) initNotifications { | |
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); | |
CVDisplayLinkSetOutputCallback(_displayLink, &MyDisplayLinkCallback, (__bridge void * _Nullable)(self)); | |
CVDisplayLinkStart(_displayLink); | |
[[NSNotificationCenter defaultCenter] addObserver:self | |
selector:@selector(surfaceNeedsUpdate:) | |
name:NSViewGlobalFrameDidChangeNotification | |
object:self.window.contentView]; | |
[[NSNotificationCenter defaultCenter] addObserver:self | |
selector:@selector(surfaceNeedsUpdate:) | |
name:NSWindowDidResizeNotification | |
object:self.window]; | |
CGDisplayRegisterReconfigurationCallback(MyDisplayReconfigurationCallBack, (__bridge void * _Nullable)(self)); | |
} | |
- (void)applicationWillTerminate:(NSNotification *)aNotification { | |
// Insert code here to tear down your application | |
CVDisplayLinkStop(_displayLink); | |
CVDisplayLinkRelease(_displayLink); | |
} | |
- (void) surfaceNeedsUpdate:(NSNotification*) notification { | |
[self update]; | |
} | |
- (void) update { | |
[_context update]; | |
[self resized:self.bounds.size]; | |
} | |
- (void) updateFrameCounter { | |
double current_seconds = CFAbsoluteTimeGetCurrent(); | |
double elapsedSeconds = current_seconds - _previousSeconds; | |
if (elapsedSeconds > 0.25) { | |
_previousSeconds = current_seconds; | |
double fps = (double)_frameCount / elapsedSeconds; | |
_frameCount = 0; | |
NSString* title = [NSString stringWithFormat:@"%@::%@: opengl @ fps %.2f", self.appName, self.title, fps]; | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
self.window.title = title; | |
}); | |
} | |
_frameCount++; | |
} | |
- (void) render { | |
if (!_context.view) { | |
_context.view = self; | |
} | |
[_context makeCurrentContext]; | |
CGLLockContext([_context CGLContextObj]); | |
[self renderDisplay]; | |
CGLFlushDrawable([_context CGLContextObj]); | |
CGLUnlockContext([_context CGLContextObj]); | |
[self updateFrameCounter]; | |
} | |
- (void)drawRect:(NSRect)dirtyRect { | |
[super drawRect:dirtyRect]; | |
// Drawing code here. | |
} | |
- (void) awakeFromNib { | |
_previousSeconds = CFAbsoluteTimeGetCurrent(); | |
_frameCount = 0; | |
_activeKeys = [NSMutableSet set]; | |
_context = [self makeContext]; | |
[_context makeCurrentContext]; | |
[self initNotifications]; | |
[self setup]; | |
[self update]; | |
} | |
- (void) keyDown:(NSEvent *)event { | |
if (!event.ARepeat) { | |
[_activeKeys addObject:[NSNumber numberWithInteger:[event keyCode]]]; | |
NSLog(@"Active keys: %@", _activeKeys); | |
} | |
} | |
- (void) keyUp:(NSEvent *)event { | |
[_activeKeys removeObject:[NSNumber numberWithInteger:[event keyCode]]]; | |
NSLog(@"Active keys: %@", _activeKeys); | |
} | |
- (NSData*) loadResourceData:(NSString*) name { | |
NSError* error; | |
NSString* resourceName = [[name lastPathComponent] stringByDeletingPathExtension]; | |
NSString* resourceType = [name pathExtension]; | |
NSBundle* bundle = [NSBundle mainBundle]; | |
NSURL* url = [bundle URLForResource:resourceName withExtension:resourceType]; | |
NSData* data = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error]; | |
if (error) { | |
NSLog(@"loadResourceData error: %@", error); | |
} | |
return data; | |
} | |
- (NSString*) loadResourceString:(NSString*) name { | |
return [[NSString alloc] initWithData:[self loadResourceData:name] encoding:NSUTF8StringEncoding]; | |
} | |
- (GLuint) createShader:(GLenum) shaderType | |
withSource:(const char*) source | |
{ | |
GLuint shader = glCreateShader(shaderType); | |
glShaderSource(shader, 1, &source, NULL); | |
glCompileShader(shader); | |
[self checkShaderCompiled:shader]; | |
return shader; | |
} | |
- (GLuint) loadShader:(GLenum) shaderType | |
named:(NSString*) name | |
{ | |
return [self createShader:shaderType withSource:[[self loadResourceString:name] UTF8String]]; | |
} | |
- (void) checkShaderCompiled: (GLuint) shaderIndex | |
{ | |
int params = -1; | |
glGetShaderiv(shaderIndex, GL_COMPILE_STATUS, ¶ms); | |
if (GL_TRUE != params) { | |
NSLog(@"GL shader index %i did not compile", shaderIndex); | |
[self printLogForShader:shaderIndex]; | |
[NSApp terminate:self]; | |
} | |
} | |
- (void) printLogForShader:(GLuint) shaderIndex | |
{ | |
int maxLength = 2048; | |
int actualLength = 0; | |
char log[2048]; | |
glGetShaderInfoLog(shaderIndex, maxLength, &actualLength, log); | |
NSLog(@"Shader info log for shader %i:\n%s", shaderIndex, log); | |
} | |
- (void) checkProgramLinked:(GLuint) program | |
{ | |
int params = -1; | |
glGetProgramiv(program, GL_LINK_STATUS, ¶ms); | |
if (GL_TRUE != params) { | |
NSLog(@"Couldn't link shader programme %u", program); | |
[self printLogForProgram:program]; | |
[NSApp terminate:self]; | |
} | |
} | |
- (void) printLogForProgram:(GLuint) program | |
{ | |
int maxLength = 2048; | |
int actualLength = 0; | |
char log[2048]; | |
glGetProgramInfoLog(program, maxLength, &actualLength, log); | |
NSLog(@"Shader info log for program %i:\n%s", program, log); | |
} | |
- (NSString*) appName { | |
return [[[NSBundle mainBundle] infoDictionary] objectForKey:(id)kCFBundleNameKey]; | |
} | |
- (NSString*) title { | |
return NSStringFromClass([self class]); | |
} | |
- (void) setup | |
{ | |
} | |
- (void) resized:(NSSize) size | |
{ | |
} | |
- (void) renderDisplay | |
{ | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment