diff --git a/include/engine.hpp b/include/engine.hpp index 9d2eaf8..6e77afc 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -1,11 +1,5 @@ #pragma once -#include - -#include "log.hpp" -#include -#include - namespace engine { struct AppInfo { diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index b5288d1..b6ff345 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -4,13 +4,16 @@ #include "engine.hpp" -class Window; +#include + +#include + namespace engine::gfx { struct ENGINE_API Device { - Device(AppInfo appInfo, const Window& window); + Device(AppInfo appInfo, SDL_Window* window); ~Device(); private: diff --git a/include/window.hpp b/include/window.hpp index 2633ad6..a7190a6 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -124,77 +124,76 @@ public: bool infoBox(const std::string& title, const std::string& msg); - std::vector getRequiredVulkanExtensions() const; - /* STATIC METHODS */ static void errorBox(const std::string& message); - private: - SDL_Window* m_handle; +public: + SDL_Window* m_handle; - bool m_shouldClose = false; +private: - std::string m_title; + bool m_shouldClose = false; - bool m_fullscreen = false; - bool m_justResized = false; - bool m_keyboardFocus = true; + std::string m_title; - // size in screen coordinates - glm::ivec2 m_winSize = glm::vec2(640, 480); + bool m_fullscreen = false; + bool m_justResized = false; + bool m_keyboardFocus = true; - // performance counter frequency - uint64_t m_counterFreq; + // size in screen coordinates + glm::ivec2 m_winSize = glm::vec2(640, 480); - // number of frames swapped - uint64_t m_frames = 0; - // frame count offset for fpsAvg - uint64_t m_avgFpsStartCount = 0; - // in nanoseconds - uint64_t m_startTime; - // in nanoseconds - uint64_t m_lastFrameStamp; - // in nanoseconds; elapsed time between frames - uint64_t m_lastFrameTime = 1; // not 0 to avoid division by zero - // in nanoseconds - uint64_t m_avgFpsStart; + // performance counter frequency + uint64_t m_counterFreq; - // input stuff + // number of frames swapped + uint64_t m_frames = 0; + // frame count offset for fpsAvg + uint64_t m_avgFpsStartCount = 0; + // in nanoseconds + uint64_t m_startTime; + // in nanoseconds + uint64_t m_lastFrameStamp; + // in nanoseconds; elapsed time between frames + uint64_t m_lastFrameTime = 1; // not 0 to avoid division by zero + // in nanoseconds + uint64_t m_avgFpsStart; - enum class ButtonDelta { - SAME = 0, - PRESSED, - RELEASED - }; + // input stuff - struct { - std::array keys; - std::array deltas; - } m_keyboard{ }; + enum class ButtonDelta { + SAME = 0, + PRESSED, + RELEASED + }; - struct { - std::array(inputs::MouseButton::M_SIZE)> buttons; - std::array deltas; - Sint32 x; - Sint32 y; - Sint32 dx; - Sint32 dy; - float xscroll; - float yscroll; - bool captured = false; - } m_mouse{ }; + struct { + std::array keys; + std::array deltas; + } m_keyboard{ }; - // private methods + struct { + std::array(inputs::MouseButton::M_SIZE)> buttons; + std::array deltas; + Sint32 x; + Sint32 y; + Sint32 dx; + Sint32 dy; + float xscroll; + float yscroll; + bool captured = false; + } m_mouse{ }; - void onResize(Sint32 width, Sint32 height); - void resetInputDeltas(); + // private methods - // event methods (like callbacks) + void onResize(Sint32 width, Sint32 height); + void resetInputDeltas(); - void onWindowEvent(SDL_WindowEvent& e); - void onKeyEvent(SDL_KeyboardEvent& e); - void onMouseButtonEvent(SDL_MouseButtonEvent& e); - void onMouseMotionEvent(SDL_MouseMotionEvent& e); - void onMouseWheelEvent(SDL_MouseWheelEvent& e); + // event methods (like callbacks) + void onWindowEvent(SDL_WindowEvent& e); + void onKeyEvent(SDL_KeyboardEvent& e); + void onMouseButtonEvent(SDL_MouseButtonEvent& e); + void onMouseMotionEvent(SDL_MouseMotionEvent& e); + void onMouseWheelEvent(SDL_MouseWheelEvent& e); }; diff --git a/src/engine.cpp b/src/engine.cpp index 0f50ff9..c13c74e 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -1,5 +1,7 @@ #include "engine.hpp" +#include + namespace engine { bool versionFromCharArray(const char* version, int* major, int* minor, int* patch) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 5d062ea..2e06cd2 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -3,14 +3,13 @@ #include "gfx_device.hpp" #include "config.h" - -#include "window.hpp" - #include "log.hpp" #define VOLK_IMPLEMENTATION #include "volk.h" +#include + #include #include @@ -18,194 +17,286 @@ namespace engine::gfx { + static std::vector getRequiredVulkanExtensions(SDL_Window* window) + { +#ifdef ENGINE_BUILD_VULKAN + 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; +#else + return std::vector{}; +#endif + } + + static VkBool32 debugMessenger( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + 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: + TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); + break; + default: + break; + } + return VK_FALSE; + } + class Device::Impl { friend Device; - VkInstance m_instance; -#ifndef NDEBUG - VkDebugUtilsMessengerEXT m_debugMessenger; -#endif - - static constexpr const char * VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; - static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - - std::vector m_layersAvailable{}; - std::optional::iterator> m_validationLayer; - - - - void findAvailableLayers() + public: + Impl(AppInfo appInfo, SDL_Window* window) { - VkResult res; - - uint32_t layerCount; - res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - assert(res == VK_SUCCESS); - m_layersAvailable.resize(layerCount); - res = vkEnumerateInstanceLayerProperties(&layerCount, m_layersAvailable.data()); - assert(res == VK_SUCCESS); - + m_instance = std::make_unique(appInfo, getRequiredVulkanExtensions(window)); #ifndef NDEBUG - for (auto it = m_layersAvailable.begin(); it != m_layersAvailable.end(); it++) { - TRACE("Found Vulkan layer: {}, {}", it->layerName, it->description); - if (strncmp(it->layerName, VALIDATION_LAYER_NAME, 256) == 0) { - m_validationLayer = it; + m_debugMessenger = std::make_unique(m_instance->getHandle()); +#endif + } + + private: + +// VkSurfaceKHR m_surface; + + static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + + class Instance { + + public: + Instance(AppInfo appInfo, const std::vector& windowExtensions) + { + VkResult res; + + findAvailableLayers(); + + int appVersionMajor, appVersionMinor, appVersionPatch; + assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); + int engineVersionMajor, engineVersionMinor, engineVersionPatch; + assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); + + VkApplicationInfo applicationInfo{ + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = nullptr, + .pApplicationName = appInfo.name, + .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), + .pEngineName = "engine", + .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), + .apiVersion = VK_API_VERSION_1_0, + }; + + // make a list of all extensions to use + std::vector extensions{}; + extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); + + std::vector layers{}; + + if (m_validationLayer.has_value()) { + layers.push_back(m_validationLayer.value()->layerName); + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } - } - if (m_validationLayer.has_value() == false) { - WARN("The validation layer was not found. Continuing."); - } -#endif - } - void createInstance(AppInfo appInfo, const std::vector& windowExtensions) - { - VkResult res; + VkInstanceCreateInfo instanceInfo{ + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .pApplicationInfo = &applicationInfo, + .enabledLayerCount = (uint32_t)layers.size(), + .ppEnabledLayerNames = layers.data(), + .enabledExtensionCount = (uint32_t)extensions.size(), + .ppEnabledExtensionNames = extensions.data(), + }; - int appVersionMajor, appVersionMinor, appVersionPatch; - assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); - int engineVersionMajor, engineVersionMinor, engineVersionPatch; - assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); - - VkApplicationInfo applicationInfo { - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pNext = nullptr, - .pApplicationName = appInfo.name, - .applicationVersion = VK_MAKE_VERSION(appVersionMajor, appVersionMinor, appVersionPatch), - .pEngineName = "engine", - .engineVersion = VK_MAKE_VERSION(engineVersionMajor, engineVersionMinor, engineVersionPatch), - .apiVersion = VK_API_VERSION_1_0, - }; - - // make a list of all extensions to use - std::vector extensions{}; - extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); -#ifndef NDEBUG - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); -#endif - - std::vector layers{}; + if (m_validationLayer.has_value()) { + VkDebugUtilsMessengerCreateInfoEXT debugMessengerCreateInfo = DebugMessenger::getCreateInfo(); + instanceInfo.pNext = &debugMessengerCreateInfo; + } #ifndef NDEBUG - if (m_validationLayer.has_value()) - layers.push_back(m_validationLayer.value()->layerName); + for (const char* ext : extensions) { + TRACE("Using Vulkan instance extension: {}", ext); + } #endif - VkInstanceCreateInfo instanceInfo { - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .pApplicationInfo = &applicationInfo, - .enabledLayerCount = (uint32_t)layers.size(), - .ppEnabledLayerNames = layers.data(), - .enabledExtensionCount = (uint32_t)extensions.size(), - .ppEnabledExtensionNames = extensions.data(), - }; - - res = vkCreateInstance(&instanceInfo, nullptr, &m_instance); - if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { - CRITICAL("The graphics driver is incompatible with vulkan"); - throw std::runtime_error("Graphics driver is incompatible with Vulkan"); + res = vkCreateInstance(&instanceInfo, nullptr, &m_handle); + if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { + CRITICAL("The graphics driver is incompatible with vulkan"); + throw std::runtime_error("Graphics driver is incompatible with Vulkan"); + } + assert(res == VK_SUCCESS); + + volkLoadInstance(m_handle); + } - assert(res == VK_SUCCESS); - } + + ~Instance() + { + vkDestroyInstance(m_handle, nullptr); + } + + VkInstance getHandle() + { + return m_handle; + } + + private: + + VkInstance m_handle; + + std::vector m_layersAvailable{}; + static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; + std::optional::iterator> m_validationLayer; + + void findAvailableLayers() + { + VkResult res; + + uint32_t layerCount; + res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + assert(res == VK_SUCCESS); + m_layersAvailable.resize(layerCount); + res = vkEnumerateInstanceLayerProperties(&layerCount, m_layersAvailable.data()); + assert(res == VK_SUCCESS); #ifndef NDEBUG - static VkBool32 debugMessenger( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageTypes, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) - { + // find validation layer and print all layers to log + for (auto it = m_layersAvailable.begin(); it != m_layersAvailable.end(); it++) { + // TRACE("Found Vulkan layer: {}, {}", it->layerName, it->description); + if (strncmp(it->layerName, VALIDATION_LAYER_NAME, 256) == 0) { + m_validationLayer = it; + } + } + if (m_validationLayer.has_value() == false) { + CRITICAL("The validation layer was not found. Quitting."); + throw std::runtime_error("Validation layer not found"); + } +#endif + } - 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.)"; +#ifndef NDEBUG + class DebugMessenger { - switch (messageSeverity) { + public: + DebugMessenger(const VkInstance& instance) : m_instance(instance) + { + VkDebugUtilsMessengerCreateInfoEXT createInfo = getCreateInfo(); + + VkResult res = vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &m_debugMessenger); + assert(res == VK_SUCCESS); + } + + ~DebugMessenger() + { + vkDestroyDebugUtilsMessengerEXT(m_instance, m_debugMessenger, nullptr); + } + + static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() + { + VkDebugUtilsMessengerCreateInfoEXT createInfo{ + .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 = debugMessenger, + .pUserData = nullptr, + }; + + switch (MESSAGE_LEVEL) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - TRACE("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + // fall-through case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - INFO("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + // fall-through case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - WARN("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + // fall-through case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - ERROR("VULKAN MESSAGE{}: ID: {} MSG: {}", msgType, pCallbackData->pMessageIdName, pCallbackData->pMessage); - break; + createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + // fall-through default: break; + } + + return createInfo; } - return VK_FALSE; - } + + private: + VkDebugUtilsMessengerEXT m_debugMessenger; + const VkInstance& m_instance; + + }; #endif - void createDebugMessenger() - { + std::unique_ptr m_instance; #ifndef NDEBUG - VkResult res; - - if (m_validationLayer.has_value() == false) return; - - VkDebugUtilsMessengerCreateInfoEXT createInfo { - .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 = debugMessenger, - .pUserData = nullptr, - }; - - switch (MESSAGE_LEVEL) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; - // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; - // fall-through - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - // fall-through - default: - break; - } - - res = vkCreateDebugUtilsMessengerEXT(m_instance, &createInfo, nullptr, &m_debugMessenger); - assert(res == VK_SUCCESS); + std::unique_ptr m_debugMessenger; #endif + +/* + + void createSurface(SDL_Window* window) + { + if (SDL_Vulkan_CreateSurface(window, m_instance, &m_surface) == false) { + CRITICAL("Unable to create window surface"); + throw std::runtime_error("Unable to create window surface"); + } } +*/ }; - - - Device::Device(AppInfo appInfo, const Window& window) : pimpl(std::make_unique()) + Device::Device(AppInfo appInfo, SDL_Window* window) { - pimpl->findAvailableLayers(); - pimpl->createInstance(appInfo, window.getRequiredVulkanExtensions()); - pimpl->createDebugMessenger(); + VkResult res; + res = volkInitialize(); + if (res == VK_ERROR_INITIALIZATION_FAILED) { + CRITICAL("Unable to load vulkan, is it installed?"); + throw std::runtime_error("Unable to load vulkan, is it installed?"); + } + assert(res == VK_SUCCESS); + + pimpl = std::make_unique(appInfo, window); + + //pimpl->createDebugMessenger(); } Device::~Device() { - vkDestroyInstance(pimpl->m_instance, nullptr); -#ifndef NDEBUG - vkDestroyDebugUtilsMessengerEXT(pimpl->m_instance, pimpl->m_debugMessenger, nullptr); -#endif + //pimpl->cleanup(); } } diff --git a/src/window.cpp b/src/window.cpp index e8e6829..84c2b77 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,12 +1,10 @@ #include "window.hpp" +#include "log.hpp" + #include #include -#ifdef ENGINE_BUILD_VULKAN -#include -#endif - const uint64_t BILLION = 1000000000; Window::Window(const std::string& title) : m_title(title) @@ -32,7 +30,7 @@ Window::Window(const std::string& title) : m_title(title) SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, static_cast(m_winSize.x), static_cast(m_winSize.y), - 0); + SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN); if (m_handle == NULL) { SDL_Quit(); throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); @@ -447,20 +445,6 @@ bool Window::infoBox(const std::string& title, const std::string& msg) } } -std::vector Window::getRequiredVulkanExtensions() const -{ -#ifdef ENGINE_BUILD_VULKAN - unsigned int sdlExtensionCount = 0; - SDL_Vulkan_GetInstanceExtensions(m_handle, &sdlExtensionCount, nullptr); - std::vector requiredExtensions(sdlExtensionCount); - SDL_Vulkan_GetInstanceExtensions(m_handle, &sdlExtensionCount, requiredExtensions.data()); - - return requiredExtensions; -#else - return std::vector{}; -#endif -} - /* STATIC METHODS */ // Display an error message box