├── SwiftOpenGL ├── SwiftOpenGL-Bridging-Header.h ├── CVDisplayLInkCallbackFunction.h ├── CVDisplayLInkCallbackFunction.m ├── AppDelegate.swift ├── SwiftOpenGLView.swift └── SwiftOpenGLView_Swift_3_0.swift └── README.md /SwiftOpenGL/SwiftOpenGL-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | 6 | #import "CVDisplayLInkCallbackFunction.h" 7 | -------------------------------------------------------------------------------- /SwiftOpenGL/CVDisplayLInkCallbackFunction.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVDisplayLInkCallbackFunction.h 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 2/15/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | @import QuartzCore.CVDisplayLink; 11 | 12 | 13 | @interface CVDisplayLinkCallbackFunction : NSObject 14 | 15 | CVDisplayLinkOutputCallback CVDLCallbackFunctionPointer(); 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SwiftOpenGL/CVDisplayLInkCallbackFunction.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVDisplayLInkCallbackFunction.m 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 2/15/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | 9 | #import "CVDisplayLinkCallbackFunction.h" 10 | #import "SwiftOpenGL-Swift.h" 11 | 12 | 13 | @implementation CVDisplayLinkCallbackFunction 14 | 15 | 16 | CVDisplayLinkOutputCallback CVDLCallbackFunctionPointer() 17 | { 18 | return CVDLCallbackFunction; 19 | } 20 | 21 | 22 | CVReturn CVDLCallbackFunction( CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext ) 23 | { 24 | CVReturn result = [(__bridge SwiftOpenGLView*)displayLinkContext getFrameForTime:inOutputTime]; 25 | 26 | return result; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /SwiftOpenGL/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 2/15/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import QuartzCore.CVDisplayLink 11 | 12 | @NSApplicationMain 13 | class AppDelegate: NSObject, NSApplicationDelegate { 14 | 15 | 16 | 17 | func applicationDidFinishLaunching(aNotification: NSNotification) { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | func applicationWillTerminate(aNotification: NSNotification) { 22 | // Insert code here to tear down your application 23 | 24 | // Grab the current window in our app, and from that grab the subviews of the attached viewController 25 | // Cycle through that array to get our SwiftOpenGLView instance 26 | 27 | let windowController = NSApplication.sharedApplication().mainWindow?.windowController() as? NSWindowController 28 | let views = windowController?.contentViewController?.view.subviews as [NSView] 29 | for view in views { 30 | if let aView = view as? SwiftOpenGLView { 31 | println("Checking if CVDisplayLink is running") 32 | if let running = CVDisplayLinkIsRunning(aView.displayLink) as Boolean? { 33 | println("Stopping CVDisplayLink") 34 | let result = CVDisplayLinkStop(aView.displayLink) 35 | if result == kCVReturnSuccess.value { println("CVDisplayLink stopped\n\tCode: \(result)") } 36 | } 37 | } 38 | } 39 | } 40 | 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftOpenGL 2 | 3 | Swift implementation of an OpenGL content view driven by CVDisplayLinkimplement 4 | 5 | PROJECT_FEATURES: 6 | Swift except for CVDisplayLinkCallback function (bridged from Obj-C Class) 7 | CVDisplayLink driven rendering 8 | Swift OpenGL view subclassed from NSOpenGLView 9 | Thought process provided in each file 10 | 11 | I have tried to make a Swift project that utilizes CVDisplayLink for driving an OpenGL rendering loop for some time. 12 | I tried to look for examples of how this could be done, but I was unsuccessful for a very long timem. Only recently, 13 | did I find the example listed below by Jonas Jongejan. Even having seen that file, I feel the it is too long and 14 | devoid of comments to be instantly useful to the reader. Considering the mass of code, and my inexperience as a 15 | programmer, I was only able to evalute the file for a specific need. Namely, using CVTimeStamp to calculate the app's 16 | current frames per second. 17 | 18 | Therein lies the purpose of this example project: to develop a simple app with a documented process of thought. In 19 | doing so, I hope future programmers will understand how to use one of Apple's "mysterious" C API's with Swift (and 20 | minimal use of Objective-C and C). 21 | 22 | It is my hope that in the future, Swift will adopt the functions necessary to work with these C API's without the need 23 | to bridge over into Objective-C. It's a new language after all, and Swift 1.2 has already shown some great development. 24 | 25 | REFERENCES: These links were of great help to me while developing this small project file. 26 | 27 | SWIFT_GRAMMAR_THEORY 28 | Swift: Generating keys, and Encrypting and Decrypting text 29 | Blog post that provided a great deal of insight into Swift's UnsafePointer, UnsafeMutablePointer, and Unmanaged 30 | http://netsplit.com/swift-generating-keys-and-encrypting-and-decrypting-text 31 | 32 | STACK_OVERFLOW_THREADS 33 | Answer to: Understanding typedefs for function pointers in C: Examples, hints and tips, please by Johnathan Leffler 34 | Very lengthy explanation of typedef, most of it was followable, but it gets a little convoluted towards the end. I just the following StackOverflow answer for clarification. 35 | http://stackoverflow.com/a/1591492/3928158 36 | Answer to: How to make a function return a pointer to a function?(C++) by Rutger Nijlunsing 37 | To better understand the use of the CVDisplayLinkCallback typedef and how to better implement it 38 | http://stackoverflow.com/a/997852/3928158 39 | 40 | CODE_EXAMPLES 41 | Random web examples using CVTimeStamp to calculate the frames per second 42 | Blog post: "Take Candle" on 14th May 2012 43 | http://www.takecandle.com/wheels-in-motion.html 44 | Code example by Jonas Jongejan on 11/27/09 45 | http://ofxcocoaplugins.googlecode.com/svn-history/r172/trunk/src/OpenGL/PluginOutputView.mm 46 | -------------------------------------------------------------------------------- /SwiftOpenGL/SwiftOpenGLView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 2/15/15. 6 | // Copyright (c) 2015 MyKo. All rights reserved. 7 | // 8 | 9 | // I am assumming the use of OpenGL 3.1 or greater 10 | // If you are using a function from OpenGL 2.1 or lower, import OpenGL.GL 11 | 12 | import Cocoa 13 | import OpenGL.GL3 14 | import QuartzCore.CVDisplayLink 15 | 16 | 17 | @objc class SwiftOpenGLView: NSOpenGLView { 18 | var displayLink: CVDisplayLink? 19 | 20 | required init?(coder: NSCoder) { 21 | 22 | // CVDisplayLinkCreateActiveCGDisplays() takes an UnsafeMutablePointer?> 23 | // UnsafeMutablePointer can be thought of as an inout parameter--pass the address of your argument (&argument) 24 | // Unmanaged? is an optional, and thus indicates the parameter will potentially be nil 25 | // (The function's CVReturn will indicate if the provided argument for the parameter was filled or nil) 26 | // Unmanaged indicates the C API does't tell the compiler if the argument was retained/unretained (it's non-ARC) 27 | // Finally, we declare our type (T) which is CVDisplayLink 28 | // 29 | // Knowing this, we declare a var of type Unmanaged?. Note that we do not need to specify it as an 30 | // UnsafeMutablePointer (that is only important for the function declaration) 31 | // 32 | // We initialize the CVDisplayLink? var in our class by retrieving the value from the temporary pointer with .takeRetainedValue() 33 | // .takeRetainedValue() returns the value from an Unmanaged reference and destroy's the reference (displayLink is initialized) 34 | 35 | var displayLinkPointer: Unmanaged? 36 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLinkPointer) 37 | displayLink = displayLinkPointer?.takeRetainedValue() 38 | 39 | super.init(coder: coder) 40 | 41 | // some OpenGL setup 42 | // NSOpenGLPixelFormatAttribute is a typealias for UInt32 in Swift, cast each attribute 43 | // Set the view's PixelFormat and Context to the custom pixelFormat and context 44 | 45 | let attributes: [NSOpenGLPixelFormatAttribute] = [ 46 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAAccelerated), 47 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAColorSize), 32, 48 | NSOpenGLPixelFormatAttribute(NSOpenGLPFADoubleBuffer), 49 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAOpenGLProfile), 50 | NSOpenGLPixelFormatAttribute(NSOpenGLProfileVersion3_2Core), 51 | 0 52 | ] 53 | let pixelFormat = NSOpenGLPixelFormat(attributes: attributes) 54 | self.pixelFormat = pixelFormat 55 | 56 | let context = NSOpenGLContext(format: pixelFormat, shareContext: nil) 57 | self.openGLContext = context 58 | 59 | // Set the swaping interval parameter on the context, setValues:forParameter: is expecting multiple values--use an array 60 | // In Swift, context parameters are accessed though the NSOpenGLContextParameter enum, use dot syntax to access the swap interval 61 | 62 | self.openGLContext.setValues([1], forParameter: .GLSwapInterval) 63 | 64 | // CVDLCallbackFunctionPointer() is a C function declared in CVDisplayLinkCallbackFunction.h 65 | // It returns a pointer to our callback: CVDisplayLinkOutputCallback 66 | // The third parameter takes an UnsafeMutablePointer and our argument needs to be our view (ie self) 67 | // We have already stated this type of parameter requires the address of operator '&' 68 | // We can't use'&' on out object, but we can still access the pointer using unsafeAddressOf() 69 | // However, this address/pointer can't be passed as is--you have to cast to UnsafeMutablePointer (where T is our class) 70 | // To se the current display from our OpenGL context, we retrieve the pixelFormat and context as CoreGraphicsLayer objects 71 | // Start the CVDisplayLink, note that we need to stop the displayLink when we are done --> done in APPDELEGATE.SWIFT!!! 72 | 73 | CVDisplayLinkSetOutputCallback(displayLink!, CVDLCallbackFunctionPointer(), UnsafeMutablePointer(unsafeAddressOf(self))) 74 | let cglPixelFormat = self.pixelFormat?.CGLPixelFormatObj 75 | let cglContext = self.openGLContext.CGLContextObj 76 | CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink!, cglContext, cglPixelFormat!) 77 | CVDisplayLinkStart(displayLink!) 78 | } 79 | 80 | // Called by the callback function to ask our model to render out a frame for our context 81 | // We have to cast from an UnsafePointer to an UnsafeMutablePointer 82 | 83 | func getFrameForTime(outputTime: UnsafePointer)->CVReturn { 84 | CVDisplayLinkGetCurrentTime(displayLink!, UnsafeMutablePointer(outputTime)) 85 | 86 | // For development purpose, calculate the frames per second using the CVTimeStamp passed to the callback function 87 | // CVTimeStamp is a C struct with several members that are accessed by going straight to their memory location with .memory 88 | // 'command' + 'click' on CVTimeStamp to see the struct's definition 89 | 90 | let fps = (outputTime.memory.rateScalar * Double(outputTime.memory.videoTimeScale) / Double(outputTime.memory.videoRefreshPeriod)) 91 | println("FPS:\t \(fps)") 92 | 93 | // It's time to draw, request the rendered frame 94 | 95 | let result = drawView() 96 | 97 | return result 98 | } 99 | 100 | override func prepareOpenGL() { 101 | // Setup OpenGL 102 | 103 | glClearColor(0.0, 0.0, 0.0, 1.0) 104 | 105 | // Run a test render 106 | 107 | _ = drawView() 108 | } 109 | 110 | override func drawRect(dirtyRect: NSRect) { 111 | _ = drawView() 112 | } 113 | 114 | func drawView() -> CVReturn { 115 | // Grab a context from our view and make it current for drawing into 116 | // CVDisplayLink uses a separate thread, lock focus or our context for thread safety 117 | 118 | guard let context = self.openGLContext else { 119 | print("Context could not be acquired.") 120 | return kCVReturnError 121 | } 122 | 123 | context.makeCurrentContext() 124 | context.lock() 125 | 126 | // Clear the context, set up the OpenGL shader program(s), call drawing commands 127 | // OpenGL targets and such are UInt32's, cast them before sending in the OpenGL function 128 | 129 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 130 | 131 | // We're using a double buffer, call CGLFlushDrawable() to swap the buffer 132 | // We're done drawing, unlock the context before moving on 133 | 134 | context.flushBuffer() 135 | context.unlock() 136 | 137 | return kCVReturnSuccess 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /SwiftOpenGL/SwiftOpenGLView_Swift_3_0.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOpenGLView_Swift_3_0.swift 3 | // SwiftOpenGL 4 | // 5 | // Created by Myles La Verne Schultz on 1/11/17. 6 | // Copyright © 2017 MyKo. All rights reserved. 7 | // 8 | // This file is an update to the previous SwiftOpenGLView used 9 | // to display animated content using the CVDisplayLink. This 10 | // version uses Swift 3.0 without the need for a bridging 11 | // header for the CVDisplayLinkCallback function. An 12 | // explanation of the CVTimeStamp is also provided. 13 | // 14 | 15 | import Cocoa 16 | import OpenGL.GL3 17 | 18 | 19 | final class SwiftOpenGLView: NSOpenGLView { 20 | 21 | // A CVDisplayLink for animating. 22 | fileprivate var displayLink: CVDisplayLink? 23 | 24 | // The current time, used to produce varying values to change background color 25 | fileprivate var currentTime = 0.0 26 | 27 | required init?(coder: NSCoder) { 28 | super.init(coder: coder) 29 | 30 | let attrs: [NSOpenGLPixelFormatAttribute] = [ 31 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAAccelerated), 32 | NSOpenGLPixelFormatAttribute(NSOpenGLPFADoubleBuffer), 33 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAColorSize), 32, 34 | NSOpenGLPixelFormatAttribute(NSOpenGLPFAOpenGLProfile), NSOpenGLPixelFormatAttribute(NSOpenGLProfileVersion3_2Core), 35 | 0 36 | ] 37 | guard let pixelFormat = NSOpenGLPixelFormat(attributes: attrs) else { 38 | print("Pixel format could not be constructed.") 39 | return nil 40 | } 41 | self.pixelFormat = pixelFormat 42 | guard let context = NSOpenGLContext(format: pixelFormat, share: nil) else { 43 | print("Context could not be constructed.") 44 | return nil 45 | } 46 | self.openGLContext = context 47 | 48 | // Set the context's swap interval parameter to 60Hz (i.e. 1 frame per swamp) 49 | self.openGLContext?.setValues([1], for: .swapInterval) 50 | 51 | } 52 | 53 | override func prepareOpenGL() { 54 | 55 | super.prepareOpenGL() 56 | 57 | glClearColor(0.0, 0.0, 0.0, 1.0) 58 | 59 | // ** ** ** ** ** ** ** ** ** // 60 | // Setup OpenGL pipline here // 61 | // ** ** ** ** ** ** ** ** ** // 62 | 63 | /* Now that the OpenGL pipeline is defined, declare a callback for our CVDisplayLink. 64 | There are three ways to do this: declare a function, declare a computed property, 65 | or declare/pass a closure. Using each requires subtle changes in the 66 | CVDisplayLinkSetOutputCallback()'s argument list. We shall declare a local 67 | closure of type CVDisplayLinkOutputCallback. 68 | */ 69 | let displayLinkOutputCallback: CVDisplayLinkOutputCallback = {(displayLink: CVDisplayLink, inNow: UnsafePointer, inOutputTime: UnsafePointer, flagsIn: CVOptionFlags, flagsOut: UnsafeMutablePointer, displayLinkContext: UnsafeMutableRawPointer?) -> CVReturn in 70 | 71 | /* It's prudent to also have a brief discussion about the CVTimeStamp. 72 | CVTimeStamp has five properties. Three of the five are very useful 73 | for keeping track of the current time, calculating delta time, the 74 | frame number, and the number of frames per second. The utility of 75 | each property is not terribly obvious from just reading the names 76 | or the descriptions in the Developer dcumentation and has been a 77 | mystery to many a developer. Thankfully, CaptainRedmuff on 78 | StackOverflow asked a question that provided the equation that 79 | calculates frames per second. From that equation, we can 80 | extrapolate the value of each field. 81 | 82 | @hostTime = current time in Units of the "root". Yeah, I don't know. 83 | The key to this field is to understand that it is in nanoseconds 84 | (e.g. 1/1_000_000_000 of a second) not units. To convert it to 85 | seconds divide by 1_000_000_000. Dividing by videoRefreshPeriod 86 | and videoTimeScale in a calculation for frames per second yields 87 | the appropriate number of frames. This works as a result of 88 | proportionality--dividing seconds by seconds. Note that dividing 89 | by videoTimeScale to get the time in seconds does not work like it 90 | does for videoTime. 91 | 92 | framesPerSecond: 93 | (videoTime / videoRefreshPeriod) / (videoTime / videoTimeScale) = 59 94 | and 95 | (hostTime / videoRefreshPeriod) / (hostTime / videoTimeScale) = 59 96 | but 97 | hostTime * videoTimeScale ≠ seconds, but Units = seconds * (Units / seconds) = Units 98 | 99 | @rateScalar = ratio of "rate of device in CVTimeStamp/unitOfTime" to 100 | the "Nominal Rate". I think the "Nominal Rate" is 101 | videoRefreshPeriod, but unfortunately, the documentation doesn't 102 | just say videoRefreshPeriod is the Nominal rate and then define 103 | what that means. Regardless, because this is a ratio, and the fact 104 | that we know the value of one of the parts (e.g. Units/frame), we 105 | then know that the "rate of the device" is frame/Units (the units of 106 | measure need to cancel out for the ratio to be a ratio). This 107 | makes sense in that rateScalar's definition tells us the rate is 108 | "measured by timeStamps". Since there is a frame for every 109 | timeStamp, the rate of the device equals CVTimeStamp/Unit or 110 | frame/Unit. Thus, 111 | 112 | rateScalar = frame/Units : Units/frame 113 | 114 | @videoTime = the time the frame was created since computer started up. 115 | If you turn your computer off and then turn it back on, this timer 116 | returns to zero. The timer is paused when you put your computer to 117 | sleep. This value is in Units not seconds. To get the number of 118 | seconds this value represents, you have to apply videoTimeScale. 119 | 120 | @videoRefreshPeriod = the number of Units per frame (i.e. Units/frame) 121 | This is useful in calculating the frame number or frames per second. 122 | The documentation calls this the "nominal update period" and I am 123 | pretty sure that is quivalent to the aforementioned "nominal rate". 124 | Unfortunately, the documetation mixes naming conventions and this 125 | inconsistency creates confusion. 126 | 127 | frame = videoTime / videoRefreshPeriod 128 | 129 | @videoTimeScale = Units/second, used to convert videoTime into seconds 130 | and may also be used with videoRefreshPeriod to calculate the expected 131 | framesPerSecond. I say expected, because videoTimeScale and 132 | videoRefreshPeriod don't change while videoTime does change. Thus, 133 | to calculate fps in the case of system slow down, one would need to 134 | use videoTime with videoTimeScale to calculate the actual fps value. 135 | 136 | seconds = videoTime / videoTimeScale 137 | 138 | framesPerSecondConstant = videoTimeScale / videoRefreshPeriod (this value does not change if their is system slowdown) 139 | 140 | USE CASE 1: Time in DD:HH:mm:ss using hostTime 141 | let rootTotalSeconds = inNow.pointee.hostTime 142 | let rootDays = inNow.pointee.hostTime / (1_000_000_000 * 60 * 60 * 24) % 365 143 | let rootHours = inNow.pointee.hostTime / (1_000_000_000 * 60 * 60) % 24 144 | let rootMinutes = inNow.pointee.hostTime / (1_000_000_000 * 60) % 60 145 | let rootSeconds = inNow.pointee.hostTime / 1_000_000_000 % 60 146 | Swift.print("rootTotalSeconds: \(rootTotalSeconds) rootDays: \(rootDays) rootHours: \(rootHours) rootMinutes: \(rootMinutes) rootSeconds: \(rootSeconds)") 147 | 148 | USE CASE 2: Time in DD:HH:mm:ss using videoTime 149 | let totalSeconds = inNow.pointee.videoTime / Int64(inNow.pointee.videoTimeScale) 150 | let days = (totalSeconds / (60 * 60 * 24)) % 365 151 | let hours = (totalSeconds / (60 * 60)) % 24 152 | let minutes = (totalSeconds / 60) % 60 153 | let seconds = totalSeconds % 60 154 | Swift.print("totalSeconds: \(totalSeconds) Days: \(days) Hours: \(hours) Minutes: \(minutes) Seconds: \(seconds)") 155 | 156 | Swift.print("fps: \(Double(inNow.pointee.videoTimeScale) / Double(inNow.pointee.videoRefreshPeriod)) seconds: \(Double(inNow.pointee.videoTime) / Double(inNow.pointee.videoTimeScale))") 157 | */ 158 | 159 | /* The displayLinkContext in CVDisplayLinkOutputCallback's parameter list is the 160 | view being driven by the CVDisplayLink. In order to use the context as an 161 | instance of SwiftOpenGLView (which has our drawView() method) we need to use 162 | unsafeBitCast() to cast this context to a SwiftOpenGLView. 163 | */ 164 | 165 | let view = unsafeBitCast(displayLinkContext, to: SwiftOpenGLView.self) 166 | // Capture the current time in the currentTime property. 167 | view.currentTime = inNow.pointee.videoTime / Int64(inNow.pointee.videoTimeScale) 168 | let result = view.drawView() 169 | 170 | // We are going to assume that everything went well, and success as the CVReturn 171 | return result 172 | } 173 | 174 | /* Grab the a link to the active displays, set the callback defined above, and start 175 | the link. An alternative to a nested function is a global function or a closure 176 | passed as the argument--a local function (i.e. a function defined within the 177 | class) is NOT allowed. The 178 | UnsafeMutableRawPointer(unmanaged.passUnretained(self).toOpaque()) passes a 179 | pointer to an instance of SwiftOpenGLView. UnsafeMutableRawPointer is a new type 180 | Swift 3.0 that does not require type definition at its creation. For greater 181 | detail place the Swift Evolution notes at https://github.com/apple/swift-evolution/blob/master/proposals/0107-unsaferawpointer.md 182 | */ 183 | CVDisplayLinkCreateWithActiveCGDisplays(&displayLink) 184 | CVDisplayLinkSetOutputCallback(displayLink!, displayLinkOutputCallback, UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())) 185 | CVDisplayLinkStart(displayLink!) 186 | 187 | // Test render 188 | _ = drawView() 189 | } 190 | 191 | override func draw(_ dirtyRect: NSRect) { 192 | // Drawing code here. 193 | // This call is not entirely necessary as the view is already 194 | // set to draw with every screen refresh. Were we to have 195 | // used the view's display() function, then this object's 196 | // draw(_:) would actually be called and this our drawView() 197 | // within it. As it is now, it's not based on our implementation. 198 | drawView() 199 | } 200 | 201 | fileprivate func drawView() -> CVReturn { 202 | // Grab a context, make it the active context for drawing, and then lock the focus 203 | // before making OpenGL calls that change state or data within objects. 204 | guard let context = self.openGLContext else { 205 | // Just a filler error 206 | Swift.print("oops") 207 | return kCVReturnError 208 | } 209 | 210 | context.makeCurrentContext() 211 | context.lock() 212 | 213 | value = sin(currentTime) 214 | glClearColor(value, value, value, 1.0) 215 | 216 | glClear(GLbitfield(GL_COLOR_BUFFER_BIT)) 217 | 218 | // glFlush() is replaced with CGLFlushDrawable() and swaps the buffer being displayed 219 | context.flushBuffer() 220 | context.unlock() 221 | 222 | return kCVReturnSuccess 223 | } 224 | 225 | deinit { 226 | // Stop the display link. A better place to stop the link is in 227 | // the viewController or windowController within functions such as 228 | // windowWillClose(_:) 229 | CVDisplayLinkStop(displayLink!) 230 | } 231 | 232 | } 233 | --------------------------------------------------------------------------------