diff --git a/CMakeLists.txt b/CMakeLists.txt index 899b09c..38b6b65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(stories VERSION 0.0.1 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) diff --git a/engine/engine.cpp b/engine/engine.cpp index ea08e49..e0e84c5 100644 --- a/engine/engine.cpp +++ b/engine/engine.cpp @@ -1,51 +1,23 @@ #include "engine.h" -#include "instance.h" - -constexpr const char* validationLayerName("VK_LAYER_KHRONOS_validation"); Engine::Engine::Engine() : initialized(false), window(new Window()), - instance(nullptr), + instance(new Instance()), surface(nullptr), physicalDevice(nullptr), logicalDevice(nullptr), - layerNames(), - instanceExtensionNames(), deviceExtensionNames(), - layers(), - instanceExtensions(), deviceExtensions() { addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } Engine::Engine::~Engine() { + delete instance; delete window; } -bool Engine::Engine::enableValidationLayers() const { - return instanceExtensionNames.count(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) > 0 && - instanceExtensionNames.count(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) > 0 && - layerNames.count(validationLayerName) > 0; -} - -void Engine::Engine::addLayer(const std::string& layerName) { - if (initialized) - throw std::runtime_error("Adding layers to an initialized engine is not supported yet"); - - layerNames.insert(layerName); -} - -void Engine::Engine::addInstanceExtension(const std::string& extensionName) { - if (initialized) - throw std::runtime_error("Adding extension to an initialized engine is not supported yet"); - - std::pair::const_iterator, bool> pair = instanceExtensionNames.insert(extensionName); - if (pair.second) - instanceExtensions.push_back(pair.first->c_str()); -} - void Engine::Engine::addDeviceExtension(const std::string& extensionName) { if (initialized) throw std::runtime_error("Adding extension to an initialized engine is not supported yet"); @@ -56,17 +28,15 @@ void Engine::Engine::addDeviceExtension(const std::string& extensionName) { } void Engine::Engine::enableDebug() { - addLayer(validationLayerName); - addInstanceExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); - addInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + instance->enableDebug(); } void Engine::Engine::initVulkan() { - instance = new Instance(this); + instance->initialize(window->getRequiredVulkanExtensions()); surface = new Surface(instance, window); pickPhysicalDevice(); - logicalDevice = new LogicalDevice(physicalDevice, surface, deviceExtensions, layers); + logicalDevice = new LogicalDevice(physicalDevice, surface, deviceExtensions, instance->getLayers()); logicalDevice->createGraphicsPipeline("shaders/shader.vert.spv", "shaders/shader.frag.spv"); logicalDevice->createSwapChain(); @@ -82,8 +52,7 @@ void Engine::Engine::cleanup() { delete surface; surface = nullptr; - delete instance; - instance = nullptr; + instance->deinitialize(); } void Engine::Engine::pickPhysicalDevice() { @@ -108,10 +77,6 @@ void Engine::Engine::pickPhysicalDevice() { throw std::runtime_error("failed to find a suitable GPU!"); } -std::vector Engine::Engine::getRequiredVulkanExtensions() const { - return window->getRequiredVulkanExtensions(); -} - void Engine::Engine::run() { initVulkan(); mainLoop(); @@ -127,8 +92,14 @@ void Engine::Engine::mainLoop() { //Handle events on queue while (SDL_PollEvent(&e) != 0) { //close the window when user clicks the X button or alt-f4s - if (e.type == SDL_QUIT) - bQuit = true; + switch (e.type) { + case SDL_QUIT: + bQuit = true; + break; + case SDL_WINDOWEVENT: + handleWindowEvent(e.window); + break; + } } logicalDevice->drawFrame(); @@ -136,3 +107,12 @@ void Engine::Engine::mainLoop() { logicalDevice->waitIdle(); } + +void Engine::Engine::handleWindowEvent(const SDL_WindowEvent& e) { + switch (e.event) { + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + logicalDevice->setResized(); + break; + } +} diff --git a/engine/engine.h b/engine/engine.h index 3e46bf2..a745c0c 100644 --- a/engine/engine.h +++ b/engine/engine.h @@ -23,6 +23,7 @@ #include "window.h" #include "surface.h" +#include "instance.h" #include "physicaldevice.h" #include "logicaldevice.h" @@ -30,21 +31,21 @@ namespace Engine { const int MAX_FRAMES_IN_FLIGHT = 2; -class Instance; - class Engine { - friend class Instance; public: Engine(); ~Engine(); void run(); - bool enableValidationLayers() const; - void addLayer(const std::string& layerName); - void addInstanceExtension(const std::string& extensionName); void addDeviceExtension(const std::string& extensionName); void enableDebug(); - std::vector getRequiredVulkanExtensions() const; + +private: + void initVulkan(); + void mainLoop(); + void cleanup(); + void pickPhysicalDevice(); + void handleWindowEvent(const SDL_WindowEvent& e); private: bool initialized; @@ -53,17 +54,9 @@ private: Surface* surface; PhysicalDevice* physicalDevice; LogicalDevice* logicalDevice; - std::set layerNames; - std::set instanceExtensionNames; std::set deviceExtensionNames; - std::vector layers; - std::vector instanceExtensions; std::vector deviceExtensions; - void initVulkan(); - void mainLoop(); - void cleanup(); - void pickPhysicalDevice(); }; } diff --git a/engine/instance.cpp b/engine/instance.cpp index d2550ef..76c5308 100644 --- a/engine/instance.cpp +++ b/engine/instance.cpp @@ -1,13 +1,26 @@ #include "instance.h" -#include "engine.h" +constexpr const char* validationLayerName("VK_LAYER_KHRONOS_validation"); -Engine::Instance::Instance(Engine* eng) : +Engine::Instance::Instance() : + initialized(false), validationLayersEnabledAndSupported(false), - engine(eng), + layerNames(), + extensionNames(), + layers(), + extensions(), vk(), debugMessenger() -{ +{} + +Engine::Instance::~Instance() { + deinitialize(); +} + +void Engine::Instance::initialize(const std::vector& requiredExtensions) { + if (initialized) + return; + VkApplicationInfo appInfo {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "Hello Triangle"; @@ -18,19 +31,19 @@ Engine::Instance::Instance(Engine* eng) : appInfo.pNext = nullptr; //TODO handle exception - Support exts = getVulkanExtensions(engine->instanceExtensionNames, engine->getRequiredVulkanExtensions()); + Support exts = getVulkanExtensions(extensionNames, requiredExtensions); if (exts.unsupportedCritical.size() > 0) { std::string error("Unable to create Vulkan instance, following critical extensions are unsupported:\n"); - for (const char* ext : exts.unsupportedCritical) { + for (const char* ext : exts.unsupportedCritical) error += "\t" + std::string(ext) + "\n"; - } + throw std::runtime_error(error); } //TODO log unsupported; exts.logSupported("Applying extensions:"); //TODO handle exception - Support layers = getVulkanLayers(engine->layerNames); + Support layers = getVulkanLayers(layerNames); //TODO log unsupported layers.logSupported("Applying layers:"); @@ -44,7 +57,7 @@ Engine::Instance::Instance(Engine* eng) : createInfo.ppEnabledLayerNames = layers.supported.data(); VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo {}; - if (engine->enableValidationLayers()) { + if (hasValidationLayers()) { validationLayersEnabledAndSupported = true; //TODO make sure it's actually supported, this shows that they are just enabled; populateDebugMessengerCreateInfo(debugCreateInfo, debugCallback); createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo; @@ -72,13 +85,49 @@ Engine::Instance::Instance(Engine* eng) : if (validationLayersEnabledAndSupported) setupDebugMessenger(); + + initialized = true; } -Engine::Instance::~Instance() { +void Engine::Instance::deinitialize() { + if (!initialized) + return; + if (validationLayersEnabledAndSupported) destroyDebugUtilsMessengerEXT(vk, debugMessenger, nullptr); vkDestroyInstance(vk, nullptr); + initialized = false; +} + +void Engine::Instance::addExtension(const std::string& extensionName) { + if (initialized) + throw std::runtime_error("Adding extension to an initialized engine is not supported yet"); + + std::pair::const_iterator, bool> pair = extensionNames.insert(extensionName); + if (pair.second) + extensions.push_back(pair.first->c_str()); +} + +void Engine::Instance::addLayer(const std::string& layerName) { + if (initialized) + throw std::runtime_error("Adding layers to an initialized engine is not supported yet"); + + std::pair::const_iterator, bool> pair = layerNames.insert(layerName); + if (pair.second) + layers.push_back(pair.first->c_str()); +} + +bool Engine::Instance::hasValidationLayers() { + return extensionNames.count(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) > 0 && + extensionNames.count(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) > 0 && + layerNames.count(validationLayerName) > 0; +} + +void Engine::Instance::enableDebug() { + addLayer(validationLayerName); + addExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + addExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } void Engine::Instance::setupDebugMessenger() { @@ -108,3 +157,6 @@ std::vector Engine::Instance::enumeratePhysicalDevices() const return devices; } +std::vector Engine::Instance::getLayers() const { + return layers; +} diff --git a/engine/instance.h b/engine/instance.h index c26d0d2..8e3ef8f 100644 --- a/engine/instance.h +++ b/engine/instance.h @@ -2,30 +2,40 @@ #include #include +#include #include "utils.h" namespace Engine { -class Engine; -class Window; class Surface; class Instance { - friend class Engine; friend class Surface; public: - Instance(Engine* engine); + Instance(); ~Instance(); + void initialize(const std::vector& requiredExtensions); + void deinitialize(); + void addLayer(const std::string& layerName); + void addExtension(const std::string& extensionName); + void enableDebug(); + std::vector getLayers() const; + std::vector enumeratePhysicalDevices() const; private: void setupDebugMessenger(); + bool hasValidationLayers(); private: + bool initialized; bool validationLayersEnabledAndSupported; - Engine* engine; + std::set layerNames; + std::set extensionNames; + std::vector layers; + std::vector extensions; VkInstance vk; VkDebugUtilsMessengerEXT debugMessenger; }; diff --git a/engine/logicaldevice.cpp b/engine/logicaldevice.cpp index 1f225fb..81176fe 100644 --- a/engine/logicaldevice.cpp +++ b/engine/logicaldevice.cpp @@ -1,6 +1,7 @@ #include "logicaldevice.h" #include "swapchain.h" +#include constexpr int MAX_FRAMES_IN_FLIGHT = 2; constexpr float queuePriority = 1.0f; @@ -269,6 +270,10 @@ void Engine::LogicalDevice::drawFrame() { currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } +void Engine::LogicalDevice::setResized() { + framebufferResized = true; +} + VkExtent2D Engine::LogicalDevice::chooseSwapExtent() const { return surface->chooseSwapExtent(phys->swapChainSupport.capabilities); } @@ -564,5 +569,6 @@ void Engine::LogicalDevice::recordCommandBuffer(VkCommandBuffer commandBuffer, u void Engine::LogicalDevice::recreateSwapChain() { surface->waitForResize(); waitIdle(); + phys->recreateSwapChainSupportDetails(surface); createSwapChain(); } diff --git a/engine/logicaldevice.h b/engine/logicaldevice.h index 30e13ac..4c53aef 100644 --- a/engine/logicaldevice.h +++ b/engine/logicaldevice.h @@ -10,11 +10,9 @@ #include "surface.h" namespace Engine { -class Engine; class SwapChain; class LogicalDevice { - friend class Engine; public: LogicalDevice( PhysicalDevice* physicalDevice, @@ -28,6 +26,7 @@ public: void clearSwapChain(); void createGraphicsPipeline(const std::string& vertexShaderPath, const std::string& fragmentShaderPath); void recreateSwapChain(); + void setResized(); void drawFrame(); diff --git a/engine/physicaldevice.cpp b/engine/physicaldevice.cpp index 50361ca..2be8034 100644 --- a/engine/physicaldevice.cpp +++ b/engine/physicaldevice.cpp @@ -32,3 +32,7 @@ bool Engine::PhysicalDevice::checkDeviceExtensionSupport(VkPhysicalDevice device return requiredExtensions.empty(); } + +void Engine::PhysicalDevice::recreateSwapChainSupportDetails(const Surface* surface) { + swapChainSupport = surface->querySwapChainSupport(vk); +} diff --git a/engine/physicaldevice.h b/engine/physicaldevice.h index 319fcf1..c052140 100644 --- a/engine/physicaldevice.h +++ b/engine/physicaldevice.h @@ -7,21 +7,22 @@ #include #include "utils.h" +#include "surface.h" namespace Engine { -class Engine; class LogicalDevice; class PhysicalDevice { - friend class Engine; friend class LogicalDevice; public: PhysicalDevice(VkPhysicalDevice raw, const QueueFamilyIndices& indices, const SwapChainSupportDetails& swapChainSupport); + void recreateSwapChainSupportDetails(const Surface* surface); + static bool checkDeviceExtensionSupport(VkPhysicalDevice device, std::set requiredExtensions); const QueueFamilyIndices indices; - const SwapChainSupportDetails swapChainSupport; + SwapChainSupportDetails swapChainSupport; private: VkPhysicalDevice vk; diff --git a/engine/surface.cpp b/engine/surface.cpp index d559176..e58de99 100644 --- a/engine/surface.cpp +++ b/engine/surface.cpp @@ -86,3 +86,6 @@ VkExtent2D Engine::Surface::waitForResize() const { return window->waitForResize(); } +VkExtent2D Engine::Surface::getSize() const { + return window->getSize(); +} diff --git a/engine/surface.h b/engine/surface.h index 8a92918..099d6c7 100644 --- a/engine/surface.h +++ b/engine/surface.h @@ -20,6 +20,7 @@ public: VkExtent2D waitForResize() const; VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const; + VkExtent2D getSize() const; VkSurfaceFormatKHR chooseSwapSurfaceFormat(const SwapChainSupportDetails& swapChainSupport) const; bool isDeviceSutable(VkPhysicalDevice device) const; SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) const; diff --git a/engine/swapchain.cpp b/engine/swapchain.cpp index 182b462..535d329 100644 --- a/engine/swapchain.cpp +++ b/engine/swapchain.cpp @@ -10,6 +10,21 @@ Engine::SwapChain::SwapChain(LogicalDevice* logical): imageViews(), frameBuffers() { + create(); +} + +Engine::SwapChain::SwapChain(LogicalDevice* logical, const VkExtent2D& extent): + logical(logical), + vk(), + images(), + extent(extent), + imageViews(), + frameBuffers() +{ + create(); +} + +void Engine::SwapChain::create() { VkResult result = logical->createVkSwapChain(extent, vk); if (result != VK_SUCCESS) @@ -31,6 +46,7 @@ Engine::SwapChain::SwapChain(LogicalDevice* logical): } } + Engine::SwapChain::~SwapChain() { for (VkFramebuffer& buffer : frameBuffers) logical->destroyVkFrameBuffer(buffer); diff --git a/engine/swapchain.h b/engine/swapchain.h index 5e5a465..7207ea0 100644 --- a/engine/swapchain.h +++ b/engine/swapchain.h @@ -11,11 +11,15 @@ class SwapChain { friend class LogicalDevice; public: SwapChain(LogicalDevice* logical); + SwapChain(LogicalDevice* logical, const VkExtent2D& extent); ~SwapChain(); VkExtent2D getExtent() const; VkFramebuffer getFrameBuffer(uint32_t index); +private: + void create(); + private: LogicalDevice* logical; VkSwapchainKHR vk; diff --git a/engine/utils.cpp b/engine/utils.cpp index 8b63217..90d6777 100644 --- a/engine/utils.cpp +++ b/engine/utils.cpp @@ -10,18 +10,17 @@ Engine::Support Engine::getVulkanLayers(const std::set& requestedLa Engine::Support result; if (!discoveredLayers) { std::vector props = getAvailableVulkanLayers(); - for (const VkLayerProperties& prop : props) { + for (const VkLayerProperties& prop : props) availableLayers.insert(std::make_pair(prop.layerName, prop)); - } + discoveredLayers = true; } for (const std::string& ext : requestedLayers) { - if (availableLayers.count(ext) > 0) { + if (availableLayers.count(ext) > 0) result.supported.push_back(ext.c_str()); - } else { + else result.unsupported.push_back(ext.c_str()); - } } return result; @@ -34,26 +33,24 @@ Engine::Support Engine::getVulkanExtensions( Support result; if (!discoveredExtensions) { std::vector props = getAvailableVulkanExtensions(); - for (const VkExtensionProperties& prop : props) { + for (const VkExtensionProperties& prop : props) availableExtensions.insert(std::make_pair(prop.extensionName, prop)); - } + discoveredExtensions = true; } for (const char* ext : criticalExtensions) { - if (availableExtensions.count(ext) > 0) { + if (availableExtensions.count(ext) > 0) result.supported.push_back(ext); - } else { + else result.unsupportedCritical.push_back(ext); - } } for (const std::string& ext : requestedExtensions) { - if (availableExtensions.count(ext) > 0) { + if (availableExtensions.count(ext) > 0) result.supported.push_back(ext.c_str()); - } else { + else result.unsupported.push_back(ext.c_str()); - } } return result; @@ -62,15 +59,13 @@ Engine::Support Engine::getVulkanExtensions( std::vector Engine::getAvailableVulkanExtensions() { unsigned int extCount = 0; VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extCount, nullptr); - if (res != VK_SUCCESS) { + if (res != VK_SUCCESS) throw std::runtime_error("unable to query vulkan extension property count"); - } std::vector extensions(extCount); res = vkEnumerateInstanceExtensionProperties(nullptr, &extCount, extensions.data()); - if (res != VK_SUCCESS) { + if (res != VK_SUCCESS) throw std::runtime_error("unable to retrieve vulkan instance layer names"); - } return extensions; } @@ -95,15 +90,13 @@ void Engine::populateDebugMessengerCreateInfo( std::vector Engine::getAvailableVulkanLayers() { unsigned int layerCount = 0; VkResult res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - if (res != VK_SUCCESS) { + if (res != VK_SUCCESS) throw std::runtime_error("unable to query vulkan instance layer property count"); - } std::vector layers(layerCount); res = vkEnumerateInstanceLayerProperties(&layerCount, layers.data()); - if (res != VK_SUCCESS) { + if (res != VK_SUCCESS) throw std::runtime_error("unable to retrieve vulkan instance layer names"); - } return layers; } @@ -175,9 +168,8 @@ std::vector Engine::readFile(const std::string& filename) { VkSurfaceFormatKHR Engine::chooseSwapSurfaceFormat(const std::vector& availableFormats) { for (const VkSurfaceFormatKHR& availableFormat : availableFormats) { - if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) return availableFormat; - } } return availableFormats[0]; @@ -185,9 +177,8 @@ VkSurfaceFormatKHR Engine::chooseSwapSurfaceFormat(const std::vector& availablePresentModes) { for (const auto& availablePresentMode : availablePresentModes) { - if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) return availablePresentMode; - } } return VK_PRESENT_MODE_FIFO_KHR; diff --git a/engine/window.cpp b/engine/window.cpp index 8c1ed3d..03a7796 100644 --- a/engine/window.cpp +++ b/engine/window.cpp @@ -1,18 +1,11 @@ #include "window.h" +#include + Engine::Window::Window(uint32_t width, uint32_t height) : sdl(createWindow(width, height)), extent({width, height}) -{ - - //glfwSetWindowUserPointer(window, this); - //glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); -} - - // static void framebufferResizeCallback(SDL_Window* window, int width, int height) { - // auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); - // app->framebufferResized = true; - // } +{} Engine::Window::~Window() { SDL_DestroyWindow(sdl); @@ -27,7 +20,7 @@ SDL_Window* Engine::Window::createWindow(uint32_t width, uint32_t height) { SDL_WINDOWPOS_UNDEFINED, width, height, - SDL_WINDOW_VULKAN + SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE ); return wnd; @@ -59,6 +52,18 @@ VkExtent2D Engine::Window::getDrawableSize() const { return actualExtent; } +VkExtent2D Engine::Window::getSize() const { + int width, height; + SDL_GetWindowSize(sdl, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; + + return actualExtent; +} + void Engine::Window::createSurface(VkInstance instance, VkSurfaceKHR* out) const { if (SDL_Vulkan_CreateSurface(sdl, instance, out) != SDL_TRUE) { throw std::runtime_error("failed to create window surface!"); @@ -74,6 +79,7 @@ VkExtent2D Engine::Window::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capa actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + std::cout << actualExtent.width << ":" << actualExtent.height << std::endl; return actualExtent; } } diff --git a/engine/window.h b/engine/window.h index f05c05f..caf3777 100644 --- a/engine/window.h +++ b/engine/window.h @@ -21,6 +21,7 @@ public: std::vector getRequiredVulkanExtensions() const; VkExtent2D waitForResize() const; + VkExtent2D getSize() const; VkExtent2D getDrawableSize() const; VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) const; void createSurface(VkInstance instance, VkSurfaceKHR* out) const;