vulkan: Implement VK_EXT_debug_utils

This implements all the necessary features of VK_EXT_debug_utils in
common code.

Signed-off-by: Yevhenii Kolesnikov <yevhenii.kolesnikov@globallogic.com>
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10318>
This commit is contained in:
Yevhenii Kolesnikov 2021-04-02 18:07:22 +03:00 committed by Marge Bot
parent 879defa3be
commit 3b361b234a
11 changed files with 519 additions and 0 deletions

View file

@ -56,6 +56,8 @@ files_vulkan_util = files(
'vk_command_buffer.h',
'vk_debug_report.c',
'vk_debug_report.h',
'vk_debug_utils.c',
'vk_debug_utils.h',
'vk_deferred_operation.c',
'vk_deferred_operation.h',
'vk_descriptors.c',

View file

@ -31,16 +31,22 @@ vk_command_buffer_init(struct vk_command_buffer *command_buffer,
vk_object_base_init(device, &command_buffer->base,
VK_OBJECT_TYPE_COMMAND_BUFFER);
util_dynarray_init(&command_buffer->labels, NULL);
command_buffer->region_begin = true;
return VK_SUCCESS;
}
void
vk_command_buffer_reset(struct vk_command_buffer *command_buffer)
{
util_dynarray_clear(&command_buffer->labels);
command_buffer->region_begin = true;
}
void
vk_command_buffer_finish(struct vk_command_buffer *command_buffer)
{
util_dynarray_fini(&command_buffer->labels);
vk_object_base_finish(&command_buffer->base);
}

View file

@ -33,6 +33,46 @@ extern "C" {
struct vk_command_buffer {
struct vk_object_base base;
/**
* VK_EXT_debug_utils
*
* The next two fields represent debug labels storage.
*
* VK_EXT_debug_utils spec requires that upon triggering a debug message
* with a command buffer attached to it, all "active" labels will also be
* provided to the callback. The spec describes two distinct ways of
* attaching a debug label to the command buffer: opening a label region
* and inserting a single label.
*
* Label region is active between the corresponding `*BeginDebugUtilsLabel`
* and `*EndDebugUtilsLabel` calls. The spec doesn't mention any limits on
* nestedness of label regions. This implementation assumes that there
* aren't any.
*
* The spec, however, doesn't explain the lifetime of a label submitted by
* an `*InsertDebugUtilsLabel` call. The LunarG whitepaper [1] (pp 12-15)
* provides a more detailed explanation along with some examples. According
* to those, such label remains active until the next `*DebugUtilsLabel`
* call. This means that there can be no more than one such label at a
* time.
*
* \c labels contains all active labels at this point in order of submission
* \c region_begin denotes whether the most recent label opens a new region
* If \t labels is empty \t region_begin must be true.
*
* Anytime we modify labels, we first check for \c region_begin. If it's
* false, it means that the most recent label was submitted by
* `*InsertDebugUtilsLabel` and we need to remove it before doing anything
* else.
*
* See the discussion here:
* https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10318#note_1061317
*
* [1] https://www.lunarg.com/wp-content/uploads/2018/05/Vulkan-Debug-Utils_05_18_v1.pdf
*/
struct util_dynarray labels;
bool region_begin;
};
VK_DEFINE_HANDLE_CASTS(vk_command_buffer, base, VkCommandBuffer,

View file

@ -0,0 +1,285 @@
/*
* Copyright © 2021 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "vk_debug_utils.h"
#include "vk_common_entrypoints.h"
#include "vk_command_buffer.h"
#include "vk_device.h"
#include "vk_queue.h"
#include "vk_object.h"
#include "vk_alloc.h"
#include "vk_util.h"
#include "stdarg.h"
#include "u_dynarray.h"
void
vk_debug_message(struct vk_instance *instance,
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT types,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData)
{
mtx_lock(&instance->debug_utils.callbacks_mutex);
list_for_each_entry(struct vk_debug_utils_messenger, messenger,
&instance->debug_utils.callbacks, link) {
if ((messenger->severity & severity) &&
(messenger->type & types))
messenger->callback(severity, types, pCallbackData, messenger->data);
}
mtx_unlock(&instance->debug_utils.callbacks_mutex);
}
/* This function intended to be used by the drivers to report a
* message to the special messenger, provided in the pNext chain while
* creating an instance. It's only meant to be used during
* vkCreateInstance or vkDestroyInstance calls.
*/
void
vk_debug_message_instance(struct vk_instance *instance,
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT types,
const char *pMessageIdName,
int32_t messageIdNumber,
const char *pMessage)
{
if (list_is_empty(&instance->debug_utils.instance_callbacks))
return;
const VkDebugUtilsMessengerCallbackDataEXT cbData = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT,
.pMessageIdName = pMessageIdName,
.messageIdNumber = messageIdNumber,
.pMessage = pMessage,
};
list_for_each_entry(struct vk_debug_utils_messenger, messenger,
&instance->debug_utils.instance_callbacks, link) {
if ((messenger->severity & severity) &&
(messenger->type & types))
messenger->callback(severity, types, &cbData, messenger->data);
}
}
VKAPI_ATTR VkResult VKAPI_CALL
vk_common_CreateDebugUtilsMessengerEXT(
VkInstance _instance,
const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkDebugUtilsMessengerEXT *pMessenger)
{
VK_FROM_HANDLE(vk_instance, instance, _instance);
struct vk_debug_utils_messenger *messenger =
vk_alloc2(&instance->alloc, pAllocator,
sizeof(struct vk_debug_utils_messenger), 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!messenger)
return VK_ERROR_OUT_OF_HOST_MEMORY;
if (pAllocator)
messenger->alloc = *pAllocator;
else
messenger->alloc = instance->alloc;
vk_object_base_init(NULL, &messenger->base,
VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT);
messenger->severity = pCreateInfo->messageSeverity;
messenger->type = pCreateInfo->messageType;
messenger->callback = pCreateInfo->pfnUserCallback;
messenger->data = pCreateInfo->pUserData;
mtx_lock(&instance->debug_utils.callbacks_mutex);
list_addtail(&messenger->link, &instance->debug_utils.callbacks);
mtx_unlock(&instance->debug_utils.callbacks_mutex);
*pMessenger = vk_debug_utils_messenger_to_handle(messenger);
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL
vk_common_SubmitDebugUtilsMessageEXT(
VkInstance _instance,
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData)
{
VK_FROM_HANDLE(vk_instance, instance, _instance);
vk_debug_message(instance, messageSeverity, messageTypes, pCallbackData);
}
VKAPI_ATTR void VKAPI_CALL
vk_common_DestroyDebugUtilsMessengerEXT(
VkInstance _instance,
VkDebugUtilsMessengerEXT _messenger,
const VkAllocationCallbacks *pAllocator)
{
VK_FROM_HANDLE(vk_instance, instance, _instance);
VK_FROM_HANDLE(vk_debug_utils_messenger, messenger, _messenger);
if (messenger == NULL)
return;
mtx_lock(&instance->debug_utils.callbacks_mutex);
list_del(&messenger->link);
mtx_unlock(&instance->debug_utils.callbacks_mutex);
vk_object_base_finish(&messenger->base);
vk_free2(&instance->alloc, pAllocator, messenger);
}
VKAPI_ATTR VkResult VKAPI_CALL
vk_common_SetDebugUtilsObjectNameEXT(
VkDevice _device,
const VkDebugUtilsObjectNameInfoEXT *pNameInfo)
{
VK_FROM_HANDLE(vk_device, device, _device);
struct vk_object_base *object =
vk_object_base_from_u64_handle(pNameInfo->objectHandle,
pNameInfo->objectType);
if (object->object_name) {
vk_free(&device->alloc, object->object_name);
object->object_name = NULL;
}
object->object_name = vk_strdup(&device->alloc, pNameInfo->pObjectName,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!object->object_name)
return VK_ERROR_OUT_OF_HOST_MEMORY;
return VK_SUCCESS;
}
VKAPI_ATTR VkResult VKAPI_CALL
vk_common_SetDebugUtilsObjectTagEXT(
VkDevice _device,
const VkDebugUtilsObjectTagInfoEXT *pTagInfo)
{
/* no-op */
return VK_SUCCESS;
}
VKAPI_ATTR void VKAPI_CALL
vk_common_CmdBeginDebugUtilsLabelEXT(
VkCommandBuffer _commandBuffer,
const VkDebugUtilsLabelEXT *pLabelInfo)
{
VK_FROM_HANDLE(vk_command_buffer, command_buffer, _commandBuffer);
/* If the latest label was submitted by CmdInsertDebugUtilsLabelEXT, we
* should remove it first.
*/
if (!command_buffer->region_begin)
(void)util_dynarray_pop(&command_buffer->labels, VkDebugUtilsLabelEXT);
util_dynarray_append(&command_buffer->labels, VkDebugUtilsLabelEXT,
*pLabelInfo);
command_buffer->region_begin = true;
}
VKAPI_ATTR void VKAPI_CALL
vk_common_CmdEndDebugUtilsLabelEXT(VkCommandBuffer _commandBuffer)
{
VK_FROM_HANDLE(vk_command_buffer, command_buffer, _commandBuffer);
/* If the latest label was submitted by CmdInsertDebugUtilsLabelEXT, we
* should remove it first.
*/
if (!command_buffer->region_begin)
(void)util_dynarray_pop(&command_buffer->labels, VkDebugUtilsLabelEXT);
(void)util_dynarray_pop(&command_buffer->labels, VkDebugUtilsLabelEXT);
command_buffer->region_begin = true;
}
VKAPI_ATTR void VKAPI_CALL
vk_common_CmdInsertDebugUtilsLabelEXT(
VkCommandBuffer _commandBuffer,
const VkDebugUtilsLabelEXT *pLabelInfo)
{
VK_FROM_HANDLE(vk_command_buffer, command_buffer, _commandBuffer);
/* If the latest label was submitted by CmdInsertDebugUtilsLabelEXT, we
* should remove it first.
*/
if (!command_buffer->region_begin)
(void)util_dynarray_pop(&command_buffer->labels, VkDebugUtilsLabelEXT);
util_dynarray_append(&command_buffer->labels, VkDebugUtilsLabelEXT,
*pLabelInfo);
command_buffer->region_begin = false;
}
VKAPI_ATTR void VKAPI_CALL
vk_common_QueueBeginDebugUtilsLabelEXT(
VkQueue _queue,
const VkDebugUtilsLabelEXT *pLabelInfo)
{
VK_FROM_HANDLE(vk_queue, queue, _queue);
/* If the latest label was submitted by QueueInsertDebugUtilsLabelEXT, we
* should remove it first.
*/
if (!queue->region_begin)
(void)util_dynarray_pop(&queue->labels, VkDebugUtilsLabelEXT);
util_dynarray_append(&queue->labels, VkDebugUtilsLabelEXT, *pLabelInfo);
queue->region_begin = true;
}
VKAPI_ATTR void VKAPI_CALL
vk_common_QueueEndDebugUtilsLabelEXT(VkQueue _queue)
{
VK_FROM_HANDLE(vk_queue, queue, _queue);
/* If the latest label was submitted by QueueInsertDebugUtilsLabelEXT, we
* should remove it first.
*/
if (!queue->region_begin)
(void)util_dynarray_pop(&queue->labels, VkDebugUtilsLabelEXT);
(void)util_dynarray_pop(&queue->labels, VkDebugUtilsLabelEXT);
queue->region_begin = true;
}
VKAPI_ATTR void VKAPI_CALL
vk_common_QueueInsertDebugUtilsLabelEXT(
VkQueue _queue,
const VkDebugUtilsLabelEXT *pLabelInfo)
{
VK_FROM_HANDLE(vk_queue, queue, _queue);
/* If the latest label was submitted by QueueInsertDebugUtilsLabelEXT, we
* should remove it first.
*/
if (!queue->region_begin)
(void)util_dynarray_pop(&queue->labels, VkDebugUtilsLabelEXT);
util_dynarray_append(&queue->labels, VkDebugUtilsLabelEXT, *pLabelInfo);
queue->region_begin = false;
}

View file

@ -0,0 +1,67 @@
/*
* Copyright © 2021 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef VK_DEBUG_UTILS_H
#define VK_DEBUG_UTILS_H
#include "vk_instance.h"
#ifdef __cplusplus
extern "C" {
#endif
struct vk_debug_utils_messenger {
struct vk_object_base base;
VkAllocationCallbacks alloc;
struct list_head link;
VkDebugUtilsMessageSeverityFlagsEXT severity;
VkDebugUtilsMessageTypeFlagsEXT type;
PFN_vkDebugUtilsMessengerCallbackEXT callback;
void *data;
};
VK_DEFINE_NONDISP_HANDLE_CASTS(vk_debug_utils_messenger, base,
VkDebugUtilsMessengerEXT,
VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT)
void
vk_debug_message(struct vk_instance *instance,
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT types,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData);
void
vk_debug_message_instance(struct vk_instance *instance,
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT types,
const char *pMessageIdName,
int32_t messageIdNumber,
const char *pMessage);
#ifdef __cplusplus
}
#endif
#endif /* VK_DEBUG_UTILS_H */

View file

@ -26,6 +26,7 @@
#include "vk_alloc.h"
#include "vk_common_entrypoints.h"
#include "vk_util.h"
#include "vk_debug_utils.h"
#include "compiler/glsl_types.h"
@ -40,6 +41,37 @@ vk_instance_init(struct vk_instance *instance,
vk_object_base_init(NULL, &instance->base, VK_OBJECT_TYPE_INSTANCE);
instance->alloc = *alloc;
/* VK_EXT_debug_utils */
/* These messengers will only be used during vkCreateInstance or
* vkDestroyInstance calls.
*/
list_inithead(&instance->debug_utils.instance_callbacks);
vk_foreach_struct_const(ext, pCreateInfo->pNext) {
if (ext->sType ==
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) {
const VkDebugUtilsMessengerCreateInfoEXT *debugMessengerCreateInfo =
(const VkDebugUtilsMessengerCreateInfoEXT *)ext;
struct vk_debug_utils_messenger *messenger =
vk_alloc2(alloc, alloc, sizeof(struct vk_debug_utils_messenger), 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!messenger)
return VK_ERROR_OUT_OF_HOST_MEMORY;
vk_object_base_init(NULL, &messenger->base,
VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT);
messenger->alloc = *alloc;
messenger->severity = debugMessengerCreateInfo->messageSeverity;
messenger->type = debugMessengerCreateInfo->messageType;
messenger->callback = debugMessengerCreateInfo->pfnUserCallback;
messenger->data = debugMessengerCreateInfo->pUserData;
list_addtail(&messenger->link,
&instance->debug_utils.instance_callbacks);
}
}
instance->app_info = (struct vk_app_info) { .api_version = 0 };
if (pCreateInfo->pApplicationInfo) {
const VkApplicationInfo *app = pCreateInfo->pApplicationInfo;
@ -93,6 +125,13 @@ vk_instance_init(struct vk_instance *instance,
list_inithead(&instance->debug_report.callbacks);
if (mtx_init(&instance->debug_utils.callbacks_mutex, mtx_plain) != 0) {
mtx_destroy(&instance->debug_report.callbacks_mutex);
return VK_ERROR_INITIALIZATION_FAILED;
}
list_inithead(&instance->debug_utils.callbacks);
glsl_type_singleton_init_or_ref();
return VK_SUCCESS;
@ -102,7 +141,25 @@ void
vk_instance_finish(struct vk_instance *instance)
{
glsl_type_singleton_decref();
if (unlikely(!list_is_empty(&instance->debug_utils.callbacks))) {
list_for_each_entry_safe(struct vk_debug_utils_messenger, messenger,
&instance->debug_utils.callbacks, link) {
list_del(&messenger->link);
vk_object_base_finish(&messenger->base);
vk_free2(&instance->alloc, &messenger->alloc, messenger);
}
}
if (unlikely(!list_is_empty(&instance->debug_utils.instance_callbacks))) {
list_for_each_entry_safe(struct vk_debug_utils_messenger, messenger,
&instance->debug_utils.instance_callbacks,
link) {
list_del(&messenger->link);
vk_object_base_finish(&messenger->base);
vk_free2(&instance->alloc, &messenger->alloc, messenger);
}
}
mtx_destroy(&instance->debug_report.callbacks_mutex);
mtx_destroy(&instance->debug_utils.callbacks_mutex);
vk_free(&instance->alloc, (char *)instance->app_info.app_name);
vk_free(&instance->alloc, (char *)instance->app_info.engine_name);
vk_object_base_finish(&instance->base);

View file

@ -56,6 +56,17 @@ struct vk_instance {
mtx_t callbacks_mutex;
struct list_head callbacks;
} debug_report;
/* VK_EXT_debug_utils */
struct {
/* These callbacks are only used while creating or destroying an
* instance
*/
struct list_head instance_callbacks;
mtx_t callbacks_mutex;
/* Persistent callbacks */
struct list_head callbacks;
} debug_utils;
};
VK_DEFINE_HANDLE_CASTS(vk_instance, base, VkInstance,

View file

@ -44,12 +44,16 @@ vk_object_base_init(struct vk_device *device,
vk_object_base_reinit(base);
base->type = obj_type;
base->device = device;
base->object_name = NULL;
}
void
vk_object_base_finish(struct vk_object_base *base)
{
util_sparse_array_finish(&base->private_data);
if (base->object_name != NULL)
vk_free(&base->device->alloc, base->object_name);
}
void

View file

@ -46,6 +46,9 @@ struct vk_object_base {
/* For VK_EXT_private_data */
struct util_sparse_array private_data;
/* VK_EXT_debug_utils */
char *object_name;
};
void vk_object_base_init(UNUSED struct vk_device *device,

View file

@ -29,11 +29,15 @@ vk_queue_init(struct vk_queue *queue, struct vk_device *device)
memset(queue, 0, sizeof(*queue));
vk_object_base_init(device, &queue->base, VK_OBJECT_TYPE_QUEUE);
util_dynarray_init(&queue->labels, NULL);
queue->region_begin = true;
return VK_SUCCESS;
}
void
vk_queue_finish(struct vk_queue *queue)
{
util_dynarray_fini(&queue->labels);
vk_object_base_finish(&queue->base);
}

View file

@ -33,6 +33,46 @@ extern "C" {
struct vk_queue {
struct vk_object_base base;
/**
* VK_EXT_debug_utils
*
* The next two fields represent debug labels storage.
*
* VK_EXT_debug_utils spec requires that upon triggering a debug message
* with a queue attached to it, all "active" labels will also be provided
* to the callback. The spec describes two distinct ways of attaching a
* debug label to the queue: opening a label region and inserting a single
* label.
*
* Label region is active between the corresponding `*BeginDebugUtilsLabel`
* and `*EndDebugUtilsLabel` calls. The spec doesn't mention any limits on
* nestedness of label regions. This implementation assumes that there
* aren't any.
*
* The spec, however, doesn't explain the lifetime of a label submitted by
* an `*InsertDebugUtilsLabel` call. The LunarG whitepaper [1] (pp 12-15)
* provides a more detailed explanation along with some examples. According
* to those, such label remains active until the next `*DebugUtilsLabel`
* call. This means that there can be no more than one such label at a
* time.
*
* \c labels contains all active labels at this point in order of submission
* \c region_begin denotes whether the most recent label opens a new region
* If \t labels is empty \t region_begin must be true.
*
* Anytime we modify labels, we first check for \c region_begin. If it's
* false, it means that the most recent label was submitted by
* `*InsertDebugUtilsLabel` and we need to remove it before doing anything
* else.
*
* See the discussion here:
* https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10318#note_1061317
*
* [1] https://www.lunarg.com/wp-content/uploads/2018/05/Vulkan-Debug-Utils_05_18_v1.pdf
*/
struct util_dynarray labels;
bool region_begin;
};
VK_DEFINE_HANDLE_CASTS(vk_queue, base, VkQueue, VK_OBJECT_TYPE_QUEUE)