When you develop complex software, you inevitably run into bugs and performance issues. Though Xcode and Instruments can provide deep detail about what's going on, often some basic information is enough. In this episode I'm going to cover simple yet powerful tools that Xcode provides for Metal diagnostics.
I assume that you compile your Metal files at the same time as your app. You also can do that at runtime, but it depends on the task and its nuances.
So, go to Metal Compiler - Build Options section and set there
Produce Debugging Information to Yes, include source code for Debug (it should be by default) - we'll need that in further episodes, but just check.Math Mode to Safe. It turns off aggressive assumptions and approximations and forces precise math. Use it for computations that require high precision.Ignore Warnings to No. Never ignore warnings - they often point to real problems.Treat Warnings as Errors to Yes. It can be annoying, but it keeps your code much cleaner and more stable.
These settings surface issues such as:
Because GPU debugging is far less flexible than CPU debugging, you should avoid fragile code as much as possible. Catching issues during editing and compile time makes future development much easier.
Open the scheme Run settings and go to the Diagnostics tab. At the bottom you'll find the Metal group:
API ValidationShader ValidationShow Graphics OverviewLog Graphics Overview
This tool catches issues on the Metal API layer. It shows each issue and where it occurred, which can be invaluable when your code is complex, you're still learning, or you're simply tired and miss something.
Example 1: Wrong offset alignment. If you set the wrong alignment, the API validator catches it, points to the precise line, and explains the problem.

Example 2: Access encoder after endEncoding. In a complex call hierarchy you might pass an encoder around and forget that it's already been ended. The validator lets you know when that happens.

Example 3: Missing binding. It's easy to forget to bind data for your shader or kernel. The validator highlights that as well.

There are many more checks (index mismatches, binding the wrong resource to the wrong slot, and so on), but the goal here is to cover the principles rather than build a complete reference.
This validator checks for state and memory errors such as out-of-bounds access or dereferencing a nil texture. Because the GPU side is notoriously difficult to debug, the validator is invaluable. I highly recommend keeping it enabled whenever you're developing the Metal layer of your app.
Example 4: Access violation. A bad index value or missing bounds check? The validator points you to the exact line in your shader where it failed.

Example 5: Nil texture. Passing a nil texture is technically valid, but it yields a zero sampling result, which isn't great. The validator logs the issue and points to the code where it happened, so you can trace it back on the CPU side.

This tool shows an overlay with nearly the same data you see in Xcode's Debug tab:

NOTE: This overlay works only when you run your app from Xcode.
This option outputs the same performance metrics to the console. You can feed that raw data into your own tooling if you need to.

Although the tool looks primitive, it still provides helpful information: not just FPS, but also the CPU versus GPU time. That makes it easier to estimate where performance issues originate.
