Browse Source

vulkan: use hashing to determine pipeline

Until now we did a linear search in a vector to find the cashed
pipeline given a certain configuration. This is of course not a very
efficient way. This commit now uses a hashmap which should still be
fast even when there are a lot of pipelines already created.
niki 3 years ago
parent
commit
29dcf3e8c0
2 changed files with 53 additions and 115 deletions
  1. 27 108
      src/modules/graphics/vulkan/Graphics.cpp
  2. 26 7
      src/modules/graphics/vulkan/Graphics.h

+ 27 - 108
src/modules/graphics/vulkan/Graphics.cpp

@@ -1098,13 +1098,15 @@ void Graphics::createDefaultShaders() {
 	}
 	}
 }
 }
 
 
+bool Graphics::usesConstantVertexColor(const VertexAttributes& vertexAttributes) {
+	return !!(vertexAttributes.enableBits & (1u << ATTRIB_COLOR));
+}
+
 void Graphics::createVulkanVertexFormat(
 void Graphics::createVulkanVertexFormat(
 	VertexAttributes vertexAttributes, 
 	VertexAttributes vertexAttributes, 
-	bool& useConstantVertexColor,
-	GraphicsPipelineConfiguration& configuration) {
+	std::vector<VkVertexInputBindingDescription> &bindingDescriptions, 
+	std::vector<VkVertexInputAttributeDescription> &attributeDescriptions) {
 	std::set<uint32_t> usedBuffers;
 	std::set<uint32_t> usedBuffers;
-	std::vector<VkVertexInputBindingDescription> bindingDescriptions;
-	std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
 
 
 	auto allBits = vertexAttributes.enableBits;
 	auto allBits = vertexAttributes.enableBits;
 
 
@@ -1166,23 +1168,17 @@ void Graphics::createVulkanVertexFormat(
 		attributeDescription.offset = 0;
 		attributeDescription.offset = 0;
 		attributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT;
 		attributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT;
 		attributeDescriptions.push_back(attributeDescription);
 		attributeDescriptions.push_back(attributeDescription);
-
-		useConstantVertexColor = true;
-	}
-	else {
-		useConstantVertexColor = false;
 	}
 	}
-
-	configuration.vertexInputBindingDescriptions = bindingDescriptions;
-	configuration.vertexInputAttributeDescriptions = attributeDescriptions;
 }
 }
 
 
 void Graphics::prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType primitiveType, CullMode cullmode) {
 void Graphics::prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType primitiveType, CullMode cullmode) {
 	GraphicsPipelineConfiguration configuration;
 	GraphicsPipelineConfiguration configuration;
+	configuration.vertexAttributes = attributes;
 	configuration.shader = (Shader*)Shader::current;
 	configuration.shader = (Shader*)Shader::current;
 	configuration.primitiveType = primitiveType;
 	configuration.primitiveType = primitiveType;
 	configuration.polygonMode = currentPolygonMode;
 	configuration.polygonMode = currentPolygonMode;
 	configuration.blendState = states.back().blend;
 	configuration.blendState = states.back().blend;
+	configuration.colorChannelMask = states.back().colorMask;
 	configuration.winding = states.back().winding;
 	configuration.winding = states.back().winding;
 	configuration.cullmode = cullmode;
 	configuration.cullmode = cullmode;
 	configuration.framebufferFormat = currentFramebufferOutputFormat;
 	configuration.framebufferFormat = currentFramebufferOutputFormat;
@@ -1195,12 +1191,8 @@ void Graphics::prepareDraw(const VertexAttributes& attributes, const BufferBindi
 		configuration.scissorRect = std::nullopt;
 		configuration.scissorRect = std::nullopt;
 	}
 	}
 	std::vector<VkBuffer> bufferVector;
 	std::vector<VkBuffer> bufferVector;
-
 	std::vector<VkDeviceSize> offsets;
 	std::vector<VkDeviceSize> offsets;
 
 
-	bool useConstantColorBuffer;
-	createVulkanVertexFormat(attributes, useConstantColorBuffer, configuration);
-
 	for (uint32_t i = 0; i < VertexAttributes::MAX; i++) {
 	for (uint32_t i = 0; i < VertexAttributes::MAX; i++) {
 		if (buffers.useBits & (1u << i)) {
 		if (buffers.useBits & (1u << i)) {
 			bufferVector.push_back((VkBuffer)buffers.info[i].buffer->getHandle());
 			bufferVector.push_back((VkBuffer)buffers.info[i].buffer->getHandle());
@@ -1208,7 +1200,7 @@ void Graphics::prepareDraw(const VertexAttributes& attributes, const BufferBindi
 		}
 		}
 	}
 	}
 
 
-	if (useConstantColorBuffer) {
+	if (usesConstantVertexColor(attributes)) {
 		bufferVector.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle());
 		bufferVector.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle());
 		offsets.push_back((VkDeviceSize)0);
 		offsets.push_back((VkDeviceSize)0);
 	}
 	}
@@ -1279,12 +1271,17 @@ void Graphics::endRenderPass() {
 VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
 VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
 	auto &shaderStages = configuration.shader->getShaderStages();
 	auto &shaderStages = configuration.shader->getShaderStages();
 
 
+	std::vector<VkVertexInputBindingDescription> bindingDescriptions;
+	std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
+	
+	createVulkanVertexFormat(configuration.vertexAttributes, bindingDescriptions, attributeDescriptions);
+
 	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;
-	vertexInputInfo.vertexBindingDescriptionCount = static_cast<uint32_t>(configuration.vertexInputBindingDescriptions.size());
-	vertexInputInfo.pVertexBindingDescriptions = configuration.vertexInputBindingDescriptions.data();
-	vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(configuration.vertexInputAttributeDescriptions.size());
-	vertexInputInfo.pVertexAttributeDescriptions = configuration.vertexInputAttributeDescriptions.data();
+	vertexInputInfo.vertexBindingDescriptionCount = static_cast<uint32_t>(bindingDescriptions.size());
+	vertexInputInfo.pVertexBindingDescriptions = bindingDescriptions.data();
+	vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
+	vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.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;
@@ -1394,23 +1391,17 @@ VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration config
 }
 }
 
 
 void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration configuration) {
 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;
+	auto it = graphicsPipelines.find(configuration);
+	if (it != graphicsPipelines.end()) {
+		if (it->second != currentGraphicsPipeline) {
+			vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, it->second);
+			currentGraphicsPipeline = it->second;
 		}
 		}
 	} else {
 	} 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;
+		VkPipeline pipeline = createGraphicsPipeline(configuration);
+		graphicsPipelines.insert({configuration, pipeline});
+		vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+		currentGraphicsPipeline = pipeline;
 	}
 	}
 }
 }
 
 
@@ -1490,78 +1481,6 @@ void Graphics::createQuadIndexBuffer() {
 	quadIndexBuffer->unmap(size);
 	quadIndexBuffer->unmap(size);
 }
 }
 
 
-bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other) {
-	if (first.scissorRect != other.scissorRect) {
-		return false;
-	}
-	if (first.viewportHeight != other.viewportHeight) {
-		return false;
-	}
-	if (first.viewportWidth != other.viewportWidth) {
-		return false;
-	}
-	if (first.framebufferFormat != other.framebufferFormat) {
-		return false;
-	}
-	if (first.cullmode != other.cullmode) {
-		return false;
-	}
-	if (first.winding != other.winding) {
-		return false;
-	}
-	if (first.colorChannelMask != other.colorChannelMask) {
-		return false;
-	}
-	if (!(first.blendState == other.blendState)) {	// not sure why != doesn't work
-		return false;
-	}
-	if (first.polygonMode != other.polygonMode) {
-		return false;
-	}
-	if (first.primitiveType != other.primitiveType) {
-		return false;
-	}
-	if (first.shader != other.shader) {
-		return false;
-	}
-	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() {
 	cleanupSwapChain();
 	cleanupSwapChain();
 
 

+ 26 - 7
src/modules/graphics/vulkan/Graphics.h

@@ -1,16 +1,20 @@
 #ifndef LOVE_GRAPHICS_VULKAN_GRAPHICS_H
 #ifndef LOVE_GRAPHICS_VULKAN_GRAPHICS_H
 #define LOVE_GRAPHICS_VULKAN_GRAPHICS_H
 #define LOVE_GRAPHICS_VULKAN_GRAPHICS_H
 
 
+// löve
 #include "graphics/Graphics.h"
 #include "graphics/Graphics.h"
 #include "StreamBuffer.h"
 #include "StreamBuffer.h"
 #include "ShaderStage.h"
 #include "ShaderStage.h"
 #include "Shader.h"
 #include "Shader.h"
 #include "Texture.h"
 #include "Texture.h"
+#include <common/config.h>
+
+// libraries
 #include <vulkan/vulkan.h>
 #include <vulkan/vulkan.h>
 #include "vk_mem_alloc.h"
 #include "vk_mem_alloc.h"
+#include "libraries/xxHash/xxhash.h"
 
 
-#include <common/config.h>
-
+// c++
 #include <optional>
 #include <optional>
 #include <iostream>
 #include <iostream>
 #include <memory>
 #include <memory>
@@ -21,8 +25,7 @@ namespace love {
 namespace graphics {
 namespace graphics {
 namespace vulkan {
 namespace vulkan {
 struct GraphicsPipelineConfiguration {
 struct GraphicsPipelineConfiguration {
-	std::vector<VkVertexInputBindingDescription> vertexInputBindingDescriptions;
-	std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions;
+	VertexAttributes vertexAttributes;
 	Shader* shader = nullptr;
 	Shader* shader = nullptr;
 	PrimitiveType primitiveType = PRIMITIVE_MAX_ENUM;
 	PrimitiveType primitiveType = PRIMITIVE_MAX_ENUM;
 	VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL;
 	VkPolygonMode polygonMode = VK_POLYGON_MODE_FILL;
@@ -35,7 +38,19 @@ struct GraphicsPipelineConfiguration {
 	float viewportHeight;
 	float viewportHeight;
 	std::optional<Rect> scissorRect;
 	std::optional<Rect> scissorRect;
 
 
-	friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
+	GraphicsPipelineConfiguration() {
+		memset(this, 0, sizeof(GraphicsPipelineConfiguration));
+	}
+
+	bool operator==(const GraphicsPipelineConfiguration& other) const {
+		return memcmp(this, &other, sizeof(GraphicsPipelineConfiguration)) == 0;
+	}
+};
+
+struct GraphicsPipelineConfigurationHasher {
+	size_t operator() (const GraphicsPipelineConfiguration &configuration) const {
+		return XXH32(&configuration, sizeof(GraphicsPipelineConfiguration), 0);
+	}
 };
 };
 
 
 struct BatchedDrawBuffers {
 struct BatchedDrawBuffers {
@@ -169,7 +184,11 @@ private:
 	void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
 	void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
 	graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
 	graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
 	void updatedBatchedDrawBuffers();
 	void updatedBatchedDrawBuffers();
-	void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
+	bool usesConstantVertexColor(const VertexAttributes&);
+	void createVulkanVertexFormat(
+		VertexAttributes vertexAttributes, 
+		std::vector<VkVertexInputBindingDescription> &bindingDescriptions, 
+		std::vector<VkVertexInputAttributeDescription> &attributeDescriptions);
 	void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
 	void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
 	void startRenderPass(Texture*, uint32_t w, uint32_t h);
 	void startRenderPass(Texture*, uint32_t w, uint32_t h);
 	void endRenderPass();
 	void endRenderPass();
@@ -187,7 +206,7 @@ private:
 	std::vector<VkImageView> swapChainImageViews;
 	std::vector<VkImageView> swapChainImageViews;
 	VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
 	VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
 	VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
 	VkPipeline currentGraphicsPipeline = VK_NULL_HANDLE;
-	std::vector<std::pair<GraphicsPipelineConfiguration, VkPipeline>> graphicsPipelines;	// FIXME improve performance by using a hash map
+	std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
 	VkCommandPool commandPool = VK_NULL_HANDLE;
 	VkCommandPool commandPool = VK_NULL_HANDLE;
 	std::vector<VkCommandBuffer> commandBuffers;
 	std::vector<VkCommandBuffer> commandBuffers;
 	std::vector<VkCommandBuffer> dataTransferCommandBuffers;
 	std::vector<VkCommandBuffer> dataTransferCommandBuffers;