Add handling for EXT_debug_utils for gathering debug messages

* Where possible we use it as we expect potentially better message formatting &
  more info we can use in future
This commit is contained in:
baldurk
2019-04-01 14:25:49 +01:00
parent afb7fa3771
commit ff354e57e6
3 changed files with 226 additions and 111 deletions
+84 -79
View File
@@ -146,7 +146,7 @@ WrappedVulkan::WrappedVulkan() : m_RenderState(this, &m_CreationInfo)
m_Device = VK_NULL_HANDLE;
m_Queue = VK_NULL_HANDLE;
m_QueueFamilyIdx = 0;
m_DbgMsgCallback = VK_NULL_HANDLE;
m_DbgReportCallback = VK_NULL_HANDLE;
m_HeaderChunk = NULL;
@@ -3232,31 +3232,9 @@ void WrappedVulkan::AddDebugMessage(DebugMessage msg)
m_DebugMessages.push_back(msg);
}
VkBool32 WrappedVulkan::DebugCallback(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType, uint64_t object,
size_t location, int32_t messageCode,
const char *pLayerPrefix, const char *pMessage)
VkBool32 WrappedVulkan::DebugCallback(MessageSeverity severity, MessageCategory category,
int messageCode, const char *pMessageId, const char *pMessage)
{
bool isDS = false, isMEM = false, isSC = false, isOBJ = false, isSWAP = false, isDL = false,
isIMG = false, isPARAM = false;
if(!strcmp(pLayerPrefix, "DS"))
isDS = true;
else if(!strcmp(pLayerPrefix, "MEM"))
isMEM = true;
else if(!strcmp(pLayerPrefix, "SC"))
isSC = true;
else if(!strcmp(pLayerPrefix, "OBJTRACK"))
isOBJ = true;
else if(!strcmp(pLayerPrefix, "SWAP_CHAIN") || !strcmp(pLayerPrefix, "Swapchain"))
isSWAP = true;
else if(!strcmp(pLayerPrefix, "DL"))
isDL = true;
else if(!strcmp(pLayerPrefix, "Image"))
isIMG = true;
else if(!strcmp(pLayerPrefix, "PARAMCHECK") || !strcmp(pLayerPrefix, "ParameterValidation"))
isPARAM = true;
{
ScopedDebugMessageSink *sink = GetDebugMessageSink();
@@ -3265,9 +3243,9 @@ VkBool32 WrappedVulkan::DebugCallback(VkDebugReportFlagsEXT flags,
DebugMessage msg;
msg.eventId = 0;
msg.category = MessageCategory::Miscellaneous;
msg.category = category;
msg.description = pMessage;
msg.severity = MessageSeverity::Low;
msg.severity = severity;
msg.messageID = messageCode;
msg.source = MessageSource::API;
@@ -3282,75 +3260,102 @@ VkBool32 WrappedVulkan::DebugCallback(VkDebugReportFlagsEXT flags,
msg.eventId = it->eventId;
}
if(flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
msg.severity = MessageSeverity::Info;
else if(flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
msg.severity = MessageSeverity::Low;
else if(flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
msg.severity = MessageSeverity::Medium;
else if(flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
msg.severity = MessageSeverity::High;
if(flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
msg.category = MessageCategory::Performance;
else if(isDS)
msg.category = MessageCategory::Execution;
else if(isMEM)
msg.category = MessageCategory::Resource_Manipulation;
else if(isSC)
msg.category = MessageCategory::Shaders;
else if(isOBJ)
msg.category = MessageCategory::State_Setting;
else if(isSWAP)
msg.category = MessageCategory::Miscellaneous;
else if(isDL)
msg.category = MessageCategory::Portability;
else if(isIMG)
msg.category = MessageCategory::State_Creation;
else if(isPARAM)
msg.category = MessageCategory::Miscellaneous;
if(isIMG || isPARAM)
msg.source = MessageSource::IncorrectAPIUse;
sink->msgs.push_back(msg);
}
}
{
// All access mask/barrier messages.
// These are just too spammy/false positive/unreliable to keep
if(isDS && messageCode == 10)
return false;
// ignore perf warnings
if(flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
return false;
// Memory is aliased between image and buffer
// ignore memory aliasing warning - we make use of the memory in disjoint ways
// and copy image data over separately, so our use is safe
// no location set for this one, so ignore by code (maybe too coarse)
if(isMEM && messageCode == 3)
return false;
// Cannot read invalid region of memory
// The validation layers can't track simultaneous use of the same memory in multiple buffers, so
// misreports any buffer which was filled by initial states (whole-memory buffer copies).
if(isMEM && messageCode == 15)
if(category == MessageCategory::Performance)
return false;
// Non-linear image is aliased with linear buffer
// Not an error, the validation layers complain at our whole-mem bufs
if(strstr(pMessage, "InvalidAliasing"))
if(strstr(pMessageId, "InvalidAliasing") || strstr(pMessage, "InvalidAliasing"))
return false;
RDCWARN("[%s:%u/%d] %s", pLayerPrefix, (uint32_t)location, messageCode, pMessage);
RDCWARN("[%s] %s", pMessageId, pMessage);
}
return false;
}
VkBool32 VKAPI_PTR WrappedVulkan::DebugReportCallbackStatic(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object, size_t location,
int32_t messageCode,
const char *pLayerPrefix,
const char *pMessage, void *pUserData)
{
MessageSeverity severity = MessageSeverity::Low;
if(flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
severity = MessageSeverity::High;
else if(flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
severity = MessageSeverity::Medium;
else if(flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
severity = MessageSeverity::Low;
else if(flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
severity = MessageSeverity::Info;
MessageCategory category = MessageCategory::Miscellaneous;
if(flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
category = MessageCategory::Performance;
return ((WrappedVulkan *)pUserData)
->DebugCallback(severity, category, messageCode, pLayerPrefix, pMessage);
}
VkBool32 VKAPI_PTR WrappedVulkan::DebugUtilsCallbackStatic(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData)
{
MessageSeverity severity = MessageSeverity::Low;
if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
severity = MessageSeverity::High;
else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
severity = MessageSeverity::Medium;
else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
severity = MessageSeverity::Low;
else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
severity = MessageSeverity::Info;
MessageCategory category = MessageCategory::Miscellaneous;
if(messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)
category = MessageCategory::Performance;
std::string msgid;
const char *pMessageId = pCallbackData->pMessageIdName;
int messageCode = pCallbackData->messageIdNumber;
if(messageCode == 0 && pMessageId && !strncmp(pMessageId, "VUID", 4))
{
const char *c = pMessageId + strlen(pMessageId) - 1;
int mult = 1;
while(c > pMessageId && *c >= '0' && *c <= '9')
{
mult *= 10;
messageCode += mult * int(*c - '0');
c--;
}
}
if(!pMessageId)
{
msgid = StringFormat::Fmt("%d", pCallbackData->messageIdNumber);
pMessageId = msgid.c_str();
}
return ((WrappedVulkan *)pUserData)
->DebugCallback(severity, category, messageCode, pMessageId, pCallbackData->pMessage);
}
bool WrappedVulkan::HasNonMarkerEvents(ResourceId cmdBuffer)
{
for(const APIEvent &ev : m_BakedCmdBufferInfo[m_LastCmdBufferID].curEvents)
+14 -13
View File
@@ -367,7 +367,8 @@ private:
// the instance corresponding to this WrappedVulkan
VkInstance m_Instance;
// the instance's dbg msg callback handle
VkDebugReportCallbackEXT m_DbgMsgCallback;
VkDebugReportCallbackEXT m_DbgReportCallback;
VkDebugUtilsMessengerEXT m_DbgUtilsCallback;
// the physical device we created m_Device with
VkPhysicalDevice m_PhysicalDevice;
// the device used for our own command buffer work
@@ -844,19 +845,19 @@ private:
WrappedVulkan(const WrappedVulkan &);
WrappedVulkan &operator=(const WrappedVulkan &);
VkBool32 DebugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType,
uint64_t object, size_t location, int32_t messageCode,
const char *pLayerPrefix, const char *pMessage);
VkBool32 DebugCallback(MessageSeverity severity, MessageCategory category, int messageCode,
const char *pMessageId, const char *pMessage);
static VkBool32 VKAPI_PTR DebugCallbackStatic(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object, size_t location,
int32_t messageCode, const char *pLayerPrefix,
const char *pMessage, void *pUserData)
{
return ((WrappedVulkan *)pUserData)
->DebugCallback(flags, objectType, object, location, messageCode, pLayerPrefix, pMessage);
}
static VkBool32 VKAPI_PTR DebugUtilsCallbackStatic(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData);
static VkBool32 VKAPI_PTR DebugReportCallbackStatic(VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object, size_t location,
int32_t messageCode, const char *pLayerPrefix,
const char *pMessage, void *pUserData);
void AddFrameTerminator(uint64_t queueMarkerTag);
public:
@@ -267,10 +267,17 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams &params, uint64_t sectionVer
}
}
// we always want this extension if it's available, and not already enabled
if(supportedExtensions.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) != supportedExtensions.end() &&
// we always want debug extensions if it available, and not already enabled
if(supportedExtensions.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != supportedExtensions.end() &&
std::find(params.Extensions.begin(), params.Extensions.end(),
VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == params.Extensions.end())
VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == params.Extensions.end())
{
RDCLOG("Enabling VK_EXT_debug_utils");
params.Extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
else if(supportedExtensions.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) != supportedExtensions.end() &&
std::find(params.Extensions.begin(), params.Extensions.end(),
VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == params.Extensions.end())
{
RDCLOG("Enabling VK_EXT_debug_report");
params.Extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
@@ -300,6 +307,36 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams &params, uint64_t sectionVer
m_Instance = VK_NULL_HANDLE;
VkValidationFeaturesEXT featuresEXT = {VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT};
VkValidationFeatureDisableEXT disableFeatures[] = {VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT};
featuresEXT.disabledValidationFeatureCount = ARRAY_COUNT(disableFeatures);
featuresEXT.pDisabledValidationFeatures = disableFeatures;
VkValidationFlagsEXT flagsEXT = {VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT};
VkValidationCheckEXT disableChecks[] = {VK_VALIDATION_CHECK_SHADERS_EXT};
flagsEXT.disabledValidationCheckCount = ARRAY_COUNT(disableChecks);
flagsEXT.pDisabledValidationChecks = disableChecks;
if(supportedExtensions.find(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) != supportedExtensions.end() &&
std::find(params.Extensions.begin(), params.Extensions.end(),
VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) == params.Extensions.end())
{
RDCLOG("Enabling VK_EXT_validation_features");
params.Extensions.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
instinfo.pNext = &featuresEXT;
}
else if(supportedExtensions.find(VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME) !=
supportedExtensions.end() &&
std::find(params.Extensions.begin(), params.Extensions.end(),
VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME) == params.Extensions.end())
{
RDCLOG("Enabling VK_EXT_validation_flags");
params.Extensions.push_back(VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME);
instinfo.pNext = &flagsEXT;
}
VkResult ret = GetInstanceDispatchTable(NULL)->CreateInstance(&instinfo, NULL, &m_Instance);
#undef CheckExt
@@ -336,25 +373,40 @@ ReplayStatus WrappedVulkan::Initialise(VkInitParams &params, uint64_t sectionVer
InitInstanceExtensionTables(m_Instance, &m_EnabledExtensions);
m_DbgMsgCallback = VK_NULL_HANDLE;
m_DbgReportCallback = VK_NULL_HANDLE;
m_DbgUtilsCallback = VK_NULL_HANDLE;
m_PhysicalDevice = VK_NULL_HANDLE;
m_Device = VK_NULL_HANDLE;
m_QueueFamilyIdx = ~0U;
m_PrevQueue = m_Queue = VK_NULL_HANDLE;
m_InternalCmds.Reset();
if(ObjDisp(m_Instance)->CreateDebugReportCallbackEXT)
if(ObjDisp(m_Instance)->CreateDebugUtilsMessengerEXT)
{
VkDebugUtilsMessengerCreateInfoEXT debugInfo = {};
debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
debugInfo.pfnUserCallback = &DebugUtilsCallbackStatic;
debugInfo.pUserData = this;
debugInfo.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;
debugInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
ObjDisp(m_Instance)
->CreateDebugUtilsMessengerEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgUtilsCallback);
}
else if(ObjDisp(m_Instance)->CreateDebugReportCallbackEXT)
{
VkDebugReportCallbackCreateInfoEXT debugInfo = {};
debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
debugInfo.pNext = NULL;
debugInfo.pfnCallback = &DebugCallbackStatic;
debugInfo.pfnCallback = &DebugReportCallbackStatic;
debugInfo.pUserData = this;
debugInfo.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT |
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT;
ObjDisp(m_Instance)
->CreateDebugReportCallbackEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgMsgCallback);
->CreateDebugReportCallbackEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgReportCallback);
}
uint32_t count = 0;
@@ -439,19 +491,42 @@ VkResult WrappedVulkan::vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo
report = (VkDebugReportCallbackCreateInfoEXT *)report->pNext;
}
// or debug utils callbacks
VkDebugUtilsMessengerCreateInfoEXT *messenger =
(VkDebugUtilsMessengerCreateInfoEXT *)pCreateInfo->pNext;
VkDebugUtilsMessengerCallbackDataEXT messengerData = {};
messengerData.messageIdNumber = 1;
messengerData.pMessageIdName = NULL;
messengerData.pMessage = "RenderDoc does not support a requested instance extension.";
messengerData.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT;
while(messenger)
{
if(messenger && messenger->sType == VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT)
messenger->pfnUserCallback(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, &messengerData,
messenger->pUserData);
messenger = (VkDebugUtilsMessengerCreateInfoEXT *)messenger->pNext;
}
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
const char **addedExts = new const char *[modifiedCreateInfo.enabledExtensionCount + 1];
bool hasDebugReport = false;
bool hasDebugReport = false, hasDebugUtils = false;
for(uint32_t i = 0; i < modifiedCreateInfo.enabledExtensionCount; i++)
{
addedExts[i] = modifiedCreateInfo.ppEnabledExtensionNames[i];
if(!strcmp(addedExts[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
hasDebugReport = true;
if(!strcmp(addedExts[i], VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
hasDebugUtils = true;
}
std::vector<VkExtensionProperties> supportedExts;
@@ -479,8 +554,19 @@ VkResult WrappedVulkan::vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo
"Couldn't load vkEnumerateInstanceExtensionProperties in vkCreateInstance to enumerate "
"instance extensions");
// always enable debug report, if it's available
if(!hasDebugReport)
// always enable debug report/utils, if it's available
if(!hasDebugUtils)
{
for(const VkExtensionProperties &ext : supportedExts)
{
if(!strcmp(ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
{
addedExts[modifiedCreateInfo.enabledExtensionCount++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
break;
}
}
}
else if(!hasDebugReport)
{
for(const VkExtensionProperties &ext : supportedExts)
{
@@ -595,25 +681,41 @@ VkResult WrappedVulkan::vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo
RenderDoc::Inst().AddDeviceFrameCapturer(LayerDisp(m_Instance), this);
m_DbgMsgCallback = VK_NULL_HANDLE;
m_DbgReportCallback = VK_NULL_HANDLE;
m_DbgUtilsCallback = VK_NULL_HANDLE;
m_PhysicalDevice = VK_NULL_HANDLE;
m_Device = VK_NULL_HANDLE;
m_QueueFamilyIdx = ~0U;
m_PrevQueue = m_Queue = VK_NULL_HANDLE;
m_InternalCmds.Reset();
if(ObjDisp(m_Instance)->CreateDebugReportCallbackEXT)
if(ObjDisp(m_Instance)->CreateDebugUtilsMessengerEXT)
{
VkDebugUtilsMessengerCreateInfoEXT debugInfo = {};
debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
debugInfo.pfnUserCallback = &DebugUtilsCallbackStatic;
debugInfo.pUserData = this;
debugInfo.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;
debugInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
ObjDisp(m_Instance)
->CreateDebugUtilsMessengerEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgUtilsCallback);
}
else if(ObjDisp(m_Instance)->CreateDebugReportCallbackEXT)
{
VkDebugReportCallbackCreateInfoEXT debugInfo = {};
debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
debugInfo.pNext = NULL;
debugInfo.pfnCallback = &DebugCallbackStatic;
debugInfo.pfnCallback = &DebugReportCallbackStatic;
debugInfo.pUserData = this;
debugInfo.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT |
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT;
ObjDisp(m_Instance)
->CreateDebugReportCallbackEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgMsgCallback);
->CreateDebugReportCallbackEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgReportCallback);
}
if(ret == VK_SUCCESS)
@@ -689,8 +791,12 @@ void WrappedVulkan::Shutdown()
SAFE_DELETE(m_ShaderCache);
if(m_Instance && ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT &&
m_DbgMsgCallback != VK_NULL_HANDLE)
ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT(Unwrap(m_Instance), m_DbgMsgCallback, NULL);
m_DbgReportCallback != VK_NULL_HANDLE)
ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT(Unwrap(m_Instance), m_DbgReportCallback, NULL);
if(m_Instance && ObjDisp(m_Instance)->DestroyDebugUtilsMessengerEXT &&
m_DbgUtilsCallback != VK_NULL_HANDLE)
ObjDisp(m_Instance)->DestroyDebugUtilsMessengerEXT(Unwrap(m_Instance), m_DbgUtilsCallback, NULL);
// need to store the unwrapped device and instance to destroy the
// API object after resource manager shutdown
@@ -729,8 +835,11 @@ void WrappedVulkan::vkDestroyInstance(VkInstance instance, const VkAllocationCal
{
RDCASSERT(m_Instance == instance);
if(ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT && m_DbgMsgCallback != VK_NULL_HANDLE)
ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT(Unwrap(m_Instance), m_DbgMsgCallback, NULL);
if(ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT && m_DbgReportCallback != VK_NULL_HANDLE)
ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT(Unwrap(m_Instance), m_DbgReportCallback, NULL);
if(ObjDisp(m_Instance)->DestroyDebugUtilsMessengerEXT && m_DbgUtilsCallback != VK_NULL_HANDLE)
ObjDisp(m_Instance)->DestroyDebugUtilsMessengerEXT(Unwrap(m_Instance), m_DbgUtilsCallback, NULL);
// the device should already have been destroyed, assuming that the
// application is well behaved. If not, we just leak.