Debugging Vulkan shaders, especially compute shaders, can be very difficult to do even with the aid of a powerful debugging tool like RenderDoc. Debug Printf is a recent Vulkan feature that allows developers to debug their shaders by inserting Debug Print statements. This feature is now supported within RenderDoc in a way that allows for per-invocation inspection of values in a shader. This article describes how to instrument your GLSL or HLSL shaders with Debug Printf and how to inspect and debug with them in RenderDoc, using vkconfig, or with environment variables.
We suggest to use Vulkan Configurator (VkConfig) to enable Debug Printf
For those who “just need to quick use it” there is a VK_LAYER_PRINTF_ONLY_PRESET environment variable that will turn on Debug Printf and turn off all of the other validation logic.
# Windows set VK_LAYER_PRINTF_ONLY_PRESET=1 # Linux export VK_LAYER_PRINTF_ONLY_PRESET=1 # Android adb shell setprop debug.vulkan.khronos_validation.printf_only_preset=1
The following are the various Debug Printf settings (listed as environment variables, but work like all other settings).
All settings also found in
VkConfig
VK_LAYER_PRINTF_ENABLE will turn on Debug Printf alongside the other validationVK_LAYER_PRINTF_ENABLE=1 turn onVK_LAYER_PRINTF_TO_STDOUT will print to stdout instead of the normal Debug CallbackVK_LAYER_PRINTF_TO_STDOUT=1 turn onVK_LAYER_PRINTF_VERBOSE will print extra information (pipeline, shader, command, etc)VK_LAYER_PRINTF_VERBOSE=1 turn onVK_LAYER_PRINTF_BUFFER_SIZE size of the buffer used to store Printf messages (buffer is shared across all calls in a single vkQueueSubmit).set VK_LAYER_PRINTF_BUFFER_SIZE=4096 (example of making it larger)To use Debug Printf in GLSL shaders, you need to enable the GL_EXT_debug_printf extension. Then add debugPrintfEXT() calls at the locations in your shader where you want to print messages and/or values Here is a very simple example (Try Online):
#version 450 #extension GL_EXT_debug_printf : enable void main() { float myfloat = 3.1415f; debugPrintfEXT("My float is %f", myfloat); }
glslang will automatically add the Debug Printf instructions
In HLSL and Slang, Debug Printf can be invoked as follows (Try Online):
void main() { float myfloat = 3.1415; printf("My float is %f", myfloat); }
Both dxc and slangc will automatically add the Debug Printf instructions
If you print every time a shader is executed you can easily get millions of things trying to print. It is recommended to use built-ins to limit what is printed
// Vertex Shader if (gl_VertexIndex == 0) { debugPrintfEXT("Only print for a single vertex shader invocation\n"); } // Fragment Shader if (gl_FragCoord.x > 0.0 && gl_FragCoord.x < 0.1 && gl_FragCoord.y > 0.0 && gl_FragCoord.y < 0.1) { debugPrintfEXT("Only print for a few fragment shader invocation\n"); } // Compute Shader if (gl_LocalInvocationIndex == 0) { debugPrintfEXT("Only print for a single compute invocation\n"); }
Debug Printf error message are returned as VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
For your custom callback, you can look for 0x4fe1fef9 in VkDebugUtilsMessengerCallbackDataEXT::messageIdNumber as the magic hash if it is a Debug Printf message
The Validation Layers will try to turn on info level messages when using Debug Printf so the message is found
The VkDebugUtilsMessengerCallbackDataEXT::pMessage will contain the location and on a newline print out the error message such as:
vkQueueSubmit(): pSubmits[0] Debug Printf:
x == 100
The format string for this implementation of debug printf is more restricted than the traditional printf format string.
Format for specifier is “%”precision <d, i, o, u, x, X, a, A, e, E, f, F, g, G, ul, lu, or lx>
Format for vector specifier is “%”precision“v” [2, 3, or 4] [specifiers list above]
Format for pointers (PhysicalStorageBuffer) is “%p”
For example:
float myfloat = 3.1415f; vec4 floatvec = vec4(1.2f, 2.2f, 3.2f, 4.2f); uint64_t bigvar = 0x2000000000000001ul;
debugPrintfEXT("Here's a float value to 2 decimals %1.2f", myfloat); Would print “Here's a float value to 2 decimals 3.14”
debugPrintfEXT("Here's a vector of floats %1.2v4f", floatvec); Would print “Here's a vector of floats 1.20, 2.20, 3.20, 4.20”
debugPrintfEXT("Unsigned long as decimal %lu and as hex 0x%lx", bigvar, bigvar); Would print “Unsigned long as decimal 2305843009213693953 and as hex 0x2000000000000001”
As of RenderDoc release 1.14, Debug Printf statements can be added to shaders, and debug printf messages will be received and logged in the Event Browser window.
Using the debugmarker sample from Sascha Willems' Vulkan samples repository:
Capture a frame:
Edit the shader:
#extension GL_EXT_debug_printf : enable to beginning of shaderdebugPrintfEXT("Position = %v4f", pos); to shader after pos definitionThe vkCmdDrawIndexed in question now has 51 messages.
Normally, developers will use a high-level language like HLSL or GLSL to generate SPIR-V. However, in some cases, developers may wish to insert Debug Printfs directly into SPIR-V
To execute Debug Printf in a SPIR-V shader, a developer will need the following two instructions specified:
OpExtension "SPV_KHR_non_semantic_info" %N0 = OpExtInstImport NonSemantic.DebugPrintf
Debug Printf operations can then be specified in any function with the following instruction: %NN = OpExtInst %void %N0 1 %N1 %N2 %N3 ... where:
N0 is the result id of the OpExtInstImport NonSemantic.DebugPrintf1 is the opcode of the Debug Printf instruction in NonSemantic.DebugPrintfN1 is the result of an OpString containing the format for the Debug PrintfN2, N3, ... are result ids of scalar and vector values to be printedNN is the result id of the Debug Printf operation. This value is undefined.
OpExtInstImportof anyNonSemantic*is properly supported with theVK_KHR_shader_non_semantic_infodevice extension. Some older compiler stacks might not handle these unknown instructions well, some will ignore it as desired.
Here's an example of adding a Debug Printf statement to the shader in the vkcube demo (from the Vulkan-Tools repository), and then using VkConfig to enable Debug Printf, launch vkcube, and see the Debug Printf output.
VK_KHR_shader_non_semantic_info to cube's CreateDevice functiondebugPrintfEXT call to the shaderglslangvalidator to compile the new shaderVkConfig to enable Debug PrintfVK_LAYER_PRINTF_BUFFER_SIZE1.2.135.0 or later is requiredfragmentStoresAndAtomics, vertexPipelineStoresAndAtomics, and timelineSemaphore features are requiredVK_KHR_shader_non_semantic_info extension must be supported and enabledDocumentation for the GL_EXT_debug_printf extension can be found here
There are many validation layer tests that demonstrates the simple and programmatic use of Debug Printf. See tests/unit/debug_printf.cpp in the Vulkan-ValidationLayers repository.
Earlier implementations implicitly included stage specific built-in variables such as gl_InvocationID, gl_VertexID and gl_FragCoord in Debug Printf messages. This functionality has been removed because it made Debug Printf unusable in shader modules that defined entry points for multiple pipeline stages. If necessary, you can add these values to your printf statements explicitly. However, you must then make sure that the printf statement can only be executed from a pipeline stage where the built-in variable is available.