Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 321272cadfb22c7e05cf9b950ac4edd369ffa975)
+++ vulkan-game.cpp	(revision bfd620e370dc06281e31f8a0451c885e1aecb45e)
@@ -17,16 +17,13 @@
 #include <cstdlib>
 #include <optional>
+#include <algorithm>
 
 #include "game-gui-sdl.hpp"
 
 using namespace std;
-using namespace glm;
+//using namespace glm;
 
 const int SCREEN_WIDTH = 800;
 const int SCREEN_HEIGHT = 600;
-
-const vector<const char*> validationLayers = {
-    "VK_LAYER_KHRONOS_validation"
-};
 
 #ifdef NDEBUG
@@ -36,4 +33,12 @@
 #endif
 
+const vector<const char*> validationLayers = {
+   "VK_LAYER_KHRONOS_validation"
+};
+
+const vector<const char*> deviceExtensions = {
+   VK_KHR_SWAPCHAIN_EXTENSION_NAME
+};
+
 struct QueueFamilyIndices {
     optional<uint32_t> graphicsFamily;
@@ -43,4 +48,10 @@
         return graphicsFamily.has_value() && presentFamily.has_value();
     }
+};
+
+struct SwapChainSupportDetails {
+    VkSurfaceCapabilitiesKHR capabilities;
+    vector<VkSurfaceFormatKHR> formats;
+    vector<VkPresentModeKHR> presentModes;
 };
 
@@ -104,4 +115,11 @@
       VkQueue graphicsQueue;
       VkQueue presentQueue;
+
+      VkSwapchainKHR swapChain;
+      vector<VkImage> swapChainImages;
+      VkFormat swapChainImageFormat;
+      VkExtent2D swapChainExtent;
+
+      vector<VkImageView> swapChainImageViews;
 
       // both SDL and GLFW create window functions return NULL on failure
@@ -135,4 +153,6 @@
          pickPhysicalDevice();
          createLogicalDevice();
+         createSwapChain();
+         createImageViews();
       }
 
@@ -247,5 +267,30 @@
          QueueFamilyIndices indices = findQueueFamilies(device);
 
-         return indices.isComplete();
+         bool extensionsSupported = checkDeviceExtensionSupport(device);
+
+         bool swapChainAdequate = false;
+
+         if (extensionsSupported) {
+            SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
+            swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
+         }
+
+         return indices.isComplete() && extensionsSupported && swapChainAdequate;
+      }
+
+      bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
+         uint32_t extensionCount;
+         vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
+
+         vector<VkExtensionProperties> availableExtensions(extensionCount);
+         vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
+
+         set<string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
+
+         for (const auto& extension : availableExtensions) {
+            requiredExtensions.erase(extension.extensionName);
+         }
+
+         return requiredExtensions.empty();
       }
 
@@ -278,5 +323,6 @@
          createInfo.pEnabledFeatures = &deviceFeatures;
 
-         createInfo.enabledExtensionCount = 0;
+         createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
+         createInfo.ppEnabledExtensionNames = deviceExtensions.data();
 
          // These fields are ignored  by up-to-date Vulkan implementations,
@@ -354,4 +400,65 @@
       }
 
+      SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
+         SwapChainSupportDetails details;
+
+         vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
+
+         uint32_t formatCount;
+         vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
+
+         if (formatCount != 0) {
+            details.formats.resize(formatCount);
+            vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
+         }
+
+         uint32_t presentModeCount;
+         vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
+
+         if (presentModeCount != 0) {
+            details.presentModes.resize(presentModeCount);
+            vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
+         }
+
+         return details;
+      }
+
+      VkSurfaceFormatKHR chooseSwapSurfaceFormat(const vector<VkSurfaceFormatKHR>& availableFormats) {
+         for (const auto& availableFormat : availableFormats) {
+            if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
+               return availableFormat;
+            }
+         }
+
+         return availableFormats[0];
+      }
+
+      VkPresentModeKHR chooseSwapPresentMode(const vector<VkPresentModeKHR>& availablePresentModes) {
+         VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;
+
+         for (const auto& availablePresentMode : availablePresentModes) {
+            if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
+               return availablePresentMode;
+            } else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
+               bestMode = availablePresentMode;
+            }
+         }
+
+         return bestMode;
+      }
+
+      VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
+         if (capabilities.currentExtent.width != numeric_limits<uint32_t>::max()) {
+            return capabilities.currentExtent;
+         } else {
+            VkExtent2D actualExtent = { SCREEN_WIDTH, SCREEN_HEIGHT };
+
+            actualExtent.width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, actualExtent.width));
+            actualExtent.height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, actualExtent.height));
+
+            return actualExtent;
+         }
+      }
+
       void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
          createInfo = {};
@@ -374,4 +481,87 @@
 
          return extensions;
+      }
+
+      void createSwapChain() {
+         SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
+
+         VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
+         VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
+         VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
+
+         uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
+         if (swapChainSupport.capabilities.maxImageCount > 0 &&
+               imageCount > swapChainSupport.capabilities.maxImageCount) {
+            imageCount = swapChainSupport.capabilities.maxImageCount;
+         }
+
+         VkSwapchainCreateInfoKHR createInfo = {};
+
+         createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+         createInfo.surface = surface;
+         createInfo.minImageCount = imageCount;
+         createInfo.imageFormat = surfaceFormat.format;
+         createInfo.imageColorSpace = surfaceFormat.colorSpace;
+         createInfo.imageExtent = extent;
+         createInfo.imageArrayLayers = 1;
+         createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+
+         QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
+         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; // Optional
+            createInfo.pQueueFamilyIndices = nullptr;
+         }
+
+         createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
+         createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+         createInfo.presentMode = presentMode;
+         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!");
+         }
+
+         vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
+         swapChainImages.resize(imageCount);
+         vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
+
+         swapChainImageFormat = surfaceFormat.format;
+         swapChainExtent = extent;
+      }
+
+      void createImageViews() {
+         swapChainImageViews.resize(swapChainImages.size());
+
+         for (size_t i=0; i<swapChainImages.size(); i++) {
+            VkImageViewCreateInfo createInfo = {};
+            createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+            createInfo.image = swapChainImages[i];
+
+            createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+            createInfo.format = swapChainImageFormat;
+
+            createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+            createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+            createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+            createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+
+            createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+            createInfo.subresourceRange.baseMipLevel = 0;
+            createInfo.subresourceRange.levelCount = 1;
+            createInfo.subresourceRange.baseArrayLayer = 0;
+            createInfo.subresourceRange.layerCount = 1;
+
+            if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
+               throw runtime_error("failed to create image views!");
+            }
+         }
       }
 
@@ -392,13 +582,20 @@
                   quit = true;
                }
-            }
-         }
-
-         SDL_FillRect(sdlSurface, nullptr, SDL_MapRGB(sdlSurface->format, 0xFF, 0xFF, 0xFF));
-
-         SDL_UpdateWindowSurface(window);
+
+               /*
+               SDL_FillRect(sdlSurface, nullptr, SDL_MapRGB(sdlSurface->format, 0xFF, 0xFF, 0xFF));
+
+               SDL_UpdateWindowSurface(window);
+               */
+            }
+         }
       }
 
       void cleanup() {
+         for (auto imageView : swapChainImageViews) {
+            vkDestroyImageView(device, imageView, nullptr);
+         }
+
+         vkDestroySwapchainKHR(device, swapChain, nullptr);
          vkDestroyDevice(device, nullptr);
 
@@ -426,7 +623,7 @@
 #endif
 
-   mat4 matrix;
-   vec4 vec;
-   vec4 test = matrix * vec;
+   glm::mat4 matrix;
+   glm::vec4 vec;
+   glm::vec4 test = matrix * vec;
 
    cout << "Starting Vulkan game..." << endl;
