Ver código fonte

Handle window size changes properly

Kajetan Johannes Hammerle 2 meses atrás
pai
commit
786cd6fe4e

+ 6 - 3
include/core/VulkanWrapper.hpp

@@ -30,7 +30,7 @@ namespace Core::Vulkan {
 
         BaseWrapper& operator=(BaseWrapper&& o) {
             swap(o);
-            o = BaseWrapper();
+            return *this;
         }
 
         void swap(BaseWrapper& o) {
@@ -81,6 +81,8 @@ namespace Core::Vulkan {
         void waitFor(u64 timeout = UINT64_MAX);
     };
 
+    enum class SwapchainResult { SUCCESS, RECREATE, ERROR_RECREATE, ERROR };
+
     struct Swapchain : public BaseWrapper<VkSwapchainKHR> {
         Swapchain() = default;
         Swapchain(Swapchain&&) = default;
@@ -96,7 +98,8 @@ namespace Core::Vulkan {
         };
 
         bool init(Data& d);
-        bool nextImage(u32& imageIndex, Semaphore& s, u64 timeout = UINT64_MAX);
+        SwapchainResult nextImage(
+            u32& imageIndex, Semaphore& s, u64 timeout = UINT64_MAX);
     };
 
     struct ShaderModule : public BaseWrapper<VkShaderModule> {
@@ -166,7 +169,7 @@ namespace Core::Vulkan {
         void submit(
             CommandBuffer& cb, Fence& f, Semaphore& wait, Semaphore& signal,
             VkPipelineStageFlags flags);
-        void present(Semaphore& signal, Swapchain& s, u32 index);
+        SwapchainResult present(Semaphore& signal, Swapchain& s, u32 index);
     };
 
 }

+ 2 - 2
include/core/WindowManager.hpp

@@ -15,13 +15,13 @@ namespace Core::Window {
         const char* name = "Unknown";
     };
 
-    bool open(const Options* options);
+    bool open(const Options& options);
     void close();
     void show();
     void trapCursor();
     void freeCursor();
     bool isCursorTrapped();
-    const IntVector2* getSize();
+    const IntVector2& getSize();
     bool hasSizeChanged();
     bool shouldClose();
 

+ 63 - 17
src/VulkanWrapper.cpp

@@ -5,11 +5,13 @@
 
 #include "GLFW.hpp"
 #include "core/VulkanBase.hpp"
+#include "core/WindowManager.hpp"
 
 namespace Vulkan = Core::Vulkan;
 using Vulkan::CommandBuffer;
 using Vulkan::Queue;
 using Vulkan::SwapchainImages;
+using Vulkan::SwapchainResult;
 
 #define WRAPPER_DESTRUCT(Type, ...)                                \
     using Core::Vulkan::Type;                                      \
@@ -17,6 +19,8 @@ using Vulkan::SwapchainImages;
         if(device != VK_NULL_HANDLE) {                             \
             vkDestroy##Type##__VA_ARGS__(device, handle, nullptr); \
         }                                                          \
+        device = VK_NULL_HANDLE;                                   \
+        handle = VK_NULL_HANDLE;                                   \
     }
 
 WRAPPER_DESTRUCT(ImageView)
@@ -31,6 +35,7 @@ WRAPPER_DESTRUCT(CommandPool)
 WRAPPER_DESTRUCT(RenderPass)
 
 bool ImageView::init(VkDevice d, VkImage image, VkFormat format) {
+    ImageView::~ImageView();
     VkImageViewCreateInfo info = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
         .image = image,
@@ -49,6 +54,7 @@ bool ImageView::init(VkDevice d, VkImage image, VkFormat format) {
 
 bool Framebuffer::init(
     const ImageView& iv, VkRenderPass rp, u32 width, u32 height) {
+    Framebuffer::~Framebuffer();
     VkFramebufferCreateInfo info = {
         .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
         .renderPass = rp,
@@ -63,6 +69,7 @@ bool Framebuffer::init(
 }
 
 bool Semaphore::init(VkDevice d) {
+    Semaphore::~Semaphore();
     VkSemaphoreCreateInfo info = {
         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
     VK_CHECK_TRUE(vkCreateSemaphore(d, &info, nullptr, &handle));
@@ -71,6 +78,7 @@ bool Semaphore::init(VkDevice d) {
 }
 
 bool Fence::init(VkDevice d) {
+    Fence::~Fence();
     VkFenceCreateInfo info = {
         .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
         .flags = VK_FENCE_CREATE_SIGNALED_BIT};
@@ -98,6 +106,7 @@ static u32 getSwapImageCount(const VkSurfaceCapabilitiesKHR* caps) {
 }
 
 bool Swapchain::init(Data& d) {
+    Swapchain::~Swapchain();
     VkSurfaceCapabilitiesKHR caps = {0};
     if(d.base.getSurfaceCapabilities(caps)) {
         return true;
@@ -124,13 +133,21 @@ bool Swapchain::init(Data& d) {
     return false;
 }
 
-bool Swapchain::nextImage(u32& imageIndex, Semaphore& s, u64 timeout) {
-    VK_CHECK_TRUE(vkAcquireNextImageKHR(
-        device, handle, timeout, s, VK_NULL_HANDLE, &imageIndex));
-    return false;
+SwapchainResult Swapchain::nextImage(
+    u32& imageIndex, Semaphore& s, u64 timeout) {
+    VkResult r = vkAcquireNextImageKHR(
+        device, handle, timeout, s, VK_NULL_HANDLE, &imageIndex);
+    if(r == VK_SUBOPTIMAL_KHR) {
+        return SwapchainResult::RECREATE;
+    } else if(r == VK_ERROR_OUT_OF_DATE_KHR) {
+        return SwapchainResult::ERROR_RECREATE;
+    }
+    VK_CHECK(SwapchainResult::ERROR, r);
+    return SwapchainResult::SUCCESS;
 }
 
 bool ShaderModule::init(VkDevice d, const char* path) {
+    ShaderModule::~ShaderModule();
     List<char> f;
     if(Core::readFile(f, path)) {
         return true;
@@ -150,6 +167,7 @@ bool ShaderModule::init(VkDevice d, const char* path) {
 }
 
 bool PipelineLayout::init(VkDevice d) {
+    PipelineLayout::~PipelineLayout();
     VkPipelineLayoutCreateInfo info = {
         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
     VK_CHECK_TRUE(vkCreatePipelineLayout(d, &info, nullptr, &handle));
@@ -172,6 +190,7 @@ bool SwapchainImages::init(const Swapchain& s, VkFormat format) {
 }
 
 bool RenderPass::init(VkDevice d, VkFormat format) {
+    RenderPass::~RenderPass();
     VkAttachmentDescription c = {
         .format = format,
         .samples = VK_SAMPLE_COUNT_1_BIT,
@@ -219,6 +238,7 @@ void Pipeline::updateSize(u32 width, u32 height) {
 }
 
 bool Pipeline::init(PipelineLayout& pl, RenderPass& rp) {
+    Pipeline::~Pipeline();
     ShaderModule vertexShaderModule;
     ShaderModule fragmentShaderModule;
     if(vertexShaderModule.init(pl.device, "shaders/vertex.spv") ||
@@ -325,6 +345,7 @@ bool Pipeline::init(PipelineLayout& pl, RenderPass& rp) {
 }
 
 bool CommandPool::init(VkDevice d, u32 queueFamily) {
+    CommandPool::~CommandPool();
     VkCommandPoolCreateInfo info = {
         .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
         .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
@@ -335,6 +356,7 @@ bool CommandPool::init(VkDevice d, u32 queueFamily) {
 }
 
 bool CommandBuffer::init(CommandPool& cp) {
+    CommandBuffer::~CommandBuffer();
     VkCommandBufferAllocateInfo info = {
         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
         .commandPool = cp,
@@ -406,7 +428,7 @@ void Queue::submit(
     VK_CHECK_VOID(vkQueueSubmit(handle, 1, &info, f));
 }
 
-void Queue::present(Semaphore& signal, Swapchain& s, u32 index) {
+SwapchainResult Queue::present(Semaphore& signal, Swapchain& s, u32 index) {
     VkPresentInfoKHR info = {
         .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
         .waitSemaphoreCount = 1,
@@ -414,7 +436,14 @@ void Queue::present(Semaphore& signal, Swapchain& s, u32 index) {
         .swapchainCount = 1,
         .pSwapchains = s,
         .pImageIndices = &index};
-    VK_CHECK_VOID(vkQueuePresentKHR(handle, &info));
+    VkResult r = vkQueuePresentKHR(handle, &info);
+    if(r == VK_SUBOPTIMAL_KHR) {
+        return SwapchainResult::RECREATE;
+    } else if(r == VK_ERROR_OUT_OF_DATE_KHR) {
+        return SwapchainResult::ERROR_RECREATE;
+    }
+    VK_CHECK(SwapchainResult::ERROR, r);
+    return SwapchainResult::SUCCESS;
 }
 
 struct VulkanDummy {
@@ -439,7 +468,6 @@ struct VulkanDummy {
     Core::List<Vulkan::Framebuffer> framebuffers{};
     CommandPool commandPool{};
     size_t currentFrame = 0;
-    bool shouldWait = false;
 
     struct Frame {
         CommandBuffer commandBuffer{};
@@ -648,18 +676,28 @@ struct VulkanDummy {
                commandPool.init(base, baseData.graphicsFamily) || initFrames();
     }
 
-    bool render() {
-        if(shouldWait) {
-            return false;
+    bool recreateSwapchain() {
+        LOG_INFO("Recreate swapchain");
+        base.waitForIdle();
+        if(initSwapchain() || initSwapchainImages() || initFramebuffers()) {
+            return true;
         }
+        pipeline.updateSize(swapchainSize.width, swapchainSize.height);
+        return false;
+    }
+
+    bool render() {
         Frame& f = frames[currentFrame];
         f.inFlightFence.waitFor();
-        f.inFlightFence.reset();
 
         u32 imageIndex = 0;
-        if(swapchain.nextImage(imageIndex, f.imageAvailableSemaphore)) {
-            return true;
+        switch(swapchain.nextImage(imageIndex, f.imageAvailableSemaphore)) {
+            case SwapchainResult::SUCCESS: break;
+            case SwapchainResult::RECREATE: break;
+            case SwapchainResult::ERROR_RECREATE: return recreateSwapchain();
+            case SwapchainResult::ERROR: return true;
         }
+        f.inFlightFence.reset();
         f.commandBuffer.reset();
         fillCommandBuffer(f.commandBuffer, imageIndex);
 
@@ -668,8 +706,18 @@ struct VulkanDummy {
             f.renderFinishedSemaphore,
             VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
 
-        presentQueue.present(f.renderFinishedSemaphore, swapchain, imageIndex);
+        bool recreate = false;
+        switch(presentQueue.present(
+            f.renderFinishedSemaphore, swapchain, imageIndex)) {
+            case SwapchainResult::SUCCESS: break;
+            case SwapchainResult::RECREATE:
+            case SwapchainResult::ERROR_RECREATE: recreate = true;
+            case SwapchainResult::ERROR: break;
+        }
         currentFrame = (currentFrame + 1) % frames.getLength();
+        if(recreate || Core::Window::hasSizeChanged()) {
+            recreateSwapchain();
+        }
         return false;
     }
 };
@@ -682,9 +730,7 @@ bool Vulkan::init() {
 }
 
 void Vulkan::render() {
-    if(dummy->render()) {
-        dummy->shouldWait = true;
-    }
+    dummy->render();
 }
 
 void Vulkan::destroy() {

+ 8 - 8
src/WindowManager.cpp

@@ -127,7 +127,7 @@ static void onMouseMove(GLFWwindow*, double x, double y) {
     mousePosition[1] = static_cast<float>(y);
 }
 
-static bool openWindowI(const Window::Options* o) {
+static bool openWindowI(const Window::Options& o) {
     if(!glfwInit()) {
         REPORT(Core::LogLevel::ERROR, "could not initialize GLFW");
         return true;
@@ -136,17 +136,17 @@ static bool openWindowI(const Window::Options* o) {
     glfwDefaultWindowHints();
     glfwWindowHint(GLFW_VISIBLE, false);
     glfwWindowHint(GLFW_RESIZABLE, true);
-    glfwWindowHint(GLFW_DECORATED, !o->fullscreen);
+    glfwWindowHint(GLFW_DECORATED, !o.fullscreen);
     glfwWindowHint(GLFW_DOUBLEBUFFER, true);
     glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
 
-    GLFWmonitor* m = o->fullscreen ? glfwGetPrimaryMonitor() : nullptr;
-    window = glfwCreateWindow(o->size[0], o->size[1], o->name, m, nullptr);
+    GLFWmonitor* m = o.fullscreen ? glfwGetPrimaryMonitor() : nullptr;
+    window = glfwCreateWindow(o.size[0], o.size[1], o.name, m, nullptr);
     if(window == nullptr) {
         REPORT(Core::LogLevel::ERROR, "could not create window");
         return true;
     }
-    size = o->size;
+    size = o.size;
     glfwSetKeyCallback(window, onKey);
     glfwSetCharCallback(window, onChar);
     glfwSetFramebufferSizeCallback(window, onResize);
@@ -155,7 +155,7 @@ static bool openWindowI(const Window::Options* o) {
     return Core::Vulkan::init();
 }
 
-bool Window::open(const Options* o) {
+bool Window::open(const Options& o) {
     if(openWindowI(o)) {
         close();
         return true;
@@ -188,8 +188,8 @@ bool Window::isCursorTrapped() {
     return glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
 }
 
-const Core::IntVector2* Window::getSize() {
-    return &size;
+const Core::IntVector2& Window::getSize() {
+    return size;
 }
 
 bool Window::hasSizeChanged() {

+ 1 - 1
test/modules/WindowManagerTests.cpp

@@ -58,7 +58,7 @@ void testWindow() {
     setReportHandler(printReport, nullptr);
 
     W::Options options = {{800, 480}, false, "Test"};
-    if(W::open(&options)) {
+    if(W::open(options)) {
         return;
     }