Due to the nature of the Vulkan interface, there is very little error
information available to the developer and application. By using the
VK_EXT_debug_utils extension, developers can obtain more
information. When combined with validation layers, even more detailed
feedback on the application’s use of Vulkan will be provided.
This extension provides the following capabilities:
The ability to create a debug messenger which will pass along debug
messages to an application supplied callback.
The ability to identify specific Vulkan objects using a name or tag
to improve tracking.
The ability to identify specific sections within a
Queue or
CommandBuffer using labels to aid
organization and offline analysis in external tools.
The main difference between this extension and VK_EXT_debug_report and
VK_EXT_debug_marker is that those extensions use
DebugReportObjectTypeEXT to
identify objects. This extension uses the core
ObjectType in place of
DebugReportObjectTypeEXT. The
primary reason for this move is that no future object type handle
enumeration values will be added to
DebugReportObjectTypeEXT since
the creation of ObjectType.
In addition, this extension combines the functionality of both
VK_EXT_debug_report and VK_EXT_debug_marker by allowing object name
and debug markers (now called labels) to be returned to the
application’s callback function. This should assist in clarifying the
details of a debug message including: what objects are involved and
potentially which location within a Queue or
CommandBuffer the message occurred.
VK_EXT_debug_utils allows an application to register multiple
callbacks with any Vulkan component wishing to report debug information.
Some callbacks may log the information to a file, others may cause a
debug break point or other application-defined behavior. An application
can register callbacks even when no validation layers are enabled, but
they will only be called for loader and, if implemented, driver events.
Example uses: Create three callback objects. One will log errors and
warnings to the debug console using Windows OutputDebugString. The
second will cause the debugger to break at that callback when an error
happens and the third will log warnings to stdout.
extern VkInstance instance;
VkResult res;
VkDebugUtilsMessengerEXT cb1, cb2, cb3;
// Must call extension functions through a function pointer:
PFN_vkCreateDebugUtilsMessengerEXT pfnCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
PFN_vkDestroyDebugUtilsMessengerEXT pfnDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
VkDebugUtilsMessengerCreateInfoEXT callback1 = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = NULL,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT,
.messageType= VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
.pfnUserCallback = myOutputDebugString,
.pUserData = NULL
};
res = pfnCreateDebugUtilsMessengerEXT(instance, &callback1, NULL, &cb1);
if (res != VK_SUCCESS) {
// Do error handling for VK_ERROR_OUT_OF_MEMORY
}
callback1.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
callback1.pfnUserCallback = myDebugBreak;
callback1.pUserData = NULL;
res = pfnCreateDebugUtilsMessengerEXT(instance, &callback1, NULL, &cb2);
if (res != VK_SUCCESS) {
// Do error handling for VK_ERROR_OUT_OF_MEMORY
}
VkDebugUtilsMessengerCreateInfoEXT callback3 = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = NULL,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
.pfnUserCallback = mystdOutLogger,
.pUserData = NULL
};
res = pfnCreateDebugUtilsMessengerEXT(instance, &callback3, NULL, &cb3);
if (res != VK_SUCCESS) {
// Do error handling for VK_ERROR_OUT_OF_MEMORY
}
...
// Remove callbacks when cleaning up
pfnDestroyDebugUtilsMessengerEXT(instance, cb1, NULL);
pfnDestroyDebugUtilsMessengerEXT(instance, cb2, NULL);
pfnDestroyDebugUtilsMessengerEXT(instance, cb3, NULL);
Example 2
Associate a name with an image, for easier debugging in external tools
or with validation layers that can print a friendly name when referring
to objects in error messages.
extern VkInstance instance;
extern VkDevice device;
extern VkImage image;
// Must call extension functions through a function pointer:
PFN_vkSetDebugUtilsObjectNameEXT pfnSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(instance, "vkSetDebugUtilsObjectNameEXT");
// Set a name on the image
const VkDebugUtilsObjectNameInfoEXT imageNameInfo =
{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.pNext = NULL,
.objectType = VK_OBJECT_TYPE_IMAGE,
.objectHandle = (uint64_t)image,
.pObjectName = "Brick Diffuse Texture",
};
pfnSetDebugUtilsObjectNameEXT(device, &imageNameInfo);
// A subsequent error might print:
// Image 'Brick Diffuse Texture' (0xc0dec0dedeadbeef) is used in a
// command buffer with no memory bound to it.
Example 3
Annotating regions of a workload with naming information so that offline
analysis tools can display a more usable visualization of the commands
submitted.
extern VkInstance instance;
extern VkCommandBuffer commandBuffer;
// Must call extension functions through a function pointer:
PFN_vkQueueBeginDebugUtilsLabelEXT pfnQueueBeginDebugUtilsLabelEXT = (PFN_vkQueueBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(instance, "vkQueueBeginDebugUtilsLabelEXT");
PFN_vkQueueEndDebugUtilsLabelEXT pfnQueueEndDebugUtilsLabelEXT = (PFN_vkQueueEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(instance, "vkQueueEndDebugUtilsLabelEXT");
PFN_vkCmdBeginDebugUtilsLabelEXT pfnCmdBeginDebugUtilsLabelEXT = (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(instance, "vkCmdBeginDebugUtilsLabelEXT");
PFN_vkCmdEndDebugUtilsLabelEXT pfnCmdEndDebugUtilsLabelEXT = (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(instance, "vkCmdEndDebugUtilsLabelEXT");
PFN_vkCmdInsertDebugUtilsLabelEXT pfnCmdInsertDebugUtilsLabelEXT = (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(instance, "vkCmdInsertDebugUtilsLabelEXT");
// Describe the area being rendered
const VkDebugUtilsLabelEXT houseLabel =
{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.pNext = NULL,
.pLabelName = "Brick House",
.color = { 1.0f, 0.0f, 0.0f, 1.0f },
};
// Start an annotated group of calls under the 'Brick House' name
pfnCmdBeginDebugUtilsLabelEXT(commandBuffer, &houseLabel);
{
// A mutable structure for each part being rendered
VkDebugUtilsLabelEXT housePartLabel =
{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.pNext = NULL,
.pLabelName = NULL,
.color = { 0.0f, 0.0f, 0.0f, 0.0f },
};
// Set the name and insert the marker
housePartLabel.pLabelName = "Walls";
pfnCmdInsertDebugUtilsLabelEXT(commandBuffer, &housePartLabel);
// Insert the drawcall for the walls
vkCmdDrawIndexed(commandBuffer, 1000, 1, 0, 0, 0);
// Insert a recursive region for two sets of windows
housePartLabel.pLabelName = "Windows";
pfnCmdBeginDebugUtilsLabelEXT(commandBuffer, &housePartLabel);
{
vkCmdDrawIndexed(commandBuffer, 75, 6, 1000, 0, 0);
vkCmdDrawIndexed(commandBuffer, 100, 2, 1450, 0, 0);
}
pfnCmdEndDebugUtilsLabelEXT(commandBuffer);
housePartLabel.pLabelName = "Front Door";
pfnCmdInsertDebugUtilsLabelEXT(commandBuffer, &housePartLabel);
vkCmdDrawIndexed(commandBuffer, 350, 1, 1650, 0, 0);
housePartLabel.pLabelName = "Roof";
pfnCmdInsertDebugUtilsLabelEXT(commandBuffer, &housePartLabel);
vkCmdDrawIndexed(commandBuffer, 500, 1, 2000, 0, 0);
}
// End the house annotation started above
pfnCmdEndDebugUtilsLabelEXT(commandBuffer);
// Do other work
vkEndCommandBuffer(commandBuffer);
// Describe the queue being used
const VkDebugUtilsLabelEXT queueLabel =
{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.pNext = NULL,
.pLabelName = "Main Render Work",
.color = { 0.0f, 1.0f, 0.0f, 1.0f },
};
// Identify the queue label region
pfnQueueBeginDebugUtilsLabelEXT(queue, &queueLabel);
// Submit the work for the main render thread
const VkCommandBuffer cmd_bufs[] = {commandBuffer};
VkSubmitInfo submit_info =
{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = NULL,
.waitSemaphoreCount = 0,
.pWaitSemaphores = NULL,
.pWaitDstStageMask = NULL,
.commandBufferCount = 1,
.pCommandBuffers = cmd_bufs,
.signalSemaphoreCount = 0,
.pSignalSemaphores = NULL
};
vkQueueSubmit(queue, 1, &submit_info, fence);
// End the queue label region
pfnQueueEndDebugUtilsLabelEXT(queue);
Issues
1) Should we just name this extension VK_EXT_debug_report2
RESOLVED: No. There is enough additional changes to the structures
to break backwards compatibility. So, a new name was decided that would
not indicate any interaction with the previous extension.
2) Will validation layers immediately support all the new features.
RESOLVED: Not immediately. As one can imagine, there is a lot of
work involved with converting the validation layer logging over to the
new functionality. Basic logging, as seen in the origin
VK_EXT_debug_report extension will be made available immediately.
However, adding the labels and object names will take time. Since the
priority for Khronos at this time is to continue focusing on Valid Usage
statements, it may take a while before the new functionality is fully
exposed.
3) If the validation layers will not expose the new functionality
immediately, then what is the point of this extension?
RESOLVED: We needed a replacement for VK_EXT_debug_report because
the DebugReportObjectTypeEXT
enumeration will no longer be updated and any new objects will need to
be debugged using the new functionality provided by this extension.
4) Should this extension be split into two separate parts (1 extension
that is an instance extension providing the callback functionality, and
another device extension providing the general debug marker and
annotation functionality)?
RESOLVED: No, the functionality for this extension is too closely
related. If we did split up the extension, where would the structures
and enums live, and how would you define that the device behavior in the
instance extension is really only valid if the device extension is
enabled, and the functionality is passed in. It is cleaner to just
define this all as an instance extension, plus it allows the application
to enable all debug functionality provided with one enable string during
createInstance.
Version History
Revision 1, 2017-09-14 (Mark Young and all listed Contributors)
Initial draft, based on VK_EXT_debug_report and
VK_EXT_debug_marker in addition to previous feedback supplied
from various companies including Valve, Epic, and Oxide games.
Revision 2, 2020-04-03 (Mark Young and Piers Daniell)
Updated to allow either NULL or an empty string to be passed
in for pObjectName in DebugUtilsObjectNameInfoEXT, because
the loader and various drivers support NULL already.
If
pNameInfo->objectHandle is the valid handle of an instance-level
object, the Device identified by devicemust be a descendent of the same Instance
as the object identified by pNameInfo->objectHandle
If
pNameInfo->objectHandle is the valid handle of a
physical-device-level object, the Device
identified by devicemust be a descendant of the same
PhysicalDevice as the object identified by
pNameInfo->objectHandle
If
pNameInfo->objectHandle is the valid handle of a device-level
object, that object must be a descendent of the
Device identified by device
vkSetDebugUtilsObjectTagEXT - Attach arbitrary data to an object
Valid Usage
If
pNameInfo->objectHandle is the valid handle of an instance-level
object, the Device identified by devicemust be a descendent of the same Instance
as the object identified by pNameInfo->objectHandle
If
pNameInfo->objectHandle is the valid handle of a
physical-device-level object, the Device
identified by devicemust be a descendant of the same
PhysicalDevice as the object identified by
pNameInfo->objectHandle
If
pNameInfo->objectHandle is the valid handle of a device-level
object, that object must be a descendent of the
Device identified by device
vkCmdEndDebugUtilsLabelEXT - Close a command buffer label region
Description
An application may open a debug label region in one command buffer and
close it in another, or otherwise split debug label regions across
multiple command buffers or multiple queue submissions. When viewed from
the linear series of submissions to a single queue, the calls to
cmdBeginDebugUtilsLabelEXT and cmdEndDebugUtilsLabelEXTmust be
matched and balanced.
There can be problems reporting command buffer debug labels during the
recording process because command buffers may be recorded out of
sequence with the resulting execution order. Since the recording order
may be different, a solitary command buffer may have an inconsistent
view of the debug label regions by itself. Therefore, if an issue occurs
during the recording of a command buffer, and the environment requires
returning debug labels, the implementation may return only those
labels it is aware of. This is true even if the implementation is aware
of only the debug labels within the command buffer being actively
recorded.
If
commandBuffer is a secondary command buffer, there must be an
outstanding cmdBeginDebugUtilsLabelEXT command recorded to
commandBuffer that has not previously been ended by a call to
cmdEndDebugUtilsLabelEXT
pCreateInfo is a pointer to a DebugUtilsMessengerCreateInfoEXT
structure containing the callback pointer, as well as defining
conditions under which this messenger will trigger the callback.
The application must ensure that createDebugUtilsMessengerEXT is not
executed in parallel with any Vulkan command that is also called with
instance or child of instance as the dispatchable argument.
To ensure that destroyDebugUtilsMessengerEXT is always called: pass
bracket (or the allocate function from your
favourite resource management library) as the last argument.
To just extract the pair pass (,) as the last argument.
messenger is the DebugUtilsMessengerEXT
object to destroy. messenger is an externally synchronized object and
must not be used on more than one thread at a time. This means that
destroyDebugUtilsMessengerEXTmust not be called when a callback is
active.
If
pAllocator is not NULL, pAllocatormust be a valid pointer
to a valid AllocationCallbacks
structure
If
messenger is a valid handle, it must have been created,
allocated, or retrieved from instance
Host Synchronization
Host access to messengermust be externally synchronized
The application must ensure that destroyDebugUtilsMessengerEXT is
not executed in parallel with any Vulkan command that is also called
with instance or child of instance as the dispatchable argument.
vkSubmitDebugUtilsMessageEXT - Inject a message into a debug stream
Description
The call will propagate through the layers and generate callback(s) as
indicated by the message’s flags. The parameters are passed on to the
callback in addition to the pUserData value that was defined at the
time the messenger was registered.
Valid Usage
The
objectType member of each element of pCallbackData->pObjectsmust not be OBJECT_TYPE_UNKNOWN
VkDebugUtilsObjectNameInfoEXT - Specify parameters of a name to give to
an object
Description
Applications may change the name associated with an object simply by
calling setDebugUtilsObjectNameEXT again with a new string. If
pObjectName is either NULL or an empty string, then any previously
set name is removed.
This structure can be included in the pNext chain of
ResourceDescriptorInfoEXT or
SamplerCreateInfo to label a descriptor or
embedded sampler. This structure may be ignored when included in the
pNext chain of SamplerCreateInfo when creating
a sampler object. The label must remain valid while the descriptor is
valid; it may be discarded if it becomes invalid.
VkDebugUtilsObjectTagInfoEXT - Specify parameters of a tag to attach to
an object
Description
The tagName parameter gives a name or identifier to the type of data
being tagged. This can be used by debugging layers to easily filter for
only data that can be used by that implementation.
color is an optional RGBA color value that can be associated with the
label. A particular implementation may choose to ignore this color
value. The values contain RGBA values in order, in the range 0.0 to 1.0.
If all elements in color are 0.0, then it is ignored.
The callback will trigger a debug message for the current event
The callback will come directly from the component that detected the
event, unless some other layer intercepts the calls for its own purposes
(filter them in a different way, log to a system error log, etc.).
An application can receive multiple callbacks if multiple
DebugUtilsMessengerEXT objects are created.
A callback will always be executed in the same thread as the originating
Vulkan call.
A callback can be called from multiple threads simultaneously (if the
application is making Vulkan calls from multiple threads).
VkDebugUtilsMessengerCallbackDataEXT - Structure specifying parameters
returned to the callback
Description
This structure should only be considered valid during the lifetime of
the triggered callback.
Since adding queue and command buffer labels behaves like pushing and
popping onto a stack, the order of both pQueueLabels and
pCmdBufLabels is based on the order the labels were defined. The
result is that the first label in either pQueueLabels or
pCmdBufLabels will be the first defined (and therefore the oldest)
while the last label in each list will be the most recent.
pQueueLabels will only be non-NULL if one of the objects in
pObjects can be related directly to a defined
Queue which has had one or more labels
associated with it.
Likewise, pCmdBufLabels will only be non-NULL if one of the objects
in pObjects can be related directly to a defined
CommandBuffer which has had one or more labels
associated with it. Additionally, while command buffer labels allow for
beginning and ending across different command buffers, the debug
messaging framework cannot guarantee that labels in pCmdBufLables
will contain those defined outside of the associated command buffer.
This is partially due to the fact that the association of one command
buffer with another may not have been defined at the time the debug
message is triggered.
pMessageIdName is NULL or a null-terminated UTF-8 string that
identifies the particular message ID that is associated with the
provided message. If the message corresponds to a validation layer
message, then this string will be the VUID.
messageIdNumber is the ID number of the triggering message. If the
message corresponds to a validation layer message, then this number is
an internal hash of the VUID.
pMessage is NULL if messageTypes is equal to
DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT, or a
null-terminated UTF-8 string detailing the trigger conditions. If the
message corresponds to a validation layer message, then this will
contain the main message with the specification text and link.
pQueueLabels is NULL or a pointer to an array of
DebugUtilsLabelEXT active in the current Queue
at the time the callback was triggered. Refer to
Queue Labels
for more information.
pObjects is a pointer to an array of DebugUtilsObjectNameInfoEXT
objects related to the detected issue. The array is roughly in order of
importance, but the 0th element is always guaranteed to be the most
important object for this message.
Instances5Extensible, Show, FromCStruct, ToCStruct, Zero
VkDebugUtilsMessageSeverityFlagBitsEXT - Bitmask specifying which
severities of events cause a debug messenger callback
Description
DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT specifies the most
verbose output indicating all diagnostic messages from the Vulkan
loader, layers, and drivers should be captured.
DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT specifies use of
Vulkan that may expose an application bug. Such cases may not be
immediately harmful, such as a fragment shader outputting to a
location with no attachment. Other cases may point to behavior
that is almost certainly bad when unintended such as using an image
whose memory has not been filled. In general if you see a warning
but you know that the behavior is intended/desired, then simply
ignore the warning.
PFN_vkDebugUtilsMessengerCallbackEXT - Application-defined debug
messenger callback function
Description
The callback returns a Bool32, which is
interpreted in a layer-specified manner. The application should always
return FALSE. The
TRUE value is reserved for use in layer
development.
Valid Usage
The callback
must not make calls to any Vulkan commands
VkDebugUtilsMessengerEXT - Opaque handle to a debug messenger object
Description
The debug messenger will provide detailed feedback on the application’s
use of Vulkan when events of interest occur. When an event of interest
does occur, the debug messenger will submit a debug message to the debug
callback that was provided during its creation. Additionally, the debug
messenger is responsible with filtering out debug messages that the
callback is not interested in and will only provide desired debug
messages.