Create a gist now

Instantly share code, notes, and snippets.

@recp /GLView.h
Last active Mar 4, 2018

Embed
What would you like to do?
Cocoa: Custom OpenGL View
/*
* Copyright (c), Recep Aslantas. All rights reserved.
*/
#import <Cocoa/Cocoa.h>
@protocol GLViewDelegate;
@interface GLView : NSView {
NSOpenGLContext * m_openGLContext;
NSOpenGLPixelFormat * m_pixelFormat;
CVDisplayLinkRef m_displayLink;
const CVTimeStamp * m_lasOutputTime;
}
+ (NSOpenGLPixelFormat *)defaultPixelFormat;
- (id)initWithFrame:(NSRect)frameRect
pixelFormat:(NSOpenGLPixelFormat *)format;
- (void)setOpenGLContext:(NSOpenGLContext *)context;
- (NSOpenGLContext *)openGLContext;
- (void)setPixelFormat:(NSOpenGLPixelFormat *)pixelFormat;
- (NSOpenGLPixelFormat *)pixelFormat;
- (void)clearGLContext;
- (void)syncWithCurrentDisplay;
- (void)update;
- (void)reshape;
- (void)start;
- (void)stop;
@property (nonatomic, assign) BOOL started;
@property (nonatomic, assign) id<GLViewDelegate> delegate;
@end
@protocol GLViewDelegate <NSObject>
@required
- (void)render;
- (void)reshape;
@end
/*
* Copyright (c), Recep Aslantas. All rights reserved.
*/
#import "GLView.h"
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl3.h>
@interface GLView()
@end
@implementation GLView
CVReturn
display_link_cb(CVDisplayLinkRef CV_NONNULL displayLink,
const CVTimeStamp * CV_NONNULL inNow,
const CVTimeStamp * CV_NONNULL inOutputTime,
CVOptionFlags flagsIn,
CVOptionFlags * CV_NONNULL flagsOut,
void * CV_NULLABLE displayLinkContext) {
/* TODO: get rid of this */
dispatch_sync(dispatch_get_main_queue(), ^{
[(__bridge GLView *)displayLinkContext renderOnce];
});
return kCVReturnSuccess;
}
- (void) initDetails {
NSNotificationCenter *ntfcenter;
[self setWantsBestResolutionOpenGLSurface: YES];
[self setPostsFrameChangedNotifications: YES];
ntfcenter = [NSNotificationCenter defaultCenter];
[ntfcenter addObserver: self
selector: @selector(_surfaceNeedsUpdate:)
name: NSViewGlobalFrameDidChangeNotification
object: self];
[ntfcenter addObserver: self
selector: @selector(_surfaceNeedsUpdate:)
name: NSViewFrameDidChangeNotification
object: self];
[self syncWithCurrentDisplay];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder: coder];
if (self) {
if ([coder allowsKeyedCoding]) {
id pformat = [coder decodeObjectForKey: @"NSPixelFormat"];
if (!pformat)
pformat = [[self class] defaultPixelFormat];
[self setPixelFormat: pformat];
} else {
[self setPixelFormat: [[self class] defaultPixelFormat]];
}
[self initDetails];
}
return self;
}
- (instancetype)initWithFrame:(NSRect)frameRect {
return [self initWithFrame: self.frame
pixelFormat: [[self class] defaultPixelFormat]];
}
- (instancetype)initWithFrame:(NSRect)frameRect
pixelFormat:(NSOpenGLPixelFormat *)format {
self = [super initWithFrame:frameRect];
if (self) {
[self setPixelFormat: format];
[self initDetails];
}
return self;
}
- (BOOL)isFlipped {
return NO;
}
- (BOOL)mouseDownCanMoveWindow {
return NO;
}
- (void) _surfaceNeedsUpdate:(NSNotification *)notification {
[[self openGLContext] makeCurrentContext];
[self update];
[self reshape];
}
- (void)lockFocus {
NSOpenGLContext *context;
[super lockFocus];
context = [self openGLContext];
if ([context view] != self) {
[context setView: self];
}
}
- (void) viewDidMoveToWindow {
[super viewDidMoveToWindow];
if (!self.window) {
[[self openGLContext] clearDrawable];
return;
}
[self.openGLContext setView: self];
}
#pragma mark -
+ (NSOpenGLPixelFormat *)defaultPixelFormat {
static NSOpenGLPixelFormat *pixelFormat;
if (pixelFormat)
return pixelFormat;
#define PixelFormatAttrib(...) __VA_ARGS__
NSOpenGLPixelFormatAttribute attribs[] = {
PixelFormatAttrib(NSOpenGLPFADoubleBuffer),
PixelFormatAttrib(NSOpenGLPFAAccelerated),
PixelFormatAttrib(NSOpenGLPFABackingStore, YES),
PixelFormatAttrib(NSOpenGLPFAColorSize, 24),
PixelFormatAttrib(NSOpenGLPFADepthSize, 24),
PixelFormatAttrib(NSOpenGLPFAAlphaSize, 8),
PixelFormatAttrib(NSOpenGLPFAOpenGLProfile),
PixelFormatAttrib(NSOpenGLProfileVersion3_2Core),
0
};
#undef PixelFormatAttrib
pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs];
return pixelFormat;
}
- (NSOpenGLContext *)openGLContext {
if (!m_openGLContext) {
m_openGLContext = [[NSOpenGLContext alloc] initWithFormat: m_pixelFormat
shareContext: nil];
[self setOpenGLContext: m_openGLContext];
}
return m_openGLContext;
}
- (void)setOpenGLContext:(NSOpenGLContext *)context {
if (context != m_openGLContext) {
[self clearGLContext];
m_openGLContext = context;
[m_openGLContext setView: self];
}
}
- (NSOpenGLPixelFormat *)pixelFormat {
return m_pixelFormat;
}
- (void)setPixelFormat:(NSOpenGLPixelFormat *)pixelFormat {
m_pixelFormat = pixelFormat;
}
- (void)clearGLContext {
if (m_openGLContext) {
[m_openGLContext clearDrawable];
}
}
/* https://developer.apple.com/library/mac/qa/qa1385/_index.html */
- (void)syncWithCurrentDisplay {
NSOpenGLContext *openGLContext;
CGLContextObj cglContext;
CGLPixelFormatObj cglPixelFormat;
GLint swapInt;
openGLContext = [self openGLContext];
/* Synchronize buffer swaps with vertical refresh rate */
swapInt = 1;
[openGLContext setValues: &swapInt
forParameter: NSOpenGLCPSwapInterval];
/* Create a display link capable of being used with all active displays */
CVDisplayLinkCreateWithActiveCGDisplays(&m_displayLink);
/* Set the renderer output callback function */
CVDisplayLinkSetOutputCallback(m_displayLink,
display_link_cb,
(__bridge void *)self);
/* Set the display link for the current renderer */
cglContext = [openGLContext CGLContextObj];
cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(m_displayLink,
cglContext,
cglPixelFormat);
}
- (void)update {
NSOpenGLContext *context;
context = [self openGLContext];
[context makeCurrentContext];
/* because display link is threaded */
CGLLockContext([context CGLContextObj]);
[context update];
CGLUnlockContext([context CGLContextObj]);
}
- (void) renderOnce {
NSOpenGLContext *context;
context = [self openGLContext];
[context makeCurrentContext];
/* because display link is threaded */
CGLLockContext([context CGLContextObj]);
[[self delegate] render];
[context flushBuffer];
CGLUnlockContext([context CGLContextObj]);
}
- (void) reshape {
[self update];
[self.delegate reshape];
if (self.started)
[self renderOnce];
}
#pragma mark -
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect: dirtyRect]; /* TODO: */
if (self.started)
[self renderOnce];
}
- (void) start {
if (!self.started) {
CVDisplayLinkStart(m_displayLink);
self.started = YES;
}
}
- (void) stop {
CVDisplayLinkStop(m_displayLink);
self.started = NO;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver: self];
[NSOpenGLContext clearCurrentContext];
[self clearGLContext];
CVDisplayLinkStop(m_displayLink);
CVDisplayLinkRelease(m_displayLink);
m_openGLContext = nil;
m_pixelFormat = nil;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment