| /* Copyright (c) 2015-2023 The Khronos Group Inc. |
| * Copyright (c) 2015-2023 Valve Corporation |
| * Copyright (c) 2015-2023 LunarG, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "logging.h" |
| |
| #include <csignal> |
| #include <cstring> |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| #include <debugapi.h> |
| #endif |
| |
| #include <vulkan/vk_enum_string_helper.h> |
| #include "generated/vk_safe_struct.h" |
| #include "generated/vk_validation_error_messages.h" |
| #include "external/xxhash.h" |
| #include "error_location.h" |
| |
| [[maybe_unused]] const char *kVUIDUndefined = "VUID_Undefined"; |
| |
| VKAPI_ATTR void SetDebugUtilsSeverityFlags(std::vector<VkLayerDbgFunctionState> &callbacks, debug_report_data *debug_data) { |
| // For all callback in list, return their complete set of severities and modes |
| for (const auto &item : callbacks) { |
| if (item.IsUtils()) { |
| debug_data->active_severities |= item.debug_utils_msg_flags; |
| debug_data->active_types |= item.debug_utils_msg_type; |
| } else { |
| VkFlags severities = 0; |
| VkFlags types = 0; |
| DebugReportFlagsToAnnotFlags(item.debug_report_msg_flags, &severities, &types); |
| debug_data->active_severities |= severities; |
| debug_data->active_types |= types; |
| } |
| } |
| } |
| |
| VKAPI_ATTR void RemoveDebugUtilsCallback(debug_report_data *debug_data, std::vector<VkLayerDbgFunctionState> &callbacks, |
| uint64_t callback) { |
| auto item = callbacks.begin(); |
| for (item = callbacks.begin(); item != callbacks.end(); item++) { |
| if (item->IsUtils()) { |
| if (item->debug_utils_callback_object == CastToHandle<VkDebugUtilsMessengerEXT>(callback)) break; |
| } else { |
| if (item->debug_report_callback_object == CastToHandle<VkDebugReportCallbackEXT>(callback)) break; |
| } |
| } |
| if (item != callbacks.end()) { |
| callbacks.erase(item); |
| } |
| SetDebugUtilsSeverityFlags(callbacks, debug_data); |
| } |
| |
| // Returns TRUE if the number of times this message has been logged is over the set limit |
| static bool UpdateLogMsgCounts(const debug_report_data *debug_data, int32_t vuid_hash) { |
| auto vuid_count_it = debug_data->duplicate_message_count_map.find(vuid_hash); |
| if (vuid_count_it == debug_data->duplicate_message_count_map.end()) { |
| debug_data->duplicate_message_count_map.emplace(vuid_hash, 1); |
| return false; |
| } else { |
| if (vuid_count_it->second >= debug_data->duplicate_message_limit) { |
| return true; |
| } else { |
| vuid_count_it->second++; |
| return false; |
| } |
| } |
| } |
| |
| static bool debug_log_msg(const debug_report_data *debug_data, VkFlags msg_flags, const LogObjectList &objects, |
| const char *layer_prefix, const char *message, const char *text_vuid) { |
| bool bail = false; |
| std::vector<VkDebugUtilsLabelEXT> queue_labels; |
| std::vector<VkDebugUtilsLabelEXT> cmd_buf_labels; |
| |
| // Convert the info to the VK_EXT_debug_utils format |
| VkDebugUtilsMessageTypeFlagsEXT types; |
| VkDebugUtilsMessageSeverityFlagsEXT severity; |
| DebugReportFlagsToAnnotFlags(msg_flags, &severity, &types); |
| |
| std::vector<std::string> object_labels; |
| // Ensures that push_back will not reallocate, thereby providing pointer |
| // stability for pushed strings. |
| object_labels.reserve(objects.object_list.size()); |
| |
| std::vector<VkDebugUtilsObjectNameInfoEXT> object_name_infos; |
| object_name_infos.reserve(objects.object_list.size()); |
| for (uint32_t i = 0; i < objects.object_list.size(); i++) { |
| // If only one VkDevice was created, it is just noise to print it out in the error message. |
| // Also avoid printing unknown objects, likely if new function is calling error with null LogObjectList |
| if ((objects.object_list[i].type == kVulkanObjectTypeDevice && debug_data->device_created <= 1) || |
| (objects.object_list[i].type == kVulkanObjectTypeUnknown) || (objects.object_list[i].handle == 0)) { |
| continue; |
| } |
| |
| VkDebugUtilsObjectNameInfoEXT object_name_info = vku::InitStructHelper(); |
| object_name_info.objectType = ConvertVulkanObjectToCoreObject(objects.object_list[i].type); |
| object_name_info.objectHandle = objects.object_list[i].handle; |
| object_name_info.pObjectName = nullptr; |
| |
| std::string object_label = {}; |
| // Look for any debug utils or marker names to use for this object |
| object_label = debug_data->DebugReportGetUtilsObjectName(objects.object_list[i].handle); |
| if (object_label.empty()) { |
| object_label = debug_data->DebugReportGetMarkerObjectName(objects.object_list[i].handle); |
| } |
| if (!object_label.empty()) { |
| object_labels.push_back(std::move(object_label)); |
| object_name_info.pObjectName = object_labels.back().c_str(); |
| } |
| |
| // If this is a queue, add any queue labels to the callback data. |
| if (VK_OBJECT_TYPE_QUEUE == object_name_info.objectType) { |
| auto label_iter = debug_data->debugUtilsQueueLabels.find(reinterpret_cast<VkQueue>(object_name_info.objectHandle)); |
| if (label_iter != debug_data->debugUtilsQueueLabels.end()) { |
| auto found_queue_labels = label_iter->second->Export(); |
| queue_labels.insert(queue_labels.end(), found_queue_labels.begin(), found_queue_labels.end()); |
| } |
| // If this is a command buffer, add any command buffer labels to the callback data. |
| } else if (VK_OBJECT_TYPE_COMMAND_BUFFER == object_name_info.objectType) { |
| auto label_iter = |
| debug_data->debugUtilsCmdBufLabels.find(reinterpret_cast<VkCommandBuffer>(object_name_info.objectHandle)); |
| if (label_iter != debug_data->debugUtilsCmdBufLabels.end()) { |
| auto found_cmd_buf_labels = label_iter->second->Export(); |
| cmd_buf_labels.insert(cmd_buf_labels.end(), found_cmd_buf_labels.begin(), found_cmd_buf_labels.end()); |
| } |
| } |
| |
| object_name_infos.push_back(object_name_info); |
| } |
| |
| const uint32_t message_id_number = text_vuid ? vvl_vuid_hash(text_vuid) : 0U; |
| |
| VkDebugUtilsMessengerCallbackDataEXT callback_data = vku::InitStructHelper(); |
| callback_data.flags = 0; |
| callback_data.pMessageIdName = text_vuid; |
| callback_data.messageIdNumber = vvl_bit_cast<int32_t>(message_id_number); |
| callback_data.pMessage = nullptr; |
| callback_data.queueLabelCount = static_cast<uint32_t>(queue_labels.size()); |
| callback_data.pQueueLabels = queue_labels.empty() ? nullptr : queue_labels.data(); |
| callback_data.cmdBufLabelCount = static_cast<uint32_t>(cmd_buf_labels.size()); |
| callback_data.pCmdBufLabels = cmd_buf_labels.empty() ? nullptr : cmd_buf_labels.data(); |
| callback_data.objectCount = static_cast<uint32_t>(object_name_infos.size()); |
| callback_data.pObjects = object_name_infos.data(); |
| |
| std::ostringstream oss; |
| if (msg_flags & kErrorBit) { |
| oss << "Validation Error: "; |
| } else if (msg_flags & kWarningBit) { |
| oss << "Validation Warning: "; |
| } else if (msg_flags & kPerformanceWarningBit) { |
| oss << "Validation Performance Warning: "; |
| } else if (msg_flags & kInformationBit) { |
| oss << "Validation Information: "; |
| } else if (msg_flags & kVerboseBit) { |
| oss << "Verbose Information: "; |
| } |
| if (text_vuid != nullptr) { |
| oss << "[ " << text_vuid << " ] "; |
| } |
| uint32_t index = 0; |
| for (const auto &src_object : object_name_infos) { |
| if (0 != src_object.objectHandle) { |
| oss << "Object " << index++ << ": handle = 0x" << std::hex << src_object.objectHandle; |
| if (src_object.pObjectName) { |
| oss << ", name = " << src_object.pObjectName << ", type = "; |
| } else { |
| oss << ", type = "; |
| } |
| oss << string_VkObjectType(src_object.objectType) << "; "; |
| } else { |
| oss << "Object " << index++ << ": VK_NULL_HANDLE, type = " << string_VkObjectType(src_object.objectType) << "; "; |
| } |
| } |
| oss << "| MessageID = 0x" << std::hex << message_id_number << " | " << message; |
| std::string composite = oss.str(); |
| |
| const auto callback_list = &debug_data->debug_callback_list; |
| // We only output to default callbacks if there are no non-default callbacks |
| bool use_default_callbacks = true; |
| for (const auto ¤t_callback : *callback_list) { |
| use_default_callbacks &= current_callback.IsDefault(); |
| } |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| if (debug_data->forceDefaultLogCallback) { |
| use_default_callbacks = true; |
| } |
| #endif |
| |
| for (const auto ¤t_callback : *callback_list) { |
| // Skip callback if it's a default callback and there are non-default callbacks present |
| if (current_callback.IsDefault() && !use_default_callbacks) continue; |
| |
| // VK_EXT_debug_utils callback |
| if (current_callback.IsUtils() && (current_callback.debug_utils_msg_flags & severity) && |
| (current_callback.debug_utils_msg_type & types)) { |
| callback_data.pMessage = composite.c_str(); |
| if (current_callback.debug_utils_callback_function_ptr(static_cast<VkDebugUtilsMessageSeverityFlagBitsEXT>(severity), |
| types, &callback_data, current_callback.pUserData)) { |
| bail = true; |
| } |
| } else if (!current_callback.IsUtils() && (current_callback.debug_report_msg_flags & msg_flags)) { |
| // VK_EXT_debug_report callback (deprecated) |
| if (object_name_infos.empty()) { |
| VkDebugUtilsObjectNameInfoEXT null_object_name = {VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, nullptr, |
| VK_OBJECT_TYPE_UNKNOWN, 0, nullptr}; |
| // need to have at least one object |
| object_name_infos.emplace_back(null_object_name); |
| } |
| if (current_callback.debug_report_callback_function_ptr( |
| msg_flags, convertCoreObjectToDebugReportObject(object_name_infos[0].objectType), |
| object_name_infos[0].objectHandle, message_id_number, 0, layer_prefix, composite.c_str(), |
| current_callback.pUserData)) { |
| bail = true; |
| } |
| } |
| } |
| return bail; |
| } |
| |
| VKAPI_ATTR void LayerDebugUtilsDestroyInstance(debug_report_data *debug_data) { delete debug_data; } |
| |
| template <typename TCreateInfo, typename TCallback> |
| static void LayerCreateCallback(DebugCallbackStatusFlags callback_status, debug_report_data *debug_data, |
| const TCreateInfo *create_info, TCallback *callback) { |
| std::unique_lock<std::mutex> lock(debug_data->debug_output_mutex); |
| |
| debug_data->debug_callback_list.emplace_back(VkLayerDbgFunctionState()); |
| auto &callback_state = debug_data->debug_callback_list.back(); |
| callback_state.callback_status = callback_status; |
| callback_state.pUserData = create_info->pUserData; |
| |
| if (callback_state.IsUtils()) { |
| auto utils_create_info = reinterpret_cast<const VkDebugUtilsMessengerCreateInfoEXT *>(create_info); |
| auto utils_callback = reinterpret_cast<VkDebugUtilsMessengerEXT *>(callback); |
| if (!(*utils_callback)) { |
| // callback constructed default callbacks have no handle -- so use struct address as unique handle |
| *utils_callback = reinterpret_cast<VkDebugUtilsMessengerEXT>(&callback_state); |
| } |
| callback_state.debug_utils_callback_object = *utils_callback; |
| callback_state.debug_utils_callback_function_ptr = utils_create_info->pfnUserCallback; |
| callback_state.debug_utils_msg_flags = utils_create_info->messageSeverity; |
| callback_state.debug_utils_msg_type = utils_create_info->messageType; |
| } else { // Debug report callback |
| auto report_create_info = reinterpret_cast<const VkDebugReportCallbackCreateInfoEXT *>(create_info); |
| auto report_callback = reinterpret_cast<VkDebugReportCallbackEXT *>(callback); |
| if (!(*report_callback)) { |
| // Internally constructed default callbacks have no handle -- so use struct address as unique handle |
| *report_callback = reinterpret_cast<VkDebugReportCallbackEXT>(&callback_state); |
| } |
| callback_state.debug_report_callback_object = *report_callback; |
| callback_state.debug_report_callback_function_ptr = report_create_info->pfnCallback; |
| callback_state.debug_report_msg_flags = report_create_info->flags; |
| } |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| // On Android, if the default callback system property is set, force the default callback to be printed |
| std::string forceLayerLog = GetEnvironment(kForceDefaultCallbackKey); |
| int forceDefaultCallback = atoi(forceLayerLog.c_str()); |
| if (forceDefaultCallback == 1) { |
| debug_data->forceDefaultLogCallback = true; |
| } |
| #endif |
| |
| SetDebugUtilsSeverityFlags(debug_data->debug_callback_list, debug_data); |
| } |
| |
| VKAPI_ATTR VkResult LayerCreateMessengerCallback(debug_report_data *debug_data, bool default_callback, |
| const VkDebugUtilsMessengerCreateInfoEXT *create_info, |
| VkDebugUtilsMessengerEXT *messenger) { |
| LayerCreateCallback((DEBUG_CALLBACK_UTILS | (default_callback ? DEBUG_CALLBACK_DEFAULT : 0)), debug_data, create_info, |
| messenger); |
| return VK_SUCCESS; |
| } |
| |
| VKAPI_ATTR VkResult LayerCreateReportCallback(debug_report_data *debug_data, bool default_callback, |
| const VkDebugReportCallbackCreateInfoEXT *create_info, |
| VkDebugReportCallbackEXT *callback) { |
| LayerCreateCallback((default_callback ? DEBUG_CALLBACK_DEFAULT : 0), debug_data, create_info, callback); |
| return VK_SUCCESS; |
| } |
| |
| VKAPI_ATTR void ActivateInstanceDebugCallbacks(debug_report_data *debug_data) { |
| auto current = debug_data->instance_pnext_chain; |
| for (;;) { |
| auto create_info = vku::FindStructInPNextChain<VkDebugUtilsMessengerCreateInfoEXT>(current); |
| if (!create_info) break; |
| current = create_info->pNext; |
| VkDebugUtilsMessengerEXT utils_callback{}; |
| LayerCreateCallback((DEBUG_CALLBACK_UTILS | DEBUG_CALLBACK_INSTANCE), debug_data, create_info, &utils_callback); |
| } |
| for (;;) { |
| auto create_info = vku::FindStructInPNextChain<VkDebugReportCallbackCreateInfoEXT>(current); |
| if (!create_info) break; |
| current = create_info->pNext; |
| VkDebugReportCallbackEXT report_callback{}; |
| LayerCreateCallback(DEBUG_CALLBACK_INSTANCE, debug_data, create_info, &report_callback); |
| } |
| } |
| |
| VKAPI_ATTR void DeactivateInstanceDebugCallbacks(debug_report_data *debug_data) { |
| if (!vku::FindStructInPNextChain<VkDebugUtilsMessengerCreateInfoEXT>(debug_data->instance_pnext_chain) && |
| !vku::FindStructInPNextChain<VkDebugReportCallbackCreateInfoEXT>(debug_data->instance_pnext_chain)) |
| return; |
| std::vector<VkDebugUtilsMessengerEXT> instance_utils_callback_handles{}; |
| std::vector<VkDebugReportCallbackEXT> instance_report_callback_handles{}; |
| for (const auto &item : debug_data->debug_callback_list) { |
| if (item.IsInstance()) { |
| if (item.IsUtils()) { |
| instance_utils_callback_handles.push_back(item.debug_utils_callback_object); |
| } else { |
| instance_report_callback_handles.push_back(item.debug_report_callback_object); |
| } |
| } |
| } |
| for (const auto &item : instance_utils_callback_handles) { |
| LayerDestroyCallback(debug_data, item); |
| } |
| for (const auto &item : instance_report_callback_handles) { |
| LayerDestroyCallback(debug_data, item); |
| } |
| } |
| |
| // helper for VUID based filtering. This needs to be separate so it can be called before incurring |
| // the cost of sprintf()-ing the err_msg needed by LogMsgLocked(). |
| static bool LogMsgEnabled(const debug_report_data *debug_data, std::string_view vuid_text, |
| VkDebugUtilsMessageSeverityFlagsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type) { |
| if (!(debug_data->active_severities & severity) || !(debug_data->active_types & type)) { |
| return false; |
| } |
| // If message is in filter list, bail out very early |
| const uint32_t message_id = vvl_vuid_hash(vuid_text); |
| if (debug_data->filter_message_ids.find(message_id) != debug_data->filter_message_ids.end()) { |
| return false; |
| } |
| if ((debug_data->duplicate_message_limit > 0) && UpdateLogMsgCounts(debug_data, static_cast<int32_t>(message_id))) { |
| // Count for this particular message is over the limit, ignore it |
| return false; |
| } |
| return true; |
| } |
| |
| VKAPI_ATTR bool LogMsg(const debug_report_data *debug_data, VkFlags msg_flags, const LogObjectList &objects, const Location *loc, |
| std::string_view vuid_text, const char *format, va_list argptr) { |
| assert(*(vuid_text.data() + vuid_text.size()) == '\0'); |
| |
| VkDebugUtilsMessageSeverityFlagsEXT severity; |
| VkDebugUtilsMessageTypeFlagsEXT type; |
| |
| DebugReportFlagsToAnnotFlags(msg_flags, &severity, &type); |
| std::unique_lock<std::mutex> lock(debug_data->debug_output_mutex); |
| // Avoid logging cost if msg is to be ignored |
| if (!LogMsgEnabled(debug_data, vuid_text, severity, type)) { |
| return false; |
| } |
| |
| // Best guess at an upper bound for message length. At least some of the extra space |
| // should get used to store the VUID URL and text in the common case, without additional allocations. |
| std::string str_plus_spec_text(1024, '\0'); |
| |
| // vsnprintf() returns the number of characters that *would* have been printed, if there was |
| // enough space. If we have a huge message, reallocate the string and try again. |
| int result; |
| size_t old_size = str_plus_spec_text.size(); |
| // The va_list will be destroyed by the call to vsnprintf(), so use a copy in case we need |
| // to try again. |
| va_list arg_copy; |
| va_copy(arg_copy, argptr); |
| result = vsnprintf(str_plus_spec_text.data(), str_plus_spec_text.size(), format, arg_copy); |
| va_end(arg_copy); |
| |
| assert(result >= 0); |
| if (result < 0) { |
| str_plus_spec_text = "Message generation failure"; |
| } else if (static_cast<size_t>(result) <= old_size) { |
| // Shrink the string to exactly fit the successfully printed string |
| str_plus_spec_text.resize(result); |
| } else { |
| // Grow buffer to fit needed size. Note that the input size to vsnprintf() must |
| // include space for the trailing '\0' character, but the return value DOES NOT |
| // include the `\0' character. |
| str_plus_spec_text.resize(result + 1); |
| // consume the va_list passed to us by the caller |
| result = vsnprintf(str_plus_spec_text.data(), str_plus_spec_text.size(), format, argptr); |
| // remove the `\0' character from the string |
| str_plus_spec_text.resize(result); |
| } |
| |
| // TODO - make Location a reference once old LogError is gone |
| if (loc) { |
| str_plus_spec_text = loc->Message() + " " + str_plus_spec_text; |
| } |
| |
| // Append the spec error text to the error message, unless it contains a word treated as special |
| if ((vuid_text.find("UNASSIGNED-") == std::string::npos) && (vuid_text.find(kVUIDUndefined) == std::string::npos) && |
| (vuid_text.rfind("SYNC-", 0) == std::string::npos) && (vuid_text.find("INTERNAL-ERROR-") == std::string::npos)) { |
| // Linear search makes no assumptions about the layout of the string table. This is not fast, but it does not need to be at |
| // this point in the error reporting path |
| uint32_t num_vuids = sizeof(vuid_spec_text) / sizeof(vuid_spec_text_pair); |
| const char *spec_text = nullptr; |
| std::string spec_type; |
| for (uint32_t i = 0; i < num_vuids; i++) { |
| if (0 == strncmp(vuid_text.data(), vuid_spec_text[i].vuid, vuid_text.size())) { |
| spec_text = vuid_spec_text[i].spec_text; |
| spec_type = vuid_spec_text[i].url_id; |
| break; |
| } |
| } |
| |
| // Construct and append the specification text and link to the appropriate version of the spec |
| if (nullptr != spec_text) { |
| std::string spec_link = "https://www.khronos.org/registry/vulkan/specs/_MAGIC_KHRONOS_SPEC_TYPE_/html/vkspec.html"; |
| #ifdef ANNOTATED_SPEC_LINK |
| spec_link = ANNOTATED_SPEC_LINK; |
| #endif |
| static std::string kAtToken = "_MAGIC_ANNOTATED_SPEC_TYPE_"; |
| static std::string kKtToken = "_MAGIC_KHRONOS_SPEC_TYPE_"; |
| static std::string kVeToken = "_MAGIC_VERSION_ID_"; |
| auto Replace = [](std::string &dest_string, const std::string &to_replace, const std::string &replace_with) { |
| if (dest_string.find(to_replace) != std::string::npos) { |
| dest_string.replace(dest_string.find(to_replace), to_replace.size(), replace_with); |
| } |
| }; |
| |
| // Add period at end if forgotten |
| // This provides better seperation between error message and spec text |
| if (str_plus_spec_text.back() != '.') { |
| str_plus_spec_text.append("."); |
| } |
| |
| str_plus_spec_text.append(" The Vulkan spec states: "); |
| str_plus_spec_text.append(spec_text); |
| if (0 == spec_type.compare("default")) { |
| str_plus_spec_text.append(" (https://github.com/KhronosGroup/Vulkan-Docs/search?q=)"); |
| } else { |
| str_plus_spec_text.append(" ("); |
| str_plus_spec_text.append(spec_link); |
| std::string major_version = std::to_string(VK_VERSION_MAJOR(VK_HEADER_VERSION_COMPLETE)); |
| std::string minor_version = std::to_string(VK_VERSION_MINOR(VK_HEADER_VERSION_COMPLETE)); |
| std::string patch_version = std::to_string(VK_VERSION_PATCH(VK_HEADER_VERSION_COMPLETE)); |
| std::string header_version = major_version + "." + minor_version + "." + patch_version; |
| std::string annotated_spec_type = major_version + "." + minor_version + "-extensions"; |
| Replace(str_plus_spec_text, kKtToken, spec_type); |
| Replace(str_plus_spec_text, kAtToken, annotated_spec_type); |
| Replace(str_plus_spec_text, kVeToken, header_version); |
| str_plus_spec_text.append("#"); // CMake hates hashes |
| } |
| str_plus_spec_text.append(vuid_text); |
| str_plus_spec_text.append(")"); |
| } |
| } |
| |
| return debug_log_msg(debug_data, msg_flags, objects, "Validation", str_plus_spec_text.c_str(), vuid_text.data()); |
| } |
| |
| VKAPI_ATTR VkBool32 VKAPI_CALL MessengerBreakCallback([[maybe_unused]] VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, |
| [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT message_type, |
| [[maybe_unused]] const VkDebugUtilsMessengerCallbackDataEXT *callback_data, |
| [[maybe_unused]] void *user_data) { |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| DebugBreak(); |
| #else |
| raise(SIGTRAP); |
| #endif |
| |
| return false; |
| } |
| |
| VKAPI_ATTR VkBool32 VKAPI_CALL MessengerLogCallback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, |
| VkDebugUtilsMessageTypeFlagsEXT message_type, |
| const VkDebugUtilsMessengerCallbackDataEXT *callback_data, void *user_data) { |
| std::ostringstream msg_buffer; |
| char msg_severity[30]; |
| char msg_type[30]; |
| |
| PrintMessageSeverity(message_severity, msg_severity); |
| PrintMessageType(message_type, msg_type); |
| |
| msg_buffer << callback_data->pMessageIdName << "(" << msg_severity << " / " << msg_type |
| << "): msgNum: " << callback_data->messageIdNumber << " - " << callback_data->pMessage << "\n"; |
| msg_buffer << " Objects: " << callback_data->objectCount << "\n"; |
| for (uint32_t obj = 0; obj < callback_data->objectCount; ++obj) { |
| msg_buffer << " [" << obj << "] " << std::hex << std::showbase |
| << HandleToUint64(callback_data->pObjects[obj].objectHandle) << ", type: " << std::dec << std::noshowbase |
| << callback_data->pObjects[obj].objectType |
| << ", name: " << (callback_data->pObjects[obj].pObjectName ? callback_data->pObjects[obj].pObjectName : "NULL") |
| << "\n"; |
| } |
| const std::string tmp = msg_buffer.str(); |
| const char *cstr = tmp.c_str(); |
| fprintf((FILE *)user_data, "%s", cstr); |
| fflush((FILE *)user_data); |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| LOGCONSOLE("%s", cstr); |
| #endif |
| |
| return false; |
| } |
| |
| VKAPI_ATTR VkBool32 VKAPI_CALL MessengerWin32DebugOutputMsg(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, |
| VkDebugUtilsMessageTypeFlagsEXT message_type, |
| const VkDebugUtilsMessengerCallbackDataEXT *callback_data, |
| [[maybe_unused]] void *user_data) { |
| std::ostringstream msg_buffer; |
| char msg_severity[30]; |
| char msg_type[30]; |
| |
| PrintMessageSeverity(message_severity, msg_severity); |
| PrintMessageType(message_type, msg_type); |
| |
| msg_buffer << callback_data->pMessageIdName << "(" << msg_severity << " / " << msg_type |
| << "): msgNum: " << callback_data->messageIdNumber << " - " << callback_data->pMessage << "\n"; |
| msg_buffer << " Objects: " << callback_data->objectCount << "\n"; |
| |
| for (uint32_t obj = 0; obj < callback_data->objectCount; ++obj) { |
| msg_buffer << " [" << obj << "] " << std::hex << std::showbase |
| << HandleToUint64(callback_data->pObjects[obj].objectHandle) << ", type: " << std::dec << std::noshowbase |
| << callback_data->pObjects[obj].objectType |
| << ", name: " << (callback_data->pObjects[obj].pObjectName ? callback_data->pObjects[obj].pObjectName : "NULL") |
| << "\n"; |
| } |
| const std::string tmp = msg_buffer.str(); |
| [[maybe_unused]] const char *cstr = tmp.c_str(); |
| |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| OutputDebugString(cstr); |
| #endif |
| |
| return false; |
| } |
| |
| uint32_t vvl_vuid_hash(std::string_view vuid) { |
| constexpr uint32_t seed = 8; |
| return XXH32(vuid.data(), vuid.size(), seed); |
| } |