Validating your app’s Metal shader usage
Catch common shader runtime issues using Shader Validation.
Overview
Metal Shader Validation detects errors only discoverable during shader execution, such as accesses to non-resident resources, out-of-bounds memory accesses, undefined behavior, and attempts to access nil textures.
Examples of issues Shader Validation can detect in Metal apps include:
Issue | Behavior that causes it |
|---|---|
Resource not resident | Using resources from shaders, or Metal 4 commands, that are not present in any residency set associated with the command buffer or queue, nor in |
Out-of-bounds memory access | Loading or storing data outside the bounds of a buffer you pass from the host to the GPU, or accessing incorrect indices, slices, or the rank of tensor objects. |
Misuse of Metal Performance Primitives | Mistakes involving tensor ranks, alignment, strides, planes, or out-of-bounds accesses. |
Undefined interpolant behavior | Storing |
Null resources usage | Accessing a null texture or buffer. |
Illegal address space cast | Casting a pointer in the generic address space to an incorrect specific address space. |
Binding of incorrect texture type | The shader is expecting a texture type different from the one the app is passing. Note: starting in GPU family |
Binding of incorrect acceleration structure type | The shader expects an instancing acceleration structure and the app binds a primitive acceleration structure, or vice versa. |
Usage flag mismatch | In Metal versions prior to Metal 4, using a texture from a shader in a manner inconsistent with the usage parameter you pass to |
You can enable Shader Validation using the runtime diagnostics options in Xcode and visualizing issues in the Xcode UI, or by using environment variables and printing its results to the app’s standard error or log stream.
To ensure you see the most up-to-date debug information, set your app’s deployment target to the matching OS version, even if only temporarily. You can change the deployment target in the Xcode project settings. If you change the deployment target temporarily, remember to change it back before deploying your app.
For more information, see the WWDC20 video Debug GPU-side errors in Metal and the WWDC21 video Discover Metal debugging, profiling, and asset creation tools.
Enable Shader Validation in Xcode
Follow these steps to enable Shader Validation using the runtime diagnostics options in the Scheme settings:
In the Xcode toolbar, choose Edit Scheme from the Scheme menu. Alternatively, choose Product > Scheme > Edit Scheme.
In the Edit Scheme dialog, select Run.
Click the Diagnostics tab.
Select Shader Validation to enable it, and click Close.
Xcode enables Shader Validation each time you run your scheme.
[Image]
Customize Shader Validation options
You can customize Shader Validation behaviors from the Diagnostics tab:
Select Raise Runtime Issues on Error to log errors Shader Validation detects in the Issue navigator. When you specify this option, you can also create a shader breakpoint.
Choose Abort on Error to cause Shader Validation to stop the program when it logs an error. Use in situations where repeated GPU restarts affect system responsiveness.
Enable Log Allocation Stacktraces to track CPU stacktraces where your app allocates Metal resources. Use this option to obtain the CPU allocation stacktrace of the resource involved in an error. This option increases memory usage.
Choose Detect GPU Stack Overflow to check for GPU stack overflow caused by indirect function calls and recursion.
Selectively enable Shader Validation
When enabling Shader Validation, you can also choose to only enable (or disable) Shader Validation for specific pipelines. This advanced control can be particularly useful when you want to focus your debugging on specific pipelines of interest. It can also greatly improve the performance of the apps you debug, due to the reduced amount of instrumented pipelines.
Shader Validation instruments all pipelines by default (MTL_SHADER_VALIDATION_DEFAULT_STATE=all). To change this behavior, you can set MTL_SHADER_VALIDATION_DEFAULT_STATE=none.
Next, you can set MTL_SHADER_VALIDATION_ENABLE_PIPELINES and MTL_SHADER_VALIDATION_DISABLE_PIPELINES to selectively enable and disable instrumentation for given pipelines. You can use the pipeline labels and Shader Validation unique identifiers (UIDs) as entries (see Print pipeline UIDs). Multiple entries need to be comma-separated, without spaces (see man MetalValidation for more information). In the following example, the pipelines with the label foo are the only pipelines not instrumented by Shader Validation.
> export MTL_SHADER_VALIDATION=1
> export MTL_SHADER_VALIDATION_DEFAULT_STATE=all
> export MTL_SHADER_VALIDATION_DISABLE_PIPELINES="foo"
...
> ./<application>Alternatively, you can programmatically set your pipeline descriptor property shaderValidation to either MTLShaderValidationEnabled or MTLShaderValidationDisabled.
In the following example, pipe is the only pipeline instrumented by Shader Validation.
> export MTL_SHADER_VALIDATION=1
> export MTL_SHADER_VALIDATION_DEFAULT_STATE=none
> ...
> ./<application>Finally, you can query the Shader Validation state of a pipeline through the shaderValidation property of pipeline state objects.
Print pipeline UIDs
Shader Validation generates UIDs for all pipelines you process, which you can use as an entry to MTL_SHADER_VALIDATION_ENABLE_PIPELINES and MTL_SHADER_VALIDATION_DISABLE_PIPELINES. This is useful when your app has no pipeline labels.
To print the UIDs to Console or a log stream instance, set MTL_SHADER_VALIDATION_DUMP_PIPELINES=1 in your terminal or Xcode Environment Variables Scheme settings.
[Image]
View Shader Validation errors in Xcode
After enabling Shader Validation, if Metal encounters errors while executing the commands in a command buffer, Xcode displays the error details in the source editor as shown below:
[Image]
You can find the breakpoint in the Breakpoint navigator if you want to modify or remove it in the future. For more information, see Setting breakpoints to pause your running app.
[Image]
If you discover an error in your shader, consider taking a capture and investigating with the shader debugger (see Investigating visual artifacts).
View Shader Validation errors in the terminal
You can enable Shader Validation for any Metal app via environment variables, even when you don’t have access to its source.
By default, Shader Validation logs any issues it finds to the OS log. You can view these directly in the terminal, using the log stream command:
log stream -process <appname>
You can also configure Shader Validation to copy its messages to the app’s standard error stream by setting environment variables MTL_SHADER_VALIDATION=1 and MTL_SHADER_VALIDATION_REPORT_TO_STDERR=1.
Enable Shader Validation with environment variables
You can also enable Shader Validation and customize its behavior by setting the following environment variables on your Metal app:
MTL_SHADER_VALIDATION=1Enables all Shader Validation tests.
MTL_SHADER_VALIDATION_ENABLE_ERROR_REPORTING=1Enables Shader Validation error reporting.
MTL_SHADER_VALIDATION_REPORT_TO_STDERR=1Prints Shader Validation messages to the standard error stream.
MTL_SHADER_VALIDATION_ABORT_ON_FAULT=1Causes Shader Validation to stop the program when it logs an error. Use in situations where repeated GPU restarts affect system responsiveness.
MTL_SHADER_VALIDATION_COMPILER_INLININGDetermines the amount of code inlining that occurs. Possible values are
defaultandfull. Setting the value tofullforces inlining. Increasing inlining can result in improved runtime performance at the cost of compile time performance. Decreasing inlining can result in improved compile time performance at the cost of runtime performance.- MTL_SHADER_VALIDATION_FAIL_MODE
Sets the behavior for handling invalid accesses. Possible values are
zerofill(default) andallow.zerofillcauses invalid reads to return0, and drops any invalid writes.allowallows an invalid read or write, but can result in command buffer failure, depending on the platform. It also reduces compile and runtime performance impact.- MTL_SHADER_VALIDATION_GLOBAL_MEMORY=1
Checks all global memory accesses. Accessing invalid memory follows the behavior that
MTL_SHADER_VALIDATION_FAIL_MODEspecifies.- MTL_SHADER_VALIDATION_THREADGROUP_MEMORY=1
Checks all threadgroup memory accesses. Accessing invalid memory follows the behavior that
MTL_SHADER_VALIDATION_FAIL_MODEspecifies.MTL_SHADER_VALIDATION_TEXTURE_USAGE=1Checks all texture member functions, such as
read,write,get_width, and so on. Metal honors your setting forMTL_SHADER_VALIDATION_FAIL_MODEwhen an app triggers an invalid texture operation, including accessing aniltexture instance, a valid but nonresident texture instance, a resident texture instance that’s a type that doesn’t match the shader’s signature, or a resident texture instance that doesn’t have an appropriate MTLResourceUsage configuration from one of the resource usage methods of an MTLComputeCommandEncoder or MTLRenderCommandEncoder instance (see Argument buffer resource preparation commands).MTL_SHADER_VALIDATION_STACK_OVERFLOW=1Checks all indirect calls (calls by function pointer, visible functions, intersection functions, and dynamic libraries), as well as recursive calls. If the call stack depth for such functions exceeds the value in
maxCallStackDepthfor that stage, an error occurs and the system skips the function call.MTL_SHADER_VALIDATION_TENSOR_VALIDATION=1Checks tensor operations for invalid arguments. If this value is set to any non-zero value, shader validation instruments all tensors.
MTL_SHADER_VALIDATION_FAIL_MODEdetermines the result of accessing invalid memory. Defaults to1. Set to0to disable.MTL_SHADER_VALIDATION_GENERIC_ADDRESS_SPACE=1Checks correctness of static casts of pointer types from generic address spaces to specific address spaces. Defaults to
1.MTL_SHADER_VALIDATION_NAN_INF=1Checks whether the vertex stage of a render pipeline state object writes
INForNaNinto any interpolants. WritingINForNaNinto interpolants, other than the one with the[[position]]attribute, results in undefined GPU behavior. Defaults to1. Set to0to disable.
For a complete list of settings, run man MetalValidation in Terminal.
If you discover an error in your shader, consider taking a capture (see Capturing a Metal workload programmatically) and investigating with the Metal debugger (see Debugging the shaders within a draw command or compute dispatch).
Review Metal Shader Validation constraints
Because Metal Shader Validation relies on live shader instrumentation, it’s incompatible with Metal Binary Archives.
Additionally, to use indirect command buffers with Shader Validation, enable pipeline and buffer inheritance.
Metal Shader Validation doesn’t track residency of pages backing Metal sparse resources.
See Also
Runtime diagnostics
Inspecting live resources at runtimeValidating your app’s Metal API usageMonitoring your Metal app’s graphics performanceCustomizing the Metal Performance HUDUnderstanding the Metal Performance HUD metricsGaining performance insights with the Metal Performance HUDGenerating performance reports with the Metal Performance HUD