瀏覽代碼

Vulkan: Some work on optimizing descriptor set

Panagiotis Christopoulos Charitos 9 年之前
父節點
當前提交
e83a9c4742

二進制
samples/assets/simple_scene.blend


+ 94 - 0
src/anki/gr/vulkan/ResourceGroupExtra.cpp

@@ -125,4 +125,98 @@ Error DescriptorSetAllocator::initGlobalDsetPool()
 	return ErrorCode::NONE;
 }
 
+VkDescriptorSetLayout DescriptorSetLayoutFactory::createLayout(
+	U texBindingCount, U uniBindingCount, U storageBindingCount, U imageBindingCount)
+{
+	LockGuard<Mutex> lock(m_mtx);
+
+	Key key(texBindingCount, uniBindingCount, storageBindingCount, imageBindingCount);
+	auto it = m_map.find(key);
+
+	if(it != m_map.getEnd())
+	{
+		return *it;
+	}
+	else
+	{
+		// Create the layout
+
+		VkDescriptorSetLayoutCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+
+		const U BINDING_COUNT =
+			MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS + MAX_IMAGE_BINDINGS;
+
+		Array<VkDescriptorSetLayoutBinding, BINDING_COUNT> bindings;
+		memset(&bindings[0], 0, sizeof(bindings));
+		ci.pBindings = &bindings[0];
+
+		U count = 0;
+		U bindingIdx = 0;
+
+		// Combined image samplers
+		for(U i = 0; i < texBindingCount; ++i)
+		{
+			VkDescriptorSetLayoutBinding& binding = bindings[count++];
+			binding.binding = bindingIdx++;
+			binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+			binding.descriptorCount = 1;
+			binding.stageFlags = VK_SHADER_STAGE_ALL;
+		}
+
+		// Uniform buffers
+		bindingIdx = MAX_TEXTURE_BINDINGS;
+		for(U i = 0; i < uniBindingCount; ++i)
+		{
+			VkDescriptorSetLayoutBinding& binding = bindings[count++];
+			binding.binding = bindingIdx++;
+			binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+			binding.descriptorCount = 1;
+			binding.stageFlags = VK_SHADER_STAGE_ALL;
+		}
+
+		// Storage buffers
+		bindingIdx = MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS;
+		for(U i = 0; i < storageBindingCount; ++i)
+		{
+			VkDescriptorSetLayoutBinding& binding = bindings[count++];
+			binding.binding = bindingIdx++;
+			binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
+			binding.descriptorCount = 1;
+			binding.stageFlags = VK_SHADER_STAGE_ALL;
+		}
+
+		// Images
+		bindingIdx = MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS;
+		for(U i = 0; i < imageBindingCount; ++i)
+		{
+			VkDescriptorSetLayoutBinding& binding = bindings[count++];
+			binding.binding = bindingIdx++;
+			binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+			binding.descriptorCount = 1;
+			binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
+		}
+
+		ANKI_ASSERT(count <= BINDING_COUNT);
+		ci.bindingCount = count;
+
+		VkDescriptorSetLayout dset;
+		ANKI_VK_CHECKF(vkCreateDescriptorSetLayout(m_dev, &ci, nullptr, &dset));
+
+		m_map.pushBack(m_alloc, key, dset);
+		return dset;
+	}
+}
+
+void DescriptorSetLayoutFactory::destroy()
+{
+	for(auto it : m_map)
+	{
+		VkDescriptorSetLayout dset = it;
+		vkDestroyDescriptorSetLayout(m_dev, dset, nullptr);
+	}
+
+	m_map.destroy(m_alloc);
+}
+
 } // end namespace anki

+ 66 - 0
src/anki/gr/vulkan/ResourceGroupExtra.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/gr/vulkan/Common.h>
+#include <anki/util/HashMap.h>
 
 namespace anki
 {
@@ -75,6 +76,71 @@ private:
 	Error initGlobalDsetLayout();
 	Error initGlobalDsetPool();
 };
+
+/// Creates descriptor set layouts.
+class DescriptorSetLayoutFactory
+{
+public:
+	DescriptorSetLayoutFactory()
+	{
+	}
+
+	~DescriptorSetLayoutFactory()
+	{
+	}
+
+	void init(GrAllocator<U8> alloc, VkDevice dev)
+	{
+		m_alloc = alloc;
+		m_dev = dev;
+	}
+
+	void destroy();
+
+	/// If there is no bindings for a specific type then pass zero.
+	VkDescriptorSetLayout createLayout(
+		U texBindingCount, U uniBindingCount, U storageBindingCount, U imageBindingCount);
+
+private:
+	class Key
+	{
+	public:
+		U64 m_hash;
+
+		Key(U a, U b, U c, U d)
+		{
+			ANKI_ASSERT(a < 0xFF && b < 0xFF && c < 0xFF && d < 0xFF);
+			m_hash = (a << 24) | (b << 16) | (c << 8) | d;
+		}
+	};
+
+	/// Hash the hash.
+	class Hasher
+	{
+	public:
+		U64 operator()(const Key& b) const
+		{
+			return b.m_hash;
+		}
+	};
+
+	/// Hash compare.
+	class Compare
+	{
+	public:
+		Bool operator()(const Key& a, const Key& b) const
+		{
+			return a.m_hash == b.m_hash;
+		}
+	};
+
+	HashMap<Key, VkDescriptorSetLayout, Hasher, Compare> m_map;
+
+	GrAllocator<U8> m_alloc;
+	VkDevice m_dev = VK_NULL_HANDLE;
+
+	Mutex m_mtx;
+};
 /// @}
 
 } // end namespace anki

+ 131 - 0
src/anki/gr/vulkan/SpirvReflection.cpp

@@ -0,0 +1,131 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/gr/vulkan/SpirvReflection.h>
+#include <anki/util/List.h>
+#include <SPIRV/spirv.hpp>
+
+namespace anki
+{
+
+using Word = U32;
+
+class SpirvHeader
+{
+public:
+	Word m_magic;
+	Word m_version;
+	Word m_generatorMagic;
+	Word m_idCount;
+	Word m_reserved;
+};
+
+class Variable
+{
+public:
+	Word m_id = 0;
+	U8 m_set = MAX_U8;
+	U8 m_binding = MAX_U8;
+};
+
+Error SpirvReflection::parse()
+{
+	ListAuto<Variable> variables(m_alloc);
+
+	// For all instructions
+	U counter = 0;
+	while(counter < m_spv.getSize())
+	{
+		Word combo = m_spv[counter];
+		Word instruction = combo & 0xFFFF;
+		Word instrctionWordCount = combo >> 16;
+
+		// If decorate
+		if(instruction == spv::OpDecorate)
+		{
+			U c = counter + sizeof(Word);
+
+			// Find or create variable
+			Word rezId = m_spv[c++];
+			Variable* var = nullptr;
+			for(Variable& v : variables)
+			{
+				if(v.m_id == rezId)
+				{
+					var = &v;
+					break;
+				}
+			}
+
+			if(var == nullptr)
+			{
+				Variable v;
+				v.m_id = rezId;
+				variables.pushBack(v);
+				var = &variables.getBack();
+			}
+
+			// Check the decoration
+			Word decoration = m_spv[c++];
+			if(decoration == Word(spv::Decoration::DecorationDescriptorSet))
+			{
+				Word set = m_spv[c];
+				if(set >= MAX_RESOURCE_GROUPS)
+				{
+					ANKI_LOGE("Cannot accept shaders with descriptor set >= to %u", MAX_RESOURCE_GROUPS);
+					return ErrorCode::USER_DATA;
+				}
+
+				m_setMask |= 1 << set;
+				ANKI_ASSERT(var.m_set == MAX_U8);
+				var->m_set = set;
+			}
+			else if(decoration == Word(spv::Decoration::DecorationBinding))
+			{
+				Word binding = m_spv[c];
+
+				ANKI_ASSERT(var.m_binding == MAX_U8);
+				var->m_binding = binding;
+			}
+		}
+
+		counter += instrctionWordCount;
+	}
+
+	// Iterate the variables
+	for(const Variable& v : variables)
+	{
+		if(v.m_set == MAX_U8 || v.m_binding == MAX_U8)
+		{
+			ANKI_LOGE("Missing set or binding decoration of ID %u", v.m_id);
+			return ErrorCode::USER_DATA;
+		}
+
+		const U32 binding = v.m_binding;
+		const U set = v.m_set;
+		if(binding < MAX_TEXTURE_BINDINGS)
+		{
+			m_texBindings[set] = max(m_texBindings[set], binding + 1);
+		}
+		else if(binding < MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS)
+		{
+			m_uniBindings[set] = max(m_uniBindings[set], binding + 1);
+		}
+		else if(binding < MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS)
+		{
+			m_storageBindings[set] = max(m_storageBindings[set], binding + 1);
+		}
+		else
+		{
+			ANKI_ASSERT(binding < MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS
+					+ MAX_IMAGE_BINDINGS);
+			m_imageBindings[set] = max(m_imageBindings[set], binding + 1);
+		}
+	}
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace anki

+ 73 - 0
src/anki/gr/vulkan/SpirvReflection.h

@@ -0,0 +1,73 @@
+// Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/gr/vulkan/Common.h>
+
+namespace anki
+{
+
+/// @addtogroup vulkan
+/// @{
+
+/// Get reflection info from SPIR-V
+class SpirvReflection
+{
+public:
+	SpirvReflection(GenericMemoryPoolAllocator<U8> alloc, const U32* spirvBegin, const U32* spirvEnd)
+		: m_alloc(alloc)
+		, m_spv(spirvBegin, PtrSize(spirvEnd - spirvBegin))
+	{
+	}
+
+	~SpirvReflection()
+	{
+	}
+
+	ANKI_USE_RESULT Error parse();
+
+	U32 getDescriptorSetMask() const
+	{
+		return m_setMask;
+	}
+
+	U getTextureBindingCount(U set) const
+	{
+		ANKI_ASSERT(m_setMask & (1 << set));
+		return m_texBindings[set];
+	}
+
+	U getUniformBindingCount(U set) const
+	{
+		ANKI_ASSERT(m_setMask & (1 << set));
+		return m_uniBindings[set];
+	}
+
+	U getStorageBindingCount(U set) const
+	{
+		ANKI_ASSERT(m_setMask & (1 << set));
+		return m_storageBindings[set];
+	}
+
+	U getImageBindingCount(U set) const
+	{
+		ANKI_ASSERT(m_setMask & (1 << set));
+		return m_imageBindings[set];
+	}
+
+private:
+	GenericMemoryPoolAllocator<U8> m_alloc;
+	WeakArray<const U32> m_spv;
+
+	U32 m_setMask = 0;
+	Array<U32, MAX_RESOURCE_GROUPS> m_texBindings = {{}};
+	Array<U32, MAX_RESOURCE_GROUPS> m_uniBindings = {{}};
+	Array<U32, MAX_RESOURCE_GROUPS> m_storageBindings = {{}};
+	Array<U32, MAX_RESOURCE_GROUPS> m_imageBindings = {{}};
+};
+/// @}
+
+} // end namespace anki

+ 1 - 1
thirdparty

@@ -1 +1 @@
-Subproject commit a81042c3c6668ff207e1932b060a3057176d60c0
+Subproject commit d2210ab120d284b0b3413bb6b395948e3793d5fa

+ 13 - 23
tools/scene/Exporter.cpp

@@ -60,7 +60,7 @@ static const aiNode* findNodeWithName(const std::string& name, const aiNode* nod
 	const aiNode* out = nullptr;
 
 	// Go to children
-	for(uint32_t i = 0; i < node->mNumChildren; i++)
+	for(unsigned i = 0; i < node->mNumChildren; i++)
 	{
 		out = findNodeWithName(name, node->mChildren[i]);
 		if(out)
@@ -424,8 +424,7 @@ void Exporter::exportLight(const aiLight& light)
 	// Colors
 	// aiColor3D linear = computeLightColor(light.mColorDiffuse);
 	aiVector3D linear(light.mColorDiffuse[0], light.mColorDiffuse[1], light.mColorDiffuse[2]);
-	file << "lcomp:setDiffuseColor(Vec4.new(" << linear[0] << ", " << linear[1] << ", " << linear[2] << ", "
-		 << "1))\n";
+	file << "lcomp:setDiffuseColor(Vec4.new(" << linear[0] << ", " << linear[1] << ", " << linear[2] << ", 1))\n";
 
 	// linear = computeLightColor(light.mColorSpecular);
 	if(light.mProperties.find("specular_color") != light.mProperties.end())
@@ -433,8 +432,7 @@ void Exporter::exportLight(const aiLight& light)
 		stringToFloatArray<3>(light.mProperties.at("specular_color"), linear);
 	}
 
-	file << "lcomp:setSpecularColor(Vec4.new(" << linear[0] << ", " << linear[1] << ", " << linear[2] << ", "
-		 << "1))\n";
+	file << "lcomp:setSpecularColor(Vec4.new(" << linear[0] << ", " << linear[1] << ", " << linear[2] << ", 1))\n";
 
 	// Geometry
 	aiVector3D direction(0.0, 0.0, 1.0);
@@ -510,8 +508,7 @@ void Exporter::exportLight(const aiLight& light)
 	{
 		if(!lfCompRetrieved)
 		{
-			file << "lfcomp = node:getSceneNodeBase():"
-				 << "getLensFlareComponent()\n";
+			file << "lfcomp = node:getSceneNodeBase():getLensFlareComponent()\n";
 			lfCompRetrieved = true;
 		}
 
@@ -524,8 +521,7 @@ void Exporter::exportLight(const aiLight& light)
 	{
 		if(!lfCompRetrieved)
 		{
-			file << "lfcomp = node:getSceneNodeBase():"
-				 << "getLensFlareComponent()\n";
+			file << "lfcomp = node:getSceneNodeBase():getLensFlareComponent()\n";
 			lfCompRetrieved = true;
 		}
 
@@ -540,8 +536,7 @@ void Exporter::exportLight(const aiLight& light)
 	{
 		if(!eventCreated)
 		{
-			file << "event = events:newLightEvent(0.0, -1.0, "
-					"node:getSceneNodeBase())\n";
+			file << "event = events:newLightEvent(0.0, -1.0, node:getSceneNodeBase())\n";
 			eventCreated = true;
 		}
 
@@ -556,8 +551,7 @@ void Exporter::exportLight(const aiLight& light)
 	{
 		if(!eventCreated)
 		{
-			file << "event = events:newLightEvent(0.0, -1.0, "
-					"node:getSceneNodeBase())\n";
+			file << "event = events:newLightEvent(0.0, -1.0, node:getSceneNodeBase())\n";
 			eventCreated = true;
 		}
 
@@ -615,15 +609,13 @@ void Exporter::exportAnimation(const aiAnimation& anim, unsigned index)
 
 			if(m_flipyz)
 			{
-				file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
-					 << "<value>" << key.mValue[0] << " " << key.mValue[2] << " " << -key.mValue[1]
-					 << "</value></key>\n";
+				file << "\t\t\t\t<key><time>" << key.mTime << "</time><value>" << key.mValue[0] << " " << key.mValue[2]
+					 << " " << -key.mValue[1] << "</value></key>\n";
 			}
 			else
 			{
-				file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
-					 << "<value>" << key.mValue[0] << " " << key.mValue[1] << " " << key.mValue[2]
-					 << "</value></key>\n";
+				file << "\t\t\t\t<key><time>" << key.mTime << "</time><value>" << key.mValue[0] << " " << key.mValue[1]
+					 << " " << key.mValue[2] << "</value></key>\n";
 			}
 		}
 		file << "\t\t\t</positionKeys>\n";
@@ -1081,8 +1073,7 @@ void Exporter::exportAll()
 		std::string nodeName = modelName + node.m_group + std::to_string(i);
 
 		// Write the main node
-		file << "\nnode = scene:newModelNode(\"" << nodeName << "\", \"" << m_rpath << modelName << ".ankimdl"
-			 << "\")\n";
+		file << "\nnode = scene:newModelNode(\"" << nodeName << "\", \"" << m_rpath << modelName << ".ankimdl\")\n";
 		writeNodeTransform("node", node.m_transform);
 
 		// Write the collision node
@@ -1104,8 +1095,7 @@ void Exporter::exportAll()
 				exportCollisionMesh(i);
 
 				std::string fname = m_rpath + node.m_collisionMesh + ".ankicl";
-				file << "node = scene:newStaticCollisionNode(\"" << nodeName << "_cl"
-					 << "\", \"" << fname << "\", trf)\n";
+				file << "node = scene:newStaticCollisionNode(\"" << nodeName << "_cl\", \"" << fname << "\", trf)\n";
 			}
 			else
 			{

+ 13 - 2
tools/texture/create_atlas.py

@@ -41,6 +41,7 @@ class Context:
 	out_file = ""
 	margin = 0
 	bg_color = 0
+	rpath = ""
 
 	sub_images = []
 	atlas_width = 0
@@ -68,6 +69,8 @@ def parse_commandline():
 
 	parser.add_argument("-b", "--background-color", help = "specify background of empty areas", default = "ff00ff00")
 
+	parser.add_argument("-r", "--rpath", help = "Path to append to the .ankiatex", default = "")
+
 	args = parser.parse_args()
 
 	ctx = Context()
@@ -75,6 +78,7 @@ def parse_commandline():
 	ctx.out_file = args.output
 	ctx.margin = args.margin
 	ctx.bg_color = int(args.background_color, 16)
+	ctx.rpath = args.rpath
 
 	if len(ctx.in_files) < 2:
 		parser.error("Not enough images")
@@ -102,6 +106,11 @@ def load_images(ctx):
 		img.mheight = img.height + ctx.margin
 
 		printi("Image \"%s\" loaded. Mode \"%s\"" % (i, img.image.mode))
+
+		for simage in ctx.sub_images:
+			if os.path.basename(simage.image_name) == os.path.basename(i):
+				raise Exception("Cannot have images with the same base %s" % i)
+
 		ctx.sub_images.append(img)
 
 def compute_atlas_rough_size(ctx):
@@ -253,14 +262,16 @@ def write_xml(ctx):
 	fname = os.path.splitext(ctx.out_file)[0] + ".ankiatex"
 	printi("Writing XML \"%s\"" % fname)
 	f = open(fname, "w")
+	f.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n")
 	f.write("<textureAtlas>\n")
-	f.write("\t<texture>%s</texture>\n" % ctx.out_file)
+	out_filename = ctx.rpath + "/" + os.path.splitext(os.path.basename(ctx.out_file))[0] + ".ankitex"
+	f.write("\t<texture>%s</texture>\n" % out_filename)
 	f.write("\t<subTextureMargin>%u</subTextureMargin>\n" % ctx.margin)
 	f.write("\t<subTextures>\n")
 
 	for sub_image in ctx.sub_images:
 		f.write("\t\t<subTexture>\n")
-		f.write("\t\t\t<name>%s</name>\n" % sub_image.image_name)
+		f.write("\t\t\t<name>%s</name>\n" % os.path.basename(sub_image.image_name))
 
 		# Now change coordinate system
 		left = sub_image.atlas_x / ctx.atlas_width

+ 8 - 14
tools/texture/segment_image.py

@@ -19,19 +19,15 @@ def printi(s):
 def parse_commandline():
 	""" Parse the command line arguments """
 
-	parser = optparse.OptionParser(usage = "usage: %prog [options]", \
-			description = "This program takes a single 2D image and spits "\
-			"a number of images. Input and output images should be TGA.")
+	parser = optparse.OptionParser(usage = "usage: %prog [options]",
+			description = "This program takes a single 2D image and spits a number of images. Input and output images "
+			"should be TGA.")
 
-	parser.add_option("-i", "--input", dest = "inp",
-			type = "string", help = "specify the image to split.")
+	parser.add_option("-i", "--input", dest = "inp", type = "string", help = "specify the image to split.")
 
-	parser.add_option("-o", "--output", dest = "out",
-			type = "string", help = "the directory of the output images.")
+	parser.add_option("-o", "--output", dest = "out", type = "string", help = "the directory of the output images.")
 
-	parser.add_option("-s", "--size", dest = "size",
-			type = "string", metavar="WxH",
-			help = "size of the splits.")
+	parser.add_option("-s", "--size", dest = "size", type = "string", metavar="WxH", help = "size of the splits.")
 
 	# Add the default value on each option when printing help
 	for option in parser.option_list:
@@ -63,8 +59,7 @@ def split(filename, split_size, out_dir):
 	""" Load an image """
 
 	# Read and check the header
-	uncompressed_tga_header = struct.pack("BBBBBBBBBBBB", \
-			0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+	uncompressed_tga_header = struct.pack("BBBBBBBBBBBB", 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0)
 
 	in_file = open(filename, "rb")
 	tga_header = in_file.read(12)
@@ -92,8 +87,7 @@ def split(filename, split_size, out_dir):
 
 	# Check split size against the image
 	if (img_width % split_size[0]) != 0 or (img_height % split_size[1]) != 0:
-		raise Exception("Sizes of the input image and the split are not " \
-				"compatible: %d %d vs %d %d"
+		raise Exception("Sizes of the input image and the split are not compatible: %d %d vs %d %d"
 				% (img_width, img_height, split_size[0], split_size[1]))
 
 	# Dump the data to an array