1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00
CLK/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m

179 lines
4.8 KiB
Objective-C

//
// CSOpenGLView
// CLK
//
// Created by Thomas Harte on 16/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
#import "CSOpenGLView.h"
@import CoreVideo;
@import GLKit;
@implementation CSOpenGLView {
CVDisplayLinkRef _displayLink;
uint32_t _updateIsOngoing;
BOOL _hasSkipped;
dispatch_queue_t _serialDispatchQueue;
}
- (void)prepareOpenGL
{
// Synchronize buffer swaps with vertical refresh rate
GLint swapInt = 1;
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
// Create a display link capable of being used with all active displays
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
// Set the renderer output callback function
CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self));
// Set the display link for the current renderer
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
// create a serial dispatch queue
_serialDispatchQueue = dispatch_queue_create("OpenGLView", DISPATCH_QUEUE_SERIAL);
// dispatch_set_target_queue(_serialDispatchQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
// set the clear colour
[self.openGLContext makeCurrentContext];
glClearColor(0.0, 0.0, 0.0, 1.0);
// Activate the display link
CVDisplayLinkStart(_displayLink);
}
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
{
CSOpenGLView *const view = (__bridge CSOpenGLView *)displayLinkContext;
[view drawAtTime:now frequency:CVDisplayLinkGetActualOutputVideoRefreshPeriod(displayLink)];
return kCVReturnSuccess;
}
- (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency
{
const uint32_t processingMask = 0x01;
// Always post an -openGLView:didUpdateToTime: if a previous one isn't still ongoing. This is the hook upon which the substantial processing occurs.
if(!OSAtomicTestAndSet(processingMask, &_updateIsOngoing))
{
CVTimeStamp time = *now;
BOOL didSkip = _hasSkipped;
dispatch_async(_serialDispatchQueue, ^{
[self.delegate openGLView:self didUpdateToTime:time didSkipPreviousUpdate:didSkip frequency:frequency];
OSAtomicTestAndClear(processingMask, &_updateIsOngoing);
});
_hasSkipped = NO;
}
else
{
_hasSkipped = YES;
}
// Draw the display now regardless of other activity.
[self drawViewOnlyIfDirty:YES];
}
- (void)invalidate
{
CVDisplayLinkStop(_displayLink);
}
- (void)dealloc
{
// Release the display link
CVDisplayLinkRelease(_displayLink);
}
- (CGSize)backingSize
{
return [self convertSizeToBacking:self.bounds.size];
}
- (void)reshape
{
[super reshape];
[self performWithGLContext:^{
CGSize viewSize = [self backingSize];
glViewport(0, 0, (GLsizei)viewSize.width, (GLsizei)viewSize.height);
}];
}
- (void)awakeFromNib
{
NSOpenGLPixelFormatAttribute attributes[] =
{
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFAMultisample,
NSOpenGLPFASampleBuffers, 1,
NSOpenGLPFASamples, 2,
0
};
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
#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 = pixelFormat;
self.openGLContext = context;
self.wantsBestResolutionOpenGLSurface = YES;
}
- (void)drawRect:(NSRect)dirtyRect
{
[self drawViewOnlyIfDirty:NO];
}
- (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty
{
[self performWithGLContext:^{
[self.delegate openGLView:self drawViewOnlyIfDirty:onlyIfDirty];
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
}];
}
- (void)performWithGLContext:(dispatch_block_t)action
{
CGLLockContext([[self openGLContext] CGLContextObj]);
[self.openGLContext makeCurrentContext];
action();
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
#pragma mark - NSResponder
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (void)keyDown:(NSEvent *)theEvent
{
[self.responderDelegate keyDown:theEvent];
}
- (void)keyUp:(NSEvent *)theEvent
{
[self.responderDelegate keyUp:theEvent];
}
- (void)flagsChanged:(NSEvent *)theEvent
{
[self.responderDelegate flagsChanged:theEvent];
}
@end