Metal Performance Shaders for the iPad playground

As many of you might have seen at WWDC 2016, the new Playground app for the iPad was a really big hit! As an already playgrounds lover, for me it was even more than that. Now we are able to easily write Swift code on the iPad and run it with just a button tap. Today we are going to have fun with Metal Performance Shaders (MPS) since I have not discussed about them before, and also because it is so easy to use them on mobile devices. This reminds me to warn you, the MPS framework only works on iOS and tvOS devices. We will look into a handy way of writing the playground on a macOS device and then sharing it with your mobile device through the iCloud Drive. Also, the iPad playgrounds are working only on iOS 10 or newer.

To start, let’s create a new iOS playground in Xcode. You can add any image you like under the Resources folder. I already added one named nature.jpg. Next, write a few lines of code in the main playground page, like in this screenshot (the code is available on Github):

alt text

Let’s go over the code. We have been writing most of this code over and over in the previous blog posts. The first new addition you will notice immediately is also generating an error, and that is because macOS does not “know” about the MPS framework. Once we load this playground on an iPad, the error will go away:

import MetalPerformanceShaders

Next, we use MTKTextureLoader to create a new texture from the image we added above. Now comes the really interesting part! Once we created our MTLCommandBuffer object, we are not going to create a MTLCommandEncoder object too from this command buffer as we were used to do. Rather, we create a new MPSImageGaussianBlur object as in the code below:

let shader = MPSImageGaussianBlur(device: view.device!, sigma: 5)
shader.encode(commandBuffer: commandBuffer, sourceTexture: texIn, destinationTexture: texOut)

What’s great about the MPS objects is that they let you apply a compute shader (kernel function) to an input texture without us even having to configure any states, descriptors, pipelines or even write a kernel function, ever! The MPS object takes care of everything for us. Of course, by taking this approach we are somewhat limited to only picking a preset shader and possibly changing a parameter such as sigma, for this particular shader.

So far so good! We are now ready to send our playground to the iPad through the iCloud Drive. Open a Finder window, click on iCloud Drive and copy your playground into this folder:

alt text

We are finally getting to use the iPad! Open the new Playgrounds app that you downloaded from the App Store and go to My Playgrounds. In the top left corner of the screen, tap on the + button. As you can see you can also create a new playground on the iPad and then easily export it to your macOS devices or share it using various other tools. For now, however, we are going to tap on iCloud Drive as seen below:

alt text

When the iCloud Drive window pops up, notice that we have access to the iPad’s private folder for playgrounds as well, however, we now want to import the one we were working on, MPS.playground, so tap on it:

alt text

As soon as the iPad loaded the playground, we’re in business! You can see the main playground page now on your iPad:

alt text

All you have to do now is tap on Run My Code and see our image with a nice blur filter applied to it:

alt text

“Ok,” you might say, “but how do we see the entire image?” The answer is in the animated GIF below. It’s as easy as long pressing in the middle of the screen until a screen separating line shows. With the finger still on the screen, drag the line to either the left side or the right side, depending on what do you need to see, your code or your output image (you might want to reload the page as the animated GIF may only run once before it stops):

alt text

Now you can see that blurred image entirely! And when you’re done admiring the image, tap that handy button on the left side (and midway vertically) of the screen to go back to split screen. Before we wrap it up, one more piece of awesomeness. Replace the line below:

let shader = MPSImageGaussianBlur(device: view.device!, sigma: 5)

with this line:

let shader = MPSImageSobel(device: device)

Check out the new output image:

alt text

Imagine the possibilities you have here by only changing one line of code! There are a couple dozen different shaders that you can try. Check out the Metal Performance Shaders API for more details. If you are interested in image processing, you might want to also check Simon Gladman’s Core Image for Swift book. If you want to learn more about the Metal backend of MPS, also check out Warren Moore’s Metal by Example book. The source code for the playground in this article is posted on Github as usual.

Until next time!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s