Article

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 code live and quickly see the results of your changes.

Set Up Your Project to Enable the Shader Debugger

To use the shader debugger on your project, set up the .metallib file to allow for debugging:

  1. In Xcode, navigate to your project's build settings.

  2. For the Debug build configuration, set "Produce debugging information" to "Yes, include source code."

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. For more ways to capture a Metal frame, see Metal GPU Capture.

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 draw in the filter field.

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

  4. Double-click Geometry 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 Geometry selected.

The geometry viewer is displayed in Xcode's center pane, as seen in Figure 3. To open the shader debugger for this vertex function, use these 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 draw in the filter field.

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

  4. Select Attachments for the draw.

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 move 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. Click to expand the ROI, highlighted at left, to list the fragment function's statements. In the center pane, observe the debugger's 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

Step through code in the debugger using the following 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 through code 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 code must be present in the file to support stepping in, as illustrated with the float3x3 function in . 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 code.

Inspect Variable Properties

To inspect a shader's variables, follow the steps 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 code 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 if you simply review 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 values [0, 0, 0, 0] are 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 the update 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 code changes you make to the same captured Metal frame. Updating the shader:

  • Redraws the application window.

  • Updates variable views 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 code 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.