Swift 3 and Metal
Nov 17, 2016
As a follow-up to the previous post, perhaps it’s also helpful to explain the easiest way to draw to a Cocoa view using Metal rather than OpenGL1. And as a bonus, I also explain how to select OpenGL or Metal rendering on the fly.
Apple has never been diligent in keeping up with new OpenGL versions (or the latest graphics cards for that matter), but the situation has reached a pretty bad point. Even the newest hardware supports only six-year-old OpenGL 4.1. By comparison, Metal is under constant development and has tons of new features announced every WWDC. But perhaps for the same reason there’s a dearth of documentation and good, introductory level and up-to-date examples2.
Now, to be honest, I’d prefer to use something open and cross-platform like Vulkan, but it’s still a no-show on macOS (and I don’t expect it to change in the future). Or I could switch to Windows. Just kidding. I’d of course switch to Linux instead.
Drawing to an MTKView
By far the easiest way to draw to a Cocoa view is to subclass
MTKView, part of MetalKit and available from iOS 9 and El Capitan onwards. This is similar to
NSOpenGLView for OpenGL, and it means that 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 Metal device, MTKView properties, command queue, render pipeline, shaders and depth stencil there. The same goes if you need textures or other stencils.
Finally, create the
draw method, which will be called either automatically every frame, automatically with a view notification or manually, depending on how you set up
Selecting Metal or OpenGL rendering on the fly
If you’re excited about the performance of Metal (like me) but want to support Mac hardware older than 2012 (like me), you probably want to select between custom subclasses of
NSOpenGLView on the fly. In that way, you can probably throw away the OpenGL code in a few years without major code rewrites.
For me, the first step was to isolate all OpenGL- and Metal-specific code into these two subclasses. After that, create a simple placeholder NSView in Interface Builder and kept a reference to it (here
view). Now, when your application loads you can do something like this:
Note a few things. If you want to support the newest Metal features as of 20163, you want to test for Sierra support. My application crashes on 10.11 even on Metal-compatible hardware4, so I’ve forced OpenGL on 10.11 or lower. The easiest way to test for Metal support is to check the return of
Also, substituting a view on the fly is tricky business. Substituting only the view will not have any effect, so you need to remove it from its parent view (in this case
splitView, an instance of
NSSplitView) and re-add your subclass of
Finally, for this to work you need to use different initialisers in your
NSOpenGLView subclasses. These should receive the view’s frame and other variables rather than an instance of
Why is Apple not putting clear version numbers?! ↩
As does Apple’s own Adopting Metal Sample code… ↩