Article

Developing Shaders and Resolving Shader Issues with the Shader Debugger

Step through shader execution with the ability to inspect variable values and update shader code in place.

Overview

The Metal shader debugger brings common debugger features to your shader development like stepping through execution, variable inspection, and the ability to edit code and update the results live. The variable views expose issues as they occur by allowing you to visually confirm the results of each statement. Combining that with the ability to step through execution, like loops, can easily show you how well your algorithm is working. With the Update Shaders feature, you can change your shader source live and quickly see the results of your changes.

Start the Shader Debugger

The shader debugger requires source file information to be included in the built .metallib. To do that, enable the Produce debugging information build setting by giving it a value of Yes, include source code. By default, this setting is "Yes" for the Debug build and "No" for Release.

Use the shader debugger within a captured Metal frame. Most commonly, you capture a Metal frame by clicking the camera button on Xcode's debug bar as covered in Performing a GPU Capture from the Debug Bar.

From the captured frame, display the call history using the steps hightlighted in Figure 1:

  1. Enable the Debug navigator.

  2. Choose View Frame By Call.

Figure 1

Viewing the frame by call.

On the left, Xcode's left sidebar with the Debug navigator annotated. In the center, the "View frame in different ways" selection menu is active with a choice of "View Frame By Call".

Open a Vertex Function for Debugging

To debug a vertex function, use the highlighted steps in Figure 2:

  1. Expand your command buffer in the call list.

  2. Filter the call list by function name by entering the text "draw" in the filter field.

  3. Select the draw call that shades the primitive you want to debug.

  4. Double-click Post Vertex Transform to open the geometry viewer.

Figure 2

Opening the geometry viewer to debug a vertex function.

On the left, the Debug pane is chosen with the View Frame By Call viewing option. On the right, the Bound Resources are shown with Post Vertex Transform selected.

The geometry viewer displays in Xcode's center pane, as seen in Figure 3. To open the shader debugger for this vertex function, do the steps:

  1. Select a primitive to debug.

  2. Click Debug.

Figure 3

Debugging a vertex primitive.

In the center pane, the geometry viewer displays the vertices included in the draw call with a selected primitive annotated. On the bottom-right, the Debug button is annotated.

The vertex function is then shown in Xcode's center pane with the parameter and variable displays populated with the values of the selected primitive. The Region of Interest (ROI), highlighted at left in Figure 4, contains all of the vertex function's statements, and the debugger has its execution playhead set to the function's entry point.

Figure 4

Controlling the execution playhead with the ROI list.

The shader debugger populated with the vertex values of the selected primitive.

Open a Fragment Function for Debugging

To debug a fragment function, use the highlighted steps in Figure 5:

  1. Expand your command buffer in the call list.

  2. Filter the call list by function name by entering the text "draw" in the filter field.

  3. Expand the draw call that runs the fragment function you want to debug.

  4. Select the draw's Attachments.

Figure 5

Selecting the draw's attachments.

On the left, the call list with Attachments selected for the draw that runs the fragment function you want to debug. On the right, the attachments are displayed with the state defined at the time of the capture.

The draw call's attachments show in Xcode's center pane. To open the shader debugger for this fragment function, do the steps highlighted in Figure 6:

  1. Click Inspect Pixels.

  2. Click an area inside one of the attachments and pan the mouse to the fragment you want to debug. Optionally, use the arrow keys to nudge the magnifying glass in single-pixel increments.

  3. Click Debug to open the fragment function.

Figure 6

Choosing a fragment to debug.

Diagram showing the draws attachments with the Inspect Pixels button enabled. A magnifying glass hovers the attachments to focus on the single fragment you want to debug.

The fragment function is shown in Xcode's center pane as seen in Figure 7. The parameter and variable displays are populated with the values of the selected fragment, at right in Figure 7. The ROI, highlighted at left, contains the fragment function's statements, and the debugger has its execution playhead set to the function's entry point.

Figure 7

A fragment function open in the shader debugger.

The shader debugger populated with the current values of the fragment selected earlier.

Step Through the Code in the Debugger

To step through code in the debugger, follow the steps, and as annotated in Figure 8:

  1. Select a statement in the ROI list.

  2. The execution playhead moves to the corresponding line in Xcode's center pane.

Figure 8

Stepping in the debugger.

On the left, the ROI list is selected at a specific statement, and on the right, the execution playhead highlights the corresponding line in the shader code.

Inside of the ROI list, use the arrow keys to move control location:

Table 1

Controlling debugger stepping with arrow keys.

Arrow Key

Stepping Direction

Down Arrow

Step forward

Right Arrow

Step in

Left Arrow

Step out

Up Arrow

Step backward

A statement's source must be present in the file to support stepping in, as illustrated with the float3x3 function in Figure 8. Unlike a conventional debugger, the shader debugger allows you to step backward in execution or even select an arbitrary statement. Changing the location of the execution playhead can affect the values of variables displayed onscreen because they represent the state of the frame's data at that point in time. This is particularly helpful when stepping through loop iterations in shader source.

Inspect Variable Properties

To inspect a shader's variables, follow the steps, and as annotated in Figure 9:

  1. Click the gray block to the right of a variable at right in Figure 9.

  2. See the variable inspection panel shown below the corresponding statement in the shader source view.

Figure 9

Inspecting variable values in the shader debugger.

On the right, a variable's gray block is checked, and in the center, the variable inspection panel shows detailed information about the variable.

You can expand nested properties in the variable inspection panel, as seen in Figure 10. Nested properties of the uniforms argument are displayed: the columns of both the projectionMatrix and modelViewMatrix.

Figure 10

Inspecting nested properties in the variable values view.

Screenshot showing the expanded view of a variable's nested properties.

Watch a variable change over time by expanding the inspection panel across its subsequent modifications. Figure 11 highlights how visualizing over time can identify problems that could otherwise go undetected by simply viewing code or numeric variable values:

  1. Expand a color sample to see its texture.

  2. Expand the variables view for the color sample's next modification to see how it's changed.

Figure 11

Inspecting two statements relating to the same variable.

Two variable inspection panels are expanded to illustrate an obvious visual change from before and after the modifying statement executed.

The numeric value [0, 0, 0, 0] is a clear indication that you have a blank color sample, but the visualization of variable textures can be helpful in cases where the numeric data isn't as immediately telling of the cause of an issue.

Update Shaders Live

After making a change to a shader you can apply those updates live using the Update Shaders button highlighted in Figure 12:

Figure 12

Updating shaders.

Screenshot annotating the Update Shaders button. It's in the bottom right corner of the shader source in the center pane.

The Update Shaders button applies the source changes you make to the same captured Metal frame. Updating the shader:

  • Redraws the application window.

  • Variable views update to show their new values.

  • Redraws Attachments in the Assistant Editor.

Because Updating Shaders maintains your place in the captured Metal frame, you can easily make successive changes to shader source for either debugging or development purposes.

See Also

Debugging

Inspecting Vertices with the Geometry Viewer

Find problems with geometry by navigating a free-fly camera outside of your camera's frustum and checking vertex values.