From 798be67cc547421088c1e26a40b3db62249f0352 Mon Sep 17 00:00:00 2001 From: Grayson Riffe Date: Thu, 13 Feb 2025 16:33:45 -0600 Subject: [PATCH] Add swapchain recreation --- NothinFancy/src/client/Window.cpp | 5 + .../src/client/render/RenderEngine.cpp | 116 ++++++++++++------ NothinFancy/src/client/render/RenderEngine.h | 8 ++ 3 files changed, 89 insertions(+), 40 deletions(-) diff --git a/NothinFancy/src/client/Window.cpp b/NothinFancy/src/client/Window.cpp index c27543f..4223137 100644 --- a/NothinFancy/src/client/Window.cpp +++ b/NothinFancy/src/client/Window.cpp @@ -29,6 +29,8 @@ namespace nf::client { void Window::setDisplay(DisplayConfig& config) { NFLog("Setting window display"); + bool wasShown = IsWindowVisible(m_handle); + show(false); // TODO: Only use "active" monitor when starting windowed POINT cursor = {}; @@ -55,12 +57,15 @@ namespace nf::client { SetWindowLongPtr(m_handle, GWL_STYLE, m_styleFullscreen); windowX = monitorX, windowY = monitorY; windowWidth = monitorWidth, windowHeight = monitorHeight; + m_currentWidth = windowWidth, m_currentHeight = windowHeight; config.width = windowWidth, config.height = windowHeight; break; } } SetWindowPos(m_handle, nullptr, windowX, windowY, windowWidth, windowHeight, SWP_NOZORDER | SWP_FRAMECHANGED); + + show(wasShown); } void Window::runLoop() { diff --git a/NothinFancy/src/client/render/RenderEngine.cpp b/NothinFancy/src/client/render/RenderEngine.cpp index 076a884..8ea9b0d 100644 --- a/NothinFancy/src/client/render/RenderEngine.cpp +++ b/NothinFancy/src/client/render/RenderEngine.cpp @@ -20,6 +20,7 @@ namespace nf::client::render { , m_renderPassOutput() , m_swapchain() , m_swapchainImageFormat() + , m_swapchainExtent() , m_swapchainImages() , m_swapchainImageViews() , m_swapchainFramebuffers() @@ -39,6 +40,7 @@ namespace nf::client::render { createDevice(); createSwapchain(); createOutputRenderPass(); + createSwapchainFramebuffers(); createOutputPipeline(); createFrameExecutors(); @@ -209,9 +211,9 @@ namespace nf::client::render { if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) chosenPresentMode = presentModes[i]; - VkExtent2D swapchainExtent = surfaceCapabilities.currentExtent; - if (swapchainExtent.width == std::numeric_limits::max()) - swapchainExtent.width = m_display.width, swapchainExtent.height = m_display.height; + m_swapchainExtent = surfaceCapabilities.currentExtent; + if (m_swapchainExtent.width == std::numeric_limits::max()) + m_swapchainExtent = { m_display.width, m_display.height }; uint32_t numRequestedImages = surfaceCapabilities.minImageCount + (surfaceCapabilities.minImageCount != surfaceCapabilities.maxImageCount ? 1 : 0); @@ -220,7 +222,7 @@ namespace nf::client::render { swapchainCI.minImageCount = numRequestedImages; swapchainCI.imageFormat = m_swapchainImageFormat = chosenSurfaceFormat.format; swapchainCI.imageColorSpace = chosenSurfaceFormat.colorSpace; - swapchainCI.imageExtent = swapchainExtent; + swapchainCI.imageExtent = m_swapchainExtent; swapchainCI.imageArrayLayers = 1; swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; @@ -297,6 +299,22 @@ namespace nf::client::render { NFError("Could not create Vulkan output render pass."); } + void RenderEngine::createSwapchainFramebuffers() { + // TODO: Won't need these forever + m_swapchainFramebuffers.resize(m_swapchainImageViews.size()); + for (int i = 0; i < m_swapchainImageViews.size(); i++) { + VkFramebufferCreateInfo framebufferCI = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + framebufferCI.renderPass = m_renderPassOutput; + framebufferCI.attachmentCount = 1; + framebufferCI.pAttachments = &m_swapchainImageViews[i]; + framebufferCI.width = m_swapchainExtent.width, framebufferCI.height = m_swapchainExtent.height; + framebufferCI.layers = 1; + + if (vkCreateFramebuffer(m_device, &framebufferCI, nullptr, &m_swapchainFramebuffers[i]) != VK_SUCCESS) + NFError("Could not create framebuffer."); + } + } + void RenderEngine::createOutputPipeline() { // First, create shader modules std::string outputShaderVertexBinary, outputShaderFragmentBinary; @@ -315,10 +333,10 @@ namespace nf::client::render { outputShaderStages[1].module = outputShaderFragmentModule.getHandle(); // Specify dynamic state - VkDynamicState dynamicState = VK_DYNAMIC_STATE_VIEWPORT; + std::vector dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicStateCI = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; - dynamicStateCI.dynamicStateCount = 1; - dynamicStateCI.pDynamicStates = &dynamicState; + dynamicStateCI.dynamicStateCount = dynamicStates.size(); + dynamicStateCI.pDynamicStates = dynamicStates.data(); // Specify vertex input state VkPipelineVertexInputStateCreateInfo vertexInputStateCI = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; @@ -328,11 +346,9 @@ namespace nf::client::render { inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; // Specify viewport state - VkRect2D scissor = { {0, 0}, {m_display.width, m_display.height} }; VkPipelineViewportStateCreateInfo viewportStateCI = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; viewportStateCI.viewportCount = 1; viewportStateCI.scissorCount = 1; - viewportStateCI.pScissors = &scissor; // Specify rasterization state VkPipelineRasterizationStateCreateInfo rasterizationCI = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; @@ -373,20 +389,6 @@ namespace nf::client::render { if (vkCreateGraphicsPipelines(m_device, nullptr, 1, &pipelineCI, nullptr, &m_pipelineOutput) != VK_SUCCESS) NFError("Could not create graphics pipeline."); - - // TODO: For now, create swapchain framebuffers - m_swapchainFramebuffers.resize(m_swapchainImageViews.size()); - for (int i = 0; i < m_swapchainImageViews.size(); i++) { - VkFramebufferCreateInfo framebufferCI = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - framebufferCI.renderPass = m_renderPassOutput; - framebufferCI.attachmentCount = 1; - framebufferCI.pAttachments = &m_swapchainImageViews[i]; - framebufferCI.width = m_display.width, framebufferCI.height = m_display.height; - framebufferCI.layers = 1; - - if (vkCreateFramebuffer(m_device, &framebufferCI, nullptr, &m_swapchainFramebuffers[i]) != VK_SUCCESS) - NFError("Could not create framebuffer."); - } } void RenderEngine::createFrameExecutors() { @@ -424,11 +426,19 @@ namespace nf::client::render { // First, wait for previous frame to complete vkWaitForFences(m_device, 1, &fenceFrameInFlight, VK_TRUE, UINT64_MAX); - vkResetFences(m_device, 1, &fenceFrameInFlight); - // Get next swapchain image + // Get next swapchain image and recreate if necessary uint32_t nextImageIndex = 0; - vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, semaphoreImageAvailable, nullptr, &nextImageIndex); + VkResult result = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, semaphoreImageAvailable, nullptr, &nextImageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapchain(); + return; + } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) + NFError("Could not aquire next swapchain image."); + + // Reset fence for this frame + vkResetFences(m_device, 1, &fenceFrameInFlight); // Begin recording VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; @@ -441,7 +451,7 @@ namespace nf::client::render { VkRenderPassBeginInfo renderPassBI = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; renderPassBI.renderPass = m_renderPassOutput; renderPassBI.framebuffer = m_swapchainFramebuffers[nextImageIndex]; - renderPassBI.renderArea.extent = { m_display.width, m_display.height }; + renderPassBI.renderArea.extent = m_swapchainExtent; renderPassBI.clearValueCount = 1; renderPassBI.pClearValues = &black; vkCmdBeginRenderPass(commandBuffer, &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); @@ -449,11 +459,13 @@ namespace nf::client::render { // Bind output pipeline vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineOutput); - // Set viewport + // Set viewport and scissor VkViewport viewport = {}; - viewport.width = static_cast(m_display.width), viewport.height = static_cast(m_display.height); + viewport.width = static_cast(m_swapchainExtent.width), viewport.height = static_cast(m_swapchainExtent.height); viewport.maxDepth = 1.0; vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + VkRect2D scissor = { {}, m_swapchainExtent }; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); // Draw vkCmdDraw(commandBuffer, 3, 1, 0, 0); @@ -487,14 +499,46 @@ namespace nf::client::render { presentInfo.pSwapchains = &m_swapchain; presentInfo.pImageIndices = &nextImageIndex; - vkQueuePresentKHR(m_queuePresent, &presentInfo); + result = vkQueuePresentKHR(m_queuePresent, &presentInfo); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) + recreateSwapchain(); + else if (result != VK_SUCCESS) + NFError("Could not present image."); // Advance to next frame executor m_currentFrameExecutor = (m_currentFrameExecutor + 1) % m_numFramesInFlight; } - RenderEngine::~RenderEngine() { + void RenderEngine::setDisplay(DisplayConfig config) { + m_display = config; + m_window->setDisplay(m_display); + } + + void RenderEngine::recreateSwapchain() { + waitIdle(); + + destroySwapchain(); + + createSwapchain(); + createSwapchainFramebuffers(); + } + + void RenderEngine::destroySwapchain() { + for (int i = 0; i < m_swapchainFramebuffers.size(); i++) + vkDestroyFramebuffer(m_device, m_swapchainFramebuffers[i], nullptr); + + for (int i = 0; i < m_swapchainImageViews.size(); i++) + vkDestroyImageView(m_device, m_swapchainImageViews[i], nullptr); + + vkDestroySwapchainKHR(m_device, m_swapchain, nullptr); + } + + void RenderEngine::waitIdle() { vkDeviceWaitIdle(m_device); + } + + RenderEngine::~RenderEngine() { + waitIdle(); for (int i = 0; i < m_frameExecutors.size(); i++) { vkDestroyFence(m_device, m_frameExecutors[i].fenceFrameInFlight, nullptr); @@ -502,19 +546,11 @@ namespace nf::client::render { vkDestroySemaphore(m_device, m_frameExecutors[i].semaphoreImageAvailable, nullptr); } + destroySwapchain(); vkDestroyCommandPool(m_device, m_commandPool, nullptr); - - for (int i = 0; i < m_swapchainFramebuffers.size(); i++) - vkDestroyFramebuffer(m_device, m_swapchainFramebuffers[i], nullptr); - vkDestroyPipeline(m_device, m_pipelineOutput, nullptr); vkDestroyPipelineLayout(m_device, m_pipelineLayoutOutput, nullptr); vkDestroyRenderPass(m_device, m_renderPassOutput, nullptr); - - for (int i = 0; i < m_swapchainImageViews.size(); i++) - vkDestroyImageView(m_device, m_swapchainImageViews[i], nullptr); - - vkDestroySwapchainKHR(m_device, m_swapchain, nullptr); vkDestroyDevice(m_device, nullptr); vkDestroySurfaceKHR(m_instance, m_surface, nullptr); vkDestroyInstance(m_instance, nullptr); diff --git a/NothinFancy/src/client/render/RenderEngine.h b/NothinFancy/src/client/render/RenderEngine.h index 85aa5b9..456459f 100644 --- a/NothinFancy/src/client/render/RenderEngine.h +++ b/NothinFancy/src/client/render/RenderEngine.h @@ -16,6 +16,7 @@ namespace nf::client::render { RenderEngine(std::shared_ptr window, DisplayConfig display); void doFrame(); + void setDisplay(DisplayConfig config); ~RenderEngine(); private: @@ -25,9 +26,15 @@ namespace nf::client::render { void createDevice(); void createSwapchain(); void createOutputRenderPass(); + void createSwapchainFramebuffers(); void createOutputPipeline(); void createFrameExecutors(); + void recreateSwapchain(); + void destroySwapchain(); + + void waitIdle(); + std::shared_ptr m_window; DisplayConfig m_display; @@ -43,6 +50,7 @@ namespace nf::client::render { // Swapchain VkSwapchainKHR m_swapchain; VkFormat m_swapchainImageFormat; + VkExtent2D m_swapchainExtent; std::vector m_swapchainImages; std::vector m_swapchainImageViews; std::vector m_swapchainFramebuffers;