diff --git a/main.cpp b/main.cpp index 9bee2e9..49d3dc5 100644 --- a/main.cpp +++ b/main.cpp @@ -8,6 +8,17 @@ #include #include #include +#include +#include + +using TimePoint = std::chrono::time_point; +using TimeMs = long; + +TimeMs timeDeltaMs(const TimePoint& point) { + auto timeDelta = std::chrono::system_clock::now() - point; + auto timeDelayMs = std::chrono::duration_cast(timeDelta).count(); + return timeDelayMs; +} const std::vector deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, @@ -35,13 +46,27 @@ public: cleanup(); } + void scheduleWindowResize(int sizeX, int sizeY) { + mWindowSizeDirtyFlagTime = std::chrono::system_clock::now(); + mWindowSizeDirtyFlag = true; + mWindowFramebufferSize = std::make_pair(sizeX, sizeY); + assert(!(sizeX <= 0 || sizeY <= 0)); + } + private: void initWindow() { + // glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + // glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); mWindow = glfwCreateWindow(800, 600, "App", nullptr, nullptr); + scheduleWindowResize(800, 600); + + glfwSetWindowUserPointer(mWindow, this); + glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow* window, int sizeX, int sizeY){ + ((Application*) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY); + }); } void initVulkan() { @@ -52,7 +77,10 @@ private: createLogicalDevice(); getQueues(); - createSwapChain(); + int width, height; + glfwGetFramebufferSize(mWindow, &width, &height); + + createSwapChain(width, height); createSwapChainImageViews(); createRenderPass(); @@ -69,6 +97,15 @@ private: void mainLoop() { while (!glfwWindowShouldClose(mWindow)) { glfwPollEvents(); + + if (mWindowSizeDirtyFlag) { + auto timeDelayMs = timeDeltaMs(mWindowSizeDirtyFlagTime); + if (timeDelayMs > mWindowSizeApplyMinDelay) { + recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second); + mWindowSizeDirtyFlag = false; + } + } + drawFrame(); } @@ -291,12 +328,24 @@ private: } } - void createSwapChain() { + void recreateSwapChain(int sizeX, int sizeY) { + vkDeviceWaitIdle(mDevice); + + destroySwapChainFramebuffers(); + destroySwapChainImageViews(); + destroySwapChain(); + + createSwapChain(sizeX, sizeY); + createSwapChainImageViews(); + createSwapChainFramebuffers(); + } + + void createSwapChain(int sizeX, int sizeY) { SwapChainSupportDetails details = querySwapChainSupportDetails(mPhysicalDevice); VkSurfaceFormatKHR surfaceFormat = pickSwapChainSurfaceFormat(details); VkPresentModeKHR presentMode = pickSwapChainPresentMode(details); - VkExtent2D extent2D = pickSwapChainExtent(details.capabilities); + VkExtent2D extent2D = pickSwapChainExtent(details.capabilities, sizeX, sizeY); // +1 so will not have to wait for device to finish frame to query an image to render to uint32_t imageCount = details.capabilities.minImageCount + 1; @@ -363,16 +412,13 @@ private: return VK_PRESENT_MODE_FIFO_KHR; // guaranteed exists } - VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities) { + static VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities, int sizeX, int sizeY) { // if set by vulkan just keep it if (capabilities.currentExtent.width != std::numeric_limits::max()) { return capabilities.currentExtent; } - int width, height; - glfwGetFramebufferSize(mWindow, &width, &height); - - VkExtent2D out = {(uint32_t) width, (uint32_t) height}; + VkExtent2D out = {(uint32_t) sizeX, (uint32_t) sizeY}; out.width = std::clamp(out.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); out.height = std::clamp(out.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); @@ -445,6 +491,10 @@ private: } } + void destroySwapChain() { + vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); + } + void destroySwapChainImageViews() { for (const auto &imageView: mSwapChainImageViews) { vkDestroyImageView(mDevice, imageView, nullptr); @@ -816,9 +866,14 @@ private: .pImageIndices = &imageIndex, }; - if (vkQueuePresentKHR(mPresentQueue, &presentInfo) != VK_SUCCESS) { - throw std::runtime_error("failed to submit to presentation queue"); + vkQueuePresentKHR(mPresentQueue, &presentInfo); + /* Somehow res is always VK_SUCCESS + if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) { + recreateSwapChain(); + } else if (res != VK_SUCCESS) { + throw std::runtime_error("cannot acquire new khr image"); } + */ } void destroySynchronizationObjects() { @@ -838,8 +893,8 @@ private: destroyGraphicsPipeline(); destroySwapChainImageViews(); + destroySwapChain(); - vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); vkDestroyDevice(mDevice, nullptr); vkDestroySurfaceKHR(mInstance, mSurface, nullptr); vkDestroyInstance(mInstance, nullptr); @@ -850,6 +905,11 @@ private: private: GLFWwindow *mWindow = nullptr; + bool mWindowSizeDirtyFlag = true; + TimePoint mWindowSizeDirtyFlagTime = std::chrono::system_clock::now(); + TimeMs mWindowSizeApplyMinDelay = 200; + std::pair mWindowFramebufferSize = { 0, 0 }; + uint32_t mGraphicsQueueFamilyIndex = -1; uint32_t mPresentationQueueFamilyIndex = -1;