From cfb54f0e00cee10915f46a18b1effd1c762e9530 Mon Sep 17 00:00:00 2001 From: Rick Rongen Date: Sun, 31 May 2020 15:15:03 +0200 Subject: [PATCH] feat: handle resizing --- src/HelloTriangleApplication.cpp | 89 +++++++++++++++++++++++++++----- src/HelloTriangleApplication.h | 9 ++-- src/MyVkInstance.cpp | 24 ++++++++- src/MyVkInstance.h | 11 ++++ 4 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/HelloTriangleApplication.cpp b/src/HelloTriangleApplication.cpp index 2bf59bb..6dad478 100644 --- a/src/HelloTriangleApplication.cpp +++ b/src/HelloTriangleApplication.cpp @@ -10,6 +10,7 @@ #define GLFW_INCLUDE_VULKAN #include +#include #include "queues.h" #include "util.h" @@ -31,18 +32,21 @@ void HelloTriangleApplication::run() { this->myVkInstance = new MyVkInstance(); this->myVkInstance->init(); initVulkan(); + this->myVkInstance->registerResizeCallback(this, frameBufferResizeCallback); mainLoop(); cleanup(); this->myVkInstance->cleanup(); } void HelloTriangleApplication::createSwapChain() { + int width, height; uint32_t queueFamilyIndices[] = {myVkInstance->indices.graphicsFamily.value(), myVkInstance->indices.presentFamily.value()}; SwapChainSupportDetails swapChainSupportDetails = querySwapChainSupport(myVkInstance->physicalDevice, myVkInstance->surface); VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupportDetails.formats); VkPresentModeKHR presentMode = chooseSwapChainPresentMode(swapChainSupportDetails.presentModes); - VkExtent2D extent = chooseSwapExtent(swapChainSupportDetails.capabilities, WIDTH, HEIGHT); + myVkInstance->getFrameBufferSize(&width, &height); + VkExtent2D extent = chooseSwapExtent(swapChainSupportDetails.capabilities, width, height); uint32_t imageCount = swapChainSupportDetails.capabilities.minImageCount + 1; if (swapChainSupportDetails.capabilities.maxImageCount > 0 && imageCount > swapChainSupportDetails.capabilities.maxImageCount) { @@ -423,6 +427,44 @@ void HelloTriangleApplication::initVulkan() { createSyncObjects(); } +void HelloTriangleApplication::recreateSwapChain() { + frameBufferResized = false; + int width = 0, height = 0; + while (width == 0 || height == 0) { + this->myVkInstance->getFrameBufferSize(&width, &height); + MyVkInstance::waitEvents(); + } + + vkDeviceWaitIdle(this->myVkInstance->device); + + cleanupSwapChain(); + + createSwapChain(); + createImageViews(); + createRenderPass(); + createGraphicsPipeline(); + createFrameBuffers(); + createCommandBuffers(); +} + +void HelloTriangleApplication::cleanupSwapChain() { + for (auto & swapChainFrameBuffer : swapChainFrameBuffers) { + vkDestroyFramebuffer(myVkInstance->device, swapChainFrameBuffer, nullptr); + } + + vkFreeCommandBuffers(myVkInstance->device, commandPool, static_cast(commandBuffers.size()), commandBuffers.data()); + + vkDestroyPipeline(myVkInstance->device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(myVkInstance->device, pipelineLayout, nullptr); + vkDestroyRenderPass(myVkInstance->device, renderPass, nullptr); + + for (auto imageView : swapChainImageViews) { + vkDestroyImageView(myVkInstance->device, imageView, nullptr); + } + + vkDestroySwapchainKHR(myVkInstance->device, swapChain, nullptr); +} + void HelloTriangleApplication::mainLoop() { while (!glfwWindowShouldClose(myVkInstance->window)) { glfwPollEvents(); @@ -434,7 +476,18 @@ void HelloTriangleApplication::mainLoop() { void HelloTriangleApplication::drawFrame() { vkWaitForFences(myVkInstance->device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); uint32_t imageIndex; - vkAcquireNextImageKHR(myVkInstance->device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + VkResult acquireNextImageResult = vkAcquireNextImageKHR(myVkInstance->device, swapChain, UINT64_MAX, + imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + switch (acquireNextImageResult) { + case VK_ERROR_OUT_OF_DATE_KHR: + recreateSwapChain(); + return; + case VK_SUCCESS: + case VK_SUBOPTIMAL_KHR: // todo handle + break; + default: + throw std::runtime_error("Failed to acquire swap chain image!"); + } if (imagesInFlight[currentFrame] != VK_NULL_HANDLE) { vkWaitForFences(myVkInstance->device, 1, &imagesInFlight[currentFrame], VK_TRUE, UINT64_MAX); @@ -472,27 +525,35 @@ void HelloTriangleApplication::drawFrame() { .pResults = nullptr, }; - vkQueuePresentKHR(myVkInstance->presentQueue, &presentInfo); - + VkResult queuePresentResult = vkQueuePresentKHR(myVkInstance->presentQueue, &presentInfo); + switch (queuePresentResult) { + case VK_ERROR_OUT_OF_DATE_KHR: + case VK_SUBOPTIMAL_KHR: + recreateSwapChain(); + break; + case VK_SUCCESS: + break; + default: + throw std::runtime_error("Failed to present queue"); + } + if (frameBufferResized) { + recreateSwapChain(); + } // vkQueueWaitIdle(presentQueue); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } void HelloTriangleApplication::cleanup() { + cleanupSwapChain(); + for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { vkDestroySemaphore(myVkInstance->device, renderFinishedSemaphores[i], nullptr); vkDestroySemaphore(myVkInstance->device, imageAvailableSemaphores[i], nullptr); vkDestroyFence(myVkInstance->device, inFlightFences[i], nullptr); } vkDestroyCommandPool(myVkInstance->device, commandPool, nullptr); - for (auto frameBuffer : swapChainFrameBuffers) { - vkDestroyFramebuffer(myVkInstance->device, frameBuffer, nullptr); - } - vkDestroyPipeline(myVkInstance->device, graphicsPipeline, nullptr); - vkDestroyPipelineLayout(myVkInstance->device, pipelineLayout, nullptr); - vkDestroyRenderPass(myVkInstance->device, renderPass, nullptr); - for (auto imageView : swapChainImageViews) { - vkDestroyImageView(myVkInstance->device, imageView, nullptr); - } - vkDestroySwapchainKHR(myVkInstance->device, swapChain, nullptr); +} + +void HelloTriangleApplication::frameBufferResizeCallback(void* thisptr, int width, int height) { + static_cast(thisptr)->frameBufferResized = true; } diff --git a/src/HelloTriangleApplication.h b/src/HelloTriangleApplication.h index 71b3e23..af5ca22 100644 --- a/src/HelloTriangleApplication.h +++ b/src/HelloTriangleApplication.h @@ -14,13 +14,11 @@ const int MAX_FRAMES_IN_FLIGHT = 2; -const int WIDTH = 800; -const int HEIGHT = 600; - class HelloTriangleApplication { public: void run(); + bool frameBufferResized = false; private: MyVkInstance *myVkInstance; VkSwapchainKHR swapChain; @@ -43,7 +41,6 @@ private: int currentFrame = 0; // Vulkan initialisation - void createSurface(); void createSwapChain(); void createImageViews(); void createRenderPass(); @@ -54,6 +51,9 @@ private: void createSyncObjects(); void initVulkan(); + void recreateSwapChain(); + void cleanupSwapChain(); + // main loop void mainLoop(); void drawFrame(); @@ -64,6 +64,7 @@ private: // utility functions VkShaderModule createShaderModule(const std::vector &code); + static void frameBufferResizeCallback(void *thisptr, int width, int height); }; diff --git a/src/MyVkInstance.cpp b/src/MyVkInstance.cpp index 84e5e33..b1dc578 100644 --- a/src/MyVkInstance.cpp +++ b/src/MyVkInstance.cpp @@ -74,7 +74,7 @@ void MyVkInstance::init() { void MyVkInstance::initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(this->width, this->height, this->title.c_str(), nullptr, nullptr); @@ -197,3 +197,25 @@ void MyVkInstance::cleanup() { glfwDestroyWindow(window); glfwTerminate(); } + +void MyVkInstance::getFrameBufferSize(int *pWidth, int *pHeight) const { + glfwGetFramebufferSize(this->window, pWidth, pHeight); +} + +void MyVkInstance::frameBufferResizeCallbackHandler(GLFWwindow *glfwWindow, int mWidth, int mHeight) { + auto ptr = reinterpret_cast(glfwGetWindowUserPointer(glfwWindow)); + ptr->width = mWidth; + ptr->height = mHeight; + if (ptr->framebufferResizeCallback != nullptr) { + ptr->framebufferResizeCallback(ptr->framebufferResizeCallbackThis, mWidth, mHeight); + } +} + +void MyVkInstance::registerResizeCallback(void* thisValue, void (*callback)(void*, int, int)) { + this->framebufferResizeCallbackThis = thisValue; + this->framebufferResizeCallback = callback; +} + +void MyVkInstance::waitEvents() { + glfwWaitEvents(); +} diff --git a/src/MyVkInstance.h b/src/MyVkInstance.h index 6d7899d..d8c17d7 100644 --- a/src/MyVkInstance.h +++ b/src/MyVkInstance.h @@ -20,6 +20,9 @@ public: void cleanup(); + void getFrameBufferSize(int *pWidth, int *pHeight) const; + void registerResizeCallback(void*, void (*callback)(void*, int, int)); + VkInstance instance{}; VkDevice device{}; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; @@ -29,10 +32,15 @@ public: GLFWwindow* window{}; QueueFamilyIndices indices; + + static void waitEvents(); + private: int width{}; int height{}; std::string title; + void *framebufferResizeCallbackThis; + void (*framebufferResizeCallback)(void*, int, int) = nullptr; #ifdef WITH_VALIDATION_LAYERS VkDebugUtilsMessengerEXT debugMessenger; @@ -47,6 +55,9 @@ private: void createSurface(); void pickPhysicalDevice(); void createLogicalDevice(); + + static void frameBufferResizeCallbackHandler(GLFWwindow *glfwWindow, int width, int height); + };