diff --git a/VulkanTest b/VulkanTest index 7f9664f..a2d8df6 100755 Binary files a/VulkanTest and b/VulkanTest differ diff --git a/cve_pipeline.cpp b/cve_pipeline.cpp index b526288..1d2ad9c 100644 --- a/cve_pipeline.cpp +++ b/cve_pipeline.cpp @@ -75,25 +75,18 @@ namespace cve { vertexInputInfo.pVertexBindingDescriptions = bindingDescriptions.data(); vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); - VkPipelineViewportStateCreateInfo viewportInfo{}; - viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportInfo.viewportCount = 1; - viewportInfo.pViewports = &configInfo.viewport; - viewportInfo.scissorCount = 1; - viewportInfo.pScissors = &configInfo.scissor; - VkGraphicsPipelineCreateInfo pipelineInfo{}; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineInfo.stageCount = 2; pipelineInfo.pStages = shaderStages; pipelineInfo.pVertexInputState = &vertexInputInfo; pipelineInfo.pInputAssemblyState = &configInfo.inputAssemblyInfo; - pipelineInfo.pViewportState = &viewportInfo; + pipelineInfo.pViewportState = &configInfo.viewportInfo; pipelineInfo.pRasterizationState = &configInfo.rasterizationInfo; pipelineInfo.pMultisampleState = &configInfo.multisampleInfo; pipelineInfo.pColorBlendState = &configInfo.colorBlendInfo; pipelineInfo.pDepthStencilState = &configInfo.depthStencilInfo; - pipelineInfo.pDynamicState = nullptr; + pipelineInfo.pDynamicState = &configInfo.dynamicStateInfo; pipelineInfo.layout = configInfo.pipelineLayout; pipelineInfo.renderPass = configInfo.renderPass; @@ -123,22 +116,16 @@ namespace cve { vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); } - PipelineConfigInfo CvePipeline::defaultPipelineConfigInfo(uint32_t width, uint32_t height) { - PipelineConfigInfo configInfo{}; - + void CvePipeline::defaultPipelineConfigInfo(PipelineConfigInfo& configInfo) { configInfo.inputAssemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; configInfo.inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; configInfo.inputAssemblyInfo.primitiveRestartEnable = VK_FALSE; - configInfo.viewport.x = 0.0f; - configInfo.viewport.y = 0.0f; - configInfo.viewport.width = static_cast(width); - configInfo.viewport.height = static_cast(height); - configInfo.viewport.minDepth = 0.0f; - configInfo.viewport.maxDepth = 1.0f; - - configInfo.scissor.offset = {0, 0}; - configInfo.scissor.extent = {width, height}; + configInfo.viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + configInfo.viewportInfo.viewportCount = 1; + configInfo.viewportInfo.pViewports = nullptr; + configInfo.viewportInfo.scissorCount = 1; + configInfo.viewportInfo.pScissors = nullptr; configInfo.rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; configInfo.rasterizationInfo.depthClampEnable = VK_FALSE; @@ -192,6 +179,11 @@ namespace cve { configInfo.depthStencilInfo.front = {}; // Optional configInfo.depthStencilInfo.back = {}; // Optional - return configInfo; + configInfo.dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + configInfo.dynamicStateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + configInfo.dynamicStateInfo.pDynamicStates = configInfo.dynamicStateEnables.data(); + configInfo.dynamicStateInfo.dynamicStateCount = + static_cast(configInfo.dynamicStateEnables.size()); + configInfo.dynamicStateInfo.flags = 0; } } diff --git a/cve_pipeline.hpp b/cve_pipeline.hpp index b7b02d1..d33bc6e 100644 --- a/cve_pipeline.hpp +++ b/cve_pipeline.hpp @@ -7,14 +7,18 @@ namespace cve { struct PipelineConfigInfo { - VkViewport viewport; - VkRect2D scissor; + PipelineConfigInfo(const PipelineConfigInfo&) = delete; + PipelineConfigInfo& operator=(const PipelineConfigInfo&) = delete; + + VkPipelineViewportStateCreateInfo viewportInfo; VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo; VkPipelineRasterizationStateCreateInfo rasterizationInfo; VkPipelineMultisampleStateCreateInfo multisampleInfo; VkPipelineColorBlendAttachmentState colorBlendAttachment; VkPipelineColorBlendStateCreateInfo colorBlendInfo; VkPipelineDepthStencilStateCreateInfo depthStencilInfo; + std::vector dynamicStateEnables; + VkPipelineDynamicStateCreateInfo dynamicStateInfo; VkPipelineLayout pipelineLayout = nullptr; VkRenderPass renderPass = nullptr; uint32_t subpass = 0; @@ -29,7 +33,7 @@ namespace cve { CvePipeline& operator=(const CvePipeline&) = delete; void bind(VkCommandBuffer commandBuffer); - static PipelineConfigInfo defaultPipelineConfigInfo(uint32_t width, uint32_t height); + static void defaultPipelineConfigInfo(PipelineConfigInfo& configInfo); private: static std::vector readFile(const std::string& filepath); diff --git a/cve_swap_chain.cpp b/cve_swap_chain.cpp index a7e0d22..ec5f3ea 100644 --- a/cve_swap_chain.cpp +++ b/cve_swap_chain.cpp @@ -13,6 +13,18 @@ namespace cve { CveSwapChain::CveSwapChain(CveDevice &deviceRef, VkExtent2D extent) : device{deviceRef}, windowExtent{extent} { + init(); +} + +CveSwapChain::CveSwapChain(CveDevice &deviceRef, VkExtent2D extent, std::shared_ptr previous) + : device{deviceRef}, windowExtent{extent}, oldSwapChain{previous} { + init(); + + // Clean up old swap chain + oldSwapChain = nullptr; +} + +void CveSwapChain::init() { createSwapChain(); createImageViews(); createRenderPass(); @@ -162,7 +174,7 @@ void CveSwapChain::createSwapChain() { createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - createInfo.oldSwapchain = VK_NULL_HANDLE; + createInfo.oldSwapchain = oldSwapChain ? oldSwapChain->swapChain : VK_NULL_HANDLE; if (vkCreateSwapchainKHR(device.device(), &createInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); diff --git a/cve_swap_chain.hpp b/cve_swap_chain.hpp index cc10591..346b530 100644 --- a/cve_swap_chain.hpp +++ b/cve_swap_chain.hpp @@ -6,6 +6,7 @@ #include // std lib headers +#include #include #include @@ -16,6 +17,7 @@ class CveSwapChain { static constexpr int MAX_FRAMES_IN_FLIGHT = 2; CveSwapChain(CveDevice &deviceRef, VkExtent2D windowExtent); + CveSwapChain(CveDevice &deviceRef, VkExtent2D windowExtent, std::shared_ptr previous); ~CveSwapChain(); CveSwapChain(const CveSwapChain &) = delete; @@ -39,6 +41,7 @@ class CveSwapChain { VkResult submitCommandBuffers(const VkCommandBuffer *buffers, uint32_t *imageIndex); private: + void init(); void createSwapChain(); void createImageViews(); void createDepthResources(); @@ -69,6 +72,7 @@ class CveSwapChain { VkExtent2D windowExtent; VkSwapchainKHR swapChain; + std::shared_ptr oldSwapChain; std::vector imageAvailableSemaphores; std::vector renderFinishedSemaphores; diff --git a/cve_window.cpp b/cve_window.cpp index a2348fd..4f36cfd 100644 --- a/cve_window.cpp +++ b/cve_window.cpp @@ -20,6 +20,8 @@ namespace cve { window = glfwCreateWindow(width, height, windowName.c_str(), nullptr, nullptr); + glfwSetWindowUserPointer(window, this); + glfwSetFramebufferSizeCallback(window, framebufferResizedCallback); } void CveWindow::createWindowSurface(VkInstance instance, VkSurfaceKHR *surface) { @@ -27,4 +29,11 @@ namespace cve { throw std::runtime_error("Failed to create window surface"); } } + + void CveWindow::framebufferResizedCallback(GLFWwindow *window, int width, int height) { + auto cveWindow = reinterpret_cast(glfwGetWindowUserPointer(window)); + cveWindow->framebufferResized = true; + cveWindow->width = width; + cveWindow->height = height; + } } diff --git a/cve_window.hpp b/cve_window.hpp index 0e08433..7dc5275 100644 --- a/cve_window.hpp +++ b/cve_window.hpp @@ -16,13 +16,17 @@ class CveWindow { bool shouldClose() { return glfwWindowShouldClose(window); }; VkExtent2D getExtent() { return { static_cast(width), static_cast(height) }; }; + bool wasWindowResized() { return framebufferResized; }; + void resetWindowResizedFlag() { framebufferResized = false; }; void createWindowSurface(VkInstance instance, VkSurfaceKHR *surface); private: + static void framebufferResizedCallback(GLFWwindow *window, int width, int height); void initWindow(); - const int width; - const int height; + int width; + int height; + bool framebufferResized = false; std::string windowName; GLFWwindow *window; diff --git a/first_app.cpp b/first_app.cpp index f0d8c72..c540d68 100644 --- a/first_app.cpp +++ b/first_app.cpp @@ -7,7 +7,7 @@ namespace cve { FirstApp::FirstApp() { loadModels(); createPipelineLayout(); - createPipeline(); + recreateSwapChain(); createCommandBuffers(); } @@ -48,8 +48,12 @@ namespace cve { } void FirstApp::createPipeline() { - auto pipelineConfig = CvePipeline::defaultPipelineConfigInfo(cveSwapChain.width(), cveSwapChain.height()); - pipelineConfig.renderPass = cveSwapChain.getRenderPass(); + assert(cveSwapChain != nullptr && "Cannot create pipeline before swap chain"); + assert(pipelineLayout != nullptr && "Cannot create pipeline before pipeline layout"); + + PipelineConfigInfo pipelineConfig{}; + CvePipeline::defaultPipelineConfigInfo(pipelineConfig); + pipelineConfig.renderPass = cveSwapChain->getRenderPass(); pipelineConfig.pipelineLayout = pipelineLayout; cvePipeline = std::make_unique( cveDevice, @@ -59,8 +63,31 @@ namespace cve { ); } + void FirstApp::recreateSwapChain() { + auto extent = cveWindow.getExtent(); + while (extent.width == 0 || extent.height == 0) { + extent = cveWindow.getExtent(); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(cveDevice.device()); + + if (cveSwapChain == nullptr) { + cveSwapChain = std::make_unique(cveDevice, extent); + } else { + cveSwapChain = std::make_unique(cveDevice, extent, std::move(cveSwapChain)); + if (cveSwapChain->imageCount() != commandBuffers.size()) { + freeCommandBuffers(); + createCommandBuffers(); + } + } + + // Check if render pass is compatible + createPipeline(); + } + void FirstApp::createCommandBuffers() { - commandBuffers.resize(cveSwapChain.imageCount()); + commandBuffers.resize(cveSwapChain->imageCount()); VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; @@ -72,50 +99,78 @@ namespace cve { VK_SUCCESS) { throw std::runtime_error("Failed to allocate command buffers"); } + } - for (int i=0; i < commandBuffers.size(); i++) { - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + void FirstApp::freeCommandBuffers() { + vkFreeCommandBuffers(cveDevice.device(), cveDevice.getCommandPool(), + static_cast(commandBuffers.size()), commandBuffers.data()); + commandBuffers.clear(); + } - if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) { - throw std::runtime_error("Failed to begin command buffer recording"); - } + void FirstApp::recordCommandBuffer(int imageIndex) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VkRenderPassBeginInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = cveSwapChain.getRenderPass(); - renderPassInfo.framebuffer = cveSwapChain.getFrameBuffer(i); + if (vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("Failed to begin command buffer recording"); + } - renderPassInfo.renderArea.offset = {0, 0}; - renderPassInfo.renderArea.extent = cveSwapChain.getSwapChainExtent(); + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = cveSwapChain->getRenderPass(); + renderPassInfo.framebuffer = cveSwapChain->getFrameBuffer(imageIndex); - std::array clearValues{}; - clearValues[0].color = {0.1f, 0.1f, 0.1f, 1.0f}; - clearValues[1].depthStencil = {1.0f, 0}; - renderPassInfo.clearValueCount = static_cast(clearValues.size()); - renderPassInfo.pClearValues = clearValues.data(); + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = cveSwapChain->getSwapChainExtent(); - vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + std::array clearValues{}; + clearValues[0].color = {0.1f, 0.1f, 0.1f, 1.0f}; + clearValues[1].depthStencil = {1.0f, 0}; + renderPassInfo.clearValueCount = static_cast(clearValues.size()); + renderPassInfo.pClearValues = clearValues.data(); - cvePipeline->bind(commandBuffers[i]); - cveModel->bind(commandBuffers[i]); - cveModel->draw(commandBuffers[i]); + vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdEndRenderPass(commandBuffers[i]); - if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { - throw std::runtime_error("Failed to record command buffer!"); - } + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(cveSwapChain->getSwapChainExtent().width); + viewport.height = static_cast(cveSwapChain->getSwapChainExtent().height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + VkRect2D scissor{{0, 0}, cveSwapChain->getSwapChainExtent()}; + vkCmdSetViewport(commandBuffers[imageIndex], 0, 1, &viewport); + vkCmdSetScissor(commandBuffers[imageIndex], 0, 1, &scissor); + + cvePipeline->bind(commandBuffers[imageIndex]); + cveModel->bind(commandBuffers[imageIndex]); + cveModel->draw(commandBuffers[imageIndex]); + + vkCmdEndRenderPass(commandBuffers[imageIndex]); + if (vkEndCommandBuffer(commandBuffers[imageIndex]) != VK_SUCCESS) { + throw std::runtime_error("Failed to record command buffer!"); } } + void FirstApp::drawFrame() { uint32_t imageIndex; - auto result = cveSwapChain.acquireNextImage(&imageIndex); + auto result = cveSwapChain->acquireNextImage(&imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(); + return; + } if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { throw std::runtime_error("Failed to acquire swap chain image!"); } - result = cveSwapChain.submitCommandBuffers(&commandBuffers[imageIndex], &imageIndex); + recordCommandBuffer(imageIndex); + result = cveSwapChain->submitCommandBuffers(&commandBuffers[imageIndex], &imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || cveWindow.wasWindowResized()) { + cveWindow.resetWindowResizedFlag(); + recreateSwapChain(); + return; + } if (result != VK_SUCCESS) { throw std::runtime_error("Failed to present swap chain image!"); } diff --git a/first_app.hpp b/first_app.hpp index 374260c..442cd68 100644 --- a/first_app.hpp +++ b/first_app.hpp @@ -27,11 +27,14 @@ namespace cve { void createPipelineLayout(); void createPipeline(); void createCommandBuffers(); + void freeCommandBuffers(); void drawFrame(); + void recreateSwapChain(); + void recordCommandBuffer(int imageIndex); CveWindow cveWindow{WIDTH, HEIGHT, "Hello Vulkan!"}; CveDevice cveDevice{cveWindow}; - CveSwapChain cveSwapChain{cveDevice, cveWindow.getExtent()}; + std::unique_ptr cveSwapChain; std::unique_ptr cvePipeline; VkPipelineLayout pipelineLayout; std::vector commandBuffers;