add validation layers + debug callback
Some checks failed
Build and Test verification / build (push) Failing after 26s
Build and Test verification / test (push) Has been skipped

This commit is contained in:
2026-05-14 21:10:36 -05:00
parent cedb80cb03
commit e3c4267fc2
3 changed files with 100 additions and 13 deletions

View File

@@ -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

View File

@@ -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<const char*> 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<uint32_t>(requiredValidationLayers.size()),
.ppEnabledLayerNames = requiredValidationLayers.data(),
.enabledExtensionCount = static_cast<uint32_t>(requiredInstanceExtensions.size()),
.ppEnabledExtensionNames = requiredInstanceExtensions.data()
};
instance_ = vk::raii::Instance(context_, instanceCreateInfo);
return (instance_ != nullptr);
}
std::vector<const char*> 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<const char*> requiredExtensions(sdlExtensions, sdlExtensions + static_cast<size_t>(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;
}

View File

@@ -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<const char*> 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<const char*> validationLayers = { "VK_LAYER_KHRONOS_validation" };