diff --git a/main.cpp b/main.cpp index c3e525b..d0d4bbc 100644 --- a/main.cpp +++ b/main.cpp @@ -36,7 +36,7 @@ int main() glfwPollEvents(); GLFWManager::UpdateWindowTitleFPSInfo(); - if (!VulkanManager::RenderPass()) + if (!VulkanManager::RenderPass(window)) CleanupAllAndExit(EXIT_FAILURE); } diff --git a/src/managers/GLFWManager.cpp b/src/managers/GLFWManager.cpp index 12abfdc..120ccb0 100644 --- a/src/managers/GLFWManager.cpp +++ b/src/managers/GLFWManager.cpp @@ -12,6 +12,13 @@ namespace PB::Renderer std::string GLFWManager::s_BaseWindowTitle = {}; + bool GLFWManager::s_Fullscreen = false; + + int GLFWManager::s_WindowedWidth = 800; + int GLFWManager::s_WindowedHeight = 600; + int GLFWManager::s_WindowedPosX = 50; + int GLFWManager::s_WindowedPosY = 50; + GLFWwindow* GLFWManager::Init(const int width, const int height, const char* title) { const bool success = glfwInit(); @@ -31,10 +38,13 @@ namespace PB::Renderer return nullptr; } + /* Creates the window */ + s_Window = glfwCreateWindow(width, height, title, nullptr, nullptr); s_BaseWindowTitle = title; - /* Stores the window and returns it to the user */ - s_Window = glfwCreateWindow(width, height, title, nullptr, nullptr); + /* Adds callbacks to the window and returns it */ + glfwSetKeyCallback(s_Window, KeyCallback); + return s_Window; } @@ -59,13 +69,44 @@ namespace PB::Renderer s_LastFPSUpdateTime = currentTime; } - double deltaTime = currentTime - s_LastDeltaUpdateTime; + const double deltaTime = currentTime - s_LastDeltaUpdateTime; s_LastDeltaUpdateTime = currentTime; - std::string fullTitle = s_BaseWindowTitle + '(' + const std::string fullTitle = s_BaseWindowTitle + '(' + std::to_string(s_FpsCounter) + " FPS | " + std::to_string(deltaTime) + " ms)"; glfwSetWindowTitle(s_Window, fullTitle.c_str()); } + + void GLFWManager::KeyCallback(GLFWwindow* window, const int key, const int scancode, const int action, const int mode) + { + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_F11: + { + s_Fullscreen = !s_Fullscreen; + if (s_Fullscreen) + { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* videoMode = glfwGetVideoMode(monitor); + + glfwGetWindowPos(window, &s_WindowedPosX, &s_WindowedPosY); + glfwGetWindowSize(window, &s_WindowedWidth, &s_WindowedHeight); + + glfwSetWindowMonitor(window, monitor, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate); + return; + } + + glfwSetWindowMonitor(window, nullptr, s_WindowedPosX, s_WindowedPosY, s_WindowedWidth, s_WindowedHeight, 0); + return; + } + + default: + return; + } + } } diff --git a/src/managers/GLFWManager.h b/src/managers/GLFWManager.h index 664024d..d5ba312 100644 --- a/src/managers/GLFWManager.h +++ b/src/managers/GLFWManager.h @@ -6,6 +6,15 @@ namespace PB::Renderer { class GLFWManager { + public: + static GLFWwindow* Init(int width, int height, const char* title = "Unnamed window"); + __forceinline static GLFWwindow* GetWindow() { return s_Window; } + + static bool Cleanup(); + + static __forceinline void PollEvents() { glfwPollEvents(); } + static void UpdateWindowTitleFPSInfo(); + private: static GLFWwindow* s_Window; @@ -16,13 +25,12 @@ namespace PB::Renderer static std::string s_BaseWindowTitle; - public: - static GLFWwindow* Init(int width, int height, const char* title = "Unnamed window"); - __forceinline static GLFWwindow* GetWindow() { return s_Window; } + static bool s_Fullscreen; + static int s_WindowedWidth; + static int s_WindowedHeight; + static int s_WindowedPosX; + static int s_WindowedPosY; - static bool Cleanup(); - - static __forceinline void PollEvents() { glfwPollEvents(); } - static void UpdateWindowTitleFPSInfo(); + static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mode); }; } diff --git a/src/managers/vulkan/VulkanManager.h b/src/managers/vulkan/VulkanManager.h index e51b990..f98d6fa 100644 --- a/src/managers/vulkan/VulkanManager.h +++ b/src/managers/vulkan/VulkanManager.h @@ -89,7 +89,7 @@ namespace PB::Renderer /* Assembles shaders, fixed function state and creates the render pipeline. - Also checks that the render pass is compatiable. + Also checks that the render pass is compatible. */ static bool CreateGraphicsPipeline(); @@ -108,9 +108,11 @@ namespace PB::Renderer /* Draws a frame to the screen using the command buffers and sync objects. */ - static bool RenderPass(); + static bool RenderPass(GLFWwindow* window); private: + // === Vulkan init helpers === // + static bool IsDeviceSuitable(const VkPhysicalDevice& device); static QueueFamilyIndices FindQueueFamilies(const VkPhysicalDevice& device); static bool CheckDeviceExtensionSupport(const VkPhysicalDevice& device); @@ -122,6 +124,15 @@ namespace PB::Renderer static VkShaderModule CreateShaderModule(const std::string& filename); + // === Vulkan render helpers === // + + static VkResult RenderPassInternal(); + + static void RecreateSwapChain(GLFWwindow* window); + static void CleanupSwapChain(); + + // === Vulkan resources === // + static VkInstance s_Instance; static VkSurfaceKHR s_Surface; diff --git a/src/managers/vulkan/VulkanManagerRender.cpp b/src/managers/vulkan/VulkanManagerRender.cpp index 305a948..ac3373d 100644 --- a/src/managers/vulkan/VulkanManagerRender.cpp +++ b/src/managers/vulkan/VulkanManagerRender.cpp @@ -1,8 +1,70 @@ #include "VulkanManager.h" +#define CHECK_RESULT(res) if (res != VK_SUCCESS) { return res; } + namespace PB::Renderer { - bool VulkanManager::RenderPass() + bool VulkanManager::RenderPass(GLFWwindow* window) + { + switch (const VkResult result = RenderPassInternal()) + { + case VK_ERROR_OUT_OF_DATE_KHR: + case VK_SUBOPTIMAL_KHR: + RecreateSwapChain(window); + return true; + + default: + return result == VK_SUCCESS; + } + } + + void VulkanManager::RecreateSwapChain(GLFWwindow* window) + { + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + /* Pauses if minimised */ + while (width == 0 || height == 0) + { + glfwGetFramebufferSize(window, &width, &height); + glfwWaitEvents(); + } + + /* Waits until device is idle before recreating swap chain */ + vkDeviceWaitIdle(s_Device); + CleanupSwapChain(); + + CreateSwapChain(window); + CreateImageViews(); + CreateRenderPass(); + CreateFramebuffer(); + CreateGraphicsPipeline(); + CreateCommandBuffers(); + CreateSemaphores(); + } + + void VulkanManager::CleanupSwapChain() + { + for (const VkFramebuffer framebuffer : s_Framebuffers) + { + vkDestroyFramebuffer(s_Device, framebuffer, nullptr); + } + s_Framebuffers.clear(); + + vkFreeCommandBuffers(s_Device, s_CommandPool, static_cast(s_CommandBuffers.size()), s_CommandBuffers.data()); + vkDestroyPipeline(s_Device, s_RenderPipeline, nullptr); + vkDestroyPipelineLayout(s_Device, s_PipelineLayout, nullptr); + vkDestroyRenderPass(s_Device, s_RenderPass, nullptr); + + for (const VkImageView view : s_SwapChainImageViews) + { + vkDestroyImageView(s_Device, view, nullptr); + } + + vkDestroySwapchainKHR(s_Device, s_SwapChain, nullptr); + } + + VkResult VulkanManager::RenderPassInternal() { uint32_t imageIndex; VkResult result = vkAcquireNextImageKHR( @@ -13,17 +75,7 @@ namespace PB::Renderer VK_NULL_HANDLE, &imageIndex ); - - if (result == VK_ERROR_OUT_OF_DATE_KHR) - { - return false; - } - - if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) - { - std::cout << "PB::Renderer::VulkanManager::RenderPass(): Failed to acquire swap chain image!" << std::endl; - return false; - } + CHECK_RESULT(result); VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -41,11 +93,8 @@ namespace PB::Renderer submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; - if (vkQueueSubmit(s_GraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) - { - std::cout << "PB::Renderer::VulkanManager::RenderPass(): Failed to submit draw command buffer!" << std::endl; - return false; - } + result = vkQueueSubmit(s_GraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + CHECK_RESULT(result); VkPresentInfoKHR presentInfo{}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; @@ -58,19 +107,9 @@ namespace PB::Renderer presentInfo.pImageIndices = &imageIndex; result = vkQueuePresentKHR(s_PresentQueue, &presentInfo); - - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) - { - return false; - } - - if (result != VK_SUCCESS) - { - std::cout << "PB::Renderer::VulkanManager::RenderPass(): Failed to present swap chain image!" << std::endl; - return false; - } + CHECK_RESULT(result); vkQueueWaitIdle(s_PresentQueue); - return true; + return VK_SUCCESS; } }