Index: IMGUI/imgui_impl_vulkan.cpp
===================================================================
--- IMGUI/imgui_impl_vulkan.cpp	(revision 3f32dfd9a09b20768e80b46b2a5ee4aea024fdc4)
+++ IMGUI/imgui_impl_vulkan.cpp	(revision ce9dc9f8d30ef01e9f3c1de05482d87c6c4a1144)
@@ -103,4 +103,5 @@
 void ImGui_ImplVulkanH_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkanH_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator);
 void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkanH_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator);
+void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count);
 void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator);
 
@@ -894,4 +895,5 @@
     IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE);
     IM_ASSERT(info->Device != VK_NULL_HANDLE);
+    IM_ASSERT(info->Queue != VK_NULL_HANDLE);
     IM_ASSERT(info->DescriptorPool != VK_NULL_HANDLE);
     IM_ASSERT(info->MinImageCount >= 2);
@@ -1070,4 +1072,180 @@
 }
 
+// Also destroy old swap chain and in-flight frames data, if any.
+void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count)
+{
+    VkResult err;
+    VkSwapchainKHR old_swapchain = wd->Swapchain;
+    wd->Swapchain = NULL;
+    err = vkDeviceWaitIdle(device);
+    check_vk_result(err);
+
+    // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one.
+    // Destroy old Framebuffer
+    for (uint32_t i = 0; i < wd->ImageCount; i++)
+    {
+        ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator);
+        ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator);
+    }
+    IM_FREE(wd->Frames);
+    IM_FREE(wd->FrameSemaphores);
+    wd->Frames = NULL;
+    wd->FrameSemaphores = NULL;
+    wd->ImageCount = 0;
+    if (wd->RenderPass)
+        vkDestroyRenderPass(device, wd->RenderPass, allocator);
+    if (wd->Pipeline)
+        vkDestroyPipeline(device, wd->Pipeline, allocator);
+
+    // If min image count was not specified, request different count of images dependent on selected present mode
+    if (min_image_count == 0)
+        min_image_count = ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(wd->PresentMode);
+
+    // Create Swapchain
+    {
+        VkSwapchainCreateInfoKHR info = {};
+        info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+        info.surface = wd->Surface;
+        info.minImageCount = min_image_count;
+        info.imageFormat = wd->SurfaceFormat.format;
+        info.imageColorSpace = wd->SurfaceFormat.colorSpace;
+        info.imageArrayLayers = 1;
+        info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+        info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;           // Assume that graphics family == present family
+        info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+        info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+        info.presentMode = wd->PresentMode;
+        info.clipped = VK_TRUE;
+        info.oldSwapchain = old_swapchain;
+        VkSurfaceCapabilitiesKHR cap;
+        err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap);
+        check_vk_result(err);
+        if (info.minImageCount < cap.minImageCount)
+            info.minImageCount = cap.minImageCount;
+        else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount)
+            info.minImageCount = cap.maxImageCount;
+
+        if (cap.currentExtent.width == 0xffffffff)
+        {
+            info.imageExtent.width = wd->Width = w;
+            info.imageExtent.height = wd->Height = h;
+        }
+        else
+        {
+            info.imageExtent.width = wd->Width = cap.currentExtent.width;
+            info.imageExtent.height = wd->Height = cap.currentExtent.height;
+        }
+        err = vkCreateSwapchainKHR(device, &info, allocator, &wd->Swapchain);
+        check_vk_result(err);
+        err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, NULL);
+        check_vk_result(err);
+        VkImage backbuffers[16] = {};
+        IM_ASSERT(wd->ImageCount >= min_image_count);
+        IM_ASSERT(wd->ImageCount < IM_ARRAYSIZE(backbuffers));
+        err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers);
+        check_vk_result(err);
+
+        IM_ASSERT(wd->Frames == NULL);
+        wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount);
+        wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->ImageCount);
+        memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount);
+        memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->ImageCount);
+        for (uint32_t i = 0; i < wd->ImageCount; i++)
+            wd->Frames[i].Backbuffer = backbuffers[i];
+    }
+    if (old_swapchain)
+        vkDestroySwapchainKHR(device, old_swapchain, allocator);
+
+    // Create the Render Pass
+    {
+        VkAttachmentDescription attachment = {};
+        attachment.format = wd->SurfaceFormat.format;
+        attachment.samples = VK_SAMPLE_COUNT_1_BIT;
+        attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+        attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+        attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+        attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+        VkAttachmentReference color_attachment = {};
+        color_attachment.attachment = 0;
+        color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+        VkSubpassDescription subpass = {};
+        subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+        subpass.colorAttachmentCount = 1;
+        subpass.pColorAttachments = &color_attachment;
+        VkSubpassDependency dependency = {};
+        dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+        dependency.dstSubpass = 0;
+        dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+        dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+        dependency.srcAccessMask = 0;
+        dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+        VkRenderPassCreateInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+        info.attachmentCount = 1;
+        info.pAttachments = &attachment;
+        info.subpassCount = 1;
+        info.pSubpasses = &subpass;
+        info.dependencyCount = 1;
+        info.pDependencies = &dependency;
+        err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass);
+        check_vk_result(err);
+
+        // We do not create a pipeline by default as this is also used by examples' main.cpp,
+        // but secondary viewport in multi-viewport mode may want to create one with:
+        //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline);
+    }
+
+    // Create The Image Views
+    {
+        VkImageViewCreateInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+        info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+        info.format = wd->SurfaceFormat.format;
+        info.components.r = VK_COMPONENT_SWIZZLE_R;
+        info.components.g = VK_COMPONENT_SWIZZLE_G;
+        info.components.b = VK_COMPONENT_SWIZZLE_B;
+        info.components.a = VK_COMPONENT_SWIZZLE_A;
+        VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+        info.subresourceRange = image_range;
+        for (uint32_t i = 0; i < wd->ImageCount; i++)
+        {
+            ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
+            info.image = fd->Backbuffer;
+            err = vkCreateImageView(device, &info, allocator, &fd->BackbufferView);
+            check_vk_result(err);
+        }
+    }
+
+    // Create Framebuffer
+    {
+        VkImageView attachment[1];
+        VkFramebufferCreateInfo info = {};
+        info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+        info.renderPass = wd->RenderPass;
+        info.attachmentCount = 1;
+        info.pAttachments = attachment;
+        info.width = wd->Width;
+        info.height = wd->Height;
+        info.layers = 1;
+        for (uint32_t i = 0; i < wd->ImageCount; i++)
+        {
+            ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
+            attachment[0] = fd->BackbufferView;
+            err = vkCreateFramebuffer(device, &info, allocator, &fd->Framebuffer);
+            check_vk_result(err);
+        }
+    }
+}
+
+// Create or resize window
+void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count)
+{
+    (void)instance;
+    ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count);
+    ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator);
+}
+
 void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator)
 {
Index: IMGUI/imgui_impl_vulkan.h
===================================================================
--- IMGUI/imgui_impl_vulkan.h	(revision 3f32dfd9a09b20768e80b46b2a5ee4aea024fdc4)
+++ IMGUI/imgui_impl_vulkan.h	(revision ce9dc9f8d30ef01e9f3c1de05482d87c6c4a1144)
@@ -32,4 +32,6 @@
     VkPhysicalDevice    PhysicalDevice;
     VkDevice            Device;
+    uint32_t            QueueFamily;
+    VkQueue             Queue;
     VkPipelineCache     PipelineCache;
     VkDescriptorPool    DescriptorPool;
@@ -71,4 +73,5 @@
 
 // Helpers
+IMGUI_IMPL_API void                 ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count);
 IMGUI_IMPL_API void                 ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator);
 IMGUI_IMPL_API VkSurfaceFormatKHR   ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space);
Index: sdl-game.cpp
===================================================================
--- sdl-game.cpp	(revision 3f32dfd9a09b20768e80b46b2a5ee4aea024fdc4)
+++ sdl-game.cpp	(revision ce9dc9f8d30ef01e9f3c1de05482d87c6c4a1144)
@@ -1,179 +1,136 @@
 #include "sdl-game.hpp"
 
+#include <array>
 #include <iostream>
 #include <set>
 #include <stdexcept>
 
+#include <stdlib.h>         // abort (only used in check_vk_result)
+
+#include <SDL2/SDL_vulkan.h>
+
 #include "IMGUI/imgui_impl_sdl.h"
 
-#include <stdio.h>          // printf, fprintf
-#include <stdlib.h>         // abort
-
-// dear imgui: standalone example application for SDL2 + Vulkan
-// If you are new to dear imgui, see examples/README.txt and documentation at the top of imgui.cpp.
-
-// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
-// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
-//   You will use those if you want to use this rendering back-end in your engine/app.
-// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
-//   the back-end itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
-// Read comments in imgui_impl_vulkan.h.
-
-#include <SDL2/SDL_vulkan.h>
-
 #include "logger.hpp"
 
-#include "vulkan-utils.hpp"
-
 using namespace std;
 
-//#define IMGUI_UNLIMITED_FRAME_RATE
-
-static VkAllocationCallbacks* g_Allocator = NULL;
-static VkInstance               g_Instance = VK_NULL_HANDLE;
-static VkPhysicalDevice         g_PhysicalDevice = VK_NULL_HANDLE;
-static VkDevice                 g_Device = VK_NULL_HANDLE;
-static VkQueue                  g_GraphicsQueue = VK_NULL_HANDLE;
-static VkQueue                  g_PresentQueue = VK_NULL_HANDLE;
-static VkPipelineCache          g_PipelineCache = VK_NULL_HANDLE;
-
-static ImGui_ImplVulkanH_Window g_MainWindowData;
-static uint32_t                 g_MinImageCount = 2;
-static bool                     g_SwapChainRebuild = false;
-
-static void check_vk_result(VkResult err)
-{
-   if (err == 0)
+#define IMGUI_UNLIMITED_FRAME_RATE
+
+static bool g_SwapChainRebuild = false;
+
+static void check_vk_result(VkResult err) {
+   if (err == 0) {
       return;
+   }
    fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
-   if (err < 0)
+   if (err < 0) {
       abort();
-}
-
-// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo.
-// Your real engine/app may not use them.
-
-static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, SDL_Window* window) {
-   // TODO: SetupVulkanWIndow calls vkGetPhysicalDeviceSurfaceSupportKHR to get the present queue. In vulkan-game, I do this in findQueueFamilies.
-   int width, height;
-   SDL_GetWindowSize(window, &width, &height);
-
-   wd->Surface = surface;
-
-   // Select Surface Format
-   const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
-   const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
-   wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace);
-
-   // Select Present Mode
-#ifdef IMGUI_UNLIMITED_FRAME_RATE
-   VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
-#else
-   VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR };
-#endif
-   wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
-   //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode);
-
-   // Create SwapChain, RenderPass, Framebuffer, etc.
-   IM_ASSERT(g_MinImageCount >= 2);
-
-   QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(g_PhysicalDevice, surface);
-
-   ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, indices.graphicsFamily.value(),
-      g_Allocator, width, height, g_MinImageCount);
-}
-
-static void CleanupVulkanWindow()
-{
-   ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator);
-}
-
-static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
-{
-   VkResult err;
-
-   VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
-   VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
-   err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex);
-   if (err == VK_ERROR_OUT_OF_DATE_KHR)
-   {
+   }
+}
+
+void VulkanGame::FrameRender(ImDrawData* draw_data) {
+   VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
+      imageAcquiredSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
+
+   if (result == VK_ERROR_OUT_OF_DATE_KHR) {
       g_SwapChainRebuild = true;
       return;
-   }
-   check_vk_result(err);
-
-   ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
-   {
-      err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX);    // wait indefinitely instead of periodically checking
-      check_vk_result(err);
-
-      err = vkResetFences(g_Device, 1, &fd->Fence);
-      check_vk_result(err);
-   }
-   {
-      err = vkResetCommandPool(g_Device, fd->CommandPool, 0);
-      check_vk_result(err);
-      VkCommandBufferBeginInfo info = {};
-      info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-      info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
-      err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
-      check_vk_result(err);
-   }
-   {
-      VkRenderPassBeginInfo info = {};
-      info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
-      info.renderPass = wd->RenderPass;
-      info.framebuffer = fd->Framebuffer;
-      info.renderArea.extent.width = wd->Width;
-      info.renderArea.extent.height = wd->Height;
-      info.clearValueCount = 1;
-      info.pClearValues = &wd->ClearValue;
-      vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
-   }
+   } else if (result != VK_SUCCESS) {
+      throw runtime_error("failed to acquire swap chain image!");
+   }
+
+   if (vkWaitForFences(device, 1, &inFlightFences[imageIndex], VK_TRUE, numeric_limits<uint64_t>::max()) != VK_SUCCESS) {
+      throw runtime_error("failed waiting for fence!");
+   }
+   if (vkResetFences(device, 1, &inFlightFences[imageIndex]) != VK_SUCCESS) {
+      throw runtime_error("failed to reset fence!");
+   }
+
+   // START OF NEW CODE
+   // I don't have analogous code in vulkan-game right now because I record command buffers once
+   // before the render loop ever starts. I should change this
+
+   result = vkResetCommandPool(device, commandPools[imageIndex], 0);
+   check_vk_result(result);
+   VkCommandBufferBeginInfo info = {};
+   info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+   info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+   result = vkBeginCommandBuffer(commandBuffers[imageIndex], &info);
+   check_vk_result(result);
+
+   VkRenderPassBeginInfo renderPassInfo = {};
+   renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+   renderPassInfo.renderPass = renderPass;
+   renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
+   renderPassInfo.renderArea.extent = swapChainExtent;
+
+   array<VkClearValue, 2> clearValues = {};
+   clearValues[0].color = { { 0.45f, 0.55f, 0.60f, 1.00f } };
+   clearValues[1].depthStencil = { 1.0f, 0 };
+
+   renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
+   renderPassInfo.pClearValues = clearValues.data();
+
+   vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
 
    // Record dear imgui primitives into command buffer
-   ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
+   ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffers[imageIndex]);
 
    // Submit command buffer
-   vkCmdEndRenderPass(fd->CommandBuffer);
-   {
-      VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-      VkSubmitInfo info = {};
-      info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-      info.waitSemaphoreCount = 1;
-      info.pWaitSemaphores = &image_acquired_semaphore;
-      info.pWaitDstStageMask = &wait_stage;
-      info.commandBufferCount = 1;
-      info.pCommandBuffers = &fd->CommandBuffer;
-      info.signalSemaphoreCount = 1;
-      info.pSignalSemaphores = &render_complete_semaphore;
-
-      err = vkEndCommandBuffer(fd->CommandBuffer);
-      check_vk_result(err);
-      err = vkQueueSubmit(g_GraphicsQueue, 1, &info, fd->Fence);
-      check_vk_result(err);
-   }
-}
-
-static void FramePresent(ImGui_ImplVulkanH_Window* wd)
-{
+   vkCmdEndRenderPass(commandBuffers[imageIndex]);
+
+   if (vkEndCommandBuffer(commandBuffers[imageIndex]) != VK_SUCCESS) {
+      throw runtime_error("failed to record command buffer!");
+   }
+
+   // END OF NEW CODE
+
+   VkSemaphore waitSemaphores[] = { imageAcquiredSemaphores[currentFrame] };
+   VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+   VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[currentFrame] };
+
+   VkSubmitInfo submitInfo = {};
+   submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+   submitInfo.waitSemaphoreCount = 1;
+   submitInfo.pWaitSemaphores = waitSemaphores;
+   submitInfo.pWaitDstStageMask = &wait_stage;
+   submitInfo.commandBufferCount = 1;
+   submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
+   submitInfo.signalSemaphoreCount = 1;
+   submitInfo.pSignalSemaphores = signalSemaphores;
+
+   if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[imageIndex]) != VK_SUCCESS) {
+      throw runtime_error("failed to submit draw command buffer!");
+   }
+}
+
+void VulkanGame::FramePresent() {
    if (g_SwapChainRebuild)
       return;
-   VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
-   VkPresentInfoKHR info = {};
-   info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
-   info.waitSemaphoreCount = 1;
-   info.pWaitSemaphores = &render_complete_semaphore;
-   info.swapchainCount = 1;
-   info.pSwapchains = &wd->Swapchain;
-   info.pImageIndices = &wd->FrameIndex;
-   VkResult err = vkQueuePresentKHR(g_PresentQueue, &info);
-   if (err == VK_ERROR_OUT_OF_DATE_KHR)
-   {
+
+   VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[currentFrame] };
+
+   VkPresentInfoKHR presentInfo = {};
+   presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+   presentInfo.waitSemaphoreCount = 1;
+   presentInfo.pWaitSemaphores = signalSemaphores;
+   presentInfo.swapchainCount = 1;
+   presentInfo.pSwapchains = &swapChain;
+   presentInfo.pImageIndices = &imageIndex;
+   presentInfo.pResults = nullptr;
+
+   VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo);
+
+   // In vulkan-game, I also handle VK_SUBOPTIMAL_KHR and framebufferResized. g_SwapChainRebuild is kind of similar
+   // to framebufferResized, but not quite the same
+   if (result == VK_ERROR_OUT_OF_DATE_KHR) {
       g_SwapChainRebuild = true;
       return;
-   }
-   check_vk_result(err);
-   wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores
+   } else if (result != VK_SUCCESS) {
+      throw runtime_error("failed to present swap chain image!");
+   }
+
+   currentFrame = (currentFrame + 1) % swapChainImageCount;
 }
 
@@ -190,7 +147,15 @@
 }
 
-VulkanGame::VulkanGame(int maxFramesInFlight) : MAX_FRAMES_IN_FLIGHT(maxFramesInFlight) {
+VulkanGame::VulkanGame() {
+   // TODO: Double-check whether initialization should happen in the header, where the variables are declared, or here
+   // Also, decide whether to use this-> for all instance variables, or only when necessary
+
+   this->debugMessenger = VK_NULL_HANDLE;
+
    this->gui = nullptr;
    this->window = nullptr;
+
+   this->swapChainPresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
+   this->swapChainMinImageCount = 0;
 }
 
@@ -208,10 +173,4 @@
 
    initVulkan();
-
-   VkResult err;
-
-   // Create Framebuffers
-   ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
-   SetupVulkanWindow(wd, surface, window);
 
    // Create Descriptor Pool
@@ -237,12 +196,14 @@
       pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
       pool_info.pPoolSizes = pool_sizes;
-      err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &descriptorPool);
-      check_vk_result(err);
-   }
+      check_vk_result(vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptorPool));
+   }
+
+   // TODO: Do this in one place and save it instead of redoing it every time I need a queue family index
+   QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
 
    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
-   ImGuiIO& io = ImGui::GetIO(); (void)io;
+   ImGuiIO& io = ImGui::GetIO();
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
@@ -255,14 +216,15 @@
    ImGui_ImplSDL2_InitForVulkan(window);
    ImGui_ImplVulkan_InitInfo init_info = {};
-   init_info.Instance = g_Instance;
-   init_info.PhysicalDevice = g_PhysicalDevice;
-   init_info.Device = g_Device;
-   init_info.PipelineCache = g_PipelineCache;
+   init_info.Instance = instance;
+   init_info.PhysicalDevice = physicalDevice;
+   init_info.Device = device;
+   init_info.QueueFamily = indices.graphicsFamily.value();
+   init_info.Queue = graphicsQueue;
    init_info.DescriptorPool = descriptorPool;
-   init_info.Allocator = g_Allocator;
-   init_info.MinImageCount = g_MinImageCount;
-   init_info.ImageCount = wd->ImageCount;
+   init_info.Allocator = nullptr;
+   init_info.MinImageCount = swapChainMinImageCount;
+   init_info.ImageCount = swapChainImageCount;
    init_info.CheckVkResultFn = check_vk_result;
-   ImGui_ImplVulkan_Init(&init_info, wd->RenderPass);
+   ImGui_ImplVulkan_Init(&init_info, renderPass);
 
    // Load Fonts
@@ -279,33 +241,14 @@
    //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
    //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
-   //IM_ASSERT(font != NULL);
+   //assert(font != NULL);
 
    // Upload Fonts
    {
-      // Use any command queue
-      VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool;
-      VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer;
-
-      err = vkResetCommandPool(g_Device, command_pool, 0);
-      check_vk_result(err);
-      VkCommandBufferBeginInfo begin_info = {};
-      begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-      begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
-      err = vkBeginCommandBuffer(command_buffer, &begin_info);
-      check_vk_result(err);
-
-      ImGui_ImplVulkan_CreateFontsTexture(command_buffer);
-
-      VkSubmitInfo end_info = {};
-      end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-      end_info.commandBufferCount = 1;
-      end_info.pCommandBuffers = &command_buffer;
-      err = vkEndCommandBuffer(command_buffer);
-      check_vk_result(err);
-      err = vkQueueSubmit(graphicsQueue, 1, &end_info, VK_NULL_HANDLE);
-      check_vk_result(err);
-
-      err = vkDeviceWaitIdle(g_Device);
-      check_vk_result(err);
+      VkCommandBuffer commandBuffer = VulkanUtils::beginSingleTimeCommands(device, resourceCommandPool);
+
+      ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);
+
+      VulkanUtils::endSingleTimeCommands(device, resourceCommandPool, commandBuffer, graphicsQueue);
+
       ImGui_ImplVulkan_DestroyFontUploadObjects();
    }
@@ -314,5 +257,4 @@
    bool show_demo_window = true;
    bool show_another_window = false;
-   ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
 
    // Main loop
@@ -337,13 +279,13 @@
          int width, height;
          SDL_GetWindowSize(window, &width, &height);
-         if (width > 0 && height > 0)
-         {
-            ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
-
-            QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(g_PhysicalDevice, surface);
-
-            ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData,
-               indices.graphicsFamily.value(), g_Allocator, width, height, g_MinImageCount);
-            g_MainWindowData.FrameIndex = 0;
+         if (width > 0 && height > 0) {
+            // TODO: This should be used if the min image count changes, presumably because a new surface was created
+            // with a different image count or something like that. Maybe I want to add code to query for a new min image count
+            // during swapchain recreation to take advantage of this
+            ImGui_ImplVulkan_SetMinImageCount(swapChainMinImageCount);
+
+            recreateSwapChain();
+
+            imageIndex = 0;
             g_SwapChainRebuild = false;
          }
@@ -361,5 +303,4 @@
       // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
       {
-         static float f = 0.0f;
          static int counter = 0;
 
@@ -369,7 +310,4 @@
          ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
          ImGui::Checkbox("Another Window", &show_another_window);
-
-         ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
-         ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
 
          if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
@@ -396,20 +334,10 @@
       ImDrawData* draw_data = ImGui::GetDrawData();
       const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);
-      if (!is_minimized)
-      {
-         memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float));
-         FrameRender(wd, draw_data);
-         FramePresent(wd);
-      }
-   }
-
-   // Cleanup
-   err = vkDeviceWaitIdle(g_Device);
-   check_vk_result(err);
-   ImGui_ImplVulkan_Shutdown();
-   ImGui_ImplSDL2_Shutdown();
-   ImGui::DestroyContext();
-
-   CleanupVulkanWindow();
+      if (!is_minimized) {
+         FrameRender(draw_data);
+         FramePresent();
+      }
+   }
+
    cleanup();
 
@@ -481,15 +409,50 @@
    pickPhysicalDevice(deviceExtensions);
    createLogicalDevice(validationLayers, deviceExtensions);
+   chooseSwapChainProperties();
+   createSwapChain();
+   createImageViews();
+   createRenderPass();
+   createResourceCommandPool();
+
+   createCommandPools();
+
+   VulkanUtils::createDepthImage(device, physicalDevice, resourceCommandPool, findDepthFormat(), swapChainExtent,
+      depthImage, graphicsQueue);
+
+   createFramebuffers();
+
+   // TODO: Initialize pipelines here
+
+   createCommandBuffers();
+
+   createSyncObjects();
 }
 
 void VulkanGame::cleanup() {
-   vkDestroyDescriptorPool(g_Device, descriptorPool, g_Allocator);
+   // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals)
+    //vkQueueWaitIdle(g_Queue);
+   if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
+      throw runtime_error("failed to wait for device!");
+   }
+
+   ImGui_ImplVulkan_Shutdown();
+   ImGui_ImplSDL2_Shutdown();
+   ImGui::DestroyContext();
+
+   cleanupSwapChain();
+
+   // this would actually be destroyed in the pipeline class
+   vkDestroyDescriptorPool(device, descriptorPool, nullptr);
+
+   vkDestroyCommandPool(device, resourceCommandPool, nullptr);
+
+   vkDestroyDevice(device, nullptr);
+   vkDestroySurfaceKHR(instance, surface, nullptr);
 
    if (ENABLE_VALIDATION_LAYERS) {
-      VulkanUtils::destroyDebugUtilsMessengerEXT(g_Instance, debugMessenger, nullptr);
-   }
-
-   vkDestroyDevice(g_Device, g_Allocator);
-   vkDestroyInstance(g_Instance, g_Allocator);
+      VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
+   }
+
+   vkDestroyInstance(instance, nullptr);
 
    gui->destroyWindow();
@@ -536,5 +499,6 @@
       populateDebugMessengerCreateInfo(debugCreateInfo);
       createInfo.pNext = &debugCreateInfo;
-   } else {
+   }
+   else {
       createInfo.enabledLayerCount = 0;
 
@@ -542,5 +506,5 @@
    }
 
-   if (vkCreateInstance(&createInfo, nullptr, &g_Instance) != VK_SUCCESS) {
+   if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
       throw runtime_error("failed to create instance!");
    }
@@ -555,5 +519,5 @@
    populateDebugMessengerCreateInfo(createInfo);
 
-   if (VulkanUtils::createDebugUtilsMessengerEXT(g_Instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
+   if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
       throw runtime_error("failed to set up debug messenger!");
    }
@@ -569,5 +533,5 @@
 
 void VulkanGame::createVulkanSurface() {
-   if (gui->createVulkanSurface(g_Instance, &surface) == RTWO_ERROR) {
+   if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
       throw runtime_error("failed to create window surface!");
    }
@@ -577,5 +541,5 @@
    uint32_t deviceCount = 0;
    // TODO: Check VkResult
-   vkEnumeratePhysicalDevices(g_Instance, &deviceCount, nullptr);
+   vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
 
    if (deviceCount == 0) {
@@ -585,10 +549,10 @@
    vector<VkPhysicalDevice> devices(deviceCount);
    // TODO: Check VkResult
-   vkEnumeratePhysicalDevices(g_Instance, &deviceCount, devices.data());
+   vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
 
    cout << endl << "Graphics cards:" << endl;
    for (const VkPhysicalDevice& device : devices) {
       if (isDeviceSuitable(device, deviceExtensions)) {
-         g_PhysicalDevice = device;
+         physicalDevice = device;
          break;
       }
@@ -596,5 +560,5 @@
    cout << endl;
 
-   if (g_PhysicalDevice == VK_NULL_HANDLE) {
+   if (physicalDevice == VK_NULL_HANDLE) {
       throw runtime_error("failed to find a suitable GPU!");
    }
@@ -612,6 +576,8 @@
 
    if (extensionsSupported) {
-      SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
-      swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
+      vector<VkSurfaceFormatKHR> formats = VulkanUtils::querySwapChainFormats(physicalDevice, surface);
+      vector<VkPresentModeKHR> presentModes = VulkanUtils::querySwapChainPresentModes(physicalDevice, surface);
+
+      swapChainAdequate = !formats.empty() && !presentModes.empty();
    }
 
@@ -623,6 +589,6 @@
 
 void VulkanGame::createLogicalDevice(const vector<const char*>& validationLayers,
-      const vector<const char*>& deviceExtensions) {
-   QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(g_PhysicalDevice, surface);
+   const vector<const char*>& deviceExtensions) {
+   QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
 
    if (!indices.isComplete()) {
@@ -666,207 +632,347 @@
       createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
       createInfo.ppEnabledLayerNames = validationLayers.data();
-   } else {
+   }
+   else {
       createInfo.enabledLayerCount = 0;
    }
 
-   if (vkCreateDevice(g_PhysicalDevice, &createInfo, nullptr, &g_Device) != VK_SUCCESS) {
+   if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
       throw runtime_error("failed to create logical device!");
    }
 
-   vkGetDeviceQueue(g_Device, indices.graphicsFamily.value(), 0, &graphicsQueue);
-   vkGetDeviceQueue(g_Device, indices.presentFamily.value(), 0, &presentQueue);
-
-   g_GraphicsQueue = graphicsQueue;
-   g_PresentQueue = presentQueue;
+   vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
+   vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
+}
+
+void VulkanGame::chooseSwapChainProperties() {
+   vector<VkSurfaceFormatKHR> availableFormats = VulkanUtils::querySwapChainFormats(physicalDevice, surface);
+   vector<VkPresentModeKHR> availablePresentModes = VulkanUtils::querySwapChainPresentModes(physicalDevice, surface);
+
+   // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation
+   // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format
+   // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40,
+   // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used.
+   swapChainSurfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(availableFormats,
+      { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM },
+      VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
+
+#ifdef IMGUI_UNLIMITED_FRAME_RATE
+   vector<VkPresentModeKHR> presentModes{
+      VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR
+   };
+#else
+   vector<VkPresentModeKHR> presentModes{ VK_PRESENT_MODE_FIFO_KHR };
+#endif
+
+   swapChainPresentMode = VulkanUtils::chooseSwapPresentMode(availablePresentModes, presentModes);
+
+   cout << "[vulkan] Selected PresentMode = " << swapChainPresentMode << endl;
+
+   VkSurfaceCapabilitiesKHR capabilities = VulkanUtils::querySwapChainCapabilities(physicalDevice, surface);
+
+   // If min image count was not specified, request different count of images dependent on selected present mode
+   if (swapChainMinImageCount == 0) {
+      if (swapChainPresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
+         swapChainMinImageCount = 3;
+      }
+      else if (swapChainPresentMode == VK_PRESENT_MODE_FIFO_KHR || swapChainPresentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) {
+         swapChainMinImageCount = 2;
+      }
+      else if (swapChainPresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
+         swapChainMinImageCount = 1;
+      }
+      else {
+         throw runtime_error("unexpected present mode!");
+      }
+   }
+
+   if (swapChainMinImageCount < capabilities.minImageCount) {
+      swapChainMinImageCount = capabilities.minImageCount;
+   }
+   else if (capabilities.maxImageCount != 0 && swapChainMinImageCount > capabilities.maxImageCount) {
+      swapChainMinImageCount = capabilities.maxImageCount;
+   }
+}
+
+void VulkanGame::createSwapChain() {
+   VkSurfaceCapabilitiesKHR capabilities = VulkanUtils::querySwapChainCapabilities(physicalDevice, surface);
+
+   swapChainExtent = VulkanUtils::chooseSwapExtent(capabilities, gui->getWindowWidth(), gui->getWindowHeight());
+
+   VkSwapchainCreateInfoKHR createInfo = {};
+   createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+   createInfo.surface = surface;
+   createInfo.minImageCount = swapChainMinImageCount;
+   createInfo.imageFormat = swapChainSurfaceFormat.format;
+   createInfo.imageColorSpace = swapChainSurfaceFormat.colorSpace;
+   createInfo.imageExtent = swapChainExtent;
+   createInfo.imageArrayLayers = 1;
+   createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+
+   // TODO: Maybe save this result so I don't have to recalculate it every time
+   QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
+   uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
+
+   if (indices.graphicsFamily != indices.presentFamily) {
+      createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
+      createInfo.queueFamilyIndexCount = 2;
+      createInfo.pQueueFamilyIndices = queueFamilyIndices;
+   }
+   else {
+      createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+      createInfo.queueFamilyIndexCount = 0;
+      createInfo.pQueueFamilyIndices = nullptr;
+   }
+
+   createInfo.preTransform = capabilities.currentTransform;
+   createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+   createInfo.presentMode = swapChainPresentMode;
+   createInfo.clipped = VK_TRUE;
+   createInfo.oldSwapchain = VK_NULL_HANDLE;
+
+   if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
+      throw runtime_error("failed to create swap chain!");
+   }
+
+   if (vkGetSwapchainImagesKHR(device, swapChain, &swapChainImageCount, nullptr) != VK_SUCCESS) {
+      throw runtime_error("failed to get swap chain image count!");
+   }
+
+   swapChainImages.resize(swapChainImageCount);
+   if (vkGetSwapchainImagesKHR(device, swapChain, &swapChainImageCount, swapChainImages.data()) != VK_SUCCESS) {
+      throw runtime_error("failed to get swap chain images!");
+   }
+}
+
+void VulkanGame::createImageViews() {
+   swapChainImageViews.resize(swapChainImageCount);
+
+   for (uint32_t i = 0; i < swapChainImageViews.size(); i++) {
+      swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainSurfaceFormat.format,
+         VK_IMAGE_ASPECT_COLOR_BIT);
+   }
+}
+
+void VulkanGame::createRenderPass() {
+   VkAttachmentDescription colorAttachment = {};
+   colorAttachment.format = swapChainSurfaceFormat.format;
+   colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
+   colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // Set to VK_ATTACHMENT_LOAD_OP_DONT_CARE to disable clearing
+   colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+   colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+   colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+   colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+   colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+   VkAttachmentReference colorAttachmentRef = {};
+   colorAttachmentRef.attachment = 0;
+   colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+   VkAttachmentDescription depthAttachment = {};
+   depthAttachment.format = findDepthFormat();
+   depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
+   depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+   depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+   depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+   depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+   depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+   depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+   VkAttachmentReference depthAttachmentRef = {};
+   depthAttachmentRef.attachment = 1;
+   depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+   VkSubpassDescription subpass = {};
+   subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+   subpass.colorAttachmentCount = 1;
+   subpass.pColorAttachments = &colorAttachmentRef;
+   //subpass.pDepthStencilAttachment = &depthAttachmentRef;
+
+   VkSubpassDependency dependency = {};
+   dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+   dependency.dstSubpass = 0;
+   dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+   dependency.srcAccessMask = 0;
+   dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+   dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+   array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
+   VkRenderPassCreateInfo renderPassInfo = {};
+   renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+   renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
+   renderPassInfo.pAttachments = attachments.data();
+   renderPassInfo.subpassCount = 1;
+   renderPassInfo.pSubpasses = &subpass;
+   renderPassInfo.dependencyCount = 1;
+   renderPassInfo.pDependencies = &dependency;
+
+   if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
+      throw runtime_error("failed to create render pass!");
+   }
+
+   // We do not create a pipeline by default as this is also used by examples' main.cpp,
+   // but secondary viewport in multi-viewport mode may want to create one with:
+   //ImGui_ImplVulkan_CreatePipeline(device, g_Allocator, VK_NULL_HANDLE, g_MainWindowData.RenderPass, VK_SAMPLE_COUNT_1_BIT, &g_MainWindowData.Pipeline);
+}
+
+VkFormat VulkanGame::findDepthFormat() {
+   return VulkanUtils::findSupportedFormat(
+      physicalDevice,
+      { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
+      VK_IMAGE_TILING_OPTIMAL,
+      VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
+   );
+}
+
+void VulkanGame::createResourceCommandPool() {
+   QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
+
+   VkCommandPoolCreateInfo poolInfo = {};
+   poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+   poolInfo.queueFamilyIndex = indices.graphicsFamily.value();
+   poolInfo.flags = 0;
+
+   if (vkCreateCommandPool(device, &poolInfo, nullptr, &resourceCommandPool) != VK_SUCCESS) {
+      throw runtime_error("failed to create resource command pool!");
+   }
+}
+
+void VulkanGame::createCommandPools() {
+   commandPools.resize(swapChainImageCount);
+
+   QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
+
+   for (size_t i = 0; i < swapChainImageCount; i++) {
+      VkCommandPoolCreateInfo poolInfo = {};
+      poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+      poolInfo.queueFamilyIndex = indices.graphicsFamily.value();
+      poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+      if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPools[i]) != VK_SUCCESS) {
+         throw runtime_error("failed to create graphics command pool!");
+      }
+   }
+}
+
+void VulkanGame::createFramebuffers() {
+   swapChainFramebuffers.resize(swapChainImageCount);
+
+   VkFramebufferCreateInfo framebufferInfo = {};
+   framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+   framebufferInfo.renderPass = renderPass;
+   framebufferInfo.width = swapChainExtent.width;
+   framebufferInfo.height = swapChainExtent.height;
+   framebufferInfo.layers = 1;
+
+   for (size_t i = 0; i < swapChainImageCount; i++) {
+      array<VkImageView, 2> attachments = {
+         swapChainImageViews[i],
+         depthImage.imageView
+      };
+
+      framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
+      framebufferInfo.pAttachments = attachments.data();
+
+      if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
+         throw runtime_error("failed to create framebuffer!");
+      }
+   }
+}
+
+void VulkanGame::createCommandBuffers() {
+   commandBuffers.resize(swapChainImageCount);
+
+   for (size_t i = 0; i < swapChainImageCount; i++) {
+      VkCommandBufferAllocateInfo allocInfo = {};
+      allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+      allocInfo.commandPool = commandPools[i];
+      allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+      allocInfo.commandBufferCount = 1;
+
+      if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffers[i]) != VK_SUCCESS) {
+         throw runtime_error("failed to allocate command buffers!");
+      }
+   }
+}
+
+void VulkanGame::createSyncObjects() {
+   imageAcquiredSemaphores.resize(swapChainImageCount);
+   renderCompleteSemaphores.resize(swapChainImageCount);
+   inFlightFences.resize(swapChainImageCount);
+
+   VkSemaphoreCreateInfo semaphoreInfo = {};
+   semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+
+   VkFenceCreateInfo fenceInfo = {};
+   fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+   fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+   for (size_t i = 0; i < swapChainImageCount; i++) {
+      if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAcquiredSemaphores[i]) != VK_SUCCESS) {
+         throw runtime_error("failed to create image acquired sempahore for a frame!");
+      }
+
+      if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderCompleteSemaphores[i]) != VK_SUCCESS) {
+         throw runtime_error("failed to create render complete sempahore for a frame!");
+      }
+
+      if (vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
+         throw runtime_error("failed to create fence for a frame!");
+      }
+   }
+}
+
+void VulkanGame::recreateSwapChain() {
+   if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
+      throw runtime_error("failed to wait for device!");
+   }
+
+   cleanupSwapChain();
+
+   createSwapChain();
+   createImageViews();
+   createRenderPass();
+
+   createCommandPools();
+
+   // The depth buffer does need to be recreated with the swap chain since its dimensions depend on the window size
+   // and resizing the window is a common reason to recreate the swapchain
+   VulkanUtils::createDepthImage(device, physicalDevice, resourceCommandPool, findDepthFormat(), swapChainExtent,
+      depthImage, graphicsQueue);
+
+   createFramebuffers();
+
+   // TODO: Update pipelines here
+
+   createCommandBuffers();
+
+   createSyncObjects();
+}
+
+void VulkanGame::cleanupSwapChain() {
+   VulkanUtils::destroyVulkanImage(device, depthImage);
+
+   for (VkFramebuffer framebuffer : swapChainFramebuffers) {
+      vkDestroyFramebuffer(device, framebuffer, nullptr);
+   }
+
+   for (uint32_t i = 0; i < swapChainImageCount; i++) {
+      vkFreeCommandBuffers(device, commandPools[i], 1, &commandBuffers[i]);
+      vkDestroyCommandPool(device, commandPools[i], nullptr);
+   }
+
+   for (uint32_t i = 0; i < swapChainImageCount; i++) {
+      vkDestroySemaphore(device, imageAcquiredSemaphores[i], nullptr);
+      vkDestroySemaphore(device, renderCompleteSemaphores[i], nullptr);
+      vkDestroyFence(device, inFlightFences[i], nullptr);
+   }
+
+   vkDestroyRenderPass(device, renderPass, nullptr);
+
+   for (VkImageView imageView : swapChainImageViews) {
+      vkDestroyImageView(device, imageView, nullptr);
+   }
+
+   vkDestroySwapchainKHR(device, swapChain, nullptr);
 }
 
 /********************************************** END OF NEW CODE **********************************************/
-
-/********************************************** START TEMP HELPER GUNVTIONS **********************************/
-
-// Forward Declarations
-void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd,
-   const VkAllocationCallbacks* allocator);
-void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd,
-   const VkAllocationCallbacks* allocator);
-void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device,
-   ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator);
-
-// Create or resize window
-void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device,
-      ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width,
-      int height, uint32_t min_image_count) {
-   (void)instance;
-   ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count);
-   ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator);
-}
-
-// Also destroy old swap chain and in-flight frames data, if any.
-void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device,
-      ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) {
-   VkResult err;
-   VkSwapchainKHR old_swapchain = wd->Swapchain;
-   wd->Swapchain = NULL;
-   err = vkDeviceWaitIdle(device);
-   check_vk_result(err);
-
-   // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one.
-   // Destroy old Framebuffer
-   for (uint32_t i = 0; i < wd->ImageCount; i++) {
-      ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator);
-      ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator);
-   }
-   IM_FREE(wd->Frames);
-   IM_FREE(wd->FrameSemaphores);
-   wd->Frames = NULL;
-   wd->FrameSemaphores = NULL;
-   wd->ImageCount = 0;
-   if (wd->RenderPass) {
-      vkDestroyRenderPass(device, wd->RenderPass, allocator);
-   }
-   if (wd->Pipeline) {
-      vkDestroyPipeline(device, wd->Pipeline, allocator);
-   }
-
-   // If min image count was not specified, request different count of images dependent on selected present mode
-   if (min_image_count == 0) {
-      min_image_count = ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(wd->PresentMode);
-   }
-
-   // Create Swapchain
-   {
-      VkSwapchainCreateInfoKHR info = {};
-      info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
-      info.surface = wd->Surface;
-      info.minImageCount = min_image_count;
-      info.imageFormat = wd->SurfaceFormat.format;
-      info.imageColorSpace = wd->SurfaceFormat.colorSpace;
-      info.imageArrayLayers = 1;
-      info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
-      info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;           // Assume that graphics family == present family
-      info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-      info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
-      info.presentMode = wd->PresentMode;
-      info.clipped = VK_TRUE;
-      info.oldSwapchain = old_swapchain;
-      VkSurfaceCapabilitiesKHR cap;
-      err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap);
-      check_vk_result(err);
-      if (info.minImageCount < cap.minImageCount) {
-         info.minImageCount = cap.minImageCount;
-      } else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount) {
-         info.minImageCount = cap.maxImageCount;
-      }
-
-      if (cap.currentExtent.width == 0xffffffff) {
-         info.imageExtent.width = wd->Width = w;
-         info.imageExtent.height = wd->Height = h;
-      } else {
-         info.imageExtent.width = wd->Width = cap.currentExtent.width;
-         info.imageExtent.height = wd->Height = cap.currentExtent.height;
-      }
-      err = vkCreateSwapchainKHR(device, &info, allocator, &wd->Swapchain);
-      check_vk_result(err);
-      err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, NULL);
-      check_vk_result(err);
-      VkImage backbuffers[16] = {};
-      IM_ASSERT(wd->ImageCount >= min_image_count);
-      IM_ASSERT(wd->ImageCount < IM_ARRAYSIZE(backbuffers));
-      err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers);
-      check_vk_result(err);
-
-      IM_ASSERT(wd->Frames == NULL);
-      wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount);
-      wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->ImageCount);
-      memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount);
-      memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->ImageCount);
-      for (uint32_t i = 0; i < wd->ImageCount; i++) {
-         wd->Frames[i].Backbuffer = backbuffers[i];
-      }
-   }
-   if (old_swapchain) {
-      vkDestroySwapchainKHR(device, old_swapchain, allocator);
-   }
-
-   // Create the Render Pass
-   {
-      VkAttachmentDescription attachment = {};
-      attachment.format = wd->SurfaceFormat.format;
-      attachment.samples = VK_SAMPLE_COUNT_1_BIT;
-      attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-      attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-      attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-      attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-      attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-      attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
-      VkAttachmentReference color_attachment = {};
-      color_attachment.attachment = 0;
-      color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-      VkSubpassDescription subpass = {};
-      subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-      subpass.colorAttachmentCount = 1;
-      subpass.pColorAttachments = &color_attachment;
-      VkSubpassDependency dependency = {};
-      dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
-      dependency.dstSubpass = 0;
-      dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-      dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-      dependency.srcAccessMask = 0;
-      dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-      VkRenderPassCreateInfo info = {};
-      info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-      info.attachmentCount = 1;
-      info.pAttachments = &attachment;
-      info.subpassCount = 1;
-      info.pSubpasses = &subpass;
-      info.dependencyCount = 1;
-      info.pDependencies = &dependency;
-      err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass);
-      check_vk_result(err);
-
-      // We do not create a pipeline by default as this is also used by examples' main.cpp,
-      // but secondary viewport in multi-viewport mode may want to create one with:
-      //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline);
-   }
-
-   // Create The Image Views
-   {
-      VkImageViewCreateInfo info = {};
-      info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-      info.viewType = VK_IMAGE_VIEW_TYPE_2D;
-      info.format = wd->SurfaceFormat.format;
-      info.components.r = VK_COMPONENT_SWIZZLE_R;
-      info.components.g = VK_COMPONENT_SWIZZLE_G;
-      info.components.b = VK_COMPONENT_SWIZZLE_B;
-      info.components.a = VK_COMPONENT_SWIZZLE_A;
-      VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
-      info.subresourceRange = image_range;
-      for (uint32_t i = 0; i < wd->ImageCount; i++) {
-         ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
-         info.image = fd->Backbuffer;
-         err = vkCreateImageView(device, &info, allocator, &fd->BackbufferView);
-         check_vk_result(err);
-      }
-   }
-
-   // Create Framebuffer
-   {
-      VkImageView attachment[1];
-      VkFramebufferCreateInfo info = {};
-      info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-      info.renderPass = wd->RenderPass;
-      info.attachmentCount = 1;
-      info.pAttachments = attachment;
-      info.width = wd->Width;
-      info.height = wd->Height;
-      info.layers = 1;
-      for (uint32_t i = 0; i < wd->ImageCount; i++) {
-         ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
-         attachment[0] = fd->BackbufferView;
-         err = vkCreateFramebuffer(device, &info, allocator, &fd->Framebuffer);
-         check_vk_result(err);
-      }
-   }
-}
-
-/*********************************************** END TEMP HELPER GUNVTIONS ***********************************/
Index: sdl-game.hpp
===================================================================
--- sdl-game.hpp	(revision 3f32dfd9a09b20768e80b46b2a5ee4aea024fdc4)
+++ sdl-game.hpp	(revision ce9dc9f8d30ef01e9f3c1de05482d87c6c4a1144)
@@ -3,12 +3,11 @@
 
 #include <vector>
-
 #include <vulkan/vulkan.h>
 
 #include <SDL2/SDL.h>
-
 #include "IMGUI/imgui_impl_vulkan.h"
 
 #include "consts.hpp"
+#include "vulkan-utils.hpp"
 
 #include "game-gui-sdl.hpp"
@@ -26,8 +25,8 @@
 class VulkanGame {
    public:
-      VulkanGame(int maxFramesInFlight);
+      VulkanGame();
       ~VulkanGame();
 
-      void run(int width, int height, unsigned char guiFlags); // Mostly example code
+      void run(int width, int height, unsigned char guiFlags);
 
    private:
@@ -38,9 +37,4 @@
          void* pUserData);
 
-      // TODO: Make these consts static
-      // Also, maybe move them into consts.hpp
-
-      const int MAX_FRAMES_IN_FLIGHT; // Unused right now
-
       // TODO: Good place to start using smart pointers
       GameGui* gui;
@@ -49,10 +43,44 @@
       SDL_Window* window;
 
+      VkInstance instance;
       VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
       VkSurfaceKHR surface; // TODO: Change the variable name to vulkanSurface
+      VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
+      VkDevice device;
 
       VkQueue graphicsQueue;
       VkQueue presentQueue;
-      
+
+      // TODO: Maybe make a swapchain struct for convenience
+      VkSurfaceFormatKHR swapChainSurfaceFormat;
+      VkPresentModeKHR swapChainPresentMode;
+      VkExtent2D swapChainExtent;
+      uint32_t swapChainMinImageCount;
+      uint32_t swapChainImageCount;
+
+      VkSwapchainKHR swapChain;
+      vector<VkImage> swapChainImages;
+      vector<VkImageView> swapChainImageViews;
+      vector<VkFramebuffer> swapChainFramebuffers;
+
+      VkRenderPass renderPass;
+
+      VkCommandPool resourceCommandPool;
+
+      vector<VkCommandPool> commandPools;
+      vector<VkCommandBuffer> commandBuffers;
+
+      VulkanImage depthImage;
+
+      // These are per frame
+      vector<VkSemaphore> imageAcquiredSemaphores;
+      vector<VkSemaphore> renderCompleteSemaphores;
+
+      // These are per swap chain image
+      vector<VkFence> inFlightFences;
+
+      uint32_t imageIndex;
+      uint32_t currentFrame;
+
       // My code, but not complete. Skips creating the SDL renderer, probably because it doesn't use hardware acceleration.
       // I should try to get uncapped framerate and compare performance w/ and w/out an SDL renderer
@@ -65,24 +93,32 @@
       void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo);
       void createVulkanSurface();
-      void pickPhysicalDevice(const vector<const char*>& deviceExtensions); // Double-check, but it should be a copy of my code. Still uses g_Instance and g_Physical device though
+      void pickPhysicalDevice(const vector<const char*>& deviceExtensions);
       bool isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions);
       void createLogicalDevice(const vector<const char*>& validationLayers,
-         const vector<const char*>& deviceExtensions); // Only creates the graphics queue. Later, checks that this queue also supports presenting, but this codebase does not seem to support a separate present queue
+         const vector<const char*>& deviceExtensions);
+      void chooseSwapChainProperties();
+      void createSwapChain();
+      void createImageViews();
+      void createRenderPass();
+      VkFormat findDepthFormat(); // TODO: Declare/define (in the cpp file) this function in some util functions section
+      void createResourceCommandPool();
+      void createCommandPools();
+      void createFramebuffers();
+      void createCommandBuffers();
+      void createSyncObjects();
+
+      void recreateSwapChain();
+
+      void cleanupSwapChain();
 
       // Pipeline variables. Hopefully, I can eventually use the GraphicsPipeline_Vulkan class for the imgui pipeline
       VkDescriptorPool descriptorPool;
 
+   // Helper methods from imgui_impl_vulkan that were moved into VulkanGame to give them access to class instance variables
+   public:
+      void FrameRender(ImDrawData* draw_data);
+      void FramePresent();
+
 };
 
-// These functions are helper functions that were used in the ImGui Vulkan+SDL example
-
-void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device,
-   ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h,
-   uint32_t min_image_count);
-
-void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device,
-   ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count);
-
-// End helper functions
-
 #endif // _SDL_GAME_H
Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 3f32dfd9a09b20768e80b46b2a5ee4aea024fdc4)
+++ vulkan-game.cpp	(revision ce9dc9f8d30ef01e9f3c1de05482d87c6c4a1144)
@@ -293,6 +293,8 @@
       check_vk_result(err);
 
-      err = vkDeviceWaitIdle(this->device);
-      check_vk_result(err);
+      if (vkDeviceWaitIdle(this->device) != VK_SUCCESS) {
+         throw runtime_error("failed to wait for device!");
+      }
+
       ImGui_ImplVulkan_DestroyFontUploadObjects();
 
@@ -1193,6 +1195,7 @@
 
 void VulkanGame::cleanup() {
-   // TODO: Should probably check the returned result
-   vkDeviceWaitIdle(device);
+   if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
+      throw runtime_error("failed to wait for device!");
+   }
 
    ImGui_ImplVulkan_Shutdown();
@@ -2120,5 +2123,7 @@
    }
 
-   vkDeviceWaitIdle(device);
+   if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
+      throw runtime_error("failed to wait for device!");
+   }
 
    cleanupSwapChain();
