Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 80de39d7bf67abe6677279753262dff6c2f42318)
+++ vulkan-game.cpp	(revision 909b51a30b250501c0b2977cede5b8ce89f45278)
@@ -15,4 +15,5 @@
 #include <stdexcept>
 #include <cstdlib>
+#include <optional>
 
 #include "game-gui-sdl.hpp"
@@ -33,4 +34,12 @@
    const bool enableValidationLayers = true;
 #endif
+
+struct QueueFamilyIndices {
+    optional<uint32_t> graphicsFamily;
+
+    bool isComplete() {
+        return graphicsFamily.has_value();
+    }
+};
 
 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
@@ -85,4 +94,7 @@
       VkInstance instance;
       VkDebugUtilsMessengerEXT debugMessenger;
+      VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
+      VkDevice device;
+      VkQueue graphicsQueue;
 
       // both SDL and GLFW create window functions return NULL on failure
@@ -113,4 +125,6 @@
          createInstance();
          setupDebugMessenger();
+         pickPhysicalDevice();
+         createLogicalDevice();
       }
 
@@ -154,4 +168,143 @@
       }
 
+      void setupDebugMessenger() {
+         if (!enableValidationLayers) return;
+
+         VkDebugUtilsMessengerCreateInfoEXT createInfo;
+         populateDebugMessengerCreateInfo(createInfo);
+
+         if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
+            throw runtime_error("failed to setup debug messenger!");
+         }
+      }
+
+      void pickPhysicalDevice() {
+         uint32_t deviceCount = 0;
+         vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
+
+         if (deviceCount == 0) {
+            throw runtime_error("failed to find GPUs with Vulkan support!");
+         }
+
+         vector<VkPhysicalDevice> devices(deviceCount);
+         vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
+
+         cout << endl << "Graphics cards:" << endl;
+         for (const VkPhysicalDevice& device : devices) {
+            if (isDeviceSuitable(device)) {
+               physicalDevice = device;
+               break;
+            }
+         }
+         cout << endl;
+
+         if (physicalDevice == VK_NULL_HANDLE) {
+            throw runtime_error("failed to find a suitable GPU!");
+         }
+      }
+
+      bool isDeviceSuitable(VkPhysicalDevice device) {
+         VkPhysicalDeviceProperties deviceProperties;
+         VkPhysicalDeviceFeatures deviceFeatures;
+
+         vkGetPhysicalDeviceProperties(device, &deviceProperties);
+         vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
+
+         cout << "Device: " << deviceProperties.deviceName << endl;
+
+         QueueFamilyIndices indices = findQueueFamilies(device);
+
+         return indices.isComplete();
+      }
+
+      void createLogicalDevice() {
+         QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
+
+         VkDeviceQueueCreateInfo queueCreateInfo = {};
+         queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+         queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
+         queueCreateInfo.queueCount = 1;
+
+         float queuePriority = 1.0f;
+         queueCreateInfo.pQueuePriorities = &queuePriority;
+
+         VkPhysicalDeviceFeatures deviceFeatures = {};
+
+         VkDeviceCreateInfo createInfo = {};
+         createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+
+         createInfo.pQueueCreateInfos = &queueCreateInfo;
+         createInfo.queueCreateInfoCount = 1;
+
+         createInfo.pEnabledFeatures = &deviceFeatures;
+
+         createInfo.enabledExtensionCount = 0;
+
+         // These fields are ignored  by up-to-date Vulkan implementations,
+         // but it's a good idea to set them for backwards compatibility
+         if (enableValidationLayers) {
+            createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
+            createInfo.ppEnabledLayerNames = validationLayers.data();
+         } else {
+            createInfo.enabledLayerCount = 0;
+         }
+
+         if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
+            throw runtime_error("failed to create logical device!");
+         }
+
+         vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
+      }
+
+      bool checkValidationLayerSupport() {
+         uint32_t layerCount;
+         vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
+
+         vector<VkLayerProperties> availableLayers(layerCount);
+         vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
+
+         for (const char* layerName : validationLayers) {
+            bool layerFound = false;
+
+            for (const auto& layerProperties : availableLayers) {
+               if (strcmp(layerName, layerProperties.layerName) == 0) {
+                  layerFound = true;
+                  break;
+               }
+            }
+
+            if (!layerFound) {
+               return false;
+            }
+         }
+
+         return true;
+      }
+
+      QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
+         QueueFamilyIndices indices;
+
+         uint32_t queueFamilyCount = 0;
+         vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
+
+         vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
+         vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
+
+         int i = 0;
+         for (const auto& queueFamily : queueFamilies) {
+            if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+               indices.graphicsFamily = i;
+            }
+
+            if (indices.isComplete()) {
+               break;
+            }
+
+            i++;
+         }
+
+         return indices;
+      }
+
       void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
          createInfo = {};
@@ -160,40 +313,4 @@
          createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
          createInfo.pfnUserCallback = debugCallback;
-      }
-
-      void setupDebugMessenger() {
-         if (!enableValidationLayers) return;
-
-         VkDebugUtilsMessengerCreateInfoEXT createInfo;
-         populateDebugMessengerCreateInfo(createInfo);
-
-         if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
-            throw runtime_error("failed to setup debug messenger!");
-         }
-      }
-
-      bool checkValidationLayerSupport() {
-         uint32_t layerCount;
-         vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
-
-         vector<VkLayerProperties> availableLayers(layerCount);
-         vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
-
-         for (const char* layerName : validationLayers) {
-            bool layerFound = false;
-
-            for (const auto& layerProperties : availableLayers) {
-               if (strcmp(layerName, layerProperties.layerName) == 0) {
-                  layerFound = true;
-                  break;
-               }
-            }
-
-            if (!layerFound) {
-               return false;
-            }
-         }
-
-         return true;
       }
 
@@ -252,4 +369,6 @@
 
       void cleanup() {
+         vkDestroyDevice(device, nullptr);
+
          if (enableValidationLayers) {
             DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
