Let’s see what are the differences between using the newer MetalKit
framework as opposed to using the earlier Metal
framework. They are both still coexisting, however, MetalKit
introduces a few powerful features such as:
- Easy
texture
loading (even asynchronous loading with a few lines of code). - Efficient data transfer between
Model I/O
meshes andMetal
buffers. MTKView
– a convenientMetal-aware
view (we’ll look at it in more detail).
I will start by reminding you how our first program looked like in chapter 1:
import MetalKit
class MetalView: MTKView {
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
render()
}
func render() {
device = MTLCreateSystemDefaultDevice()
if let rpd = currentRenderPassDescriptor, drawable = currentDrawable {
rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0.5, 0.5, 1.0)
let command_buffer = device!.newCommandQueue().commandBuffer()
let command_encoder = command_buffer.renderCommandEncoderWithDescriptor(rpd)
command_encoder.endEncoding()
command_buffer.presentDrawable(drawable)
command_buffer.commit()
}
}
}
That was it! Simple and elegant way to clear your background color to a custom color of your choice. Now let’s switch to using the Metal
framework which does not have the MTKView
so we will need to subclass NSView
(or UIView
in iOS
) instead.
import Cocoa
class MetalView: NSView {
You will immediately notice a few errors signaling that the following properties are not provided to us by default anymore:
- a device
- a drawable
- a render pass descriptor
Let’s fix that. First, since NSView
is not Metal-aware
we need to create a CAMetalLayer
and tell NSView
to use it as its backing layer. CAMetalLayer
is a Core Animation
layer that manages a pool of textures for rendering its content. To use Metal
for rendering, we need to use this class as the backing layer for our view by returning it from your view’s layerClass() class method.:
override class func layerClass() -> AnyClass {
return CAMetalLayer.self
}
var metalLayer: CAMetalLayer {
return self.layer as! CAMetalLayer
}
Next, inside the render() function create a new device
and tell metalLayer that it owns it, and also set the pixel format the layer will use. Then, create a drawable
. Notice that we are not using a currentDrawable
that was provided with the MTKView
. Rather, CAMetalLayer
provides a nextDrawable for us to use. Finally, create a render pass descriptor. Again, notice that we are not provided with a currentRenderPassDescriptor
either:
let device = MTLCreateSystemDefaultDevice()!
metalLayer.device = device
metalLayer.pixelFormat = .BGRA8Unorm
let drawable = metalLayer.nextDrawable()
let texture = drawable!.texture
let rpd = MTLRenderPassDescriptor()
Before the end of this episode, let’s look at the MTKView class to see once again why this is the preferred way of using Metal
for rendering content in our apps:
@available(OSX 10.11, *)
public class MTKView : NSView, NSCoding {
public init(frame frameRect: CGRect, device: MTLDevice?)
public init(coder: NSCoder)
weak public var delegate: MTKViewDelegate?
public var device: MTLDevice?
public var currentDrawable: CAMetalDrawable? { get }
public var framebufferOnly: Bool
public var presentsWithTransaction: Bool
public var colorPixelFormat: MTLPixelFormat
public var depthStencilPixelFormat: MTLPixelFormat
public var sampleCount: Int
public var clearColor: MTLClearColor
public var clearDepth: Double
public var clearStencil: UInt32
public var depthStencilTexture: MTLTexture? { get }
public var multisampleColorTexture: MTLTexture? { get }
public func releaseDrawables()
public var currentRenderPassDescriptor: MTLRenderPassDescriptor? { get }
public var preferredFramesPerSecond: Int
public var enableSetNeedsDisplay: Bool
public var autoResizeDrawable: Bool
public var drawableSize: CGSize
public var paused: Bool
public func draw()
}
@available(OSX 10.11, *)
public protocol MTKViewDelegate : NSObjectProtocol {
public func mtkView(view: MTKView, drawableSizeWillChange size: CGSize)
public func drawInMTKView(view: MTKView)
}
Among the plethora of properties, notice the ones we were interested in particular: device, currentDrawable and currentRenderPassDescriptor. Also worth mentioning, the class provides a protocol for its MTKViewDelegate property. To read more about each of these properties and functions, see the MTKView reference documentation.
The source code is posted on Github as usual.
Until next time!