From e3c4267fc2db853200a60c73e567be71429fc144 Mon Sep 17 00:00:00 2001 From: homeburger Date: Thu, 14 May 2026 21:10:36 -0500 Subject: [PATCH] add validation layers + debug callback --- README.md | 10 ++++++ src/Engine.cpp | 83 +++++++++++++++++++++++++++++++++++++++++++------- src/Engine.hpp | 20 ++++++++++-- 3 files changed, 100 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 090bdaa..7c5315e 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,16 @@ $ gcovr -r .. --filter "../src" Basically these are some tricky situations that I encountered when trying to execute this app throughout this development phase. If you are running on WSL Ubuntu 26.04 like me, then you mightr run into these too, hopefully my steps help fix. note: I am running an x86_64 system with an Nvidia GPU so some things may be slightly different if your system doesn't match. +### [WARN: COPY MODE] +This seems like a WSL specific error and causes real issues with relaying graphics from linux to windows. I fixed this by installing new mesa drivers as reccommended by https://github.com/microsoft/wslg/discussions/312: +```bash +$ sudo add-apt-repository ppa:kisak/kisak-mesa +$ sudo apt-get update && sudo apt upgrade +``` +Note: this resulted in the following erre "WARNING: dzn is not a conformant Vulkan implementation, testing use only." Running `$ vkcube` showed that this indeed was just a warning. + +(for those curious, dzn is a compaitibility layer between DirectX12 and Vulkan for that WSL conformity) + ## Development Roadmap ### lots of todo here diff --git a/src/Engine.cpp b/src/Engine.cpp index e43408d..35afc4f 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -45,9 +45,7 @@ bool Engine::createInstance() { .apiVersion = VK_API_VERSION_1_4 // this one is most important }; - // get extensions that our windowing library requires - uint32_t instanceExtensionsCount = 0; - char const* const* instanceExtensions{ SDL_Vulkan_GetInstanceExtensions(&instanceExtensionsCount) }; + std::vector requiredInstanceExtensions = getRequiredInstanceExtensions(); // get all available extensions auto extensionProperties = context_.enumerateInstanceExtensionProperties(); @@ -56,23 +54,23 @@ bool Engine::createInstance() { std::cout << "Available Vulkan Extensions: " << std::endl; for(const auto& extensionProperty : extensionProperties) { std::cout << "\t" << extensionProperty.extensionName << std::endl; - } + } // this would be a logger.debug(...) // check that all required extensions are available - for(uint32_t i = 0; i < instanceExtensionsCount; i++) { // for each extension that we require + for(uint32_t i = 0; i < requiredInstanceExtensions.size(); i++) { // for each extension that we require bool found = false; for(const auto& extensionProperty : extensionProperties) { // see if it matches any extensions that are provided - if(strcmp(extensionProperty.extensionName, instanceExtensions[i]) == 0) { + if(strcmp(extensionProperty.extensionName, requiredInstanceExtensions[i]) == 0) { found = true; break; } } if(!found) { - std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required SDL3 extension not supported: " << instanceExtensions[i] << std::endl; + std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Required SDL3 extension not supported: " << requiredInstanceExtensions[i] << std::endl; errorCount++; } else { // in case you're curious - //std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] SDL3 extension located: " << instanceExtensions[i] << std::endl; + //std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] SDL3 extension located: " << requiredInstanceExtensions[i] << std::endl; } } @@ -107,15 +105,78 @@ bool Engine::createInstance() { } // if any we had errors then we must exit - if(errorCount != 0) return false; + if(errorCount != 0) { + std::cout << "[" << __FUNCTION__ << ": " << __LINE__ << "] Unable to create Vulkan instance. Error count: " << errorCount << std::endl; + return false; + } vk::InstanceCreateInfo instanceCreateInfo { .pApplicationInfo = &appInfo, - .enabledExtensionCount = instanceExtensionsCount, - .ppEnabledExtensionNames = instanceExtensions, + .enabledLayerCount = static_cast(requiredValidationLayers.size()), + .ppEnabledLayerNames = requiredValidationLayers.data(), + .enabledExtensionCount = static_cast(requiredInstanceExtensions.size()), + .ppEnabledExtensionNames = requiredInstanceExtensions.data() }; instance_ = vk::raii::Instance(context_, instanceCreateInfo); return (instance_ != nullptr); } +std::vector Engine::getRequiredInstanceExtensions() { + + // get extensions that our windowing library requires + uint32_t sdlExtensionsCount = 0; + const char* const* sdlExtensions{ SDL_Vulkan_GetInstanceExtensions(&sdlExtensionsCount) }; + // what in the world is this kind of pointer btw + + std::vector requiredExtensions(sdlExtensions, sdlExtensions + static_cast(sdlExtensionsCount)); + + // manually add an extension for handling validation layers + if(enableValidationLayers) { + requiredExtensions.push_back(vk::EXTDebugUtilsExtensionName); + } + + return requiredExtensions; +} + +bool Engine::initDebugMessenger() { + + if(!enableValidationLayers) return false; + + // masks for which debug messages we want to see + vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError); + vk::DebugUtilsMessageTypeFlagsEXT messageTypeFlags( vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation); + vk::DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfoEXT{.messageSeverity = severityFlags, + .messageType = messageTypeFlags, + .pfnUserCallback = &debugCallback}; + debugMessenger_ = instance_.createDebugUtilsMessengerEXT( debugUtilsMessengerCreateInfoEXT ); + // we could get rid of this and just pass all the control to the logger + // like: "treat all messages of severity vk::eVerbose as our own DebugVerbosity::Info" + + return (debugMessenger_ != nullptr); +} + +VKAPI_ATTR vk::Bool32 VKAPI_CALL Engine::debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity, + vk::DebugUtilsMessageTypeFlagsEXT type, + const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) { + + // this will eventually go through our logger + std::cout << "[ Validation Layer ] [Type: " << to_string(type) << "] " << pCallbackData->pMessage << std::endl; + /* + vk severity types: + vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose + vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo + vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning + vk::DebugUtilsMessageSeverityFlagBitsEXT::eError + + vk message types: + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral + vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation + vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance + */ + + // returns whether or not we should abort, we'll always say no + return vk::False; +} + diff --git a/src/Engine.hpp b/src/Engine.hpp index bbcc7a8..9963109 100644 --- a/src/Engine.hpp +++ b/src/Engine.hpp @@ -13,6 +13,7 @@ public: Engine(Window* window); ~Engine() = default; + // initializes and sets up the vulkan instance. outside of constructor to allow control of order the order of initialization void init(); // draw is called every render iteration in that while loop @@ -20,14 +21,29 @@ public: private: - Window* window_; + // returns a list of the required extensions needed by our engine + std::vector getRequiredInstanceExtensions(); + // helper for organizing the creation of the vulkan instance. returns success or failure bool createInstance(); - // Vulkan specific + // helper for attaching callbacks to the instance + bool initDebugMessenger(); + + // callback function for the vulkan debug extension: routes debug messages from validation layers to our app + static VKAPI_ATTR vk::Bool32 VKAPI_CALL debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity, + vk::DebugUtilsMessageTypeFlagsEXT type, + const vk::DebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); + + Window* window_; + + // Vulkan specific instance members vk::raii::Context context_; vk::raii::Instance instance_ = nullptr; + vk::raii::DebugUtilsMessengerEXT debugMessenger_ = nullptr; + // members for control over validation layers static constexpr bool enableValidationLayers = true; // TODO: only true in debug mode const std::vector validationLayers = { "VK_LAYER_KHRONOS_validation" };