Some days ago I was trying to use the debugPrintf functionality that was introduced to Vulkan and adjacent projects more than a year ago. Since I haven’t found (or maybe I missed it) a good online document that describes all the steps to enable such functionality programmatically, I thought it might be a good idea to document it myself. This is going to be a short post.
First of all, what is debugPrintf? debugPrintf is a way to write text messages from shaders that execute in the GPU to stdout or to your output of your own choosing. In other words, the GPU can print text messages that the CPU will display. debugPrintf’s primary use-case is to help debug shaders.
How it works? Someone can add expressions like these in their GLSL shaders:
#extension GLSL_EXT_debug_printf : enable
...
debugPrintfEXT("This is a message from the GPU. Some float=%f, some int=%d", 1.0, 123);
As you can see debugPrintfEXT looks quite similar to printf which makes it quite powerful. Using glslang (aka glslangValidator) you can convert shaders that contain debugPrintfEXT to SPIR-V and pass that SPIR-V to a VkShaderModule.
The majority of the the implementation of debugPrintf lives in the Vulkan validation layer. The validation layer will rewrite the SPIR-V generated by glslang and add code that processes the given text and sends it down to the CPU. The validation layer will make use of a hidden descriptor set, atomics and hidden buffers to pass data from the GPU to the CPU. All this work and setup is transparent to the user and it can be quite slow.
So what are the steps to start using debugPrintf?
Step 1: Enable the extension in your shaders by adding: #extension GLSL_EXT_debug_printf : enable
Step 2: Enable the validation layer while creating the VkInstance:
VkInstanceCreateInfo instanceCreateInfo;
...
const char* layerNames[1] = {"VK_LAYER_KHRONOS_validation"};
instanceCreateInfo.ppEnabledLayerNames = &layerNames[0];
instanceCreateInfo.enabledLayerCount = 1;
Step 3: Enable the debugPrintf validation layer feature while creating the VkInstance:
// Populate the VkValidationFeaturesEXT
VkValidationFeaturesEXT validationFeatures = {};
validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
validationFeatures.enabledValidationFeatureCount = 1;
VkValidationFeatureEnableEXT enabledValidationFeatures[1] = {
VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT};
validationFeatures.pEnabledValidationFeatures = enabledValidationFeatures;
// Then add the VkValidationFeaturesEXT to the VkInstanceCreateInfo
validationFeatures.pNext = instanceCreateInfo.pNext;
instanceCreateInfo.pNext = &validationFeatures;
Step 4: Setup the callback that will print the messages:
VkDebugReportCallbackEXT debugCallbackHandle;
// Populate the VkDebugReportCallbackCreateInfoEXT
VkDebugReportCallbackCreateInfoEXT ci = {};
ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
ci.pfnCallback = myDebugCallback;
ci.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT;
ci.pUserData = myUserData;
// Create the callback handle
vkCreateDebugReportCallbackEXT(vulkanInstance, &ci, nullptr, &debugCallbackHandle);
...
// And this is the callback that the validator will call
VkBool32 myDebugCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object,
size_t location,
int32_t messageCode,
const char* pLayerPrefix,
const char* pMessage,
void* pUserData)
{
if(flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
{
printf("debugPrintfEXT: %s", pMessage);
}
return false;
}
Step 5: Make sure you enable the VK_KHR_shader_non_semantic_info device extension while building your VkDevice. This is pretty trivial so I won’t show any code.
Some additional notes:
- It is possible to use debugPrintf with the DirectX compiler. In DX land debugPrintfEXT is just named printf
- It is also possible to avoid all this annoying setup and use the Vulkan configurator (aka vkconfig). vkconfig is part of Vulkan SDK. More info on vkconfig here https://vulkan.lunarg.com/doc/view/1.2.135.0/windows/vkconfig.html
- Latest RenderDoc also supports debugPrintf but I’m unsure about the details
And that’s pretty much it. If I missed something feel free to drop a comment bellow.
This page is useful : https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/master/docs/debug_printf.md
Maybe mention that the amount of output is limited by a buffer size that can be changed.
There is a bug in the latest SDK (1.2.176.1) that can cause a compilation crash when using HLSL printf. It will be fixed in the next SDK release in July 2021. If you are building your own glslang or dxc, the bug is fixed in top-of-tree spirv-tools.
We will also be publishing a paper shortly on how recent changes in renderdoc make using debugprintf even more convenient. Watch lunarg.com or LunarG communications for availability.
Isn’t the debugPrintf messages INFO level? The debug callback here only prints if the flag is ERROR.
“`
if(flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) <- shouldn't this be VK_DEBUG_REPORT_INFORMATION_BIT_EXT
{
printf("debugPrintfEXT: %s", pMessage);
}
“`
Actually, this whole thing is using DebugReport, the older deprecated debug extension. DebugUtils replaced it and should be used instead.
Imho GLSL_EXT_debug_printf should be GL_EXT_debug_printf.