Swift 3 and OpenGL
Nov 5, 2016
Learning Swift has long been in my to-do list. So, when I recently undertook the project to write a CityGML viewer for Mac (to be released soon), I decided the time to learn Swift was now. Fortuitously, Apple had just released Swift 3, and since I like shiny new things, I decided to learn Swift 3 directly.
However, I soon realised that Apple’s documentation is very spotty. Despite the new look of the documentation pages, almost all sample code and examples still use Objective-C. Moreover, interacting with low-level C APIs like OpenGL is more than a little complex, requiring several conversions and the use of pointer-like data types.
Thus, I hope to document a few of the issues that stumped me and how I solved them. In this post, I will explain how to draw OpenGL to an NSOpenGLView or to a custom NSView. For more info, see Apple’s documentation (which uses Objective-C). Heads up to this Stack Overflow question and answers for how this works with Swift 2.
Using NSOpenGLView
The easiest way to draw OpenGL is to subclass NSOpenGLView
. In this way, most things are managed for you automatically. So, you probably need to set up a class like this:
Afterwards, create the init?()
method and set up the OpenGL pixel format, context and swap interval there.
Then, create the prepareOpenGL()
method and its callback. This will be called before the first render and is meant to initialise the state of OpenGL. In this example, we set up CVDisplayLink
so that a callback function is called to render every new frame.
Note the pointer types UnsafePointer
, UnsafeMutablePointer
and UnsafeMutableRawPointer
, and how unsafeBitCast
is used in order to convert the UnsafeMutableRawPointer
to an instance of the current class. This is necessary in order to call any methods.
The renderFrame()
method is called by the callback function in the CVDisplayLink
. There, you can use any OpenGL drawing functions.
The draw method should call renderFrame()
as well.
Finally, the deinit()
method should stop the display link.
Drawing to a custom NSView
Sometimes, NSOpenGLView is not good enough. For instance, NSOpenGLView doesn’t support context sharing, which is a problem if you want to create full screen applications. In those cases, you need to subclass NSView
instead. Apple provides this piece of sample code for that purpose, but it is also written in Objective-C and way out of date.
Subclassing NSView
is similar to the previous case. However, you need to keep your own NSOpenGLPixelFormat
and NSOpenGLContext
.
The init?
method can then be pretty much as before, but it is necessary to make it call you own method to initialise OpenGL (setupDisplayLink()
) and the display link. If you want context sharing, you probably want to create another init?
method that receives the context to share. Make sure to call makeCurrentContext()
on the OpenGL context.
The overriden methods to lock the focus and to draw the view should set the view of the context to itself.
Finally, the method to draw things (as called by draw()
or by the display link callback) should do as before, but also make sure to set the current OpenGL context.