Vulkan Tutorial: Problem With Debug Message Callbacks

So I decided to try out Vulkan, and I am currently on the tutorial’s validation layers part, and specifically on the Message Callback chapter. However, I have hit a wall, and I do not know if it’s a problem on my end or not. I am by no means a professional or anything close, so I decided to come and ask here on the forums.

So my problem, as far I can tell is that I am not getting messages about the debug messenger not being destroyed before the instance.

As I was reading the documentation/specification, I found out I could also send a message via the vkSubmitDebugUtilsMessageEXT function, so I loaded it and managed to successfully trigger my application’s callback function and get output to the console window.
When sending messages via vkSubmitDebugUtilsMessageEXT, I tried all kinds of possible combinations with the SeverityFlags and TypeFlags arguments, and every single time the message was successfully output to the console window.

When i comment out the DestroyDebugUtilsMessengerEXT function, i get callback messages about unloading layer libraries, however there’s nothing informing me about me forgetting to destroy the debug messenger, even though I have turned on all the bits in the SeverityFlags and TypeFlags members.

OS: Windows 10 Pro (64 bit)
GPU: GTX 1060 6GB
Driver Version: 443.09
I use msys2, not using an IDE, currently g++ is version 9.3.0

Here is the source code (cpp file):

#include "main.hpp"
#include <iostream> 	// for std::cout, std::cerr and similar
#include <cstdlib>  	// for the EXIT_FAILURE and EXIT_SUCCESS macros
#include <exception> 	// for std::exception
#include <vector>  		// for std::vector
#include <cstddef> 		// for std::size_t
#include <string.h>		// for strcmp()

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

#ifdef NDEBUG
	const bool g_enableValidationLayers{ false };
#else
	const bool g_enableValidationLayers{ true };
#endif

const std::vector<const char*> g_validationLayers{
	"VK_LAYER_KHRONOS_validation"
};

int main()
{
	HelloTriangleApplication app;

	try
	{
		app.run();
	}

	catch (const std::exception& exception)
	{
		std::cerr << exception.what() << std::endl;
		return EXIT_FAILURE;
	}

	std::cin.get();
	return EXIT_SUCCESS;
}

// Function for loading the vkCreateDebugUtilsMessengerEXT extension function.
VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
										const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
{
	auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");

	if (func != nullptr)
	{
		return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
	}
	else
	{
		return VK_ERROR_EXTENSION_NOT_PRESENT;
	}
}

// Function for loading the vkDestroyDebugUtilsMessengerEXT extension function.
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator)
{
	auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");

	if (func != nullptr)
	{
		func(instance, messenger, pAllocator);
	}
}

// Function for loading the vkSubmitDebugUtilsMessageEXT extension function.
void SubmitDebugUtilsMessageEXT(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
								VkDebugUtilsMessageTypeFlagsEXT messageType, VkDebugUtilsMessengerCallbackDataEXT* pCallbackData)
{
	auto func = (PFN_vkSubmitDebugUtilsMessageEXT) vkGetInstanceProcAddr(instance, "vkSubmitDebugUtilsMessageEXT");

	if (func != nullptr)
	{
		func(instance, messageSeverity, messageType, pCallbackData);
	}
}

HelloTriangleApplication::HelloTriangleApplication() {}

HelloTriangleApplication::~HelloTriangleApplication() {}

void HelloTriangleApplication::initWindow()
{
	glfwInit();

	glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);

	// Make the window non-resizable via the GLFW_RESIZABLE hint, which we set to GLFW_FALSE.
	glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 

	m_window = glfwCreateWindow(m_width, m_height, "Vulkan Window", nullptr, nullptr);
}

void HelloTriangleApplication::initVulkan()
{
	createInstance();
	setupDebugMessenger();
}

void HelloTriangleApplication::mainLoop()
{
	while (!glfwWindowShouldClose(m_window))
	{
		glfwPollEvents();
	}

	// Test code in order to see if the callback is working properly.
	VkDebugUtilsMessengerCallbackDataEXT obTest{};
	obTest.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT;
	obTest.pMessage = "Testing Testing Testing\n";
	SubmitDebugUtilsMessageEXT(m_instance, VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT,
								 VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, &obTest);
}

void HelloTriangleApplication::cleanup()
{
	if (g_enableValidationLayers)
	{
		//DestroyDebugUtilsMessengerEXT(m_instance, m_debugMessenger, nullptr);
	}

	vkDestroyInstance(m_instance, nullptr);

	glfwDestroyWindow(m_window);

	// Terminates the GLFW library.
	glfwTerminate();
}

void HelloTriangleApplication::createInstance()
{
	// Optional context.
	VkApplicationInfo appInfo{};
	fillApplicationInfo(appInfo);

	// Non-optional context.
	VkInstanceCreateInfo instanceCreateInfo{};
	instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
	instanceCreateInfo.pApplicationInfo = &appInfo;

	// Create a vector and fill it with the required by GLFW and Vulkan extensions.
	std::vector<const char*> extensions;
	getRequiredExtensions(extensions);

	// Checks if all the extensions needed by GLFW and Vulkan are supported by Vulkan.
	if (checkExtensionsSupport(instanceCreateInfo) == false)
	{
		throw std::runtime_error("One or more extensions not supported!");
	}

	// Fill the instanceCreateInfo context with the extension data.
	instanceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
	instanceCreateInfo.ppEnabledExtensionNames = extensions.data();

	if (g_enableValidationLayers && !checkValidationLayerSupport())
	{
		throw std::runtime_error("One or more validation layers requested, but not supported!");
	}

	if (g_enableValidationLayers)
	{
		instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(g_validationLayers.size());
		instanceCreateInfo.ppEnabledLayerNames = g_validationLayers.data();
	}
	else
	{
		instanceCreateInfo.enabledLayerCount = 0;
	}

	if (vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance) != VK_SUCCESS)
	{
		throw std::runtime_error("Failed to create instance!");
	}
}

// Get the extensions required by GLFW and Vulkan.
void HelloTriangleApplication::getRequiredExtensions(std::vector<const char*>& extensions)
{
	uint32_t glfwExtensionCount{ 0 };
	const char** glfwExtensions{ glfwGetRequiredInstanceExtensions(&glfwExtensionCount) };

	// Place the GLFW extensions into the vector using the assign() member.
	extensions.assign(glfwExtensions, glfwExtensions + glfwExtensionCount);

	// If validation layers have been enabled, add the VK_EXT_debug_utils Vulkan extension.
	if (g_enableValidationLayers)
	{
		extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
	}
}

// Checks if all the extensions needed by GLFW and Vulkan are supported by Vulkan.
bool HelloTriangleApplication::checkExtensionsSupport
(const VkInstanceCreateInfo& instanceCreateInfo)
{
	uint32_t extensionCount{ 0 };
	vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);  // Requests the number of extensions.
	std::vector<VkExtensionProperties> extensions{ extensionCount };
	vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); // Request/Query extension details data.

	for(std::size_t oIndex{ 0 }; oIndex < instanceCreateInfo.enabledExtensionCount; ++oIndex)
	{
		bool state{ false };

		for(std::size_t iIndex{ 0 }; iIndex < extensions.size(); ++iIndex)
		{
			if (strcmp(instanceCreateInfo.ppEnabledExtensionNames[oIndex], extensions[iIndex].extensionName) == 0) 
			{
				state = true;
				break;
			}
		}
		if (state == false)
		{
			return false;
		}
	}

	return true;
}

bool HelloTriangleApplication::checkValidationLayerSupport()
{
	uint32_t layerCount{ 0 };
	vkEnumerateInstanceLayerProperties(&layerCount, nullptr); // Requests the number of layers.
	std::vector<VkLayerProperties> supportedlayers{ layerCount };
	vkEnumerateInstanceLayerProperties(&layerCount, supportedlayers.data()); // Request/Query validation layers data.

	for(std::size_t oIndex{ 0 }; oIndex < g_validationLayers.size(); ++oIndex)
	{
		bool state{ false };

		for(std::size_t iIndex{ 0 }; iIndex < supportedlayers.size(); ++iIndex)
		{
			if (strcmp(g_validationLayers[oIndex], supportedlayers[iIndex].layerName) == 0)
			{
				state = true;
				break;
			}
		}
		if (state == false)
		{
			return false;
		}
	}

	return true;
}

VKAPI_ATTR VkBool32 VKAPI_CALL HelloTriangleApplication::debugCallback(
		VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
		VkDebugUtilsMessageTypeFlagsEXT messageTypes,
		const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
		void* pUserData)
{
	std::cerr << "\nValidation layer: " << pCallbackData->pMessage << std::endl;

	return VK_FALSE;
}

void HelloTriangleApplication::setupDebugMessenger()
{
	if (!g_enableValidationLayers)
	{
		return;
	}

	VkDebugUtilsMessengerCreateInfoEXT createInfo{};

	fillDebugMessengerCreateInfo(createInfo);

	if (CreateDebugUtilsMessengerEXT(m_instance, &createInfo, nullptr, &m_debugMessenger) != VK_SUCCESS)
	{
		throw std::runtime_error("Failed to set up debug messenger!");
	}
}

void HelloTriangleApplication::fillApplicationInfo(VkApplicationInfo& appInfo)
{
	appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
	appInfo.pApplicationName = "Hello Triangle!";
	appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
	appInfo.pEngineName = "No Engine";
	appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
	appInfo.apiVersion = VK_API_VERSION_1_0;
}

void HelloTriangleApplication::fillDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& messengerCreateInfo)
{
	messengerCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;

	// The type of severity the callback will be called for.
	messengerCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | 
								VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | 
								VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
								VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;

	// Which type of messages the callback function will be notified about.
	messengerCreateInfo.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;

	// Pointer to the callback function.
	messengerCreateInfo.pfnUserCallback = debugCallback;

	// Optional member for user provided data, which is left as nullptr for now. It is of void* type.
	messengerCreateInfo.pUserData = nullptr;
}

void HelloTriangleApplication::run()
{
	initWindow();
	initVulkan();
	mainLoop();
	cleanup();
}

And also the header file:

#ifndef MAIN_HPP
#define MAIN_HPP
#include <vector>  // for std::vector

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

class HelloTriangleApplication
{
private:
	GLFWwindow* m_window;
	VkInstance m_instance;
	VkDebugUtilsMessengerEXT m_debugMessenger;
	const int m_width{ 1280 };
	const int m_height{ 720 };

	void initWindow();
	void initVulkan();
	void mainLoop();
	void cleanup();
	void createInstance();
	void setupDebugMessenger();
	void fillDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& messengerCreateInfo);
	void fillApplicationInfo(VkApplicationInfo& appInfo);
	bool checkExtensionsSupport(const VkInstanceCreateInfo& instanceCreateInfo);
	void getRequiredExtensions(std::vector<const char*>& extensions);
	bool checkValidationLayerSupport();
	static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
		VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
		VkDebugUtilsMessageTypeFlagsEXT messageTypes,
		const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
		void* pUserData);

public:
	HelloTriangleApplication();
	~HelloTriangleApplication();
	void run();
};

#endif
  • For a simple app, much less tutorial, custom logging is overkill. You should just use VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation env.
  • Not necessarily everything is covered by the layers, or because of implementation concerns. You can find coverage at https://vulkan.lunarg.com/doc/sdk/latest/windows/validation_error_database.html
  • Reporting vkCreateInstance and vkDestroyInstance violations may need chained debug callback as well. Take your VkDebugUtilsMessengerCreateInfoEXT and chain it to VkInstanceCreateInfo::pNext.

I chained a VkDebugUtilsMessengerCreateInfoEXT and I am getting a lot of messages about extensions, files being found/located and similar - that’s with the MESSAGE_SEVERITY_INFO_BIT_EXT turned on. Without it there’s nothing, so I guess that means there are no problems during the creation and destruction of the instance.

I checked the coverage link you provided, and as far as I could tell, vkDestroyDebugUtilsMessengerEXT is implicitly covered/checked. Maybe I read it wrong?

However, either I simply don’t understand something or the tutorials are wrong. I am going to post 2 images - one from the online tutorial and one from the epub version of the tutorial :

Online version:

Epub version:

I am not getting any callback messages about me forgetting to destroy the messenger before the instance, even with all the severity and type flags turned on.

I even copy-pasted the code sample provided from the online tutorial and commented out the DestroyDebugUtilsMessengerEXT call there, but still I am still not getting anything

Also, for some reason when the INFO_BIT_EXT flag is turned on, and the DestroyDebugUtilsMessengerEXT function is commented out, i get each unloading layer library message twice, rather than once.

Well, try this one instead: vkGetPhysicalDeviceProperties(physicalDevice, nullptr);