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?
7) Creating a Command Queue
The final one-time-setup step that you need to do is to create an MTLCommandQueue
.
Think of this as an ordered list of commands that you tell the GPU to execute, one at a time.
To create a command queue, simply add a new property:
var commandQueue: MTLCommandQueue!
Then, add the following line at the end of viewDidLoad()
:
commandQueue = device.makeCommandQueue()
Congrats — your one-time setup code is done!
Rendering the Triangle
Now, it’s time to move on to the code that executes each frame — to render the triangle!
This is done in five steps:
- Create a Display Link
- Create a Render Pass Descriptor
- Create a Command Buffer
- Create a Render Command Encoder
- Commit your Command Buffer
1) Creating a Display Link
You need a way to redraw the screen every time the device screen refreshes.
CADisplayLink
is a timer synchronized to the displays refresh rate. The perfect tool for the job! To use it, add a new property to the class:
var timer: CADisplayLink!
Initialize it at the end of viewDidLoad()
as follows:
timer = CADisplayLink(target: self, selector: #selector(gameloop))
timer.add(to: RunLoop.main, forMode: .default)
This sets up your code to call a method named gameloop()
every time the screen refreshes.
Finally, add these stub methods to the class:
func render() {
// TODO
}
@objc func gameloop() {
autoreleasepool {
self.render()
}
}
Here, gameloop()
simply calls render()
each frame, which, right now, just has an empty implementation. Time to flesh this out.
2) Creating a Render Pass Descriptor
The next step is to create an MTLRenderPassDescriptor
, which is an object that configures which texture is being rendered to, what the clear color is and a bit of other configuration.
Add these lines inside render()
, in place of // TODO
:
guard let drawable = metalLayer?.nextDrawable() else { return }
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(
red: 0.0,
green: 104.0/255.0,
blue: 55.0/255.0,
alpha: 1.0)
First, you call nextDrawable()
on the Metal layer you created earlier, which returns the texture in which you need to draw in order for something to appear on the screen.
Next, you configure the render pass descriptor to use that texture. You set the load action to Clear, which means “set the texture to the clear color before doing any drawing,” and you set the clear color to the green color used on the site.
3) Creating a Command Buffer
The next step is to create a command buffer. Think of this as the list of render commands that you wish to execute for this frame. The cool thing is that nothing actually happens until you commit the command buffer, giving you fine-grained control over when things occur.
Creating a command buffer is easy. Simply add this line to the end of render()
:
let commandBuffer = commandQueue.makeCommandBuffer()!
A command buffer contains one or more render commands. You’ll create one of these next.
4) Creating a Render Command Encoder
To create a render command, you use a helper object called a render command encoder. To try this out, add these lines to the end of render()
:
let renderEncoder = commandBuffer
.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
renderEncoder
.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3, instanceCount: 1)
renderEncoder.endEncoding()
Here, you create a command encoder and specify the pipeline and vertex buffer that you created earlier.
The most important part is the call to drawPrimitives(type:vertexStart:vertexCount:instanceCount:)
. Here, you’re telling the GPU to draw a set of triangles, based on the vertex buffer. To keep things simple, you are only drawing one. The method arguments tell Metal that each triangle consists of three vertices, starting at index 0 inside the vertex buffer, and there is one triangle total.
When you’re done, you simply call endEncoding()
.
5) Committing Your Command Buffer
The final step is to commit the command buffer. Add these lines to the end of render()
:
commandBuffer.present(drawable)
commandBuffer.commit()
The first line is needed to make sure that the GPU presents the new texture as soon as the drawing completes. Then you commit the transaction to send the task to the GPU.
Phew! That was a ton of code, but, at long last, you are done! Build and run the app and bask in your triangular glory:
Where to Go From Here?
The final project for this tutorial is in the tutorial materials bundle using the Download Materials button at the top or bottom of this tutorial.
You have learned a ton about the Metal API! You now have an understanding of some of the most important concepts in Metal, such as shaders, devices, command buffers, pipelines and more.
Also, be sure to check out some great resources from Apple:
- Apple’s Metal for Developers page, with tons of links to documentation, videos and sample code
- Apple’s Metal Programming Guide
- Apple’s Metal Shading Language Guide
- The Metal videos from WWDC 2014
You also might enjoy the Beginning Metal course on our site, where we explain these same concepts in video form, but with even more detail.
Or you can dive into books: Check out our Metal by Tutorials book.
I hope you enjoyed this tutorial, and if you have any comments or questions, please join the forum discussion below!