diff --git a/include/vulkan/basalt_context.h b/include/vulkan/basalt_context.h new file mode 100644 index 0000000..e07a142 --- /dev/null +++ b/include/vulkan/basalt_context.h @@ -0,0 +1,25 @@ +#pragma once +#include "vulkan/basalt_window.h" +#include "containers/basalt_darray.h" + +namespace basalt +{ + class Context + { + public: + Context(const Context& src) = delete; + Context& operator=(const Context& src) = delete; + Context(Context&& src) noexcept; + Context& operator=(Context&& src) noexcept; + + Context(const char* app_name, const basalt::darray& required_layers, const basalt::darray& required_extensions, uint32_t app_version=VK_MAKE_API_VERSION(1, 1, 0, 0), uint32_t vulkan_version = VK_MAKE_API_VERSION(0, 1, 2, 0)); + ~Context(); + + VkInstance inst = VK_NULL_HANDLE; + VkAllocationCallbacks* vk_alloc = VK_NULL_HANDLE; + VkDebugUtilsMessengerEXT dbg_msger = VK_NULL_HANDLE; + + bool should_free = true; + bool using_validation_layers = false; + }; +} \ No newline at end of file diff --git a/src/vulkan/basalt_context.cpp b/src/vulkan/basalt_context.cpp new file mode 100644 index 0000000..543524d --- /dev/null +++ b/src/vulkan/basalt_context.cpp @@ -0,0 +1,147 @@ +#include "vulkan/basalt_context.h" +#include + + +static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); + +basalt::Context::Context(Context&& src) noexcept +{ + this->inst = src.inst; + this->vk_alloc = src.vk_alloc; + src.inst = VK_NULL_HANDLE; + src.vk_alloc = VK_NULL_HANDLE; +} + +basalt::Context& basalt::Context::operator=(Context&& src) noexcept +{ + if (&src == this) return *this; + if (this->inst == src.inst && this->inst != VK_NULL_HANDLE) + vkDestroyInstance(this->inst, this->vk_alloc); + this->inst = src.inst; + this->vk_alloc = src.vk_alloc; + src.inst = VK_NULL_HANDLE; + src.vk_alloc = VK_NULL_HANDLE; + return *this; +} + +basalt::Context::Context(const char* app_name, + const basalt::darray& required_layers, + const basalt::darray& required_extensions, + uint32_t app_version, uint32_t vulkan_version) +{ + VkDebugUtilsMessengerCreateInfoEXT dbg_ci = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT }; + VkResult err = VK_SUCCESS; + + VkApplicationInfo appinfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + appinfo.apiVersion = vulkan_version; + appinfo.applicationVersion = app_version; + appinfo.engineVersion = VK_MAKE_API_VERSION(0, 1, 0, 0); + appinfo.pApplicationName = app_name; + appinfo.pEngineName = "basalt"; + appinfo.pNext = VK_NULL_HANDLE; + + VkInstanceCreateInfo ci = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + ci.pApplicationInfo = &appinfo; + + u32 n_exts; + const char** glfw_exts = glfwGetRequiredInstanceExtensions(&n_exts); + basalt::darray exts(required_extensions.m_nelements + n_exts); + exts.push_back(glfw_exts, glfw_exts + n_exts); + exts.push_back(required_extensions); + + ci.enabledExtensionCount = exts.m_nelements; + ci.ppEnabledExtensionNames = exts.m_pdata; + + u32 n_layers = 0; + VkLayerProperties* p_layers = nullptr; + VK_ASSERT(vkEnumerateInstanceLayerProperties(&n_layers, nullptr), "Failed to enumerate instance layer properties\n\tAt %s:%d\n\tError %s\n", string_VkResult(err)); + p_layers = basalt::mem::allocT(n_layers, MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY); + VK_ASSERT(vkEnumerateInstanceLayerProperties(&n_layers, p_layers), "Failed to enumerate instance layer properties\n\tAt %s:%d\n\tError %s\n", string_VkResult(err)); + for (u32 i = 0; i < required_layers.m_nelements; ++i) + { + bool found = false; + this->using_validation_layers |= strcmp(required_layers[i], "VK_LAYER_KHRONOS_validation") == 0; + for (u32 j = 0; j < n_layers; ++j) + { + if (strcmp(p_layers[j].layerName, required_layers[i]) == 0) + { + found = true; + break; + } + } + BASSERT_FATAL(found, "Assertion %s failed at %s:%d\n\nRequired instance layer %s is not present\n", required_layers[i]); + } + ci.enabledLayerCount = required_layers.m_nelements; + ci.ppEnabledLayerNames = required_layers.m_pdata; + + if (this->using_validation_layers) + { + dbg_ci.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + dbg_ci.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; + dbg_ci.pfnUserCallback = debugCallback; + dbg_ci.pUserData = this; + ci.pNext = &dbg_ci; + } + + VK_ASSERT(vkCreateInstance(&ci, this->vk_alloc, &this->inst), "Failed to create vulkan instance\n\tAt %s:%d\n\tError %s\n", string_VkResult(err)); + basalt::mem::dealloc(p_layers, n_layers*sizeof(VkLayerProperties), MEMORY_TAG_CLASS_ARRAY | MEMORY_TAG_ZONE_ENGINE | MEMORY_TAG_ALIGN_ANY); + + if (this->using_validation_layers) + { + dbg_ci.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + dbg_ci.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; + dbg_ci.pfnUserCallback = debugCallback; + dbg_ci.pUserData = this; + PFN_vkCreateDebugUtilsMessengerEXT fn = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(this->inst, "vkCreateDebugUtilsMessengerEXT"); + BASSERT_FATAL(fn != VK_NULL_HANDLE, "Assertion %s failed at %s:%d\n\tFailed to get extension function vkCreateDebugUtilsMessengerEXT when creating validation layers\n\tIs the extension present?\n"); + VK_ASSERT(fn(this->inst, &dbg_ci, this->vk_alloc, &this->dbg_msger), "Failed to create vulkan debug messenger\n\tAt %s:%d\n\tError %d\n", string_VkResult(err)); + } +} + +basalt::Context::~Context() +{ + if (this->using_validation_layers && this->should_free) + { + PFN_vkDestroyDebugUtilsMessengerEXT fn = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(this->inst, "vkDestroyDebugUtilsMessengerEXT"); + BASSERT_ERROR(fn != VK_NULL_HANDLE, {}, "Assertion %s failed at %s:%d\n\tFailed to get extension function vkDestroyDebugUtilsMessengerEXT when destroying validation layers\n\tIs the extension present?\n"); + if (fn != VK_NULL_HANDLE) + fn(this->inst, this->dbg_msger, this->vk_alloc); + } + if (this->inst != VK_NULL_HANDLE && this->should_free) + vkDestroyInstance(this->inst, this->vk_alloc); +} + + +static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) +{ + switch (messageSeverity) + { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + basalt_log(LOG_TRACE, "%s\n", pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + basalt_log(LOG_INFO, "%s\n", pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + basalt_log(LOG_WARN, "%s\n", pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + basalt_log(LOG_ERROR, "%s\n", pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT: + basalt_log(LOG_WEIRD, "%s\n", pCallbackData->pMessage); + break; + default: + basalt_log(LOG_WEIRD, "%s\n", pCallbackData->pMessage); + break; + } + return VK_FALSE; +}