Setting up Vulkan debugging capabilities
After creating a Vulkan instance, we can start monitoring all potential errors and warnings generated by the validation layers. This is done by using the VK_EXT_debug_utils
extension to create a callback function and register it with the Vulkan instance. In this recipe, we’ll learn how to set up and use this feature.
Getting ready
Please revising the first recipe Initializing Vulkan instance and graphical device for details how to initialize Vulkan in your applications and enable the instance extension VK_EXT_debug_utils
.
How to do it...
We have to provide a callback function to Vulkan to catch the debug output. In LightweightVK it is called vulkanDebugCallback()
. Here’s how it can be passed into Vulkan to intercept logs.
- Let’s create a debug messenger to forward debug messages to an application-provided callback function,
vulkanDebugCallback()
. This can be done right after theVkInstance
object has been created.... vkCreateInstance(&ci, nullptr, &vkInstance_); volkLoadInstance(vkInstance_); const VkDebugUtilsMessengerCreateInfoEXT ci = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, .pfnUserCallback = &vulkanDebugCallback, .pUserData = this, }; vkCreateDebugUtilsMessengerEXT( vkInstance_, &ci, nullptr, &vkDebugUtilsMessenger_);
- The callback code is more elaborate and can provide information about the Vulkan object causing an error or warning. However, we won’t cover tagged object allocation or associating custom data. Some performance warnings are suppressed to keep the debug output easier to read.
VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT msgSeverity, VkDebugUtilsMessageTypeFlagsEXT msgType, const VkDebugUtilsMessengerCallbackDataEXT* cbData, void* userData) { if (msgSeverity < VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) return VK_FALSE; const bool isError = (msgSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0; const bool isWarning = (msgSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) != 0; lvk::VulkanContext* ctx = static_cast<lvk::VulkanContext*>(userData); minilog::eLogLevel level = minilog::Log; if (isError) { level = ctx->config_.terminateOnValidationError ? minilog::FatalError : minilog::Warning; } MINILOG_LOG_PROC( level, "%sValidation layer:\n%s\n", isError ? "\nERROR:\n" : "", cbData->pMessage); if (isError) { lvk::VulkanContext* ctx = static_cast<lvk::VulkanContext*>(userData); if (ctx->config_.terminateOnValidationError) { std::terminate(); } } return VK_FALSE; }
This code is enough to get you started with reading validation layer messages and debugging your Vulkan applications. Remember to destroy the validation layer callbacks just before destroying the Vulkan instance. Refer to the full source code for all the details https://github.com/corporateshark/lightweightvk/blob/master/lvk/vulkan/VulkanClasses.cpp.
There’s more…
The extension VK_EXT_debug_utils
provides the ability to identify specific Vulkan objects using a textual name or tag to improve Vulkan objects tracking and debugging experience.
For example, in LightweightVK, we can assign a name to our VkDevice
object.
lvk::setDebugObjectName(vkDevice_, VK_OBJECT_TYPE_DEVICE,
(uint64_t)vkDevice_, "Device: VulkanContext::vkDevice_");
This helper function is implemented in lvk/vulkan/VulkanUtils.cpp
and looks as follows:
VkResult lvk::setDebugObjectName(VkDevice device, VkObjectType type,
uint64_t handle, const char* name)
{
if (!name || !*name) return VK_SUCCESS;
const VkDebugUtilsObjectNameInfoEXT ni = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = type,
.objectHandle = handle,
.pObjectName = name,
};
return vkSetDebugUtilsObjectNameEXT(device, &ni);
}