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