Index: graphics-pipeline_vulkan.cpp
===================================================================
--- graphics-pipeline_vulkan.cpp	(revision 771b33a137aed1be0c097e401feb8e3727bb1166)
+++ graphics-pipeline_vulkan.cpp	(revision b79417880394af692ba165be4054f0843fdb1c91)
@@ -3,7 +3,14 @@
 #include <fstream>
 #include <stdexcept>
-
-GraphicsPipeline_Vulkan::GraphicsPipeline_Vulkan(VkDevice device, Viewport viewport, int vertexSize) {
+#include <iostream>
+
+using namespace std;
+
+// TODO: Remove any instances of cout and instead throw exceptions
+
+GraphicsPipeline_Vulkan::GraphicsPipeline_Vulkan(VkDevice device, VkRenderPass renderPass, Viewport viewport,
+      int vertexSize) {
    this->device = device;
+   this->renderPass = renderPass;
    this->viewport = viewport;
 
@@ -25,4 +32,12 @@
 
    this->attributeDescriptions.push_back(attributeDesc);
+}
+
+void GraphicsPipeline_Vulkan::addDescriptorInfo(VkDescriptorType type, VkShaderStageFlags stageFlags, vector<VkDescriptorBufferInfo>* bufferData) {
+   this->descriptorInfoList.push_back({ type, stageFlags, bufferData, nullptr });
+}
+
+void GraphicsPipeline_Vulkan::addDescriptorInfo(VkDescriptorType type, VkShaderStageFlags stageFlags, VkDescriptorImageInfo* imageData) {
+   this->descriptorInfoList.push_back({ type, stageFlags, nullptr, imageData });
 }
 
@@ -131,7 +146,119 @@
    pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    pipelineLayoutInfo.setLayoutCount = 1;
-
-   vkDestroyShaderModule(device, vertShaderModule, nullptr);
-   vkDestroyShaderModule(device, fragShaderModule, nullptr);
+   pipelineLayoutInfo.pSetLayouts = &this->descriptorSetLayout;
+   pipelineLayoutInfo.pushConstantRangeCount = 0;
+
+   if (vkCreatePipelineLayout(this->device, &pipelineLayoutInfo, nullptr, &this->pipelineLayout) != VK_SUCCESS) {
+      throw runtime_error("failed to create pipeline layout!");
+   }
+
+   VkGraphicsPipelineCreateInfo pipelineInfo = {};
+   pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+   pipelineInfo.stageCount = 2;
+   pipelineInfo.pStages = shaderStages;
+   pipelineInfo.pVertexInputState = &vertexInputInfo;
+   pipelineInfo.pInputAssemblyState = &inputAssembly;
+   pipelineInfo.pViewportState = &viewportState;
+   pipelineInfo.pRasterizationState = &rasterizer;
+   pipelineInfo.pMultisampleState = &multisampling;
+   pipelineInfo.pDepthStencilState = &depthStencil;
+   pipelineInfo.pColorBlendState = &colorBlending;
+   pipelineInfo.pDynamicState = nullptr;
+   pipelineInfo.layout = this->pipelineLayout;
+   pipelineInfo.renderPass = this->renderPass;
+   pipelineInfo.subpass = 0;
+   pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
+   pipelineInfo.basePipelineIndex = -1;
+
+   if (vkCreateGraphicsPipelines(this->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &this->pipeline) != VK_SUCCESS) {
+      throw runtime_error("failed to create graphics pipeline!");
+   }
+
+   vkDestroyShaderModule(this->device, vertShaderModule, nullptr);
+   vkDestroyShaderModule(this->device, fragShaderModule, nullptr);
+}
+
+void GraphicsPipeline_Vulkan::createDescriptorSetLayout() {
+   vector<VkDescriptorSetLayoutBinding> bindings(this->descriptorInfoList.size());
+
+   for (size_t i = 0; i < bindings.size(); i++) {
+      bindings[i].binding = i;
+      bindings[i].descriptorCount = 1;
+      bindings[i].descriptorType = this->descriptorInfoList[i].type;
+      bindings[i].stageFlags = this->descriptorInfoList[i].stageFlags;
+      bindings[i].pImmutableSamplers = nullptr;
+   }
+
+   VkDescriptorSetLayoutCreateInfo layoutInfo = {};
+   layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+   layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
+   layoutInfo.pBindings = bindings.data();
+
+   if (vkCreateDescriptorSetLayout(this->device, &layoutInfo, nullptr, &this->descriptorSetLayout) != VK_SUCCESS) {
+      throw runtime_error("failed to create descriptor set layout!");
+   }
+}
+
+void GraphicsPipeline_Vulkan::createDescriptorPool(vector<VkImage>& swapChainImages) {
+   vector<VkDescriptorPoolSize> poolSizes(this->descriptorInfoList.size());
+
+   for (size_t i = 0; i < poolSizes.size(); i++) {
+      poolSizes[i].type = this->descriptorInfoList[i].type;
+      poolSizes[i].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
+   }
+
+   VkDescriptorPoolCreateInfo poolInfo = {};
+   poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+   poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
+   poolInfo.pPoolSizes = poolSizes.data();
+   poolInfo.maxSets = static_cast<uint32_t>(swapChainImages.size());
+
+   if (vkCreateDescriptorPool(this->device, &poolInfo, nullptr, &this->descriptorPool) != VK_SUCCESS) {
+      throw runtime_error("failed to create descriptor pool!");
+   }
+}
+
+void GraphicsPipeline_Vulkan::createDescriptorSets(vector<VkImage>& swapChainImages) {
+   vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), this->descriptorSetLayout);
+
+   VkDescriptorSetAllocateInfo allocInfo = {};
+   allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+   allocInfo.descriptorPool = this->descriptorPool;
+   allocInfo.descriptorSetCount = static_cast<uint32_t>(swapChainImages.size());
+   allocInfo.pSetLayouts = layouts.data();
+
+   this->descriptorSets.resize(swapChainImages.size());
+   if (vkAllocateDescriptorSets(device, &allocInfo, this->descriptorSets.data()) != VK_SUCCESS) {
+      throw runtime_error("failed to allocate descriptor sets!");
+   }
+
+   for (size_t i = 0; i < swapChainImages.size(); i++) {
+      vector<VkWriteDescriptorSet> descriptorWrites(this->descriptorInfoList.size());
+
+      for (size_t j = 0; j < descriptorWrites.size(); j++) {
+         descriptorWrites[j].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+         descriptorWrites[j].dstSet = this->descriptorSets[i];
+         descriptorWrites[j].dstBinding = j;
+         descriptorWrites[j].dstArrayElement = 0;
+         descriptorWrites[j].descriptorType = this->descriptorInfoList[j].type;
+         descriptorWrites[j].descriptorCount = 1;
+         descriptorWrites[j].pBufferInfo = nullptr;
+         descriptorWrites[j].pImageInfo = nullptr;
+         descriptorWrites[j].pTexelBufferView = nullptr;
+
+         switch (descriptorWrites[j].descriptorType) {
+            case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+               descriptorWrites[j].pBufferInfo = &(*this->descriptorInfoList[j].bufferDataList)[i];
+               break;
+            case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+               descriptorWrites[j].pImageInfo = this->descriptorInfoList[j].imageData;
+               break;
+            default:
+               cout << "Unknown descriptor type: " << descriptorWrites[j].descriptorType << endl;
+         }
+      }
+
+      vkUpdateDescriptorSets(this->device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
+   }
 }
 
@@ -143,5 +270,5 @@
 
    VkShaderModule shaderModule;
-   if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
+   if (vkCreateShaderModule(this->device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
       throw runtime_error("failed to create shader module!");
    }
@@ -167,2 +294,12 @@
    return buffer;
 }
+
+void GraphicsPipeline_Vulkan::cleanup() {
+   vkDestroyPipeline(this->device, this->pipeline, nullptr);
+   vkDestroyDescriptorPool(this->device, this->descriptorPool, nullptr);
+   vkDestroyPipelineLayout(this->device, this->pipelineLayout, nullptr);
+}
+
+void GraphicsPipeline_Vulkan::cleanupBuffers() {
+   vkDestroyDescriptorSetLayout(this->device, this->descriptorSetLayout, nullptr);
+}
