Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 771b33a137aed1be0c097e401feb8e3727bb1166)
+++ vulkan-game.cpp	(revision b79417880394af692ba165be4054f0843fdb1c91)
@@ -9,7 +9,12 @@
 
 #include "utils.hpp"
-#include "vulkan-utils.hpp"
 
 using namespace std;
+
+struct UniformBufferObject {
+   alignas(16) mat4 model;
+   alignas(16) mat4 view;
+   alignas(16) mat4 proj;
+};
 
 VulkanGame::VulkanGame() {
@@ -84,4 +89,25 @@
       cout << gui->getError() << endl;
       return RTWO_ERROR;
+   }
+
+   SDL_VERSION(&sdlVersion);
+
+   // In SDL 2.0.10 (currently, the latest), SDL_TEXTUREACCESS_TARGET is required to get a transparent overlay working
+   // However, the latest SDL version available through homebrew on Mac is 2.0.9, which requires SDL_TEXTUREACCESS_STREAMING
+   // I tried building sdl 2.0.10 (and sdl_image and sdl_ttf) from source on Mac, but had some issues, so this is easier
+   // until the homebrew recipe is updated
+   if (sdlVersion.major == 2 && sdlVersion.minor == 0 && sdlVersion.patch == 9) {
+      uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING,
+         gui->getWindowWidth(), gui->getWindowHeight());
+   } else {
+      uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
+         gui->getWindowWidth(), gui->getWindowHeight());
+   }
+
+   if (uiOverlay == nullptr) {
+      cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << endl;
+   }
+   if (SDL_SetTextureBlendMode(uiOverlay, SDL_BLENDMODE_BLEND) != 0) {
+      cout << "Unable to set texture blend mode! SDL Error: " << SDL_GetError() << endl;
    }
 
@@ -107,5 +133,7 @@
    createCommandPool();
 
-   graphicsPipelines.push_back(GraphicsPipeline_Vulkan(device, viewport, sizeof(Vertex)));
+   createVulkanResources();
+
+   graphicsPipelines.push_back(GraphicsPipeline_Vulkan(device, renderPass, viewport, sizeof(Vertex)));
 
    graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&Vertex::pos));
@@ -113,12 +141,26 @@
    graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&Vertex::texCoord));
 
+   graphicsPipelines.back().addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+      VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList);
+   graphicsPipelines.back().addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+      VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
+
+   graphicsPipelines.back().createDescriptorSetLayout();
    graphicsPipelines.back().createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
-
-   graphicsPipelines.push_back(GraphicsPipeline_Vulkan(device, viewport, sizeof(OverlayVertex)));
+   graphicsPipelines.back().createDescriptorPool(swapChainImages);
+   graphicsPipelines.back().createDescriptorSets(swapChainImages);
+
+   graphicsPipelines.push_back(GraphicsPipeline_Vulkan(device, renderPass, viewport, sizeof(OverlayVertex)));
 
    graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&OverlayVertex::pos));
    graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&OverlayVertex::texCoord));
 
+   graphicsPipelines.back().addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+      VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
+
+   graphicsPipelines.back().createDescriptorSetLayout();
    graphicsPipelines.back().createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
+   graphicsPipelines.back().createDescriptorPool(swapChainImages);
+   graphicsPipelines.back().createDescriptorSets(swapChainImages);
 
    cout << "Created " << graphicsPipelines.size() << " graphics pipelines" << endl;
@@ -189,4 +231,8 @@
    cleanupSwapChain();
 
+   for (GraphicsPipeline_Vulkan pipeline : graphicsPipelines) {
+      pipeline.cleanupBuffers();
+   }
+
    vkDestroyCommandPool(device, commandPool, nullptr);
    vkDestroyDevice(device, nullptr);
@@ -198,4 +244,12 @@
 
    vkDestroyInstance(instance, nullptr);
+
+   // TODO: Check if any of these functions accept null parameters
+   // If they do, I don't need to check for that
+
+   if (uiOverlay != nullptr) {
+      SDL_DestroyTexture(uiOverlay);
+      uiOverlay = nullptr;
+   }
 
    SDL_DestroyRenderer(renderer);
@@ -343,5 +397,5 @@
    QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
 
-   vector<VkDeviceQueueCreateInfo> queueCreateInfos;
+   vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
    set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
 
@@ -354,5 +408,5 @@
       queueCreateInfo.pQueuePriorities = &queuePriority;
 
-      queueCreateInfos.push_back(queueCreateInfo);
+      queueCreateInfoList.push_back(queueCreateInfo);
    }
 
@@ -362,6 +416,6 @@
    VkDeviceCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
-   createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
-   createInfo.pQueueCreateInfos = queueCreateInfos.data();
+   createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
+   createInfo.pQueueCreateInfos = queueCreateInfoList.data();
 
    createInfo.pEnabledFeatures = &deviceFeatures;
@@ -529,8 +583,77 @@
 }
 
+void VulkanGame::createVulkanResources() {
+   createTextureSampler();
+   createUniformBuffers();
+
+   // TODO: Make sure that Vulkan complains about these images not being destroyed and then destroy them
+
+   VulkanUtils::createVulkanImageFromFile(device, physicalDevice, commandPool, "textures/texture.jpg",
+      floorTextureImage, graphicsQueue);
+   VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
+
+   floorTextureImageDescriptor = {};
+   floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+   floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
+   floorTextureImageDescriptor.sampler = textureSampler;
+
+   sdlOverlayImageDescriptor = {};
+   sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+   sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
+   sdlOverlayImageDescriptor.sampler = textureSampler;
+}
+
+void VulkanGame::createTextureSampler() {
+   VkSamplerCreateInfo samplerInfo = {};
+   samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+   samplerInfo.magFilter = VK_FILTER_LINEAR;
+   samplerInfo.minFilter = VK_FILTER_LINEAR;
+
+   samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+   samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+   samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+
+   samplerInfo.anisotropyEnable = VK_TRUE;
+   samplerInfo.maxAnisotropy = 16;
+   samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
+   samplerInfo.unnormalizedCoordinates = VK_FALSE;
+   samplerInfo.compareEnable = VK_FALSE;
+   samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
+   samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+   samplerInfo.mipLodBias = 0.0f;
+   samplerInfo.minLod = 0.0f;
+   samplerInfo.maxLod = 0.0f;
+
+   if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
+      throw runtime_error("failed to create texture sampler!");
+   }
+}
+
+void VulkanGame::createUniformBuffers() {
+   VkDeviceSize bufferSize = sizeof(UniformBufferObject);
+
+   uniformBuffers.resize(swapChainImages.size());
+   uniformBuffersMemory.resize(swapChainImages.size());
+   uniformBufferInfoList.resize(swapChainImages.size());
+
+   for (size_t i = 0; i < swapChainImages.size(); i++) {
+      VulkanUtils::createBuffer(device, physicalDevice, bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+         uniformBuffers[i], uniformBuffersMemory[i]);
+
+      uniformBufferInfoList[i].buffer = uniformBuffers[i];
+      uniformBufferInfoList[i].offset = 0;
+      uniformBufferInfoList[i].range = sizeof(UniformBufferObject);
+   }
+}
+
 void VulkanGame::cleanupSwapChain() {
+   for (GraphicsPipeline_Vulkan pipeline : graphicsPipelines) {
+      pipeline.cleanup();
+   }
+
    vkDestroyRenderPass(device, renderPass, nullptr);
 
-   for (auto imageView : swapChainImageViews) {
+   for (VkImageView imageView : swapChainImageViews) {
       vkDestroyImageView(device, imageView, nullptr);
    }
