Metal Tutorial: Getting Started
In this Metal tutorial, you will learn how to get started with Apple’s 3D graphics API by rendering a simple triangle to the screen. By Andrew Kharchyshyn.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Metal Tutorial: Getting Started
25 mins
- Metal vs. SpriteKit, SceneKit or Unity
- Metal vs. OpenGL ES
- Getting Started
- 1) Creating an MTLDevice
- 2) Creating a CAMetalLayer
- 3) Creating a Vertex Buffer
- 4) Creating a Vertex Shader
- 5) Creating a Fragment Shader
- 6) Creating a Render Pipeline
- 7) Creating a Command Queue
- Rendering the Triangle
- 1) Creating a Display Link
- 2) Creating a Render Pass Descriptor
- 3) Creating a Command Buffer
- 4) Creating a Render Command Encoder
- 5) Committing Your Command Buffer
- Where to Go From Here?
In iOS 8, Apple released its own API for GPU-accelerated 3D graphics: Metal.
Metal is similar to OpenGL ES in that it’s a low-level API for interacting with 3D graphics hardware.
The difference is that Metal is not cross-platform. Instead, it’s designed to be extremely efficient with Apple hardware, offering improved speed and low overhead compared to using OpenGL ES.
In this tutorial, you’ll get hands-on experience using the Metal API to create a bare-bones app: drawing a simple triangle. In doing so, you’ll learn some of the most important classes in Metal, such as devices, command queues and more.
This tutorial is designed so that anyone can go through it, regardless of your 3D graphics background — however, things will move along fairly quickly. If you do have some prior 3D-programming or OpenGL experience, you’ll find things much easier, as many of the same concepts apply to Metal.
Metal vs. SpriteKit, SceneKit or Unity
Before you get started, it’ll be helpful to understand how Metal compares to higher-level frameworks like SpriteKit, SceneKit or Unity.
Metal is a low-level 3D graphics API, similar to OpenGL ES, but with lower overhead meaning better performance. It’s a very thin layer above the GPU, which means that, in doing just about anything, such as rendering a sprite or a 3D model to the screen, it requires you to write all of the code to do this. The trade-off is that you have full power and control.
Conversely, higher-level game frameworks like SpriteKit, SceneKit and Unity are built on top of a lower-level 3D graphics APIs like Metal or OpenGL ES. They provide much of the boilerplate code you normally need to write in a game, such as rendering a sprite or 3D model to the screen.
If all you’re trying to do is make a game, you’ll probably use a higher-level game framework like SpriteKit, SceneKit or Unity most of the time because doing so will make your life much easier. If this sounds like you, we have tons of tutorials to help you get started with Apple Game Frameworks or Unity.
However, there are still two really good reasons to learn Metal:
- Push the hardware to its limits: Since Metal is at such a low level, it allows you to really push the hardware to its limits and have full control over how your game works.
- It’s a great learning experience: Learning Metal teaches you a lot about 3D graphics, writing your own game engine, and how higher-level game frameworks work.
If either of these sound like good reasons to you, keep reading!
Metal vs. OpenGL ES
OpenGL ES is designed to be cross platform. That means you can write C++ OpenGL ES code, and, most of the time, with some small modifications, you can run it on other platforms, such as Android.
Apple realized that, although the cross-platform support of OpenGL ES was nice, it was missing something fundamental to how Apple designs its products: the famous Apple integration of the operating system, hardware and software as a complete package.
So Apple took a clean-room approach to see what it would look like if it were to design a graphics API specifically for Apple hardware with the goal of being extremely low overhead and performant, while supporting the latest and greatest features.
The result is Metal, which can provide up to 10✕ the number of draw calls for your app compared to OpenGL ES. This can result in some amazing effects — you may remember from the Zen Garden example in the WWDC 2014 keynote, as an example.
Time to dig right in and see some Metal code!
Getting Started
Xcode’s iOS game template comes with a Metal option, but you won’t choose that here. This is because you’re going to put together a Metal app almost from scratch, so you can understand every step of the process.
Download the files that you need for this tutorial using the Download Materials button at the top or bottom of this tutorial. Once you have the files, open HelloMetal.xcodeproj in the HelloMetal_starter folder. You’ll see an empty project with a single ViewController.
There are seven steps required to set up Metal so that you can begin rendering. You need to create a:
- MTLDevice
- CAMetalLayer
- Vertex Buffer
- Vertex Shader
- Fragment Shader
- Render Pipeline
- Command Queue
Going through them one at a time.
1) Creating an MTLDevice
You’ll first need to get a reference to an MTLDevice
.
Think of MTLDevice
as your direct connection to the GPU. You’ll create all the other Metal objects you need (like command queues, buffers and textures) using this MTLDevice
.
To do this, open ViewController.swift and add this import to the top of the file:
import Metal
This imports the Metal framework so that you can use Metal classes such as MTLDevice
inside this file.
Next, add this property to the ViewController
:
var device: MTLDevice!
You’re going to initialize this property in viewDidLoad()
rather than in an initializer, so it has to be an optional. Since you know you’re definitely going to initialize it before you use it, you mark it as an implicitly unwrapped optional, for convenience purposes.
Finally, add viewDidLoad()
and initialize the device property, like this:
override func viewDidLoad() {
super.viewDidLoad()
device = MTLCreateSystemDefaultDevice()
}
MTLCreateSystemDefaultDevice
returns a references to the default MTLDevice
your code should use.
2) Creating a CAMetalLayer
In iOS, everything you see on screen is backed by a CALayer
. There are subclasses of CALayers
for different effects, such as gradient layers, shape layers, replicator layers and more.
If you want to draw something on the screen with Metal, you need to use a special subclass of CALayer
called CAMetalLayer
. You’ll add one of these to your view controller.
First, add this new property to the class:
var metalLayer: CAMetalLayer!
This will store a handy reference to your new layer.
Next, add this code to the end of viewDidLoad()
:
metalLayer = CAMetalLayer() // 1
metalLayer.device = device // 2
metalLayer.pixelFormat = .bgra8Unorm // 3
metalLayer.framebufferOnly = true // 4
metalLayer.frame = view.layer.frame // 5
view.layer.addSublayer(metalLayer) // 6
Going over this line by line:
- Create a new
CAMetalLayer
. - You must specify the
MTLDevice
the layer should use. You simply set this to the device you obtained earlier. - Set the pixel format to
bgra8Unorm
, which is a fancy way of saying “8 bytes for Blue, Green, Red and Alpha, in that order — with normalized values between 0 and 1.” This is one of only two possible formats to use for aCAMetalLayer
, so normally you’d just leave this as-is. - Apple encourages you to set
framebufferOnly
totrue
for performance reasons unless you need to sample from the textures generated for this layer, or if you need to enable compute kernels on the layer drawable texture. Most of the time, you don’t need to do this. - You set the frame of the layer to match the frame of the view.
- Finally, you add the layer as a sublayer of the view’s main layer.