Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 47bff4c38afaacac0cea0f1ebd84ac7c3fd740d4)
+++ vulkan-game.cpp	(revision 75108efed1de8df522fe278cac2afd7a2ecb8973)
@@ -129,4 +129,6 @@
       size_t currentFrame = 0;
 
+      bool framebufferResized = false;
+
       // both SDL and GLFW create window functions return NULL on failure
       bool initWindow() {
@@ -142,5 +144,5 @@
                SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                SCREEN_WIDTH, SCREEN_HEIGHT,
-               SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN);
+               SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
 
             if (window == nullptr) {
@@ -169,4 +171,44 @@
       }
 
+      void recreateSwapChain() {
+         int width = 0, height = 0;
+         SDL_GetWindowSize(window, &width, &height);
+
+         while (width == 0 || height == 0 ||
+            (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0) {
+            SDL_WaitEvent(nullptr);
+            SDL_GetWindowSize(window, &width, &height);
+         }
+
+         vkDeviceWaitIdle(device);
+
+         cleanupSwapChain();
+
+         createSwapChain();
+         createImageViews();
+         createRenderPass();
+         createGraphicsPipeline();
+         createFramebuffers();
+         createCommandBuffers();
+      }
+
+      void cleanupSwapChain() {
+         for (auto framebuffer : swapChainFramebuffers) {
+            vkDestroyFramebuffer(device, framebuffer, nullptr);
+         }
+
+         vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
+
+         vkDestroyPipeline(device, graphicsPipeline, nullptr);
+         vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
+         vkDestroyRenderPass(device, renderPass, nullptr);
+
+         for (auto imageView : swapChainImageViews) {
+            vkDestroyImageView(device, imageView, nullptr);
+         }
+
+         vkDestroySwapchainKHR(device, swapChain, nullptr);
+      }
+
       void createInstance() {
          if (enableValidationLayers && !checkValidationLayerSupport()) {
@@ -464,5 +506,11 @@
             return capabilities.currentExtent;
          } else {
-            VkExtent2D actualExtent = { SCREEN_WIDTH, SCREEN_HEIGHT };
+            int width, height;
+            SDL_GetWindowSize(window, &width, &height);
+
+            VkExtent2D actualExtent = {
+               static_cast<uint32_t>(width),
+               static_cast<uint32_t>(height)
+            };
 
             actualExtent.width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, actualExtent.width));
@@ -867,4 +915,11 @@
                   quit = true;
                }
+               if (e.type == SDL_WINDOWEVENT) {
+                  if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
+                     framebufferResized = true;
+                  } else if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) {
+                     framebufferResized = true;
+                  }
+               }
             }
 
@@ -880,9 +935,15 @@
       void drawFrame() {
          vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
-         vkResetFences(device, 1, &inFlightFences[currentFrame]);
 
          uint32_t imageIndex;
 
-         vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(), imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
+         VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(), imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
+
+         if (result == VK_ERROR_OUT_OF_DATE_KHR) {
+            recreateSwapChain();
+            return;
+         } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
+            throw runtime_error("failed to acquire swap chain image!");
+         }
 
          VkSubmitInfo submitInfo = {};
@@ -903,4 +964,6 @@
          submitInfo.pSignalSemaphores = signalSemaphores;
 
+         vkResetFences(device, 1, &inFlightFences[currentFrame]);
+
          if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
             throw runtime_error("failed to submit draw command buffer!");
@@ -919,5 +982,12 @@
          presentInfo.pResults = nullptr;
 
-         vkQueuePresentKHR(presentQueue, &presentInfo);
+         result = vkQueuePresentKHR(presentQueue, &presentInfo);
+
+         if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
+            framebufferResized = false;
+            recreateSwapChain();
+         } else if (result != VK_SUCCESS) {
+            throw runtime_error("failed to present swap chain image!");
+         }
 
          currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
@@ -925,4 +995,6 @@
 
       void cleanup() {
+         cleanupSwapChain();
+
          for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
             vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
@@ -933,17 +1005,4 @@
          vkDestroyCommandPool(device, commandPool, nullptr);
 
-         for (auto framebuffer : swapChainFramebuffers) {
-            vkDestroyFramebuffer(device, framebuffer, nullptr);
-         }
-
-         vkDestroyPipeline(device, graphicsPipeline, nullptr);
-         vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
-         vkDestroyRenderPass(device, renderPass, nullptr);
-
-         for (auto imageView : swapChainImageViews) {
-            vkDestroyImageView(device, imageView, nullptr);
-         }
-
-         vkDestroySwapchainKHR(device, swapChain, nullptr);
          vkDestroyDevice(device, nullptr);
 
