Pārlūkot izejas kodu

Some more work on reflection

Panagiotis Christopoulos Charitos 6 gadi atpakaļ
vecāks
revīzija
6da0d391bf

+ 11 - 26
src/anki/gr/Enums.h

@@ -479,32 +479,14 @@ enum class ShaderVariableDataType : U8
 {
 	NONE,
 
-	INT,
-	UINT,
-	FLOAT,
-	IVEC2,
-	UVEC2,
-	VEC2,
-	IVEC3,
-	UVEC3,
-	VEC3,
-	IVEC4,
-	UVEC4,
-	VEC4,
-	MAT3,
-	MAT4,
-
-	TEXTURE_1D,
-	TEXTURE_1D_ARRAY,
-	TEXTURE_2D,
-	TEXTURE_2D_ARRAY,
-	TEXTURE_3D,
-	TEXTURE_CUBE,
-	TEXTURE_CUBE_ARRAY,
-
-	SAMPLER,
-
-	// Combined
+#define ANKI_SVDT_MACRO(x, y) x,
+#define ANKI_SVDT_MACRO_2(x, y) x,
+#include <anki/gr/ShaderVariableDataTypeDefs.h>
+#undef ANKI_SVDT_MACRO
+#undef ANKI_SVDT_MACRO_2
+
+	// Derived
+
 	NUMERICS_FIRST = INT,
 	NUMERICS_LAST = MAT4,
 
@@ -522,6 +504,9 @@ enum class ShaderVariableDataType : U8
 
 	TEXTURE_FIRST = TEXTURE_1D,
 	TEXTURE_LAST = TEXTURE_CUBE_ARRAY,
+
+	IMAGE_FIRST = IMAGE_1D,
+	IMAGE_LAST = IMAGE_CUBE_ARRAY,
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderVariableDataType, inline)
 

+ 48 - 0
src/anki/gr/ShaderVariableDataTypeDefs.h

@@ -0,0 +1,48 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+// ShaderVariableDataType defines
+
+#if defined(ANKI_SVDT_MACRO)
+ANKI_SVDT_MACRO(INT, I32)
+ANKI_SVDT_MACRO(UINT, U32)
+ANKI_SVDT_MACRO(FLOAT, F32)
+
+ANKI_SVDT_MACRO(IVEC2, IVec2)
+ANKI_SVDT_MACRO(UVEC2, UVec2)
+ANKI_SVDT_MACRO(VEC2, Vec2)
+
+ANKI_SVDT_MACRO(IVEC3, IVec3)
+ANKI_SVDT_MACRO(UVEC3, UVec3)
+ANKI_SVDT_MACRO(VEC3, Vec3)
+
+ANKI_SVDT_MACRO(IVEC4, IVec4)
+ANKI_SVDT_MACRO(UVEC4, UVec4)
+ANKI_SVDT_MACRO(VEC4, Vec4)
+
+ANKI_SVDT_MACRO(MAT3, Mat3)
+ANKI_SVDT_MACRO(MAT3X4, Mat3x4)
+ANKI_SVDT_MACRO(MAT4, Mat4)
+#endif
+
+#if defined(ANKI_SVDT_MACRO_2)
+ANKI_SVDT_MACRO_2(TEXTURE_1D, texture1D)
+ANKI_SVDT_MACRO_2(TEXTURE_1D_ARRAY, texture1DArray)
+ANKI_SVDT_MACRO_2(TEXTURE_2D, texture2D)
+ANKI_SVDT_MACRO_2(TEXTURE_2D_ARRAY, texture2DArray)
+ANKI_SVDT_MACRO_2(TEXTURE_3D, texture3D)
+ANKI_SVDT_MACRO_2(TEXTURE_CUBE, textureCube)
+ANKI_SVDT_MACRO_2(TEXTURE_CUBE_ARRAY, textureCubeArray)
+
+ANKI_SVDT_MACRO_2(IMAGE_1D, image1D)
+ANKI_SVDT_MACRO_2(IMAGE_1D_ARRAY, image1DArray)
+ANKI_SVDT_MACRO_2(IMAGE_2D, image2D)
+ANKI_SVDT_MACRO_2(IMAGE_2D_ARRAY, image2DArray)
+ANKI_SVDT_MACRO_2(IMAGE_3D, image3D)
+ANKI_SVDT_MACRO_2(IMAGE_CUBE, imageCube)
+ANKI_SVDT_MACRO_2(IMAGE_CUBE_ARRAY, imageCubeArray)
+
+ANKI_SVDT_MACRO_2(SAMPLER, sampler)
+#endif

+ 24 - 0
src/anki/gr/utils/Functions.cpp

@@ -141,4 +141,28 @@ void writeShaderBlockMemory(ShaderVariableDataType type,
 	}
 }
 
+const CString shaderVariableDataTypeToString(ShaderVariableDataType t)
+{
+#define ANKI_SVDT_MACRO(svdt, akType) \
+	case ShaderVariableDataType::svdt: \
+		return ANKI_STRINGIZE(akType);
+
+#define ANKI_SVDT_MACRO_2(svdt, akType) ANKI_SVDT_MACRO(svdt, akType)
+
+	switch(t)
+	{
+	case ShaderVariableDataType::NONE:
+		return "NONE";
+#include <anki/gr/ShaderVariableDataTypeDefs.h>
+	default:
+		ANKI_ASSERT(0);
+	}
+
+#undef ANKI_SVDT_MACRO
+#undef ANKI_SVDT_MACRO_2
+
+	ANKI_ASSERT(0);
+	return "";
+}
+
 } // end namespace anki

+ 8 - 19
src/anki/gr/utils/Functions.h

@@ -38,29 +38,15 @@ inline Bool blendingDisabled(BlendFactor srcFactorRgb,
 template<typename T>
 ShaderVariableDataType getShaderVariableTypeFromTypename();
 
-#define ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(typename_, type_) \
+#define ANKI_SVDT_MACRO(svdt, akType) \
 	template<> \
-	inline ShaderVariableDataType getShaderVariableTypeFromTypename<typename_>() \
+	inline ShaderVariableDataType getShaderVariableTypeFromTypename<akType>() \
 	{ \
-		return ShaderVariableDataType::type_; \
+		return ShaderVariableDataType::svdt; \
 	}
 
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(I32, INT)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(IVec2, IVEC2)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(IVec3, IVEC3)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(IVec4, IVEC4)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(U32, UINT)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(UVec2, UVEC2)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(UVec3, UVEC3)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(UVec4, UVEC4)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(F32, FLOAT)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(Vec2, VEC2)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(Vec3, VEC3)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(Vec4, VEC4)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(Mat3, MAT3)
-ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET(Mat4, MAT4)
-
-#undef ANKI_SPECIALIZE_SHADER_VAR_TYPE_GET
+#include <anki/gr/ShaderVariableDataTypeDefs.h>
+#undef ANKI_SVDT_MACRO
 
 /// Shader block information.
 class ShaderVariableBlockInfo
@@ -85,4 +71,7 @@ void writeShaderBlockMemory(ShaderVariableDataType type,
 	void* buffBegin,
 	const void* buffEnd);
 
+/// Convert a ShaderVariableDataType to string.
+const CString shaderVariableDataTypeToString(ShaderVariableDataType t);
+
 } // end namespace anki

+ 9 - 387
src/anki/shader_compiler/ShaderProgramCompiler.cpp

@@ -6,6 +6,7 @@
 #include <anki/shader_compiler/ShaderProgramCompiler.h>
 #include <anki/shader_compiler/ShaderProgramParser.h>
 #include <anki/shader_compiler/Glslang.h>
+#include <anki/shader_compiler/ShaderProgramReflection.h>
 #include <anki/util/Serializer.h>
 #include <anki/util/HashMap.h>
 #include <SPIRV-Cross/spirv_glsl.hpp>
@@ -130,386 +131,6 @@ static Bool spinDials(DynamicArrayAuto<U32>& dials, ConstWeakArray<ShaderProgram
 	return done;
 }
 
-/// Populates the reflection info.
-class SpirvReflector : public spirv_cross::Compiler
-{
-public:
-	SpirvReflector(const U32* ir, PtrSize wordCount)
-		: spirv_cross::Compiler(ir, wordCount)
-	{
-	}
-
-	ANKI_USE_RESULT static Error performSpirvReflection(ShaderProgramBinaryReflection& refl,
-		Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
-		GenericMemoryPoolAllocator<U8> tmpAlloc,
-		GenericMemoryPoolAllocator<U8> binaryAlloc);
-
-private:
-	ANKI_USE_RESULT Error spirvTypeToAnki(const spirv_cross::SPIRType& type, ShaderVariableDataType& out) const;
-
-	ANKI_USE_RESULT Error blockReflection(
-		const spirv_cross::Resource& res, Bool isStorage, DynamicArrayAuto<ShaderProgramBinaryBlock>& blocks) const;
-
-	ANKI_USE_RESULT Error opaqueReflection(
-		const spirv_cross::Resource& res, DynamicArrayAuto<ShaderProgramBinaryOpaque>& opaques) const;
-
-	ANKI_USE_RESULT Error constsReflection(DynamicArrayAuto<ShaderProgramBinaryConstant>& consts) const;
-};
-
-Error SpirvReflector::blockReflection(
-	const spirv_cross::Resource& res, Bool isStorage, DynamicArrayAuto<ShaderProgramBinaryBlock>& blocks) const
-{
-	ShaderProgramBinaryBlock newBlock;
-	const spirv_cross::SPIRType type = get_type(res.type_id);
-	const spirv_cross::Bitset decorationMask = get_decoration_bitset(res.id);
-
-	const Bool isPushConstant = get_storage_class(res.id) == spv::StorageClassPushConstant;
-	const Bool isBlock = get_decoration_bitset(type.self).get(spv::DecorationBlock)
-						 || get_decoration_bitset(type.self).get(spv::DecorationBufferBlock);
-
-	const spirv_cross::ID fallbackId =
-		(!isPushConstant && isBlock) ? spirv_cross::ID(res.base_type_id) : spirv_cross::ID(res.id);
-
-	// Name
-	{
-		const std::string name = (!res.name.empty()) ? res.name : get_fallback_name(fallbackId);
-		if(name.length() == 0 || name.length() > MAX_SHADER_BINARY_NAME_LENGTH)
-		{
-			ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
-			return Error::USER_DATA;
-		}
-		memcpy(newBlock.m_name.getBegin(), name.c_str(), name.length() + 1);
-	}
-
-	// Set
-	if(!isPushConstant)
-	{
-		newBlock.m_set = get_decoration(res.id, spv::DecorationDescriptorSet);
-		if(newBlock.m_set >= MAX_DESCRIPTOR_SETS)
-		{
-			ANKI_SHADER_COMPILER_LOGE("Too high descriptor set: %u", newBlock.m_set);
-			return Error::USER_DATA;
-		}
-	}
-
-	// Binding
-	if(!isPushConstant)
-	{
-		newBlock.m_binding = get_decoration(res.id, spv::DecorationBinding);
-	}
-
-	// Size
-	newBlock.m_size = U32(get_declared_struct_size(get_type(res.base_type_id)));
-	ANKI_ASSERT(isStorage || newBlock.m_size > 0);
-
-	// Add it
-	Bool found = false;
-	for(const ShaderProgramBinaryBlock& other : blocks)
-	{
-		const Bool bindingSame = other.m_set == newBlock.m_set && other.m_binding == newBlock.m_binding;
-		const Bool nameSame = strcmp(other.m_name.getBegin(), newBlock.m_name.getBegin()) == 0;
-		const Bool sizeSame = other.m_size == newBlock.m_size;
-		const Bool varsSame = other.m_variables.getSize() == newBlock.m_variables.getSize();
-
-		const Bool err0 = bindingSame && (!nameSame || !sizeSame || !varsSame);
-		const Bool err1 = nameSame && (!bindingSame || !sizeSame || !varsSame);
-		if(err0 || err1)
-		{
-			ANKI_SHADER_COMPILER_LOGE("Linking error");
-			return Error::USER_DATA;
-		}
-
-		if(bindingSame)
-		{
-			found = true;
-			break;
-		}
-	}
-
-	if(!found)
-	{
-		blocks.emplaceBack(newBlock);
-	}
-
-	return Error::NONE;
-}
-
-Error SpirvReflector::spirvTypeToAnki(const spirv_cross::SPIRType& type, ShaderVariableDataType& out) const
-{
-	switch(type.basetype)
-	{
-	case spirv_cross::SPIRType::Image:
-	case spirv_cross::SPIRType::SampledImage:
-	{
-		switch(type.image.dim)
-		{
-		case spv::Dim1D:
-			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_1D_ARRAY : ShaderVariableDataType::TEXTURE_1D;
-			break;
-		case spv::Dim2D:
-			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_2D_ARRAY : ShaderVariableDataType::TEXTURE_2D;
-			break;
-		case spv::Dim3D:
-			out = ShaderVariableDataType::TEXTURE_3D;
-			break;
-		case spv::DimCube:
-			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_CUBE_ARRAY
-									   : ShaderVariableDataType::TEXTURE_CUBE;
-			break;
-		default:
-			ANKI_ASSERT(0);
-		}
-
-		break;
-	}
-	case spirv_cross::SPIRType::Sampler:
-		out = ShaderVariableDataType::SAMPLER;
-		break;
-	default:
-		ANKI_SHADER_COMPILER_LOGE("Can't determine the type");
-		return Error::USER_DATA;
-	}
-
-	return Error::NONE;
-}
-
-Error SpirvReflector::opaqueReflection(
-	const spirv_cross::Resource& res, DynamicArrayAuto<ShaderProgramBinaryOpaque>& opaques) const
-{
-	ShaderProgramBinaryOpaque newOpaque;
-	const spirv_cross::SPIRType type = get_type(res.type_id);
-	const spirv_cross::Bitset decorationMask = get_decoration_bitset(res.id);
-
-	const spirv_cross::ID fallbackId = spirv_cross::ID(res.id);
-
-	// Name
-	const std::string name = (!res.name.empty()) ? res.name : get_fallback_name(fallbackId);
-	if(name.length() == 0 || name.length() > MAX_SHADER_BINARY_NAME_LENGTH)
-	{
-		ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
-		return Error::USER_DATA;
-	}
-	memcpy(newOpaque.m_name.getBegin(), name.c_str(), name.length() + 1);
-
-	// Type
-	ANKI_CHECK(spirvTypeToAnki(type, newOpaque.m_type));
-
-	// Set
-	newOpaque.m_set = get_decoration(res.id, spv::DecorationDescriptorSet);
-	if(newOpaque.m_set >= MAX_DESCRIPTOR_SETS)
-	{
-		ANKI_SHADER_COMPILER_LOGE("Too high descriptor set: %u", newOpaque.m_set);
-		return Error::USER_DATA;
-	}
-
-	// Binding
-	newOpaque.m_binding = get_decoration(res.id, spv::DecorationBinding);
-
-	// Size
-	if(type.array.size() == 0)
-	{
-		newOpaque.m_arraySize = 1;
-	}
-	else if(type.array.size() == 1)
-	{
-		newOpaque.m_arraySize = type.array[0];
-	}
-	else
-	{
-		ANKI_SHADER_COMPILER_LOGE("Can't support multi-dimensional arrays: %s", newOpaque.m_name.getBegin());
-		return Error::USER_DATA;
-	}
-
-	// Add it
-	Bool found = false;
-	for(const ShaderProgramBinaryOpaque& other : opaques)
-	{
-		const Bool bindingSame = other.m_set == newOpaque.m_set && other.m_binding == newOpaque.m_binding;
-		const Bool nameSame = strcmp(other.m_name.getBegin(), newOpaque.m_name.getBegin()) == 0;
-		const Bool sizeSame = other.m_arraySize == newOpaque.m_arraySize;
-		const Bool typeSame = other.m_type == newOpaque.m_type;
-
-		const Bool err0 = bindingSame && (!nameSame || !sizeSame || !typeSame);
-		const Bool err1 = nameSame && (!bindingSame || !sizeSame || !typeSame);
-		if(err0 || err1)
-		{
-			ANKI_SHADER_COMPILER_LOGE("Linking error");
-			return Error::USER_DATA;
-		}
-
-		if(bindingSame)
-		{
-			found = true;
-			break;
-		}
-	}
-
-	if(!found)
-	{
-		opaques.emplaceBack(newOpaque);
-	}
-
-	return Error::NONE;
-}
-
-Error SpirvReflector::constsReflection(DynamicArrayAuto<ShaderProgramBinaryConstant>& consts) const
-{
-	spirv_cross::SmallVector<spirv_cross::SpecializationConstant> specConsts = get_specialization_constants();
-	for(const spirv_cross::SpecializationConstant& c : specConsts)
-	{
-		ShaderProgramBinaryConstant newConst;
-
-		const spirv_cross::SPIRConstant cc = get<spirv_cross::SPIRConstant>(c.id);
-		const spirv_cross::SPIRType type = get<spirv_cross::SPIRType>(cc.constant_type);
-
-		const std::string name = get_name(c.id);
-		if(name.length() == 0 || name.length() > MAX_SHADER_BINARY_NAME_LENGTH)
-		{
-			ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
-			return Error::USER_DATA;
-		}
-		memcpy(newConst.m_name.getBegin(), name.c_str(), name.length() + 1);
-
-		newConst.m_constantId = c.constant_id;
-
-		switch(type.basetype)
-		{
-		case spirv_cross::SPIRType::UInt:
-		case spirv_cross::SPIRType::Int:
-			newConst.m_type = ShaderVariableDataType::INT;
-			break;
-		case spirv_cross::SPIRType::Float:
-			newConst.m_type = ShaderVariableDataType::FLOAT;
-			break;
-		default:
-			ANKI_SHADER_COMPILER_LOGE("Can't determine the type of the spec constant: %s", name.c_str());
-			return Error::USER_DATA;
-		}
-
-		// Add it
-		Bool found = false;
-		for(const ShaderProgramBinaryConstant& other : consts)
-		{
-			const Bool nameSame = strcmp(other.m_name.getBegin(), newConst.m_name.getBegin()) == 0;
-			const Bool typeSame = other.m_type == newConst.m_type;
-			const Bool idSame = other.m_constantId == newConst.m_constantId;
-
-			const Bool err0 = nameSame && (!typeSame || !idSame);
-			const Bool err1 = idSame && (!nameSame || !typeSame);
-			if(err0 || err1)
-			{
-				ANKI_SHADER_COMPILER_LOGE("Linking error");
-				return Error::USER_DATA;
-			}
-
-			if(idSame)
-			{
-				found = true;
-				break;
-			}
-		}
-
-		if(!found)
-		{
-			consts.emplaceBack(newConst);
-		}
-	}
-
-	return Error::NONE;
-}
-
-Error SpirvReflector::performSpirvReflection(ShaderProgramBinaryReflection& refl,
-	Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
-	GenericMemoryPoolAllocator<U8> tmpAlloc,
-	GenericMemoryPoolAllocator<U8> binaryAlloc)
-{
-	DynamicArrayAuto<ShaderProgramBinaryBlock> uniformBlocks(binaryAlloc);
-	DynamicArrayAuto<ShaderProgramBinaryBlock> storageBlocks(binaryAlloc);
-	DynamicArrayAuto<ShaderProgramBinaryBlock> pushConstantBlock(binaryAlloc);
-	DynamicArrayAuto<ShaderProgramBinaryOpaque> opaques(binaryAlloc);
-	DynamicArrayAuto<ShaderProgramBinaryConstant> specializationConstants(binaryAlloc);
-
-	// Perform reflection for each stage
-	for(const ShaderType type : EnumIterable<ShaderType>())
-	{
-		if(spirv[type].getSize() == 0)
-		{
-			continue;
-		}
-
-		// Parse SPIR-V
-		const unsigned int* spvb = reinterpret_cast<const unsigned int*>(spirv[type].getBegin());
-		SpirvReflector compiler(spvb, spirv[type].getSizeInBytes() / sizeof(unsigned int));
-
-		// Uniform blocks
-		for(const spirv_cross::Resource& res : compiler.get_shader_resources().uniform_buffers)
-		{
-			ANKI_CHECK(compiler.blockReflection(res, false, uniformBlocks));
-		}
-
-		// Sorage blocks
-		for(const spirv_cross::Resource& res : compiler.get_shader_resources().storage_buffers)
-		{
-			ANKI_CHECK(compiler.blockReflection(res, true, storageBlocks));
-		}
-
-		// Push constants
-		if(compiler.get_shader_resources().push_constant_buffers.size() == 1)
-		{
-			ANKI_CHECK(compiler.blockReflection(
-				compiler.get_shader_resources().push_constant_buffers[0], false, pushConstantBlock));
-		}
-		else if(compiler.get_shader_resources().push_constant_buffers.size() > 1)
-		{
-			ANKI_SHADER_COMPILER_LOGE("Expecting only a single push constants block");
-			return Error::USER_DATA;
-		}
-
-		// Opaque
-		for(const spirv_cross::Resource& res : compiler.get_shader_resources().separate_images)
-		{
-			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
-		}
-		for(const spirv_cross::Resource& res : compiler.get_shader_resources().storage_images)
-		{
-			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
-		}
-		for(const spirv_cross::Resource& res : compiler.get_shader_resources().separate_samplers)
-		{
-			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
-		}
-
-		// Spec consts
-		ANKI_CHECK(compiler.constsReflection(specializationConstants));
-
-		// TODO
-	}
-
-	ShaderProgramBinaryBlock* firstBlock;
-	U32 size, storage;
-	uniformBlocks.moveAndReset(firstBlock, size, storage);
-	refl.m_uniformBlocks.setArray(firstBlock, size);
-
-	storageBlocks.moveAndReset(firstBlock, size, storage);
-	refl.m_storageBlocks.setArray(firstBlock, size);
-
-	if(pushConstantBlock.getSize() == 1)
-	{
-		pushConstantBlock.moveAndReset(firstBlock, size, storage);
-		refl.m_pushConstantBlock = firstBlock;
-	}
-
-	ShaderProgramBinaryOpaque* firstOpaque;
-	opaques.moveAndReset(firstOpaque, size, storage);
-	refl.m_opaques.setArray(firstOpaque, size);
-
-	ShaderProgramBinaryConstant* firstConst;
-	specializationConstants.moveAndReset(firstConst, size, storage);
-	refl.m_specializationConstants.setArray(firstConst, size);
-
-	return Error::NONE;
-}
-
 static Error compileVariant(ConstWeakArray<MutatorValue> mutation,
 	const ShaderProgramParser& parser,
 	ShaderProgramBinaryVariant& variant,
@@ -572,7 +193,7 @@ static Error compileVariant(ConstWeakArray<MutatorValue> mutation,
 	}
 
 	// Do reflection
-	ANKI_CHECK(SpirvReflector::performSpirvReflection(variant.m_reflection, spirvBinaries, tmpAlloc, binaryAlloc));
+	ANKI_CHECK(performSpirvReflection(variant.m_reflection, spirvBinaries, tmpAlloc, binaryAlloc));
 
 	return Error::NONE;
 }
@@ -786,9 +407,10 @@ static void disassembleBlock(const ShaderProgramBinaryBlock& block, StringListAu
 
 	for(const ShaderProgramBinaryVariable& var : block.m_variables)
 	{
-		lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB ANKI_TAB "\"%s\" type: %u active: %s blockInfo: %d,%d,%d,%d\n",
+		lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB ANKI_TAB
+			"%-28s type: %8s active: %4s blockInfo: %d,%d,%d,%d\n",
 			var.m_name.getBegin(),
-			U32(var.m_type),
+			shaderVariableDataTypeToString(var.m_type).cstr(),
 			var.m_active ? "true" : "false",
 			var.m_blockInfo.m_offset,
 			var.m_blockInfo.m_arraySize,
@@ -874,11 +496,11 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Opaque\n");
 			for(const ShaderProgramBinaryOpaque& o : variant.m_reflection.m_opaques)
 			{
-				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s set %4u binding %4u type %4u arraySize %4u\n",
+				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s set %4u binding %4u type %8s arraySize %4u\n",
 					o.m_name.getBegin(),
 					o.m_set,
 					o.m_binding,
-					o.m_type,
+					shaderVariableDataTypeToString(o.m_type).cstr(),
 					o.m_arraySize);
 			}
 		}
@@ -896,9 +518,9 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Specialization constants\n");
 			for(const ShaderProgramBinaryConstant& c : variant.m_reflection.m_specializationConstants)
 			{
-				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s type %4u id %4u\n",
+				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s type %8s id %4u\n",
 					c.m_name.getBegin(),
-					U32(c.m_type),
+					shaderVariableDataTypeToString(c.m_type).cstr(),
 					c.m_constantId);
 			}
 		}

+ 580 - 0
src/anki/shader_compiler/ShaderProgramReflection.cpp

@@ -0,0 +1,580 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/shader_compiler/ShaderProgramReflection.h>
+#include <anki/shader_compiler/ShaderProgramBinary.h>
+#include <SPIRV-Cross/spirv_glsl.hpp>
+
+namespace anki
+{
+
+/// Populates the reflection info.
+class SpirvReflector : public spirv_cross::Compiler
+{
+public:
+	SpirvReflector(const U32* ir, PtrSize wordCount)
+		: spirv_cross::Compiler(ir, wordCount)
+	{
+	}
+
+	ANKI_USE_RESULT static Error performSpirvReflection(ShaderProgramBinaryReflection& refl,
+		Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
+		GenericMemoryPoolAllocator<U8> tmpAlloc,
+		GenericMemoryPoolAllocator<U8> binaryAlloc);
+
+private:
+	ANKI_USE_RESULT Error spirvTypeToAnki(const spirv_cross::SPIRType& type, ShaderVariableDataType& out) const;
+
+	ANKI_USE_RESULT Error blockReflection(
+		const spirv_cross::Resource& res, Bool isStorage, DynamicArrayAuto<ShaderProgramBinaryBlock>& blocks) const;
+
+	ANKI_USE_RESULT Error opaqueReflection(
+		const spirv_cross::Resource& res, DynamicArrayAuto<ShaderProgramBinaryOpaque>& opaques) const;
+
+	ANKI_USE_RESULT Error constsReflection(DynamicArrayAuto<ShaderProgramBinaryConstant>& consts) const;
+
+	ANKI_USE_RESULT Error blockVariablesReflection(
+		spirv_cross::TypeID resourceId, DynamicArrayAuto<ShaderProgramBinaryVariable>& vars) const;
+
+	ANKI_USE_RESULT Error blockVariableReflection(
+		const spirv_cross::SPIRType& type, DynamicArrayAuto<ShaderProgramBinaryVariable>& vars) const;
+};
+
+Error SpirvReflector::blockVariablesReflection(
+	spirv_cross::TypeID resourceId, DynamicArrayAuto<ShaderProgramBinaryVariable>& vars) const
+{
+	Bool found = false;
+	Error err = Error::NONE;
+	ir.for_each_typed_id<spirv_cross::SPIRType>([&](uint32_t, const spirv_cross::SPIRType& type) {
+		if(err)
+		{
+			return;
+		}
+
+		if(type.basetype == spirv_cross::SPIRType::Struct && !type.pointer && type.array.empty())
+		{
+			if(type.self == resourceId)
+			{
+				found = true;
+				err = blockVariableReflection(type, vars);
+			}
+		}
+	});
+	ANKI_CHECK(err);
+
+	if(!found)
+	{
+		ANKI_SHADER_COMPILER_LOGE("Can't determine the type of a block");
+		return Error::USER_DATA;
+	}
+
+	return Error::NONE;
+}
+
+Error SpirvReflector::blockVariableReflection(
+	const spirv_cross::SPIRType& type, DynamicArrayAuto<ShaderProgramBinaryVariable>& vars) const
+{
+	ANKI_ASSERT(type.basetype == spirv_cross::SPIRType::Struct);
+
+	for(U32 i = 0; i < type.member_types.size(); ++i)
+	{
+		ShaderProgramBinaryVariable var;
+		const spirv_cross::SPIRType& memberType = get<spirv_cross::SPIRType>(type.member_types[i]);
+
+		// Name
+		{
+			const spirv_cross::Meta* meta = ir.find_meta(type.self);
+			ANKI_ASSERT(meta);
+			ANKI_ASSERT(i < meta->members.size());
+			ANKI_ASSERT(!meta->members[i].alias.empty());
+			const std::string& name = meta->members[i].alias;
+			strcpy(var.m_name.getBegin(), name.c_str());
+		}
+
+		// Offset
+		{
+			auto it = ir.meta.find(type.self);
+			ANKI_ASSERT(it != ir.meta.end());
+			const spirv_cross::Vector<spirv_cross::Meta::Decoration>& memb = it->second.members;
+			ANKI_ASSERT(i < memb.size());
+			const spirv_cross::Meta::Decoration& dec = memb[i];
+			ANKI_ASSERT(dec.decoration_flags.get(spv::DecorationOffset));
+			var.m_blockInfo.m_offset = I16(dec.offset);
+		}
+
+		// Array size
+		{
+			if(!memberType.array.empty())
+			{
+				if(memberType.array.size() > 1)
+				{
+					ANKI_SHADER_COMPILER_LOGE("Can't support multi-dimentional arrays at the moment");
+					return Error::USER_DATA;
+				}
+
+				var.m_blockInfo.m_arraySize = I16(memberType.array[0]);
+			}
+			else
+			{
+				var.m_blockInfo.m_arraySize = 1;
+			}
+		}
+
+		// TODO stride
+
+		// Type
+		auto func = [&](const Array<ShaderVariableDataType, 3>& arr) {
+			switch(memberType.basetype)
+			{
+			case spirv_cross::SPIRType::UInt:
+				var.m_type = arr[0];
+				break;
+			case spirv_cross::SPIRType::Int:
+				var.m_type = arr[1];
+				break;
+			case spirv_cross::SPIRType::Float:
+				var.m_type = arr[2];
+				break;
+			default:
+				ANKI_ASSERT(0);
+			}
+		};
+
+		const Bool isNumeric = memberType.basetype == spirv_cross::SPIRType::UInt
+							   || memberType.basetype == spirv_cross::SPIRType::Int
+							   || memberType.basetype == spirv_cross::SPIRType::Float;
+
+		if(memberType.basetype == spirv_cross::SPIRType::Struct)
+		{
+			// ANKI_ASSERT(!"TODO");
+			printf("%s\n", var.m_name.getBegin());
+		}
+		else if(memberType.vecsize == 1 && memberType.columns == 1 && isNumeric)
+		{
+			static const Array<ShaderVariableDataType, 3> arr = {
+				{ShaderVariableDataType::UINT, ShaderVariableDataType::INT, ShaderVariableDataType::FLOAT}};
+			func(arr);
+		}
+		else if(memberType.vecsize == 2 && memberType.columns == 1 && isNumeric)
+		{
+			static const Array<ShaderVariableDataType, 3> arr = {
+				{ShaderVariableDataType::UVEC2, ShaderVariableDataType::IVEC2, ShaderVariableDataType::VEC2}};
+			func(arr);
+		}
+		else if(memberType.vecsize == 3 && memberType.columns == 1 && isNumeric)
+		{
+			static const Array<ShaderVariableDataType, 3> arr = {
+				{ShaderVariableDataType::UVEC3, ShaderVariableDataType::IVEC3, ShaderVariableDataType::VEC3}};
+			func(arr);
+		}
+		else if(memberType.vecsize == 4 && memberType.columns == 1 && isNumeric)
+		{
+			static const Array<ShaderVariableDataType, 3> arr = {
+				{ShaderVariableDataType::UVEC4, ShaderVariableDataType::IVEC4, ShaderVariableDataType::VEC4}};
+			func(arr);
+		}
+		else if(memberType.vecsize == 3 && memberType.columns == 3
+				&& memberType.basetype == spirv_cross::SPIRType::Float)
+		{
+			var.m_type = ShaderVariableDataType::MAT3;
+			var.m_blockInfo.m_matrixStride = 16;
+		}
+		else if(memberType.vecsize == 4 && memberType.columns == 3
+				&& memberType.basetype == spirv_cross::SPIRType::Float)
+		{
+			var.m_type = ShaderVariableDataType::MAT3X4;
+			var.m_blockInfo.m_matrixStride = 16;
+		}
+		else if(memberType.vecsize == 4 && memberType.columns == 4
+				&& memberType.basetype == spirv_cross::SPIRType::Float)
+		{
+			var.m_type = ShaderVariableDataType::MAT4;
+			var.m_blockInfo.m_matrixStride = 16;
+		}
+		else
+		{
+			ANKI_SHADER_COMPILER_LOGE("Unhandled base type for member: %s", var.m_name.getBegin());
+			return Error::FUNCTION_FAILED;
+		}
+
+		// Store the member
+		vars.emplaceBack(var);
+	}
+
+	return Error::NONE;
+}
+
+Error SpirvReflector::blockReflection(
+	const spirv_cross::Resource& res, Bool isStorage, DynamicArrayAuto<ShaderProgramBinaryBlock>& blocks) const
+{
+	ShaderProgramBinaryBlock newBlock;
+	const spirv_cross::SPIRType type = get_type(res.type_id);
+	const spirv_cross::Bitset decorationMask = get_decoration_bitset(res.id);
+
+	const Bool isPushConstant = get_storage_class(res.id) == spv::StorageClassPushConstant;
+	const Bool isBlock = get_decoration_bitset(type.self).get(spv::DecorationBlock)
+						 || get_decoration_bitset(type.self).get(spv::DecorationBufferBlock);
+
+	const spirv_cross::ID fallbackId =
+		(!isPushConstant && isBlock) ? spirv_cross::ID(res.base_type_id) : spirv_cross::ID(res.id);
+
+	// Name
+	{
+		const std::string name = (!res.name.empty()) ? res.name : get_fallback_name(fallbackId);
+		if(name.length() == 0 || name.length() > MAX_SHADER_BINARY_NAME_LENGTH)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
+			return Error::USER_DATA;
+		}
+		memcpy(newBlock.m_name.getBegin(), name.c_str(), name.length() + 1);
+	}
+
+	// Set
+	if(!isPushConstant)
+	{
+		newBlock.m_set = get_decoration(res.id, spv::DecorationDescriptorSet);
+		if(newBlock.m_set >= MAX_DESCRIPTOR_SETS)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Too high descriptor set: %u", newBlock.m_set);
+			return Error::USER_DATA;
+		}
+	}
+
+	// Binding
+	if(!isPushConstant)
+	{
+		newBlock.m_binding = get_decoration(res.id, spv::DecorationBinding);
+	}
+
+	// Size
+	newBlock.m_size = U32(get_declared_struct_size(get_type(res.base_type_id)));
+	ANKI_ASSERT(isStorage || newBlock.m_size > 0);
+
+	// Add it
+	Bool found = false;
+	for(const ShaderProgramBinaryBlock& other : blocks)
+	{
+		const Bool bindingSame = other.m_set == newBlock.m_set && other.m_binding == newBlock.m_binding;
+		const Bool nameSame = strcmp(other.m_name.getBegin(), newBlock.m_name.getBegin()) == 0;
+		const Bool sizeSame = other.m_size == newBlock.m_size;
+		const Bool varsSame = other.m_variables.getSize() == newBlock.m_variables.getSize();
+
+		const Bool err0 = bindingSame && (!nameSame || !sizeSame || !varsSame);
+		const Bool err1 = nameSame && (!bindingSame || !sizeSame || !varsSame);
+		if(err0 || err1)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Linking error");
+			return Error::USER_DATA;
+		}
+
+		if(bindingSame)
+		{
+			found = true;
+			break;
+		}
+	}
+
+	if(!found)
+	{
+		// Get the variables
+		DynamicArrayAuto<ShaderProgramBinaryVariable> vars(blocks.getAllocator());
+		ANKI_CHECK(blockVariablesReflection(res.base_type_id, vars));
+		ShaderProgramBinaryVariable* firstVar;
+		U32 size, storage;
+		vars.moveAndReset(firstVar, size, storage);
+		newBlock.m_variables.setArray(firstVar, size);
+
+		// Store the block
+		blocks.emplaceBack(newBlock);
+	}
+
+	return Error::NONE;
+}
+
+Error SpirvReflector::spirvTypeToAnki(const spirv_cross::SPIRType& type, ShaderVariableDataType& out) const
+{
+	switch(type.basetype)
+	{
+	case spirv_cross::SPIRType::Image:
+	case spirv_cross::SPIRType::SampledImage:
+	{
+		switch(type.image.dim)
+		{
+		case spv::Dim1D:
+			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_1D_ARRAY : ShaderVariableDataType::TEXTURE_1D;
+			break;
+		case spv::Dim2D:
+			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_2D_ARRAY : ShaderVariableDataType::TEXTURE_2D;
+			break;
+		case spv::Dim3D:
+			out = ShaderVariableDataType::TEXTURE_3D;
+			break;
+		case spv::DimCube:
+			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_CUBE_ARRAY
+									   : ShaderVariableDataType::TEXTURE_CUBE;
+			break;
+		default:
+			ANKI_ASSERT(0);
+		}
+
+		break;
+	}
+	case spirv_cross::SPIRType::Sampler:
+		out = ShaderVariableDataType::SAMPLER;
+		break;
+	default:
+		ANKI_SHADER_COMPILER_LOGE("Can't determine the type");
+		return Error::USER_DATA;
+	}
+
+	return Error::NONE;
+}
+
+Error SpirvReflector::opaqueReflection(
+	const spirv_cross::Resource& res, DynamicArrayAuto<ShaderProgramBinaryOpaque>& opaques) const
+{
+	ShaderProgramBinaryOpaque newOpaque;
+	const spirv_cross::SPIRType type = get_type(res.type_id);
+	const spirv_cross::Bitset decorationMask = get_decoration_bitset(res.id);
+
+	const spirv_cross::ID fallbackId = spirv_cross::ID(res.id);
+
+	// Name
+	const std::string name = (!res.name.empty()) ? res.name : get_fallback_name(fallbackId);
+	if(name.length() == 0 || name.length() > MAX_SHADER_BINARY_NAME_LENGTH)
+	{
+		ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
+		return Error::USER_DATA;
+	}
+	memcpy(newOpaque.m_name.getBegin(), name.c_str(), name.length() + 1);
+
+	// Type
+	ANKI_CHECK(spirvTypeToAnki(type, newOpaque.m_type));
+
+	// Set
+	newOpaque.m_set = get_decoration(res.id, spv::DecorationDescriptorSet);
+	if(newOpaque.m_set >= MAX_DESCRIPTOR_SETS)
+	{
+		ANKI_SHADER_COMPILER_LOGE("Too high descriptor set: %u", newOpaque.m_set);
+		return Error::USER_DATA;
+	}
+
+	// Binding
+	newOpaque.m_binding = get_decoration(res.id, spv::DecorationBinding);
+
+	// Size
+	if(type.array.size() == 0)
+	{
+		newOpaque.m_arraySize = 1;
+	}
+	else if(type.array.size() == 1)
+	{
+		newOpaque.m_arraySize = type.array[0];
+	}
+	else
+	{
+		ANKI_SHADER_COMPILER_LOGE("Can't support multi-dimensional arrays: %s", newOpaque.m_name.getBegin());
+		return Error::USER_DATA;
+	}
+
+	// Add it
+	Bool found = false;
+	for(const ShaderProgramBinaryOpaque& other : opaques)
+	{
+		const Bool bindingSame = other.m_set == newOpaque.m_set && other.m_binding == newOpaque.m_binding;
+		const Bool nameSame = strcmp(other.m_name.getBegin(), newOpaque.m_name.getBegin()) == 0;
+		const Bool sizeSame = other.m_arraySize == newOpaque.m_arraySize;
+		const Bool typeSame = other.m_type == newOpaque.m_type;
+
+		const Bool err0 = bindingSame && (!nameSame || !sizeSame || !typeSame);
+		const Bool err1 = nameSame && (!bindingSame || !sizeSame || !typeSame);
+		if(err0 || err1)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Linking error");
+			return Error::USER_DATA;
+		}
+
+		if(bindingSame)
+		{
+			found = true;
+			break;
+		}
+	}
+
+	if(!found)
+	{
+		opaques.emplaceBack(newOpaque);
+	}
+
+	return Error::NONE;
+}
+
+Error SpirvReflector::constsReflection(DynamicArrayAuto<ShaderProgramBinaryConstant>& consts) const
+{
+	spirv_cross::SmallVector<spirv_cross::SpecializationConstant> specConsts = get_specialization_constants();
+	for(const spirv_cross::SpecializationConstant& c : specConsts)
+	{
+		ShaderProgramBinaryConstant newConst;
+
+		const spirv_cross::SPIRConstant cc = get<spirv_cross::SPIRConstant>(c.id);
+		const spirv_cross::SPIRType type = get<spirv_cross::SPIRType>(cc.constant_type);
+
+		const std::string name = get_name(c.id);
+		if(name.length() == 0 || name.length() > MAX_SHADER_BINARY_NAME_LENGTH)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
+			return Error::USER_DATA;
+		}
+		memcpy(newConst.m_name.getBegin(), name.c_str(), name.length() + 1);
+
+		newConst.m_constantId = c.constant_id;
+
+		switch(type.basetype)
+		{
+		case spirv_cross::SPIRType::UInt:
+		case spirv_cross::SPIRType::Int:
+			newConst.m_type = ShaderVariableDataType::INT;
+			break;
+		case spirv_cross::SPIRType::Float:
+			newConst.m_type = ShaderVariableDataType::FLOAT;
+			break;
+		default:
+			ANKI_SHADER_COMPILER_LOGE("Can't determine the type of the spec constant: %s", name.c_str());
+			return Error::USER_DATA;
+		}
+
+		// Add it
+		Bool found = false;
+		for(const ShaderProgramBinaryConstant& other : consts)
+		{
+			const Bool nameSame = strcmp(other.m_name.getBegin(), newConst.m_name.getBegin()) == 0;
+			const Bool typeSame = other.m_type == newConst.m_type;
+			const Bool idSame = other.m_constantId == newConst.m_constantId;
+
+			const Bool err0 = nameSame && (!typeSame || !idSame);
+			const Bool err1 = idSame && (!nameSame || !typeSame);
+			if(err0 || err1)
+			{
+				ANKI_SHADER_COMPILER_LOGE("Linking error");
+				return Error::USER_DATA;
+			}
+
+			if(idSame)
+			{
+				found = true;
+				break;
+			}
+		}
+
+		if(!found)
+		{
+			consts.emplaceBack(newConst);
+		}
+	}
+
+	return Error::NONE;
+}
+
+Error SpirvReflector::performSpirvReflection(ShaderProgramBinaryReflection& refl,
+	Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
+	GenericMemoryPoolAllocator<U8> tmpAlloc,
+	GenericMemoryPoolAllocator<U8> binaryAlloc)
+{
+	DynamicArrayAuto<ShaderProgramBinaryBlock> uniformBlocks(binaryAlloc);
+	DynamicArrayAuto<ShaderProgramBinaryBlock> storageBlocks(binaryAlloc);
+	DynamicArrayAuto<ShaderProgramBinaryBlock> pushConstantBlock(binaryAlloc);
+	DynamicArrayAuto<ShaderProgramBinaryOpaque> opaques(binaryAlloc);
+	DynamicArrayAuto<ShaderProgramBinaryConstant> specializationConstants(binaryAlloc);
+
+	// Perform reflection for each stage
+	for(const ShaderType type : EnumIterable<ShaderType>())
+	{
+		if(spirv[type].getSize() == 0)
+		{
+			continue;
+		}
+
+		// Parse SPIR-V
+		const unsigned int* spvb = reinterpret_cast<const unsigned int*>(spirv[type].getBegin());
+		SpirvReflector compiler(spvb, spirv[type].getSizeInBytes() / sizeof(unsigned int));
+
+		// Uniform blocks
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().uniform_buffers)
+		{
+			ANKI_CHECK(compiler.blockReflection(res, false, uniformBlocks));
+		}
+
+		// Sorage blocks
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().storage_buffers)
+		{
+			ANKI_CHECK(compiler.blockReflection(res, true, storageBlocks));
+		}
+
+		// Push constants
+		if(compiler.get_shader_resources().push_constant_buffers.size() == 1)
+		{
+			ANKI_CHECK(compiler.blockReflection(
+				compiler.get_shader_resources().push_constant_buffers[0], false, pushConstantBlock));
+		}
+		else if(compiler.get_shader_resources().push_constant_buffers.size() > 1)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Expecting only a single push constants block");
+			return Error::USER_DATA;
+		}
+
+		// Opaque
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().separate_images)
+		{
+			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
+		}
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().storage_images)
+		{
+			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
+		}
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().separate_samplers)
+		{
+			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
+		}
+
+		// Spec consts
+		ANKI_CHECK(compiler.constsReflection(specializationConstants));
+
+		// TODO
+	}
+
+	ShaderProgramBinaryBlock* firstBlock;
+	U32 size, storage;
+	uniformBlocks.moveAndReset(firstBlock, size, storage);
+	refl.m_uniformBlocks.setArray(firstBlock, size);
+
+	storageBlocks.moveAndReset(firstBlock, size, storage);
+	refl.m_storageBlocks.setArray(firstBlock, size);
+
+	if(pushConstantBlock.getSize() == 1)
+	{
+		pushConstantBlock.moveAndReset(firstBlock, size, storage);
+		refl.m_pushConstantBlock = firstBlock;
+	}
+
+	ShaderProgramBinaryOpaque* firstOpaque;
+	opaques.moveAndReset(firstOpaque, size, storage);
+	refl.m_opaques.setArray(firstOpaque, size);
+
+	ShaderProgramBinaryConstant* firstConst;
+	specializationConstants.moveAndReset(firstConst, size, storage);
+	refl.m_specializationConstants.setArray(firstConst, size);
+
+	return Error::NONE;
+}
+
+Error performSpirvReflection(ShaderProgramBinaryReflection& refl,
+	Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
+	GenericMemoryPoolAllocator<U8> tmpAlloc,
+	GenericMemoryPoolAllocator<U8> binaryAlloc)
+{
+	return SpirvReflector::performSpirvReflection(refl, spirv, tmpAlloc, binaryAlloc);
+}
+
+} // end namespace anki

+ 27 - 0
src/anki/shader_compiler/ShaderProgramReflection.h

@@ -0,0 +1,27 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/shader_compiler/Common.h>
+#include <anki/gr/Enums.h>
+#include <anki/util/WeakArray.h>
+
+namespace anki
+{
+
+// Forward
+class ShaderProgramBinaryReflection;
+
+/// @addtogroup shader_compiler
+/// @{
+
+ANKI_USE_RESULT Error performSpirvReflection(ShaderProgramBinaryReflection& refl,
+	Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
+	GenericMemoryPoolAllocator<U8> tmpAlloc,
+	GenericMemoryPoolAllocator<U8> binaryAlloc);
+/// @}
+
+} // end namespace anki

+ 6 - 0
src/anki/util/DynamicArray.h

@@ -441,6 +441,12 @@ public:
 		// Don't touch the m_alloc
 	}
 
+	/// Get the allocator.
+	const GenericMemoryPoolAllocator<T>& getAllocator() const
+	{
+		return m_alloc;
+	}
+
 private:
 	GenericMemoryPoolAllocator<T> m_alloc;
 };

+ 13 - 8
tests/shader_compiler/ShaderProgramCompiler.cpp

@@ -19,19 +19,24 @@ ANKI_TEST(ShaderCompiler, ShaderProgramCompiler)
 
 ANKI_SPECIALIZATION_CONSTANT_I32(INSTANCE_COUNT, 0, 1);
 
-layout(set = 1, binding = 0) uniform u_
+struct PerInstance
 {
-	Mat4 u_mvp[INSTANCE_COUNT];
+	Mat4 m_mvp;
 #if PASS > 1
-	Mat3 u_normalMat[INSTANCE_COUNT];
+	Mat3 m_normalMat;
 #endif
 };
 
-layout(set = 1, binding = 1) buffer u2_
+layout(set = 1, binding = 0) uniform perInstance
 {
-	Mat4 u_mvp2[INSTANCE_COUNT];
+	PerInstance u_perInstance[INSTANCE_COUNT];
+};
+
+layout(set = 1, binding = 1) buffer perDrawcall
+{
+	Vec4 u_color;
 #if PASS > 1
-	Mat3 u_normalMat2[INSTANCE_COUNT];
+	Mat3 u_someMat;
 #endif
 };
 
@@ -50,7 +55,7 @@ out gl_PerVertex
 
 void main()
 {
-	gl_Position = u_mvp[gl_InstanceID] * u_mvp2[gl_InstanceID] * Vec4(gl_VertexID) * Vec4(specConst);
+	gl_Position = u_perInstance[gl_InstanceID].m_mvp * Vec4(specConst);
 }
 #pragma anki end
 
@@ -62,7 +67,7 @@ void main()
 #if DIFFUSE_TEX == 1
 	out_color = texture(sampler2D(u_tex[0], u_sampler), Vec2(0));
 #else
-	out_color = Vec4(0);
+	out_color = u_color;
 #endif
 }
 #pragma anki end