diff --git a/src/main.c b/src/main.c index 71d9893..e15eec2 100644 --- a/src/main.c +++ b/src/main.c @@ -41,6 +41,8 @@ const char *validationLayers[] = {"VK_LAYER_KHRONOS_validation"}; size_t validationLayerCount = sizeof(validationLayers) / sizeof(validationLayers[0]); +static bool framebufferResized; // initialised to 0 + typedef struct Application { GLFWwindow *window; VkInstance instance; @@ -64,9 +66,9 @@ typedef struct Application { uint32_t swapChainFramebufferCount; VkCommandPool commandPool; VkCommandBuffer commandBuffers[MAX_FRAMES_IN_FLIGHT]; - VkSemaphore imageAvailableSemaphore[MAX_FRAMES_IN_FLIGHT]; + VkSemaphore imageAvailableSemaphores[MAX_FRAMES_IN_FLIGHT]; VkSemaphore renderFinishedSemaphore[MAX_FRAMES_IN_FLIGHT]; - VkFence inFlightFence[MAX_FRAMES_IN_FLIGHT]; + VkFence inFlightFences[MAX_FRAMES_IN_FLIGHT]; uint32_t currentFrame; // initialised to 0 } Application; @@ -204,11 +206,18 @@ void createInstance(Application *app) { } } +static void framebufferResizeCallback(GLFWwindow *window, int width, + int height) { + framebufferResized = true; +} + void initWindow(Application *app) { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); app->window = glfwCreateWindow(800, 600, "Vulkan", NULL, NULL); + + glfwSetFramebufferSizeCallback(app->window, framebufferResizeCallback); } VkResult CreateDebugUtilsMessengerEXT( @@ -997,10 +1006,10 @@ void createSyncObjects(Application *app) { for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { if (vkCreateSemaphore(app->device, &semaphoreInfo, NULL, - &app->imageAvailableSemaphore[i]) != VK_SUCCESS || + &app->imageAvailableSemaphores[i]) != VK_SUCCESS || vkCreateSemaphore(app->device, &semaphoreInfo, NULL, &app->renderFinishedSemaphore[i]) != VK_SUCCESS || - vkCreateFence(app->device, &fenceInfo, NULL, &app->inFlightFence[i]) != + vkCreateFence(app->device, &fenceInfo, NULL, &app->inFlightFences[i]) != VK_SUCCESS) { fprintf(stderr, "Failed to create semaphores!"); exit(EXIT_FAILURE); @@ -1008,6 +1017,37 @@ void createSyncObjects(Application *app) { } } +void cleanupSwapChain(Application *app) { + for (size_t i = 0; i < app->swapChainFramebufferCount; i++) { + vkDestroyFramebuffer(app->device, app->swapChainFramebuffers[i], NULL); + } + + for (size_t i = 0; i < app->swapChainImageViewCount; i++) { + vkDestroyImageView(app->device, app->swapChainImageViews[i], NULL); + } + + vkDestroySwapchainKHR(app->device, app->swapChain, NULL); +} + +void recreateSwapChain(Application *app) { + int width = 0, height = 0; + glfwGetFramebufferSize(app->window, &width, &height); + while (width == 0 || height == 0) { + glfwGetFramebufferSize(app->window, &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(app->device); + + free(app->swapChainFramebuffers); + free(app->swapChainImageViews); + free(app->swapChainImages); + + createSwapChain(app); + createImageViews(app); + createFramebuffers(app); +} + void initVulkan(Application *app) { createInstance(app); setupDebugMessenger(app); @@ -1025,20 +1065,33 @@ void initVulkan(Application *app) { } void drawFrame(Application *app) { - vkWaitForFences(app->device, 1, &app->inFlightFence[app->currentFrame], VK_TRUE, UINT64_MAX); - vkResetFences(app->device, 1, &app->inFlightFence[app->currentFrame]); + vkWaitForFences(app->device, 1, &app->inFlightFences[app->currentFrame], + VK_TRUE, UINT64_MAX); uint32_t imageIndex; - vkAcquireNextImageKHR(app->device, app->swapChain, UINT64_MAX, - app->imageAvailableSemaphore[app->currentFrame], VK_NULL_HANDLE, - &imageIndex); + VkResult result = + vkAcquireNextImageKHR(app->device, app->swapChain, UINT64_MAX, + app->imageAvailableSemaphores[app->currentFrame], + VK_NULL_HANDLE, &imageIndex); + + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + recreateSwapChain(app); + return; + } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + fprintf(stderr, "Failed to acquire swap chain image!"); + exit(EXIT_FAILURE); + } + + // Only reset the fence if we are submitting work + vkResetFences(app->device, 1, &app->inFlightFences[app->currentFrame]); vkResetCommandBuffer(app->commandBuffers[app->currentFrame], 0); recordCommandBuffer(app, app->commandBuffers[app->currentFrame], imageIndex); VkSubmitInfo submitInfo = {0}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore waitSemaphores[] = {app->imageAvailableSemaphore[app->currentFrame]}; + VkSemaphore waitSemaphores[] = { + app->imageAvailableSemaphores[app->currentFrame]}; VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; submitInfo.waitSemaphoreCount = 1; @@ -1046,12 +1099,13 @@ void drawFrame(Application *app) { submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &app->commandBuffers[app->currentFrame]; - VkSemaphore signalSemaphores[] = {app->renderFinishedSemaphore[app->currentFrame]}; + VkSemaphore signalSemaphores[] = { + app->renderFinishedSemaphore[app->currentFrame]}; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; - if (vkQueueSubmit(app->graphicsQueue, 1, &submitInfo, app->inFlightFence[app->currentFrame]) != - VK_SUCCESS) { + if (vkQueueSubmit(app->graphicsQueue, 1, &submitInfo, + app->inFlightFences[app->currentFrame]) != VK_SUCCESS) { fprintf(stderr, "failed to submit draw command buffer!"); exit(EXIT_FAILURE); } @@ -1066,7 +1120,15 @@ void drawFrame(Application *app) { presentInfo.pImageIndices = &imageIndex; presentInfo.pResults = NULL; // Optional - vkQueuePresentKHR(app->presentQueue, &presentInfo); + result = vkQueuePresentKHR(app->presentQueue, &presentInfo); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || + framebufferResized) { + framebufferResized = false; + recreateSwapChain(app); + } else if (result != VK_SUCCESS) { + fprintf(stderr, "failed to present swap chain image!"); + exit(EXIT_FAILURE); + } app->currentFrame = (app->currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } @@ -1081,25 +1143,23 @@ void mainLoop(Application *app) { } void cleanup(Application *app) { - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(app->device, app->imageAvailableSemaphore[i], NULL); - vkDestroySemaphore(app->device, app->renderFinishedSemaphore[i], NULL); - vkDestroyFence(app->device, app->inFlightFence[i], NULL); - } - vkDestroyCommandPool(app->device, app->commandPool, NULL); - for (uint i = 0; i < app->swapChainFramebufferCount; i++) { - vkDestroyFramebuffer(app->device, app->swapChainFramebuffers[i], NULL); - } + cleanupSwapChain(app); free(app->swapChainFramebuffers); - vkDestroyPipeline(app->device, app->graphicsPipeline, NULL); - vkDestroyPipelineLayout(app->device, app->pipelineLayout, NULL); - vkDestroyRenderPass(app->device, app->renderPass, NULL); - for (size_t i = 0; i < app->swapChainImageViewCount; i++) { - vkDestroyImageView(app->device, app->swapChainImageViews[i], NULL); - } free(app->swapChainImageViews); free(app->swapChainImages); - vkDestroySwapchainKHR(app->device, app->swapChain, NULL); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(app->device, app->imageAvailableSemaphores[i], NULL); + vkDestroySemaphore(app->device, app->renderFinishedSemaphore[i], NULL); + vkDestroyFence(app->device, app->inFlightFences[i], NULL); + } + vkDestroyCommandPool(app->device, app->commandPool, NULL); + + vkDestroyPipeline(app->device, app->graphicsPipeline, NULL); + vkDestroyPipelineLayout(app->device, app->pipelineLayout, NULL); + + vkDestroyRenderPass(app->device, app->renderPass, NULL); + vkDestroyDevice(app->device, NULL); if (enableValidationLayers) { DestroyDebugUtilsMessengerEXT(app->instance, app->debugMessenger, NULL);