Browse Source

stop hardcoding vulkan vertex layouts

niki 3 years ago
parent
commit
b5ba2b65b5

+ 2 - 0
CMakeLists.txt

@@ -606,6 +606,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_VULKAN
 	src/modules/graphics/vulkan/Buffer.cpp
 	src/modules/graphics/vulkan/Buffer.cpp
 	src/modules/graphics/vulkan/Texture.h
 	src/modules/graphics/vulkan/Texture.h
 	src/modules/graphics/vulkan/Texture.cpp
 	src/modules/graphics/vulkan/Texture.cpp
+	src/modules/graphics/vulkan/Vulkan.h
+	src/modules/graphics/vulkan/Vulkan.cpp
 	src/modules/graphics/vulkan/vk_mem_alloc.h
 	src/modules/graphics/vulkan/vk_mem_alloc.h
 	src/modules/graphics/vulkan/UseVMA.cpp
 	src/modules/graphics/vulkan/UseVMA.cpp
 )
 )

+ 159 - 56
src/modules/graphics/vulkan/Graphics.cpp

@@ -5,6 +5,7 @@
 #include "common/Exception.h"
 #include "common/Exception.h"
 #include "Shader.h"
 #include "Shader.h"
 #include "graphics/Texture.h"
 #include "graphics/Texture.h"
+#include "Vulkan.h"
 
 
 #include <vector>
 #include <vector>
 #include <cstring>
 #include <cstring>
@@ -112,7 +113,7 @@ namespace love {
 				const auto& commandBuffer = commandBuffers.at(imageIndex);
 				const auto& commandBuffer = commandBuffers.at(imageIndex);
 
 
 				vkCmdBeginRenderPass(commandBuffers.at(imageIndex), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
 				vkCmdBeginRenderPass(commandBuffers.at(imageIndex), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
-				vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
+				currentGraphicsPipeline = VK_NULL_HANDLE;
 			}
 			}
 
 
 			void Graphics::endRecordingGraphicsCommands() {
 			void Graphics::endRecordingGraphicsCommands() {
@@ -215,7 +216,6 @@ namespace love {
 				createRenderPass();
 				createRenderPass();
 				createDefaultShaders();
 				createDefaultShaders();
 				createDescriptorSetLayout();
 				createDescriptorSetLayout();
-				createGraphicsPipeline();
 				createFramebuffers();
 				createFramebuffers();
 				createCommandPool();
 				createCommandPool();
 				createCommandBuffers();
 				createCommandBuffers();
@@ -313,17 +313,19 @@ namespace love {
 				std::vector<VkBuffer> buffers;
 				std::vector<VkBuffer> buffers;
 				std::vector<VkDeviceSize> offsets;
 				std::vector<VkDeviceSize> offsets;
 
 
-				// vertices
-				buffers.push_back((VkBuffer)cmd.buffers->info[0].buffer->getHandle());
-				offsets.push_back((VkDeviceSize)cmd.buffers->info[0].offset);
+				bool useConstantColorBuffer;
+				GraphicsPipelineConfiguration configuration;
+				createVulkanVertexFormat(*cmd.attributes, useConstantColorBuffer, configuration);
 
 
-				// tex coords
-				buffers.push_back((VkBuffer)cmd.buffers->info[1].buffer->getHandle());
-				offsets.push_back((VkDeviceSize)cmd.buffers->info[1].offset);
+				for (uint32_t i = 0; i < 2; i++) {
+					buffers.push_back((VkBuffer)cmd.buffers->info[i].buffer->getHandle());
+					offsets.push_back((VkDeviceSize)cmd.buffers->info[i].offset);
+				}
 
 
-				//constant color
-				buffers.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle());
-				offsets.push_back((VkDeviceSize)0);
+				if (useConstantColorBuffer) {
+					buffers.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle());
+					offsets.push_back((VkDeviceSize)0);
+				}
 
 
 				if (cmd.texture == nullptr) {
 				if (cmd.texture == nullptr) {
 					setTexture(standardTexture);
 					setTexture(standardTexture);
@@ -332,6 +334,8 @@ namespace love {
 					setTexture(cmd.texture);
 					setTexture(cmd.texture);
 				}
 				}
 
 
+				ensureGraphicsPipelineConfiguration(configuration);
+
 				vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, getDescriptorSet(currentFrame), 0, nullptr);
 				vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, getDescriptorSet(currentFrame), 0, nullptr);
 				vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, buffers.size(), buffers.data(), offsets.data());
 				vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, buffers.size(), buffers.data(), offsets.data());
 				vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), 0, getVulkanIndexBufferType(cmd.indexType));
 				vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), 0, getVulkanIndexBufferType(cmd.indexType));
@@ -930,7 +934,7 @@ namespace love {
 				uboLayoutBinding.binding = 0;
 				uboLayoutBinding.binding = 0;
 				uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 				uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 				uboLayoutBinding.descriptorCount = 1;
 				uboLayoutBinding.descriptorCount = 1;
-				uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+				uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
 
 
 				VkDescriptorSetLayoutBinding samplerLayoutBinding{};
 				VkDescriptorSetLayoutBinding samplerLayoutBinding{};
 				samplerLayoutBinding.binding = 1;
 				samplerLayoutBinding.binding = 1;
@@ -970,6 +974,8 @@ namespace love {
 				poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
 				poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
 				poolInfo.pPoolSizes = poolSizes.data();
 				poolInfo.pPoolSizes = poolSizes.data();
 				// FIXME: When using more than 128 textures at once we will run out of memory.
 				// FIXME: When using more than 128 textures at once we will run out of memory.
+				// we probably want to reuse descriptors per flight image
+				// and use multiple pools in case of too many allocations
 				poolInfo.maxSets = 128 * static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
 				poolInfo.maxSets = 128 * static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
 
 
 				if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
 				if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
@@ -1039,53 +1045,85 @@ namespace love {
 				return newDescriptorSets;
 				return newDescriptorSets;
 			}
 			}
 
 
-			void Graphics::createGraphicsPipeline() {
+			void Graphics::createVulkanVertexFormat(
+				VertexAttributes vertexAttributes, 
+				bool& useConstantVertexColor,
+				GraphicsPipelineConfiguration& configuration) {
+				std::set<uint32_t> usedBuffers;
+				std::vector<VkVertexInputBindingDescription> bindingDescriptions;
+				std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
+
+				auto allBits = vertexAttributes.enableBits;
+
+				bool usesColor = false;
+				
+				for (uint32_t i = 0; i < 32; i++) {	// change to loop like in opengl implementation ?
+					uint32 bit = 1u << i;
+					if (allBits & bit) {
+						if (i == ATTRIB_COLOR) {
+							usesColor = true;
+						}
+
+						auto attrib = vertexAttributes.attribs[i];
+						auto bufferBinding = attrib.bufferIndex;
+						if (usedBuffers.find(bufferBinding) == usedBuffers.end()) {	// use .contains() when c++20 is enabled
+							usedBuffers.insert(bufferBinding);
+
+							VkVertexInputBindingDescription bindingDescription{};
+							bindingDescription.binding = bufferBinding;
+							bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+							bindingDescription.stride = vertexAttributes.bufferLayouts[bufferBinding].stride;
+							bindingDescriptions.push_back(bindingDescription);
+						}
+
+						VkVertexInputAttributeDescription attributeDescription{};
+						attributeDescription.location = i;
+						attributeDescription.binding = bufferBinding;
+						attributeDescription.offset = attrib.offsetFromVertex;
+						attributeDescription.format = Vulkan::getVulkanVertexFormat(attrib.format);
+
+						attributeDescriptions.push_back(attributeDescription);
+					}
+				}
+
+				// do we need to use a constant VertexColor?
+				if (!usesColor) {
+					constexpr uint32_t constantColorBufferBinding = 2;
+
+					VkVertexInputBindingDescription bindingDescription{};
+					bindingDescription.binding = constantColorBufferBinding;
+					bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+					bindingDescription.stride = 0;	// no stride, will always read the same color multiple times.
+					bindingDescriptions.push_back(bindingDescription);
+
+					VkVertexInputAttributeDescription attributeDescription{};
+					attributeDescription.binding = constantColorBufferBinding;
+					attributeDescription.location = ATTRIB_COLOR;
+					attributeDescription.offset = 0;
+					attributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT;
+
+					useConstantVertexColor = true;
+				}
+				else {
+					useConstantVertexColor = false;
+				}
+
+				configuration.vertexInputBindingDescriptions = bindingDescriptions;
+				configuration.vertexInputAttributeDescriptions = attributeDescriptions;
+			}
+
+			VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
+
 				auto shader = reinterpret_cast<love::graphics::vulkan::Shader*>(love::graphics::vulkan::Shader::standardShaders[Shader::STANDARD_DEFAULT]);
 				auto shader = reinterpret_cast<love::graphics::vulkan::Shader*>(love::graphics::vulkan::Shader::standardShaders[Shader::STANDARD_DEFAULT]);
 				auto shaderStages = shader->getShaderStages();
 				auto shaderStages = shader->getShaderStages();
 
 
 				VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
 				VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
 				vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
 				vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
 
 
-				VkVertexInputBindingDescription vertexBindingDescription;
-				vertexBindingDescription.binding = 0;
-				vertexBindingDescription.stride = 2 * sizeof(float);	// just position for now
-				vertexBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
-
-				VkVertexInputAttributeDescription positionInputAttributeDescription;
-				positionInputAttributeDescription.binding = 0;
-				positionInputAttributeDescription.location = 0;
-				positionInputAttributeDescription.format = VK_FORMAT_R32G32_SFLOAT;
-				positionInputAttributeDescription.offset = 0;
-
-				VkVertexInputBindingDescription texCoordsBindingDescription;
-				texCoordsBindingDescription.binding = 1;
-				texCoordsBindingDescription.stride = sizeof(STf_RGBAub);
-				texCoordsBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
-
-				VkVertexInputAttributeDescription texCoordsInputAttributeDescription;
-				texCoordsInputAttributeDescription.binding = 1;
-				texCoordsInputAttributeDescription.location = 1;
-				texCoordsInputAttributeDescription.format = VK_FORMAT_R32G32_SFLOAT;
-				texCoordsInputAttributeDescription.offset = 0;
-
-				VkVertexInputBindingDescription constantColorBindingDescription;
-				constantColorBindingDescription.binding = 2;
-				constantColorBindingDescription.stride = 0;
-				constantColorBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
-
-				VkVertexInputAttributeDescription constantColorInputAttributeDescription;
-				constantColorInputAttributeDescription.binding = 2;
-				constantColorInputAttributeDescription.location = 2;
-				constantColorInputAttributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT;
-				constantColorInputAttributeDescription.offset = 0;
-
-				std::vector<VkVertexInputBindingDescription> bindingDescriptions = { vertexBindingDescription, texCoordsBindingDescription, constantColorBindingDescription };
-				std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions = { positionInputAttributeDescription, texCoordsInputAttributeDescription, constantColorInputAttributeDescription };
-
-				vertexInputInfo.vertexBindingDescriptionCount = bindingDescriptions.size();
-				vertexInputInfo.pVertexBindingDescriptions = bindingDescriptions.data();
-				vertexInputInfo.vertexAttributeDescriptionCount = vertexInputAttributeDescriptions.size();
-				vertexInputInfo.pVertexAttributeDescriptions = vertexInputAttributeDescriptions.data();
+				vertexInputInfo.vertexBindingDescriptionCount = configuration.vertexInputBindingDescriptions.size();
+				vertexInputInfo.pVertexBindingDescriptions = configuration.vertexInputBindingDescriptions.data();
+				vertexInputInfo.vertexAttributeDescriptionCount = configuration.vertexInputAttributeDescriptions.size();
+				vertexInputInfo.pVertexAttributeDescriptions = configuration.vertexInputAttributeDescriptions.data();
 
 
 				VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
 				VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
 				inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
 				inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
@@ -1176,9 +1214,32 @@ namespace love {
 				pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 				pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 				pipelineInfo.basePipelineIndex = -1;
 				pipelineInfo.basePipelineIndex = -1;
 
 
+				VkPipeline graphicsPipeline;
 				if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
 				if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
 					throw love::Exception("failed to create graphics pipeline");
 					throw love::Exception("failed to create graphics pipeline");
 				}
 				}
+				return graphicsPipeline;
+			}
+
+			void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration configuration) {
+				VkPipeline pipeline = VK_NULL_HANDLE;
+				for (auto const& p : graphicsPipelines) {
+					if (p.first == configuration) {
+						pipeline = p.second;
+						break;
+					}
+				}
+				if (pipeline != VK_NULL_HANDLE) {
+					if (currentGraphicsPipeline != pipeline) {
+						vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+						currentGraphicsPipeline = pipeline;
+					}
+				} else {
+					VkPipeline newPipeLine = createGraphicsPipeline(configuration);
+					graphicsPipelines.push_back(std::make_pair(configuration, newPipeLine));
+					vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, newPipeLine);
+					currentGraphicsPipeline = newPipeLine;
+				}
 			}
 			}
 
 
 			void Graphics::createFramebuffers() {
 			void Graphics::createFramebuffers() {
@@ -1257,6 +1318,45 @@ namespace love {
 				standardTexture = newTexture(settings);
 				standardTexture = newTexture(settings);
 			}
 			}
 
 
+			bool operator==(const Graphics::GraphicsPipelineConfiguration& first, const Graphics::GraphicsPipelineConfiguration& other) {
+				if (first.vertexInputAttributeDescriptions.size() != other.vertexInputAttributeDescriptions.size()) {
+					return false;
+				}
+				if (first.vertexInputBindingDescriptions.size() != other.vertexInputBindingDescriptions.size()) {
+					return false;
+				}
+				for (uint32_t i = 0; i < first.vertexInputAttributeDescriptions.size(); i++) {
+					const VkVertexInputAttributeDescription& x = first.vertexInputAttributeDescriptions[i];
+					const VkVertexInputAttributeDescription& y = other.vertexInputAttributeDescriptions[i];
+					if (x.binding != y.binding) {
+						return false;
+					}
+					if (x.location != y.location) {
+						return false;
+					}
+					if (x.offset != y.offset) {
+						return false;
+					}
+					if (x.format != y.format) {
+						return false;
+					}
+				}
+				for (uint32_t i = 0; i < first.vertexInputBindingDescriptions.size(); i++) {
+					const VkVertexInputBindingDescription& x = first.vertexInputBindingDescriptions[i];
+					const VkVertexInputBindingDescription& y = other.vertexInputBindingDescriptions[i];
+					if (x.binding != y.binding) {
+						return false;
+					}
+					if (x.inputRate != y.inputRate) {
+						return false;
+					}
+					if (x.stride != y.stride) {
+						return false;
+					}
+				}
+				return true;
+			}
+
 			void Graphics::cleanup() {
 			void Graphics::cleanup() {
 				vkDeviceWaitIdle(device);
 				vkDeviceWaitIdle(device);
 
 
@@ -1284,8 +1384,12 @@ namespace love {
 					vkDestroyFramebuffer(device, swapChainFramBuffers[i], nullptr);
 					vkDestroyFramebuffer(device, swapChainFramBuffers[i], nullptr);
 				}
 				}
 				vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
 				vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
-				vkDestroyPipeline(device, graphicsPipeline, nullptr);
-				vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
+				for (auto const& p : graphicsPipelines) {
+					vkDestroyPipeline(device, p.second, nullptr);
+				}
+				graphicsPipelines.clear();
+				currentGraphicsPipeline = VK_NULL_HANDLE;
+				// vkDestroyPipelineLayout(device, pipelineLayout, nullptr); FIXME
 				vkDestroyRenderPass(device, renderPass, nullptr);
 				vkDestroyRenderPass(device, renderPass, nullptr);
 				for (size_t i = 0; i < swapChainImageViews.size(); i++) {
 				for (size_t i = 0; i < swapChainImageViews.size(); i++) {
 					vkDestroyImageView(device, swapChainImageViews[i], nullptr);
 					vkDestroyImageView(device, swapChainImageViews[i], nullptr);
@@ -1303,7 +1407,6 @@ namespace love {
 				createSwapChain();
 				createSwapChain();
 				createImageViews();
 				createImageViews();
 				createRenderPass();
 				createRenderPass();
-				createGraphicsPipeline();
 				createFramebuffers();
 				createFramebuffers();
 				createUniformBuffers();
 				createUniformBuffers();
 				createDescriptorPool();
 				createDescriptorPool();

+ 17 - 2
src/modules/graphics/vulkan/Graphics.h

@@ -80,6 +80,13 @@ namespace love {
 				VkCommandBuffer beginSingleTimeCommands();
 				VkCommandBuffer beginSingleTimeCommands();
 				void endSingleTimeCommands(VkCommandBuffer);
 				void endSingleTimeCommands(VkCommandBuffer);
 
 
+				struct GraphicsPipelineConfiguration {
+					std::vector<VkVertexInputBindingDescription> vertexInputBindingDescriptions;
+					std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions;
+
+					friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
+				};
+
 			protected:
 			protected:
 				graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { 
 				graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override { 
 					std::cout << "newShaderStageInternal "; 
 					std::cout << "newShaderStageInternal "; 
@@ -125,6 +132,12 @@ namespace love {
 				std::vector<BatchedDrawBuffers> batchedDrawBuffers;
 				std::vector<BatchedDrawBuffers> batchedDrawBuffers;
 				void updatedBatchedDrawBuffers();
 				void updatedBatchedDrawBuffers();
 
 
+
+				void createVulkanVertexFormat(
+					VertexAttributes vertexAttributes,
+					bool& useConstantVertexColor,
+					GraphicsPipelineConfiguration& configuration);
+
 				// vulkan specific member functions and variables
 				// vulkan specific member functions and variables
 
 
 				struct QueueFamilyIndices {
 				struct QueueFamilyIndices {
@@ -163,7 +176,7 @@ namespace love {
 				void createUniformBuffers();
 				void createUniformBuffers();
 				void createDescriptorPool();
 				void createDescriptorPool();
 				std::vector<VkDescriptorSet> createDescriptorSets(graphics::Texture* texture);
 				std::vector<VkDescriptorSet> createDescriptorSets(graphics::Texture* texture);
-				void createGraphicsPipeline();
+				VkPipeline createGraphicsPipeline(GraphicsPipelineConfiguration);
 				void createFramebuffers();
 				void createFramebuffers();
 				void createCommandPool();
 				void createCommandPool();
 				void createCommandBuffers();
 				void createCommandBuffers();
@@ -175,6 +188,7 @@ namespace love {
 
 
 				void startRecordingGraphicsCommands();
 				void startRecordingGraphicsCommands();
 				void endRecordingGraphicsCommands();
 				void endRecordingGraphicsCommands();
+				void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
 
 
 				void prepareDraw(uint32_t currentImage);
 				void prepareDraw(uint32_t currentImage);
 				
 				
@@ -192,7 +206,8 @@ namespace love {
 				VkDescriptorSetLayout descriptorSetLayout;
 				VkDescriptorSetLayout descriptorSetLayout;
 				VkPipelineLayout pipelineLayout;
 				VkPipelineLayout pipelineLayout;
 				VkRenderPass renderPass;
 				VkRenderPass renderPass;
-				VkPipeline graphicsPipeline;
+				VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
+				std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines;	// FIXME improve performance by using a hash map
 				std::vector<VkFramebuffer> swapChainFramBuffers;
 				std::vector<VkFramebuffer> swapChainFramBuffers;
 				VkCommandPool commandPool;
 				VkCommandPool commandPool;
 				std::vector<VkCommandBuffer> commandBuffers;
 				std::vector<VkCommandBuffer> commandBuffers;

+ 88 - 0
src/modules/graphics/vulkan/Vulkan.cpp

@@ -0,0 +1,88 @@
+#include "Vulkan.h"
+
+namespace love {
+	namespace graphics {
+		namespace vulkan {
+			VkFormat Vulkan::getVulkanVertexFormat(DataFormat format) {
+				switch (format) {
+				case DATAFORMAT_FLOAT:
+					return VK_FORMAT_R32_SFLOAT;
+				case DATAFORMAT_FLOAT_VEC2:
+					return VK_FORMAT_R32G32_SFLOAT;
+				case DATAFORMAT_FLOAT_VEC3:
+					return VK_FORMAT_R32G32B32_SFLOAT;
+				case DATAFORMAT_FLOAT_VEC4:
+					return VK_FORMAT_R32G32B32A32_SFLOAT;
+
+				case DATAFORMAT_FLOAT_MAT2X2:
+				case DATAFORMAT_FLOAT_MAT2X3:
+				case DATAFORMAT_FLOAT_MAT2X4:
+				case DATAFORMAT_FLOAT_MAT3X2:
+				case DATAFORMAT_FLOAT_MAT3X3:
+				case DATAFORMAT_FLOAT_MAT3X4:
+				case DATAFORMAT_FLOAT_MAT4X2:
+				case DATAFORMAT_FLOAT_MAT4X3:
+				case DATAFORMAT_FLOAT_MAT4X4:
+					throw love::Exception("unimplemented data format (matnxm)");
+
+				case DATAFORMAT_INT32:
+					return VK_FORMAT_R32_SINT;
+				case DATAFORMAT_INT32_VEC2:
+					return VK_FORMAT_R32G32_SINT;
+				case DATAFORMAT_INT32_VEC3:
+					return VK_FORMAT_R32G32B32_SINT;
+				case DATAFORMAT_INT32_VEC4:
+					return VK_FORMAT_R32G32B32A32_SINT;
+
+				case DATAFORMAT_UINT32:
+					return VK_FORMAT_R32_UINT;
+				case DATAFORMAT_UINT32_VEC2:
+					return VK_FORMAT_R32G32_UINT;
+				case DATAFORMAT_UINT32_VEC3:
+					return VK_FORMAT_R32G32B32_UINT;
+				case DATAFORMAT_UINT32_VEC4:
+					return VK_FORMAT_R32G32B32A32_UINT;
+
+				case DATAFORMAT_SNORM8_VEC4:
+					return VK_FORMAT_R8G8B8A8_SNORM;
+				case DATAFORMAT_UNORM8_VEC4:
+					return VK_FORMAT_R8G8B8A8_UNORM;
+				case DATAFORMAT_INT8_VEC4:
+					return VK_FORMAT_R8G8B8A8_SINT;
+				case DATAFORMAT_UINT8_VEC4:
+					return VK_FORMAT_R8G8B8A8_UINT;
+
+				case DATAFORMAT_SNORM16_VEC2:
+					return VK_FORMAT_R16G16_SNORM;
+				case DATAFORMAT_SNORM16_VEC4:
+					return VK_FORMAT_R16G16B16A16_SNORM;
+				case DATAFORMAT_UNORM16_VEC2:
+					return VK_FORMAT_R16G16_UNORM;
+				case DATAFORMAT_UNORM16_VEC4:
+					return VK_FORMAT_R16G16B16A16_UNORM;
+
+				case DATAFORMAT_INT16_VEC2:
+					return VK_FORMAT_R16G16_SINT;
+				case DATAFORMAT_INT16_VEC4:
+					return VK_FORMAT_R16G16B16A16_SINT;
+
+				case DATAFORMAT_UINT16:
+					return VK_FORMAT_R16_UINT;
+				case DATAFORMAT_UINT16_VEC2:
+					return VK_FORMAT_R16G16_UINT;
+				case DATAFORMAT_UINT16_VEC4:
+					return VK_FORMAT_R16G16B16A16_UINT;
+
+				case DATAFORMAT_BOOL:
+				case DATAFORMAT_BOOL_VEC2:
+				case DATAFORMAT_BOOL_VEC3:
+				case DATAFORMAT_BOOL_VEC4:
+					throw love::Exception("unimplemented data format (bool)");
+
+				default:
+					throw love::Exception("unknown data format");
+				}
+			}
+		}
+	}
+}

+ 18 - 0
src/modules/graphics/vulkan/Vulkan.h

@@ -0,0 +1,18 @@
+#ifndef LOVE_GRAPHICS_VULKAN_VULKAN_H
+#define LOVE_GRAPHICS_VULKAN_VULKAN_H
+
+#include "Vulkan.h"
+#include "Graphics.h"
+
+namespace love {
+	namespace graphics {
+		namespace vulkan {
+			class Vulkan {
+			public:
+				static VkFormat getVulkanVertexFormat(DataFormat format);
+			};
+		}
+	}
+}
+
+#endif