#include #include #include #include #include #include "util.hpp" #include "config.h" #include "log.hpp" #include "instance.h" namespace engine { static std::vector getWindowExtensions(SDL_Window* window) { [[maybe_unused]] SDL_bool res; unsigned int sdlExtensionCount = 0; res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, nullptr); assert(res == SDL_TRUE); std::vector requiredExtensions(sdlExtensionCount); res = SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, requiredExtensions.data()); assert(res == SDL_TRUE); return requiredExtensions; } static const char* getValidationLayer() { const char* const VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; [[maybe_unused]] VkResult res; uint32_t layerCount; res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); assert(res == VK_SUCCESS); std::vector layers(layerCount); res = vkEnumerateInstanceLayerProperties(&layerCount, layers.data()); assert(res == VK_SUCCESS); // find validation layer and print all layers to log for (const auto& layer : layers) { if (strncmp(layer.layerName, VALIDATION_LAYER_NAME, 256) == 0) { return VALIDATION_LAYER_NAME; } } throw std::runtime_error("Unable to find validation layer!"); } static VkBool32 messengerCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { (void)pUserData; std::string msgType{}; if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) msgType += " (GENERAL)"; if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) msgType += " (PERF.)"; if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) msgType += " (VALID.)"; switch (messageSeverity) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: LOG_DEBUG("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: LOG_INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: LOG_WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); break; case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: LOG_ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); break; default: break; } return VK_FALSE; } static VkDebugUtilsMessengerCreateInfoEXT getDebugMessengerCreateInfo() { VkDebugUtilsMessengerCreateInfoEXT debugMessengerInfo{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .pNext = nullptr, .flags = 0, .messageSeverity = 0, .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, .pfnUserCallback = messengerCallback, .pUserData = nullptr, }; enum class MessageSeverity { SEV_VERBOSE, SEV_INFO, SEV_WARNING, SEV_ERROR // windows.h defines ERROR annoyingly }; constexpr MessageSeverity MESSAGE_LEVEL = MessageSeverity::SEV_WARNING; switch (MESSAGE_LEVEL) { case MessageSeverity::SEV_VERBOSE: debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; [[fallthrough]]; case MessageSeverity::SEV_INFO: debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; [[fallthrough]]; case MessageSeverity::SEV_WARNING: debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; [[fallthrough]]; case MessageSeverity::SEV_ERROR: debugMessengerInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; [[fallthrough]]; default: break; } return debugMessengerInfo; } Instance createVulkanInstance(SDL_Window* window, const char* appName, const char* appVersion) { Instance instance; // get the both the engine and application versions int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0; versionFromCharArray(appVersion, &appVersionMajor, &appVersionMinor, &appVersionPatch); int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0; versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch); VkApplicationInfo applicationInfo{ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = nullptr, .pApplicationName = appName, .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), .pEngineName = "engine", .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), .apiVersion = VK_API_VERSION_1_3, }; const std::vector windowExtensions = getWindowExtensions(window); std::vector instanceExtensionsToUse = windowExtensions; instanceExtensionsToUse.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); const char* const validationLayer = getValidationLayer(); VkDebugUtilsMessengerCreateInfoEXT debugMessengerInfo = getDebugMessengerCreateInfo(); VkInstanceCreateInfo instanceInfo{ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = &debugMessengerInfo, .flags = 0, .pApplicationInfo = &applicationInfo, .enabledLayerCount = 1, .ppEnabledLayerNames = &validationLayer, .enabledExtensionCount = (uint32_t)instanceExtensionsToUse.size(), .ppEnabledExtensionNames = instanceExtensionsToUse.data(), }; VkResult res; res = vkCreateInstance(&instanceInfo, nullptr, &instance.instance); if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { throw std::runtime_error("The graphics driver is incompatible with vulkan"); } else if (res != VK_SUCCESS) { throw std::runtime_error("vkCreateInstance failed: " + std::to_string(res)); } volkLoadInstanceOnly(instance.instance); // create the debug messenger VkDebugUtilsMessengerCreateInfoEXT createInfo = getDebugMessengerCreateInfo(); res = vkCreateDebugUtilsMessengerEXT(instance.instance, &createInfo, nullptr, &instance.debugMessenger); if (res != VK_SUCCESS) { throw std::runtime_error("vkCreateDebugUtilsMessengerExt failed: " + std::to_string(res)); } return instance; } void destroyVulkanInstance(Instance instance) { vkDestroyDebugUtilsMessengerEXT(instance.instance, instance.debugMessenger, nullptr); vkDestroyInstance(instance.instance, nullptr); } }