/* * 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_log.h" #include "vk_debug_utils.h" #include "vk_debug_report.h" #include "vk_command_buffer.h" #include "vk_enum_to_str.h" #include "vk_queue.h" #include "vk_device.h" #include "vk_physical_device.h" #include "ralloc.h" #include "log.h" static struct vk_device * vk_object_to_device(struct vk_object_base *obj) { assert(obj->device); return obj->device; } static struct vk_physical_device * vk_object_to_physical_device(struct vk_object_base *obj) { switch (obj->type) { case VK_OBJECT_TYPE_INSTANCE: unreachable("Unsupported object type"); case VK_OBJECT_TYPE_PHYSICAL_DEVICE: return container_of(obj, struct vk_physical_device, base); case VK_OBJECT_TYPE_SURFACE_KHR: case VK_OBJECT_TYPE_DISPLAY_KHR: case VK_OBJECT_TYPE_DISPLAY_MODE_KHR: case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT: case VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT: unreachable("Unsupported object type"); default: return vk_object_to_device(obj)->physical; } } static struct vk_instance * vk_object_to_instance(struct vk_object_base *obj) { if (obj == NULL) return NULL; if (obj->type == VK_OBJECT_TYPE_INSTANCE) { return container_of(obj, struct vk_instance, base); } else { return vk_object_to_physical_device(obj)->instance; } } void __vk_log_impl(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT types, int object_count, const void **objects_or_instance, const char *file, int line, const char *format, ...) { struct vk_instance *instance = NULL; struct vk_object_base **objects = NULL; if (object_count == 0) { instance = (struct vk_instance *) objects_or_instance; } else { objects = (struct vk_object_base **) objects_or_instance; for (unsigned i = 0; i < object_count; i++) { if (unlikely(objects[i] == NULL)) { mesa_logw("vk_log*() called with NULL object\n"); continue; } if (unlikely(!objects[i]->client_visible)) { mesa_logw("vk_log*() called with client-invisible object %p " "of type %s", objects[i], vk_ObjectType_to_str(objects[i]->type)); } if (!instance) { instance = vk_object_to_instance(objects[i]); assert(instance->base.client_visible); } else { assert(vk_object_to_instance(objects[i]) == instance); } break; } } #ifndef DEBUG if (unlikely(!instance) || (likely(list_is_empty(&instance->debug_utils.callbacks)) && likely(list_is_empty(&instance->debug_report.callbacks)))) return; #endif va_list va; char *message = NULL; va_start(va, format); message = ralloc_vasprintf(NULL, format, va); va_end(va); char *message_idname = ralloc_asprintf(NULL, "%s:%d", file, line); #if DEBUG switch (severity) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: mesa_logd("%s: %s", message_idname, message); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: mesa_logi("%s: %s", message_idname, message); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: if (types & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) mesa_logw("%s: PERF: %s", message_idname, message); else mesa_logw("%s: %s", message_idname, message); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: mesa_loge("%s: %s", message_idname, message); break; default: unreachable("Invalid debug message severity"); break; } if (!instance) { ralloc_free(message); ralloc_free(message_idname); return; } #endif if (!instance->base.client_visible) { vk_debug_message_instance(instance, severity, types, message_idname, 0, message); ralloc_free(message); ralloc_free(message_idname); return; } /* If VK_EXT_debug_utils messengers have been set up, form the * message */ if (!list_is_empty(&instance->debug_utils.callbacks)) { VkDebugUtilsMessengerCallbackDataEXT cb_data = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT, .pMessageIdName = message_idname, .messageIdNumber = 0, .pMessage = message, }; VkDebugUtilsObjectNameInfoEXT *object_name_infos = ralloc_array(NULL, VkDebugUtilsObjectNameInfoEXT, object_count); ASSERTED int cmdbuf_n = 0, queue_n = 0, obj_n = 0; for (int i = 0; i < object_count; i++) { struct vk_object_base *base = objects[i]; if (base == NULL || !base->client_visible) continue; switch (base->type) { case VK_OBJECT_TYPE_COMMAND_BUFFER: { /* We allow at most one command buffer to be submitted at a time */ assert(++cmdbuf_n <= 1); struct vk_command_buffer *cmd_buffer = (struct vk_command_buffer *)base; if (cmd_buffer->labels.size > 0) { cb_data.cmdBufLabelCount = util_dynarray_num_elements( &cmd_buffer->labels, VkDebugUtilsLabelEXT); cb_data.pCmdBufLabels = cmd_buffer->labels.data; } break; } case VK_OBJECT_TYPE_QUEUE: { /* We allow at most one queue to be submitted at a time */ assert(++queue_n <= 1); struct vk_queue *queue = (struct vk_queue *)base; if (queue->labels.size > 0) { cb_data.queueLabelCount = util_dynarray_num_elements(&queue->labels, VkDebugUtilsLabelEXT); cb_data.pQueueLabels = queue->labels.data; } break; } default: break; } object_name_infos[obj_n++] = (VkDebugUtilsObjectNameInfoEXT){ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .pNext = NULL, .objectType = base->type, .objectHandle = (uint64_t)(uintptr_t)base, .pObjectName = base->object_name, }; } cb_data.objectCount = obj_n; cb_data.pObjects = object_name_infos; vk_debug_message(instance, severity, types, &cb_data); ralloc_free(object_name_infos); } /* If VK_EXT_debug_report callbacks also have been set up, forward * the message there as well */ if (!list_is_empty(&instance->debug_report.callbacks)) { VkDebugReportFlagsEXT flags = 0; switch (severity) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: flags |= VK_DEBUG_REPORT_DEBUG_BIT_EXT; break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: flags |= VK_DEBUG_REPORT_INFORMATION_BIT_EXT; break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: if (types & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) flags |= VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; else flags |= VK_DEBUG_REPORT_WARNING_BIT_EXT; break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: flags |= VK_DEBUG_REPORT_ERROR_BIT_EXT; break; default: unreachable("Invalid debug message severity"); break; } /* VK_EXT_debug_report-provided callback accepts only one object * related to the message. Since they are given to us in * decreasing order of importance, we're forwarding the first * one. */ vk_debug_report(instance, flags, object_count ? objects[0] : NULL, 0, 0, message_idname, message); } ralloc_free(message); ralloc_free(message_idname); } static struct vk_object_base * vk_object_for_error(struct vk_object_base *obj, VkResult error) { if (obj == NULL) return NULL; switch (error) { case VK_ERROR_OUT_OF_HOST_MEMORY: case VK_ERROR_LAYER_NOT_PRESENT: case VK_ERROR_EXTENSION_NOT_PRESENT: case VK_ERROR_UNKNOWN: return &vk_object_to_instance(obj)->base; case VK_ERROR_FEATURE_NOT_PRESENT: return &vk_object_to_physical_device(obj)->base; case VK_ERROR_OUT_OF_DEVICE_MEMORY: case VK_ERROR_MEMORY_MAP_FAILED: case VK_ERROR_TOO_MANY_OBJECTS: return &vk_object_to_device(obj)->base; default: return obj; } } VkResult __vk_errorv(const void *_obj, VkResult error, const char *file, int line, const char *format, va_list va) { struct vk_object_base *object = (struct vk_object_base *)_obj; struct vk_instance *instance = vk_object_to_instance(object); object = vk_object_for_error(object, error); /* If object->client_visible isn't set then the object hasn't been fully * constructed and we shouldn't hand it back to the client. This typically * happens if an error is thrown during object construction. This is safe * to do as long as vk_object_base_init() has already been called. */ if (object && !object->client_visible) object = NULL; const char *error_str = vk_Result_to_str(error); if (format) { char *message = ralloc_vasprintf(NULL, format, va); if (object) { __vk_log(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, VK_LOG_OBJS(object), file, line, "%s (%s)", message, error_str); } else { __vk_log(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, VK_LOG_NO_OBJS(instance), file, line, "%s (%s)", message, error_str); } ralloc_free(message); } else { if (object) { __vk_log(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, VK_LOG_OBJS(object), file, line, "%s", error_str); } else { __vk_log(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, VK_LOG_NO_OBJS(instance), file, line, "%s", error_str); } } return error; } VkResult __vk_errorf(const void *_obj, VkResult error, const char *file, int line, const char *format, ...) { va_list va; va_start(va, format); VkResult result = __vk_errorv(_obj, error, file, line, format, va); va_end(va); return result; }