diff --git a/aster_core/context.cpp b/aster_core/context.cpp new file mode 100644 index 0000000..4bf07c9 --- /dev/null +++ b/aster_core/context.cpp @@ -0,0 +1,99 @@ +// ============================================= +// Aster: context.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "context.h" + +VKAPI_ATTR b32 VKAPI_CALL Context::debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT _message_severity, VkDebugUtilsMessageTypeFlagsEXT _message_type, const VkDebugUtilsMessengerCallbackDataEXT *_callback_data, [[maybe_unused]] void *_user_data) { + using Severity = vk::DebugUtilsMessageSeverityFlagsEXT; + using SeverityBits = vk::DebugUtilsMessageSeverityFlagBitsEXT; + using MessageType = vk::DebugUtilsMessageTypeFlagsEXT; + using MessageTypeBits = vk::DebugUtilsMessageTypeFlagBitsEXT; + + const auto severity = Severity(_message_severity); + const auto message_type = MessageType(_message_type); + + if (message_type & MessageTypeBits::eValidation) { + if (severity & SeverityBits::eError) + ERROR(_callback_data->pMessage); + if (severity & SeverityBits::eWarning) + WARN(_callback_data->pMessage); + if (severity & SeverityBits::eInfo) + INFO(_callback_data->pMessage); + if (severity & SeverityBits::eVerbose) + VERBOSE(_callback_data->pMessage); + } + + return false; +} + +void Context::init(const std::string_view &_app_name, const Version &_app_version) { + vk::Result result; + + INFO_IF(enable_validation_layers, "Validation Layers enabled"); + + // Creating Instance + vk::ApplicationInfo app_info = { + .pApplicationName = _app_name.data(), + .applicationVersion = VK_MAKE_VERSION(_app_version.major, _app_version.minor, _app_version.patch), + .pEngineName = PROJECT_NAME, + .engineVersion = VK_MAKE_VERSION(VERSION.major, VERSION.minor, VERSION.patch), + .apiVersion = VK_API_VERSION_1_2, + }; + + vk::DebugUtilsMessengerCreateInfoEXT debug_messenger_create_info = { + .messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo, + .messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | + vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | + vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation, + .pfnUserCallback = debug_callback, + .pUserData = nullptr, + }; + + u32 glfw_extension_count = 0; + const char **glfw_extensions = nullptr; + + glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count); + std::vector vulkan_extensions(glfw_extensions, glfw_extensions + glfw_extension_count); + if (enable_validation_layers) { + vulkan_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + const vk::DynamicLoader dl; + // ReSharper disable once CppInconsistentNaming + const auto vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); + VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); + + tie(result, instance) = vk::createInstance({ + .pNext = enable_validation_layers ? &debug_messenger_create_info : nullptr, + .pApplicationInfo = &app_info, + .enabledLayerCount = enable_validation_layers ? cast(validation_layers.size()) : 0, + .ppEnabledLayerNames = enable_validation_layers ? validation_layers.data() : nullptr, + .enabledExtensionCount = cast(vulkan_extensions.size()), + .ppEnabledExtensionNames = vulkan_extensions.data(), + }); + ERROR_IF(failed(result), "Failed to create Vulkan instance with "s + to_string(result)) + THEN_CRASH(result) + ELSE_INFO("Instance Created."); + VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); + + // Debug Messenger + if (enable_validation_layers) { + tie(result, debug_messenger) = instance.createDebugUtilsMessengerEXT(debug_messenger_create_info); + ERROR_IF(failed(result), "Debug Messenger creation failed with "s + to_string(result)) + ELSE_INFO("Debug Messenger Created."); + } +} + +Context::~Context() { + if (instance) { + if (enable_validation_layers && debug_messenger) { + instance.destroyDebugUtilsMessengerEXT(debug_messenger); + } + instance.destroy(); + INFO("Context destroyed"); + } +} diff --git a/aster_core/context.h b/aster_core/context.h new file mode 100644 index 0000000..b6c6f60 --- /dev/null +++ b/aster_core/context.h @@ -0,0 +1,86 @@ +// ============================================= +// Aster: context.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" +#include +#include + +/** + * @class Context + * + * @brief Vulkan context to handle device initialization logic. + * + * Handles the required hardware interactions. + */ +class Context final { +public: + Context(const std::string_view &_app_name, const Version &_app_version, const b8 _enable_validation = true) : + enable_validation_layers{ _enable_validation } { + init(_app_name, _app_version); + } + + Context(const std::string_view &_app_name, const Version &_app_version, const std::vector &_additional_device_extensions, const b8 _enable_validation = true) : + enable_validation_layers{ _enable_validation } { + device_extensions.reserve(device_extensions.size() + _additional_device_extensions.size()); + device_extensions.insert(device_extensions.end(), _additional_device_extensions.begin(), _additional_device_extensions.end()); + + init(_app_name, _app_version); + } + + Context(const std::string_view &_app_name, const Version &_app_version, const std::vector &_additional_device_extensions, const std::vector &_additional_validation_layers) { + device_extensions.reserve(device_extensions.size() + _additional_device_extensions.size()); + device_extensions.insert(device_extensions.end(), _additional_device_extensions.begin(), _additional_device_extensions.end()); + + validation_layers.reserve(validation_layers.size() + _additional_validation_layers.size()); + validation_layers.insert(validation_layers.end(), _additional_validation_layers.begin(), _additional_validation_layers.end()); + + init(_app_name, _app_version); + } + + Context(const Context &_other) = delete; + + Context(Context &&_other) noexcept : + enable_validation_layers{ _other.enable_validation_layers }, validation_layers{ std::move(_other.validation_layers) }, device_extensions{ std::move(_other.device_extensions) }, instance{ std::exchange(_other.instance, nullptr) }, debug_messenger{ std::exchange(_other.debug_messenger, nullptr) } {} + + Context &operator=(const Context &_other) = delete; + + Context &operator=(Context &&_other) noexcept { + if (this == &_other) + return *this; + enable_validation_layers = _other.enable_validation_layers; + validation_layers = std::move(_other.validation_layers); + device_extensions = std::move(_other.device_extensions); + instance = std::exchange(_other.instance, nullptr); + debug_messenger = std::exchange(_other.debug_messenger, nullptr); + return *this; + } + + ~Context(); + + // Fields + bool enable_validation_layers{ true }; + + std::vector validation_layers = { + "VK_LAYER_KHRONOS_validation", + }; + + std::vector device_extensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_KHR_MULTIVIEW_EXTENSION_NAME, + }; + + vk::Instance instance; + vk::DebugUtilsMessengerEXT debug_messenger; + +private: + void init(const std::string_view &_app_name, const Version &_app_version); + + static VKAPI_ATTR b32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT _message_severity, + VkDebugUtilsMessageTypeFlagsEXT _message_type, + const VkDebugUtilsMessengerCallbackDataEXT *_callback_data, + void *_user_data); +};