From 595526e44623b950acd2bae1e958d21d55df0333 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 9 Aug 2022 15:56:27 +0200 Subject: [PATCH] vulkan: Port to VK_EXT_debug_utils Fixes: QTBUG-89762 Change-Id: Ie32043578968cbeda7c7f87990b98c10f5d06ff8 Reviewed-by: Andy Nichols Reviewed-by: Qt CI Bot --- src/gui/rhi/qrhivulkan.cpp | 138 ++++++++++-------- src/gui/rhi/qrhivulkan_p_p.h | 26 ++-- .../vulkan/qbasicvulkanplatforminstance.cpp | 103 ++++++++----- .../vulkan/qbasicvulkanplatforminstance_p.h | 17 ++- src/gui/vulkan/qplatformvulkaninstance.cpp | 5 + src/gui/vulkan/qplatformvulkaninstance.h | 1 + src/gui/vulkan/qvulkaninstance.cpp | 89 ++++++++++- src/gui/vulkan/qvulkaninstance.h | 21 +++ src/gui/vulkan/qvulkaninstance_p.h | 3 +- 9 files changed, 291 insertions(+), 112 deletions(-) diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 231dac06b3..bd6a168e31 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -300,7 +300,6 @@ QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice() { return { QByteArrayLiteral("VK_KHR_swapchain"), - QByteArrayLiteral("VK_EXT_debug_marker"), QByteArrayLiteral("VK_EXT_vertex_attribute_divisor") }; } @@ -328,20 +327,19 @@ QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *im } } -static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, - size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage) +static bool qvk_debug_filter(QVulkanInstance::DebugMessageSeverityFlags severity, + QVulkanInstance::DebugMessageTypeFlags type, + const void *callbackData) { - Q_UNUSED(flags); - Q_UNUSED(objectType); - Q_UNUSED(object); - Q_UNUSED(location); - Q_UNUSED(messageCode); - Q_UNUSED(pLayerPrefix); + Q_UNUSED(severity); + Q_UNUSED(type); +#ifdef VK_EXT_debug_utils + const VkDebugUtilsMessengerCallbackDataEXT *d = static_cast(callbackData); // Filter out certain misleading validation layer messages, as per // VulkanMemoryAllocator documentation. - if (strstr(pMessage, "Mapping an image with layout") - && strstr(pMessage, "can result in undefined behavior if this memory is used by the device")) + if (strstr(d->pMessage, "Mapping an image with layout") + && strstr(d->pMessage, "can result in undefined behavior if this memory is used by the device")) { return true; } @@ -352,9 +350,11 @@ static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTyp // then move on to another pool. If there is a real error, a qWarning // message is shown by allocateDescriptorSet(), so the validation warning // does not have any value and is just noise. - if (strstr(pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307")) + if (strstr(d->pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307")) return true; - +#else + Q_UNUSED(callbackData); +#endif return false; } @@ -394,6 +394,7 @@ bool QRhiVulkan::create(QRhi::Flags flags) for (const char *ext : inst->extensions()) qCDebug(QRHI_LOG_INFO, " %s", ext); } + caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils")); QList queueFamilyProps; auto queryQueueFamilyProps = [this, &queueFamilyProps] { @@ -551,12 +552,6 @@ bool QRhiVulkan::create(QRhi::Flags flags) QList requestedDevExts; requestedDevExts.append("VK_KHR_swapchain"); - caps.debugMarkers = false; - if (devExts.contains(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) { - requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); - caps.debugMarkers = true; - } - caps.vertexAttribDivisor = false; if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) { if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) { @@ -770,12 +765,14 @@ bool QRhiVulkan::create(QRhi::Flags flags) timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair timestampQueryPoolMap.fill(false); - if (caps.debugMarkers) { - vkCmdDebugMarkerBegin = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT")); - vkCmdDebugMarkerEnd = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT")); - vkCmdDebugMarkerInsert = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerInsertEXT")); - vkDebugMarkerSetObjectName = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkDebugMarkerSetObjectNameEXT")); +#ifdef VK_EXT_debug_utils + if (caps.debugUtils) { + vkSetDebugUtilsObjectNameEXT = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkSetDebugUtilsObjectNameEXT")); + vkCmdBeginDebugUtilsLabelEXT = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdBeginDebugUtilsLabelEXT")); + vkCmdEndDebugUtilsLabelEXT = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdEndDebugUtilsLabelEXT")); + vkCmdInsertDebugUtilsLabelEXT = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCmdInsertDebugUtilsLabelEXT")); } +#endif deviceLost = false; @@ -3914,17 +3911,23 @@ void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD) cmd.args.drawIndexed.firstInstance); break; case QVkCommandBuffer::Command::DebugMarkerBegin: - cmd.args.debugMarkerBegin.marker.pMarkerName = - cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.markerNameIndex].constData(); - vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker); +#ifdef VK_EXT_debug_utils + cmd.args.debugMarkerBegin.label.pLabelName = + cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.labelNameIndex].constData(); + vkCmdBeginDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerBegin.label); +#endif break; case QVkCommandBuffer::Command::DebugMarkerEnd: - vkCmdDebugMarkerEnd(cbD->cb); +#ifdef VK_EXT_debug_utils + vkCmdEndDebugUtilsLabelEXT(cbD->cb); +#endif break; case QVkCommandBuffer::Command::DebugMarkerInsert: - cmd.args.debugMarkerInsert.marker.pMarkerName = - cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.markerNameIndex].constData(); - vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker); +#ifdef VK_EXT_debug_utils + cmd.args.debugMarkerInsert.label.pLabelName = + cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.labelNameIndex].constData(); + vkCmdInsertDebugUtilsLabelEXT(cbD->cb, &cmd.args.debugMarkerInsert.label); +#endif break; case QVkCommandBuffer::Command::TransitionPassResources: recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]); @@ -4251,7 +4254,7 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const case QRhi::MultisampleRenderBuffer: return true; case QRhi::DebugMarkers: - return caps.debugMarkers; + return caps.debugUtils; case QRhi::Timestamps: return timestampValidBits != 0; case QRhi::Instancing: @@ -5046,58 +5049,72 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) { - if (!debugMarkers || !caps.debugMarkers) +#ifdef VK_EXT_debug_utils + if (!debugMarkers || !caps.debugUtils) return; - VkDebugMarkerMarkerInfoEXT marker = {}; - marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + VkDebugUtilsLabelEXT label = {}; + label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) { - marker.pMarkerName = name.constData(); - vkCmdDebugMarkerBegin(cbD->activeSecondaryCbStack.last(), &marker); + label.pLabelName = name.constData(); + vkCmdBeginDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label); } else { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin; - cmd.args.debugMarkerBegin.marker = marker; - cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerData.count(); + cmd.args.debugMarkerBegin.label = label; + cmd.args.debugMarkerBegin.labelNameIndex = cbD->pools.debugMarkerData.count(); cbD->pools.debugMarkerData.append(name); } +#else + Q_UNUSED(cb); + Q_UNUSED(name); +#endif } void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb) { - if (!debugMarkers || !caps.debugMarkers) +#ifdef VK_EXT_debug_utils + if (!debugMarkers || !caps.debugUtils) return; QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) { - vkCmdDebugMarkerEnd(cbD->activeSecondaryCbStack.last()); + vkCmdEndDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last()); } else { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd; } +#else + Q_UNUSED(cb); +#endif } void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) { - if (!debugMarkers || !caps.debugMarkers) +#ifdef VK_EXT_debug_utils + if (!debugMarkers || !caps.debugUtils) return; - VkDebugMarkerMarkerInfoEXT marker = {}; - marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + VkDebugUtilsLabelEXT label = {}; + label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->passUsesSecondaryCb) { - marker.pMarkerName = msg.constData(); - vkCmdDebugMarkerInsert(cbD->activeSecondaryCbStack.last(), &marker); + label.pLabelName = msg.constData(); + vkCmdInsertDebugUtilsLabelEXT(cbD->activeSecondaryCbStack.last(), &label); } else { QVkCommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert; - cmd.args.debugMarkerInsert.marker = marker; - cmd.args.debugMarkerInsert.markerNameIndex = cbD->pools.debugMarkerData.count(); + cmd.args.debugMarkerInsert.label = label; + cmd.args.debugMarkerInsert.labelNameIndex = cbD->pools.debugMarkerData.count(); cbD->pools.debugMarkerData.append(msg); } +#else + Q_UNUSED(cb); + Q_UNUSED(msg); +#endif } const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb) @@ -5178,22 +5195,29 @@ void QRhiVulkan::endExternal(QRhiCommandBuffer *cb) cbD->resetCachedState(); } -void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot) +void QRhiVulkan::setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot) { - if (!debugMarkers || !caps.debugMarkers || name.isEmpty()) +#ifdef VK_EXT_debug_utils + if (!debugMarkers || !caps.debugUtils || name.isEmpty()) return; - VkDebugMarkerObjectNameInfoEXT nameInfo = {}; - nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT; + VkDebugUtilsObjectNameInfoEXT nameInfo = {}; + nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; nameInfo.objectType = type; - nameInfo.object = object; + nameInfo.objectHandle = object; QByteArray decoratedName = name; if (slot >= 0) { decoratedName += '/'; decoratedName += QByteArray::number(slot); } nameInfo.pObjectName = decoratedName.constData(); - vkDebugMarkerSetObjectName(dev, &nameInfo); + vkSetDebugUtilsObjectNameEXT(dev, &nameInfo); +#else + Q_UNUSED(object); + Q_UNUSED(type); + Q_UNUSED(name); + Q_UNUSED(slot); +#endif } static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage) @@ -5681,7 +5705,7 @@ bool QVkBuffer::create() if (err != VK_SUCCESS) break; allocations[i] = allocation; - rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName, + rhiD->setObjectName(uint64_t(buffers[i]), VK_OBJECT_TYPE_BUFFER, m_objectName, m_type == Dynamic ? i : -1); } } @@ -5831,7 +5855,7 @@ bool QVkRenderBuffer::create() { return false; } - rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName); + rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName); break; default: Q_UNREACHABLE(); @@ -6095,7 +6119,7 @@ bool QVkTexture::create() if (!finishCreate()) return false; - rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName); + rhiD->setObjectName(uint64_t(image), VK_OBJECT_TYPE_IMAGE, m_objectName); owns = true; rhiD->registerResource(this); diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index 5785762859..fceb3efd1d 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -499,14 +499,18 @@ struct QVkCommandBuffer : public QRhiCommandBuffer uint32_t firstInstance; } drawIndexed; struct { - VkDebugMarkerMarkerInfoEXT marker; - int markerNameIndex; +#ifdef VK_EXT_debug_utils + VkDebugUtilsLabelEXT label; + int labelNameIndex; +#endif } debugMarkerBegin; struct { } debugMarkerEnd; struct { - VkDebugMarkerMarkerInfoEXT marker; - int markerNameIndex; +#ifdef VK_EXT_debug_utils + VkDebugUtilsLabelEXT label; + int labelNameIndex; +#endif } debugMarkerInsert; struct { int trackerIndex; @@ -795,7 +799,7 @@ public: void executeDeferredReleases(bool forced = false); void finishActiveReadbacks(bool forced = false); - void setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot = -1); + void setObjectName(uint64_t object, VkObjectType type, const QByteArray &name, int slot = -1); void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot, VkAccessFlags access, VkPipelineStageFlags stage); void trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD, @@ -833,10 +837,12 @@ public: bool deviceLost = false; bool releaseCachedResourcesCalledBeforeFrameStart = false; - PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBegin = nullptr; - PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEnd = nullptr; - PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsert = nullptr; - PFN_vkDebugMarkerSetObjectNameEXT vkDebugMarkerSetObjectName = nullptr; +#ifdef VK_EXT_debug_utils + PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = nullptr; + PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT = nullptr; + PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT = nullptr; + PFN_vkCmdInsertDebugUtilsLabelEXT vkCmdInsertDebugUtilsLabelEXT = nullptr; +#endif PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr; PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; @@ -850,7 +856,7 @@ public: struct { bool compute = false; bool wideLines = false; - bool debugMarkers = false; + bool debugUtils = false; bool vertexAttribDivisor = false; bool texture3DSliceAs2D = false; bool tessellation = false; diff --git a/src/gui/vulkan/qbasicvulkanplatforminstance.cpp b/src/gui/vulkan/qbasicvulkanplatforminstance.cpp index 6825da8069..c5a0b5de17 100644 --- a/src/gui/vulkan/qbasicvulkanplatforminstance.cpp +++ b/src/gui/vulkan/qbasicvulkanplatforminstance.cpp @@ -26,11 +26,6 @@ Q_LOGGING_CATEGORY(lcPlatVk, "qt.vulkan") */ QBasicPlatformVulkanInstance::QBasicPlatformVulkanInstance() - : m_vkInst(VK_NULL_HANDLE), - m_vkGetInstanceProcAddr(nullptr), - m_ownsVkInst(false), - m_errorCode(VK_SUCCESS), - m_debugCallback(VK_NULL_HANDLE) { } @@ -39,8 +34,10 @@ QBasicPlatformVulkanInstance::~QBasicPlatformVulkanInstance() if (!m_vkInst) return; - if (m_debugCallback && m_vkDestroyDebugReportCallbackEXT) - m_vkDestroyDebugReportCallbackEXT(m_vkInst, m_debugCallback, nullptr); +#ifdef VK_EXT_debug_utils + if (m_debugMessenger) + m_vkDestroyDebugUtilsMessengerEXT(m_vkInst, m_debugMessenger, nullptr); +#endif if (m_ownsVkInst) m_vkDestroyInstance(m_vkInst, nullptr); @@ -208,8 +205,7 @@ void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const m_enabledExtensions = instance->extensions(); if (!m_vkInst) { - VkApplicationInfo appInfo; - memset(&appInfo, 0, sizeof(appInfo)); + VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; QByteArray appName = QCoreApplication::applicationName().toUtf8(); appInfo.pApplicationName = appName.constData(); @@ -221,7 +217,7 @@ void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const } if (!flags.testFlag(QVulkanInstance::NoDebugOutputRedirect)) - m_enabledExtensions.append("VK_EXT_debug_report"); + m_enabledExtensions.append("VK_EXT_debug_utils"); m_enabledExtensions.append("VK_KHR_surface"); @@ -259,8 +255,7 @@ void QBasicPlatformVulkanInstance::initInstance(QVulkanInstance *instance, const } qDebug(lcPlatVk) << "Enabling Vulkan instance extensions:" << m_enabledExtensions; - VkInstanceCreateInfo instInfo; - memset(&instInfo, 0, sizeof(instInfo)); + VkInstanceCreateInfo instInfo = {}; instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instInfo.pApplicationInfo = &appInfo; @@ -365,55 +360,93 @@ void QBasicPlatformVulkanInstance::setDebugFilters(const QList &filters) +{ + m_debugUtilsFilters = filters; +} + void QBasicPlatformVulkanInstance::destroySurface(VkSurfaceKHR surface) const { if (m_destroySurface && surface) m_destroySurface(m_vkInst, surface, nullptr); } -static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallbackFunc(VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, - size_t location, - int32_t messageCode, - const char *pLayerPrefix, - const char *pMessage, +#ifdef VK_EXT_debug_utils +static VKAPI_ATTR VkBool32 VKAPI_CALL defaultDebugCallbackFunc(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) { QBasicPlatformVulkanInstance *self = static_cast(pUserData); + + // legacy filters for (QVulkanInstance::DebugFilter filter : *self->debugFilters()) { - if (filter(flags, objectType, object, location, messageCode, pLayerPrefix, pMessage)) + // As per docs in qvulkaninstance.cpp we pass object, messageCode, + // pMessage to the callback with the legacy signature. + uint64_t object = 0; + if (pCallbackData->objectCount > 0) + object = pCallbackData->pObjects[0].objectHandle; + if (filter(0, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, object, 0, + pCallbackData->messageIdNumber, "", pCallbackData->pMessage)) + { + return VK_FALSE; + } + } + + // filters with new signature + for (QVulkanInstance::DebugUtilsFilter filter : *self->debugUtilsFilters()) { + QVulkanInstance::DebugMessageSeverityFlags severity; + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) + severity |= QVulkanInstance::VerboseSeverity; + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) + severity |= QVulkanInstance::InfoSeverity; + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) + severity |= QVulkanInstance::WarningSeverity; + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + severity |= QVulkanInstance::ErrorSeverity; + QVulkanInstance::DebugMessageTypeFlags type; + if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + type |= QVulkanInstance::GeneralMessage; + if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) + type |= QVulkanInstance::ValidationMessage; + if (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) + type |= QVulkanInstance::PerformanceMessage; + if (filter(severity, type, pCallbackData)) return VK_FALSE; } // not categorized, just route to plain old qDebug - qDebug("vkDebug: %s: %d: %s", pLayerPrefix, messageCode, pMessage); + qDebug("vkDebug: %s", pCallbackData->pMessage); return VK_FALSE; } +#endif void QBasicPlatformVulkanInstance::setupDebugOutput() { - if (!m_enabledExtensions.contains("VK_EXT_debug_report")) +#ifdef VK_EXT_debug_utils + if (!m_enabledExtensions.contains("VK_EXT_debug_utils")) return; - PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback = reinterpret_cast( - m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDebugReportCallbackEXT")); - m_vkDestroyDebugReportCallbackEXT = reinterpret_cast( - m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyDebugReportCallbackEXT")); + PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = reinterpret_cast( + m_vkGetInstanceProcAddr(m_vkInst, "vkCreateDebugUtilsMessengerEXT")); - VkDebugReportCallbackCreateInfoEXT dbgCallbackInfo; - memset(&dbgCallbackInfo, 0, sizeof(dbgCallbackInfo)); - dbgCallbackInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - dbgCallbackInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT - | VK_DEBUG_REPORT_WARNING_BIT_EXT - | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - dbgCallbackInfo.pfnCallback = defaultDebugCallbackFunc; - dbgCallbackInfo.pUserData = this; + m_vkDestroyDebugUtilsMessengerEXT = reinterpret_cast( + m_vkGetInstanceProcAddr(m_vkInst, "vkDestroyDebugUtilsMessengerEXT")); - VkResult err = createDebugReportCallback(m_vkInst, &dbgCallbackInfo, nullptr, &m_debugCallback); + VkDebugUtilsMessengerCreateInfoEXT messengerInfo = {}; + messengerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + messengerInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + messengerInfo.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; + messengerInfo.pfnUserCallback = defaultDebugCallbackFunc; + messengerInfo.pUserData = this; + VkResult err = vkCreateDebugUtilsMessengerEXT(m_vkInst, &messengerInfo, nullptr, &m_debugMessenger); if (err != VK_SUCCESS) qWarning("Failed to create debug report callback: %d", err); +#endif } QT_END_NAMESPACE diff --git a/src/gui/vulkan/qbasicvulkanplatforminstance_p.h b/src/gui/vulkan/qbasicvulkanplatforminstance_p.h index 70e9d65ef3..dfa5003f16 100644 --- a/src/gui/vulkan/qbasicvulkanplatforminstance_p.h +++ b/src/gui/vulkan/qbasicvulkanplatforminstance_p.h @@ -40,17 +40,19 @@ public: PFN_vkVoidFunction getInstanceProcAddr(const char *name) override; bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override; void setDebugFilters(const QList &filters) override; + void setDebugUtilsFilters(const QList &filters) override; void destroySurface(VkSurfaceKHR surface) const; const QList *debugFilters() const { return &m_debugFilters; } + const QList *debugUtilsFilters() const { return &m_debugUtilsFilters; } protected: void loadVulkanLibrary(const QString &defaultLibraryName, int defaultLibraryVersion = -1); void init(QLibrary *lib); void initInstance(QVulkanInstance *instance, const QByteArrayList &extraExts); - VkInstance m_vkInst; - PFN_vkGetInstanceProcAddr m_vkGetInstanceProcAddr; + VkInstance m_vkInst = VK_NULL_HANDLE; + PFN_vkGetInstanceProcAddr m_vkGetInstanceProcAddr = nullptr; PFN_vkGetPhysicalDeviceSurfaceSupportKHR m_getPhysDevSurfaceSupport; PFN_vkDestroySurfaceKHR m_destroySurface; @@ -59,8 +61,8 @@ private: std::unique_ptr m_vulkanLib; - bool m_ownsVkInst; - VkResult m_errorCode; + bool m_ownsVkInst = false; + VkResult m_errorCode = VK_SUCCESS; QVulkanInfoVector m_supportedLayers; QVulkanInfoVector m_supportedExtensions; QVersionNumber m_supportedApiVersion; @@ -73,9 +75,12 @@ private: PFN_vkDestroyInstance m_vkDestroyInstance; - VkDebugReportCallbackEXT m_debugCallback; - PFN_vkDestroyDebugReportCallbackEXT m_vkDestroyDebugReportCallbackEXT; +#ifdef VK_EXT_debug_utils + VkDebugUtilsMessengerEXT m_debugMessenger = VK_NULL_HANDLE; + PFN_vkDestroyDebugUtilsMessengerEXT m_vkDestroyDebugUtilsMessengerEXT; +#endif QList m_debugFilters; + QList m_debugUtilsFilters; }; QT_END_NAMESPACE diff --git a/src/gui/vulkan/qplatformvulkaninstance.cpp b/src/gui/vulkan/qplatformvulkaninstance.cpp index 0349358993..2955f2f300 100644 --- a/src/gui/vulkan/qplatformvulkaninstance.cpp +++ b/src/gui/vulkan/qplatformvulkaninstance.cpp @@ -59,4 +59,9 @@ void QPlatformVulkanInstance::setDebugFilters(const QList &filters) +{ + Q_UNUSED(filters); +} + QT_END_NAMESPACE diff --git a/src/gui/vulkan/qplatformvulkaninstance.h b/src/gui/vulkan/qplatformvulkaninstance.h index 2bb89f1018..a3dcc37f0b 100644 --- a/src/gui/vulkan/qplatformvulkaninstance.h +++ b/src/gui/vulkan/qplatformvulkaninstance.h @@ -45,6 +45,7 @@ public: virtual void presentAboutToBeQueued(QWindow *window); virtual void presentQueued(QWindow *window); virtual void setDebugFilters(const QList &filters); + virtual void setDebugUtilsFilters(const QList &filters); private: QScopedPointer d_ptr; diff --git a/src/gui/vulkan/qvulkaninstance.cpp b/src/gui/vulkan/qvulkaninstance.cpp index bfc4b38b5a..83d1e9b1b5 100644 --- a/src/gui/vulkan/qvulkaninstance.cpp +++ b/src/gui/vulkan/qvulkaninstance.cpp @@ -104,7 +104,7 @@ QT_BEGIN_NAMESPACE \note It is up to the component creating the external instance to ensure the necessary extensions are enabled on it. These are: \c{VK_KHR_surface}, the WSI-specific \c{VK_KHR_*_surface} that is appropriate for the platform - in question, and \c{VK_EXT_debug_report} in case QVulkanInstance's debug + in question, and \c{VK_EXT_debug_utils} in case QVulkanInstance's debug output redirection is desired. \section1 Accessing Core Vulkan Commands @@ -205,7 +205,7 @@ QT_BEGIN_NAMESPACE This enum describes the flags that can be passed to setFlags(). These control the behavior of create(). - \value NoDebugOutputRedirect Disables Vulkan debug output (\c{VK_EXT_debug_report}) redirection to qDebug. + \value NoDebugOutputRedirect Disables Vulkan debug output (\c{VK_EXT_debug_utils}) redirection to qDebug. */ bool QVulkanInstancePrivate::ensureVulkan() @@ -425,7 +425,7 @@ QVersionNumber QVulkanInstance::supportedApiVersion() const \note \a existingVkInstance must have at least \c{VK_KHR_surface} and the appropriate WSI-specific \c{VK_KHR_*_surface} extensions enabled. To ensure - debug output redirection is functional, \c{VK_EXT_debug_report} is needed as + debug output redirection is functional, \c{VK_EXT_debug_utils} is needed as well. \note This function can only be called before create() and has no effect if @@ -812,10 +812,21 @@ void QVulkanInstance::presentQueued(QWindow *window) Typedef for debug filtering callback functions. + Returning \c true suppresses the printing of the message. + + \note Starting with Qt 6.5 \c{VK_EXT_debug_utils} is used instead of the + deprecated \c{VK_EXT_debug_report}. The callback signature is based on + VK_EXT_debug_report. Therefore, not all arguments can be expected to be + valid anymore. Avoid relying on arguments other than \c pMessage, \c + messageCode, and \c object. Applications wishing to access all the callback + data as specified in VK_EXT_debug_utils should migrate to DebugUtilsFilter. + \sa installDebugOutputFilter(), removeDebugOutputFilter() */ /*! + \overload + Installs a \a filter function that is called for every Vulkan debug message. When the callback returns \c true, the message is stopped (filtered out) and will not appear on the debug output. @@ -837,6 +848,8 @@ void QVulkanInstance::installDebugOutputFilter(DebugFilter filter) } /*! + \overload + Removes a \a filter function previously installed by installDebugOutputFilter(). @@ -851,6 +864,76 @@ void QVulkanInstance::removeDebugOutputFilter(DebugFilter filter) d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); } +/*! + \typedef QVulkanInstance::DebugUtilsFilter + + Typedef for debug filtering callback functions. The \c callbackData + argument is a pointer to the VkDebugUtilsMessengerCallbackDataEXT + structure. + + Returning \c true suppresses the printing of the message. + + \sa installDebugOutputFilter(), removeDebugOutputFilter() + \since 6.5 + */ + +/*! + \enum QVulkanInstance::DebugMessageSeverityFlag + \since 6.5 + + \value VerboseSeverity + \value InfoSeverity + \value WarningSeverity + \value ErrorSeverity + */ + +/*! + \enum QVulkanInstance::DebugMessageTypeFlag + \since 6.5 + + \value GeneralMessage + \value ValidationMessage + \value PerformanceMessage + */ + +/*! + Installs a \a filter function that is called for every Vulkan debug + message. When the callback returns \c true, the message is stopped (filtered + out) and will not appear on the debug output. + + \note Filtering is only effective when NoDebugOutputRedirect is not + \l{setFlags()}{set}. Installing filters has no effect otherwise. + + \note This function can be called before create(). + + \sa removeDebugOutputFilter() + \since 6.5 + */ +void QVulkanInstance::installDebugOutputFilter(DebugUtilsFilter filter) +{ + if (!d_ptr->debugUtilsFilters.contains(filter)) { + d_ptr->debugUtilsFilters.append(filter); + if (d_ptr->platformInst) + d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters); + } +} + +/*! + Removes a \a filter function previously installed by + installDebugOutputFilter(). + + \note This function can be called before create(). + + \sa installDebugOutputFilter() + \since 6.5 + */ +void QVulkanInstance::removeDebugOutputFilter(DebugUtilsFilter filter) +{ + d_ptr->debugUtilsFilters.removeOne(filter); + if (d_ptr->platformInst) + d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters); +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QVulkanLayer &layer) { diff --git a/src/gui/vulkan/qvulkaninstance.h b/src/gui/vulkan/qvulkaninstance.h index a51ce5ec99..6fd5116aab 100644 --- a/src/gui/vulkan/qvulkaninstance.h +++ b/src/gui/vulkan/qvulkaninstance.h @@ -187,6 +187,25 @@ public: void installDebugOutputFilter(DebugFilter filter); void removeDebugOutputFilter(DebugFilter filter); + enum DebugMessageSeverityFlag { + VerboseSeverity = 0x01, + InfoSeverity = 0x02, + WarningSeverity = 0x04, + ErrorSeverity = 0x08 + }; + Q_DECLARE_FLAGS(DebugMessageSeverityFlags, DebugMessageSeverityFlag) + + enum DebugMessageTypeFlag { + GeneralMessage = 0x01, + ValidationMessage = 0x02, + PerformanceMessage = 0x04 + }; + Q_DECLARE_FLAGS(DebugMessageTypeFlags, DebugMessageTypeFlag) + + typedef bool (*DebugUtilsFilter)(DebugMessageSeverityFlags severity, DebugMessageTypeFlags type, const void *callbackData); + void installDebugOutputFilter(DebugUtilsFilter filter); + void removeDebugOutputFilter(DebugUtilsFilter filter); + private: friend class QVulkanInstancePrivate; QScopedPointer d_ptr; @@ -194,6 +213,8 @@ private: }; Q_DECLARE_OPERATORS_FOR_FLAGS(QVulkanInstance::Flags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QVulkanInstance::DebugMessageTypeFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QVulkanInstance::DebugMessageSeverityFlags) QT_END_NAMESPACE diff --git a/src/gui/vulkan/qvulkaninstance_p.h b/src/gui/vulkan/qvulkaninstance_p.h index 27dc5383cf..9545d4e688 100644 --- a/src/gui/vulkan/qvulkaninstance_p.h +++ b/src/gui/vulkan/qvulkaninstance_p.h @@ -49,7 +49,8 @@ public: VkResult errorCode; QScopedPointer funcs; QHash deviceFuncs; - QList debugFilters; + QList debugFilters; // legacy filters based on VK_EXT_debug_report + QList debugUtilsFilters; // the modern version based on VK_EXT_debug_utils }; QT_END_NAMESPACE