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.
For an overview of how to configure layers, refer to the Layers Overview and Configuration document.
The Debug Printf settings are managed by configuring the Validation Layer. These settings are described in the VK_LAYER_KHRONOS_validation document.
Debug Printf settings can also be managed using the Vulkan Configurator included with the Vulkan SDK.
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:
#version 450 #extension GL_EXT_debug_printf : enable void main() { float myfloat = 3.1415f; debugPrintfEXT("My float is %f", myfloat); }
Then use glslangValidator to generate SPIR-V to use in vkCreateShaderModule. “glslangvalidator --target-env vulkan1.2 -e main -o shader.vert.spv shader.vert” would be one example of compiling shader.vert
Note that every time this shader is executed, “My float is 3.141500” will be printed. If this were in a vertex shader and a triangle was drawn, it would be printed 3 times.
Note also that the VK_KHR_shader_non_semantic_info device extension must be enabled in the Vulkan application using this shader.
In HLSL, debug printf can be invoked as follows:
void main() { float myfloat = 3.1415; printf("My float is %f", myfloat); }
Use glslangValidator or dxc to generate SPIR-V for this shader. For instance: glslangValidator.exe -D --target-env vulkan1.2 -e main -o shader.vert.spvx shader.vert dxc.exe -spirv -E main -T ps_6_0 -fspv-target-env=vulkan1.2 shader.vert -Fo shader.vert.spv
Note that the VK_KHR_shader_non_semantic_info device extension must also be enabled in the Vulkan application using this shader.
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 printfs 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:
Note that the VK_KHR_shader_non_semantic_info device extension must also be enabled in the Vulkan application using this shader.
The strings resulting from a Debug Printf will, by default, be sent to the debug callback which is either specified by the app, or by default sent to stdout. They are sent at the VK_DEBUG_REPORT_INFORMATION_BIT_EXT or VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT level.
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:
The vkCmdDrawIndexed in question now has 51 messages.
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.
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]
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”
Documentation for the GL_EXT_debug_printf extension can be found here
There is a validation layer test that demonstrates the simple and programmatic use of Debug Printf. It is called “NegativeDebugPrintf.BasicUsage” and is in tests/unit/debug_printf.cpp
in the Vulkan-ValidationLayers repository.