Browse Source

Merge pull request #61 from godlikepanos/shader_compiler_2

Shader compiler #2
Panagiotis Christopoulos Charitos 5 years ago
parent
commit
d8d9aa41cf

+ 12 - 27
src/anki/gr/Enums.h

@@ -478,29 +478,14 @@ inline ShaderTypeBit shaderTypeToBit(ShaderType type)
 enum class ShaderVariableDataType : U8
 {
 	NONE,
-	INT,
-	UINT,
-	FLOAT,
-	IVEC2,
-	UVEC2,
-	VEC2,
-	IVEC3,
-	UVEC3,
-	VEC3,
-	IVEC4,
-	UVEC4,
-	VEC4,
-	MAT3,
-	MAT4,
-	COMBINED_TEXTURE_SAMPLER_2D,
-	COMBINED_TEXTURE_SAMPLER_3D,
-	COMBINED_TEXTURE_SAMPLER_2D_ARRAY,
-	COMBINED_TEXTURE_SAMPLER_CUBE,
-	TEXTURE_2D,
-	TEXTURE_3D,
-	TEXTURE_2D_ARRAY,
-	TEXTURE_CUBE,
-	SAMPLER,
+
+#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,
@@ -517,11 +502,11 @@ enum class ShaderVariableDataType : U8
 	MATRIX_FIRST = MAT3,
 	MATRIX_LAST = MAT4,
 
-	COMBINED_TEXTURE_SAMPLERS_FIRST = COMBINED_TEXTURE_SAMPLER_2D,
-	COMBINED_TEXTURE_SAMPLERS_LAST = COMBINED_TEXTURE_SAMPLER_CUBE,
+	TEXTURE_FIRST = TEXTURE_1D,
+	TEXTURE_LAST = TEXTURE_CUBE_ARRAY,
 
-	TEXTURE_FIRST = TEXTURE_2D,
-	TEXTURE_LAST = TEXTURE_CUBE,
+	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

+ 2 - 2
src/anki/gr/vulkan/ShaderImpl.cpp

@@ -21,7 +21,7 @@ namespace anki
 class ShaderImpl::SpecConstsVector
 {
 public:
-	std::vector<spirv_cross::SpecializationConstant> m_vec;
+	spirv_cross::SmallVector<spirv_cross::SpecializationConstant> m_vec;
 };
 
 ShaderImpl::~ShaderImpl()
@@ -120,7 +120,7 @@ void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specCo
 	}};
 	Array2d<DescriptorBinding, MAX_DESCRIPTOR_SETS, MAX_BINDINGS_PER_DESCRIPTOR_SET> descriptors;
 
-	auto func = [&](const std::vector<spirv_cross::Resource>& resources, DescriptorType type) -> void {
+	auto func = [&](const spirv_cross::SmallVector<spirv_cross::Resource>& resources, DescriptorType type) -> void {
 		for(const spirv_cross::Resource& r : resources)
 		{
 			const U32 id = r.id;

+ 0 - 2
src/anki/shader_compiler/Common.h

@@ -20,10 +20,8 @@ namespace anki
 #define ANKI_SHADER_COMPILER_LOGW(...) ANKI_LOG("SHCO", WARNING, __VA_ARGS__)
 #define ANKI_SHADER_COMPILER_LOGF(...) ANKI_LOG("SHCO", FATAL, __VA_ARGS__)
 
-constexpr U32 MAX_SHADER_PROGRAM_INPUT_VARIABLES = 128;
 constexpr U32 MAX_SHADER_BINARY_NAME_LENGTH = 63;
 
-using ActiveProgramInputVariableMask = BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES, U64>;
 using MutatorValue = I32; ///< The type of the mutator value
 
 /// An interface used by the ShaderProgramParser and ShaderProgramCompiler to abstract file loading.

+ 0 - 48
src/anki/shader_compiler/Glslang.h

@@ -16,54 +16,6 @@ namespace anki
 /// @addtogroup shader_compiler
 /// @{
 
-/// A variable inside a uniform or storage blocks or push constants.
-class GlslReflectionBlockVariable
-{
-public:
-	String m_name;
-	ShaderVariableBlockInfo m_info;
-	ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
-};
-
-/// A uniform buffer or storage buffer.
-class GlslReflectionBlock
-{
-public:
-	DynamicArray<GlslReflectionBlockVariable> m_variables;
-	PtrSize m_size = MAX_PTR_SIZE;
-	U32 m_binding = MAX_U32;
-	U8 m_set = MAX_U8;
-};
-
-/// A spec constant variable.
-class GlslReflectionSpecializationConstant
-{
-public:
-	String m_name;
-	U32 m_id = MAX_U32;
-	ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
-};
-
-/// GLSL reflection.
-class GlslReflection
-{
-public:
-	GlslReflection(GenericMemoryPoolAllocator<U8> alloc)
-		: m_alloc(alloc)
-	{
-	}
-
-	~GlslReflection();
-
-	DynamicArray<GlslReflectionBlock> m_uniformBlocks;
-	DynamicArray<GlslReflectionBlock> m_storageBlocks;
-	GlslReflectionBlock* m_pushConstants = nullptr;
-	DynamicArray<GlslReflectionSpecializationConstant> m_specializationConstants;
-
-private:
-	GenericMemoryPoolAllocator<U8> m_alloc;
-};
-
 /// Run glslang's preprocessor.
 ANKI_USE_RESULT Error preprocessGlsl(CString in, StringAuto& out);
 

+ 203 - 52
src/anki/shader_compiler/ShaderProgramBinary.h

@@ -14,37 +14,204 @@
 namespace anki
 {
 
-/// Shader program input variable.
-class ShaderProgramBinaryInput
+/// Storage or uniform variable.
+class ShaderProgramBinaryVariable
 {
 public:
-	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name;
-	U32 m_firstSpecializationConstantIndex; ///< It's MAX_U32 if it's not a constant.
-	Bool m_instanced;
-	ShaderVariableDataType m_dataType;
+	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name = {};
+	ShaderVariableBlockInfo m_blockInfo;
+	ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
+	Bool m_active = true;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doArray("m_name",
+			offsetof(ShaderProgramBinaryVariable, m_name),
+			&self.m_name[0],
+			MAX_SHADER_BINARY_NAME_LENGTH + 1);
+		s.doValue("m_blockInfo", offsetof(ShaderProgramBinaryVariable, m_blockInfo), self.m_blockInfo);
+		s.doValue("m_type", offsetof(ShaderProgramBinaryVariable, m_type), self.m_type);
+		s.doValue("m_active", offsetof(ShaderProgramBinaryVariable, m_active), self.m_active);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryVariable&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryVariable&>(serializer, *this);
+	}
+};
+
+/// Storage or uniform block.
+class ShaderProgramBinaryBlock
+{
+public:
+	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name = {};
+	WeakArray<ShaderProgramBinaryVariable> m_variables;
+	U32 m_binding = MAX_U32;
+	U32 m_set = MAX_U32;
+	U32 m_size = MAX_U32;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
 		s.doArray(
-			"m_name", offsetof(ShaderProgramBinaryInput, m_name), &self.m_name[0], MAX_SHADER_BINARY_NAME_LENGTH + 1);
-		s.doValue("m_firstSpecializationConstantIndex",
-			offsetof(ShaderProgramBinaryInput, m_firstSpecializationConstantIndex),
-			self.m_firstSpecializationConstantIndex);
-		s.doValue("m_instanced", offsetof(ShaderProgramBinaryInput, m_instanced), self.m_instanced);
-		s.doValue("m_dataType", offsetof(ShaderProgramBinaryInput, m_dataType), self.m_dataType);
+			"m_name", offsetof(ShaderProgramBinaryBlock, m_name), &self.m_name[0], MAX_SHADER_BINARY_NAME_LENGTH + 1);
+		s.doValue("m_variables", offsetof(ShaderProgramBinaryBlock, m_variables), self.m_variables);
+		s.doValue("m_binding", offsetof(ShaderProgramBinaryBlock, m_binding), self.m_binding);
+		s.doValue("m_set", offsetof(ShaderProgramBinaryBlock, m_set), self.m_set);
+		s.doValue("m_size", offsetof(ShaderProgramBinaryBlock, m_size), self.m_size);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryInput&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryBlock&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryInput&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryBlock&>(serializer, *this);
+	}
+};
+
+/// Sampler or texture or image.
+class ShaderProgramBinaryOpaque
+{
+public:
+	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name = {};
+	ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
+	U32 m_binding = MAX_U32;
+	U32 m_set = MAX_U32;
+	U32 m_arraySize = MAX_U32;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doArray(
+			"m_name", offsetof(ShaderProgramBinaryOpaque, m_name), &self.m_name[0], MAX_SHADER_BINARY_NAME_LENGTH + 1);
+		s.doValue("m_type", offsetof(ShaderProgramBinaryOpaque, m_type), self.m_type);
+		s.doValue("m_binding", offsetof(ShaderProgramBinaryOpaque, m_binding), self.m_binding);
+		s.doValue("m_set", offsetof(ShaderProgramBinaryOpaque, m_set), self.m_set);
+		s.doValue("m_arraySize", offsetof(ShaderProgramBinaryOpaque, m_arraySize), self.m_arraySize);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryOpaque&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryOpaque&>(serializer, *this);
+	}
+};
+
+/// Specialization constant.
+class ShaderProgramBinaryConstant
+{
+public:
+	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name;
+	ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
+	U32 m_constantId = MAX_U32;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doArray("m_name",
+			offsetof(ShaderProgramBinaryConstant, m_name),
+			&self.m_name[0],
+			MAX_SHADER_BINARY_NAME_LENGTH + 1);
+		s.doValue("m_type", offsetof(ShaderProgramBinaryConstant, m_type), self.m_type);
+		s.doValue("m_constantId", offsetof(ShaderProgramBinaryConstant, m_constantId), self.m_constantId);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryConstant&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryConstant&>(serializer, *this);
+	}
+};
+
+/// ShaderProgramBinaryReflection class.
+class ShaderProgramBinaryReflection
+{
+public:
+	WeakArray<ShaderProgramBinaryBlock> m_uniformBlocks;
+	WeakArray<ShaderProgramBinaryBlock> m_storageBlocks;
+	ShaderProgramBinaryBlock* m_pushConstantBlock = nullptr;
+	WeakArray<ShaderProgramBinaryOpaque> m_opaques;
+	WeakArray<ShaderProgramBinaryConstant> m_specializationConstants;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doValue("m_uniformBlocks", offsetof(ShaderProgramBinaryReflection, m_uniformBlocks), self.m_uniformBlocks);
+		s.doValue("m_storageBlocks", offsetof(ShaderProgramBinaryReflection, m_storageBlocks), self.m_storageBlocks);
+		s.doPointer("m_pushConstantBlock",
+			offsetof(ShaderProgramBinaryReflection, m_pushConstantBlock),
+			self.m_pushConstantBlock);
+		s.doValue("m_opaques", offsetof(ShaderProgramBinaryReflection, m_opaques), self.m_opaques);
+		s.doValue("m_specializationConstants",
+			offsetof(ShaderProgramBinaryReflection, m_specializationConstants),
+			self.m_specializationConstants);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryReflection&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryReflection&>(serializer, *this);
+	}
+};
+
+/// ShaderProgramBinaryVariant class.
+class ShaderProgramBinaryVariant
+{
+public:
+	ShaderProgramBinaryReflection m_reflection;
+	Array<U32, U32(ShaderType::COUNT)> m_codeBlockIndices = {}; ///< Index in ShaderProgramBinary::m_codeBlocks.
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doValue("m_reflection", offsetof(ShaderProgramBinaryVariant, m_reflection), self.m_reflection);
+		s.doArray("m_codeBlockIndices",
+			offsetof(ShaderProgramBinaryVariant, m_codeBlockIndices),
+			&self.m_codeBlockIndices[0],
+			U32(ShaderType::COUNT));
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryVariant&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryVariant&>(serializer, *this);
 	}
 };
 
@@ -52,9 +219,8 @@ public:
 class ShaderProgramBinaryMutator
 {
 public:
-	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name;
+	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name = {};
 	WeakArray<MutatorValue> m_values;
-	Bool m_instanceCount;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
@@ -62,7 +228,6 @@ public:
 		s.doArray(
 			"m_name", offsetof(ShaderProgramBinaryMutator, m_name), &self.m_name[0], MAX_SHADER_BINARY_NAME_LENGTH + 1);
 		s.doValue("m_values", offsetof(ShaderProgramBinaryMutator, m_values), self.m_values);
-		s.doValue("m_instanceCount", offsetof(ShaderProgramBinaryMutator, m_instanceCount), self.m_instanceCount);
 	}
 
 	template<typename TDeserializer>
@@ -78,69 +243,57 @@ public:
 	}
 };
 
-/// ShaderProgramBinaryVariant class.
-class ShaderProgramBinaryVariant
+/// Contains the IR (SPIR-V).
+class ShaderProgramBinaryCodeBlock
 {
 public:
-	ActiveProgramInputVariableMask m_activeVariables = {false};
-	WeakArray<MutatorValue> m_mutation;
-	Bool m_usesPushConstants;
-	WeakArray<ShaderVariableBlockInfo> m_blockInfos;
-	U32 m_blockSize;
-	WeakArray<I16> m_bindings;
-	Array<U32, U32(ShaderType::COUNT)> m_binaryIndices; ///< Index in ShaderProgramBinary::m_codeBlocks.
+	WeakArray<U8, PtrSize> m_binary;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doValue("m_activeVariables", offsetof(ShaderProgramBinaryVariant, m_activeVariables), self.m_activeVariables);
-		s.doValue("m_mutation", offsetof(ShaderProgramBinaryVariant, m_mutation), self.m_mutation);
-		s.doValue(
-			"m_usesPushConstants", offsetof(ShaderProgramBinaryVariant, m_usesPushConstants), self.m_usesPushConstants);
-		s.doValue("m_blockInfos", offsetof(ShaderProgramBinaryVariant, m_blockInfos), self.m_blockInfos);
-		s.doValue("m_blockSize", offsetof(ShaderProgramBinaryVariant, m_blockSize), self.m_blockSize);
-		s.doValue("m_bindings", offsetof(ShaderProgramBinaryVariant, m_bindings), self.m_bindings);
-		s.doArray("m_binaryIndices",
-			offsetof(ShaderProgramBinaryVariant, m_binaryIndices),
-			&self.m_binaryIndices[0],
-			U32(ShaderType::COUNT));
+		s.doValue("m_binary", offsetof(ShaderProgramBinaryCodeBlock, m_binary), self.m_binary);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryVariant&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryCodeBlock&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryVariant&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryCodeBlock&>(serializer, *this);
 	}
 };
 
-/// ShaderProgramBinaryCode class.
-class ShaderProgramBinaryCode
+/// ShaderProgramBinaryMutation class.
+class ShaderProgramBinaryMutation
 {
 public:
-	WeakArray<U8, PtrSize> m_binary;
+	WeakArray<MutatorValue> m_values;
+	U32 m_variantIndex = MAX_U32;
+	U64 m_hash = 0; ///< Mutation hash.
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
-		s.doValue("m_binary", offsetof(ShaderProgramBinaryCode, m_binary), self.m_binary);
+		s.doValue("m_values", offsetof(ShaderProgramBinaryMutation, m_values), self.m_values);
+		s.doValue("m_variantIndex", offsetof(ShaderProgramBinaryMutation, m_variantIndex), self.m_variantIndex);
+		s.doValue("m_hash", offsetof(ShaderProgramBinaryMutation, m_hash), self.m_hash);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryCode&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryMutation&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryCode&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryMutation&>(serializer, *this);
 	}
 };
 
@@ -148,23 +301,21 @@ public:
 class ShaderProgramBinary
 {
 public:
-	Array<U8, 8> m_magic;
+	Array<U8, 8> m_magic = {};
 	WeakArray<ShaderProgramBinaryMutator> m_mutators;
-	WeakArray<ShaderProgramBinaryInput> m_inputVariables;
-	WeakArray<ShaderProgramBinaryCode> m_codeBlocks;
+	WeakArray<ShaderProgramBinaryCodeBlock> m_codeBlocks;
 	WeakArray<ShaderProgramBinaryVariant> m_variants;
-	U32 m_descriptorSet;
-	ShaderTypeBit m_presentShaderTypes;
+	WeakArray<ShaderProgramBinaryMutation> m_mutations;
+	ShaderTypeBit m_presentShaderTypes = ShaderTypeBit::NONE;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
 	{
 		s.doArray("m_magic", offsetof(ShaderProgramBinary, m_magic), &self.m_magic[0], 8);
 		s.doValue("m_mutators", offsetof(ShaderProgramBinary, m_mutators), self.m_mutators);
-		s.doValue("m_inputVariables", offsetof(ShaderProgramBinary, m_inputVariables), self.m_inputVariables);
 		s.doValue("m_codeBlocks", offsetof(ShaderProgramBinary, m_codeBlocks), self.m_codeBlocks);
 		s.doValue("m_variants", offsetof(ShaderProgramBinary, m_variants), self.m_variants);
-		s.doValue("m_descriptorSet", offsetof(ShaderProgramBinary, m_descriptorSet), self.m_descriptorSet);
+		s.doValue("m_mutations", offsetof(ShaderProgramBinary, m_mutations), self.m_mutations);
 		s.doValue(
 			"m_presentShaderTypes", offsetof(ShaderProgramBinary, m_presentShaderTypes), self.m_presentShaderTypes);
 	}

+ 60 - 21
src/anki/shader_compiler/ShaderProgramBinary.xml

@@ -6,50 +6,89 @@
 	</includes>
 
 	<classes>
-		<class name="ShaderProgramBinaryInput" comment="Shader program input variable">
+		<class name="ShaderProgramBinaryVariable" comment="Storage or uniform variable">
 			<members>
-				<member name="m_name" type="char" array_size="MAX_SHADER_BINARY_NAME_LENGTH + 1" />
-				<member name="m_firstSpecializationConstantIndex" type="U32" comment="It's MAX_U32 if it's not a constant" />
-				<member name="m_instanced" type="Bool" />
-				<member name="m_dataType" type="ShaderVariableDataType" />
+				<member name="m_name" type="char" array_size="MAX_SHADER_BINARY_NAME_LENGTH + 1" constructor="= {}" />
+				<member name="m_blockInfo" type="ShaderVariableBlockInfo" />
+				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::NONE" />
+				<member name="m_active" type="Bool" constructor="= true" />
 			</members>
 		</class>
 
-		<class name="ShaderProgramBinaryMutator" comment="Shader program mutator">
+		<class name="ShaderProgramBinaryBlock" comment="Storage or uniform block">
+			<members>
+				<member name="m_name" type="char" array_size="MAX_SHADER_BINARY_NAME_LENGTH + 1" constructor="= {}" />
+				<member name="m_variables" type="WeakArray&lt;ShaderProgramBinaryVariable&gt;" />
+				<member name="m_binding" type="U32" constructor="= MAX_U32" />
+				<member name="m_set" type="U32" constructor="= MAX_U32" />
+				<member name="m_size" type="U32" constructor="= MAX_U32" />
+			</members>
+		</class>
+
+		<class name="ShaderProgramBinaryOpaque" comment="Sampler or texture or image">
+			<members>
+				<member name="m_name" type="char" array_size="MAX_SHADER_BINARY_NAME_LENGTH + 1" constructor="= {}" />
+				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::NONE" />
+				<member name="m_binding" type="U32" constructor="= MAX_U32" />
+				<member name="m_set" type="U32" constructor="= MAX_U32" />
+				<member name="m_arraySize" type="U32" constructor="= MAX_U32" />
+			</members>
+		</class>
+
+		<class name="ShaderProgramBinaryConstant" comment="Specialization constant">
 			<members>
 				<member name="m_name" type="char" array_size="MAX_SHADER_BINARY_NAME_LENGTH + 1" />
-				<member name="m_values" type="WeakArray&lt;MutatorValue&gt;" />
-				<member name="m_instanceCount" type="Bool" />
+				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::NONE" />
+				<member name="m_constantId" type="U32" constructor="= MAX_U32"/>
+			</members>
+		</class>
+
+		<class name="ShaderProgramBinaryReflection">
+			<members>
+				<member name="m_uniformBlocks" type="WeakArray&lt;ShaderProgramBinaryBlock&gt;" />
+				<member name="m_storageBlocks" type="WeakArray&lt;ShaderProgramBinaryBlock&gt;" />
+				<member name="m_pushConstantBlock" type="ShaderProgramBinaryBlock" pointer="true" constructor="= nullptr" />
+				<member name="m_opaques" type="WeakArray&lt;ShaderProgramBinaryOpaque&gt;" />
+				<member name="m_specializationConstants" type="WeakArray&lt;ShaderProgramBinaryConstant&gt;" />
 			</members>
 		</class>
 
 		<class name="ShaderProgramBinaryVariant">
 			<members>
-				<member name="m_activeVariables" type="ActiveProgramInputVariableMask" constructor="false" />
-				<member name="m_mutation" type="WeakArray&lt;MutatorValue&gt;" />
-				<member name="m_usesPushConstants" type="Bool" />
-				<member name="m_blockInfos" type="WeakArray&lt;ShaderVariableBlockInfo&gt;" />
-				<member name="m_blockSize" type="U32" />
-				<member name="m_bindings" type="WeakArray&lt;I16&gt;" />
-				<member name="m_binaryIndices" type="U32" array_size="U32(ShaderType::COUNT)" comment="Index in ShaderProgramBinary::m_codeBlocks" />
+				<member name="m_reflection" type="ShaderProgramBinaryReflection" />
+				<member name="m_codeBlockIndices" type="U32" array_size="U32(ShaderType::COUNT)" comment="Index in ShaderProgramBinary::m_codeBlocks" constructor="= {}" />
 			</members>
 		</class>
 
-		<class name="ShaderProgramBinaryCode">
+		<class name="ShaderProgramBinaryMutator" comment="Shader program mutator">
+			<members>
+				<member name="m_name" type="char" array_size="MAX_SHADER_BINARY_NAME_LENGTH + 1" constructor="= {}" />
+				<member name="m_values" type="WeakArray&lt;MutatorValue&gt;" />
+			</members>
+		</class>
+
+		<class name="ShaderProgramBinaryCodeBlock" comment="Contains the IR (SPIR-V)">
 			<members>
 				<member name="m_binary" type="WeakArray&lt;U8, PtrSize&gt;" />
 			</members>
 		</class>
 
+		<class name="ShaderProgramBinaryMutation">
+			<members>
+				<member name="m_values" type="WeakArray&lt;MutatorValue&gt;" />
+				<member name="m_variantIndex" type="U32" constructor="= MAX_U32" />
+				<member name="m_hash" type="U64" comment="Mutation hash" constructor="= 0" />
+			</members>
+		</class>
+
 		<class name="ShaderProgramBinary">
 			<members>
-				<member name="m_magic" type="U8" array_size="8" />
+				<member name="m_magic" type="U8" array_size="8" constructor="= {}" />
 				<member name="m_mutators" type="WeakArray&lt;ShaderProgramBinaryMutator&gt;" />
-				<member name="m_inputVariables" type="WeakArray&lt;ShaderProgramBinaryInput&gt;" />
-				<member name="m_codeBlocks" type="WeakArray&lt;ShaderProgramBinaryCode&gt;" />
+				<member name="m_codeBlocks" type="WeakArray&lt;ShaderProgramBinaryCodeBlock&gt;" />
 				<member name="m_variants" type="WeakArray&lt;ShaderProgramBinaryVariant&gt;" />
-				<member name="m_descriptorSet" type="U32" />
-				<member name="m_presentShaderTypes" type="ShaderTypeBit" />
+				<member name="m_mutations" type="WeakArray&lt;ShaderProgramBinaryMutation&gt;" />
+				<member name="m_presentShaderTypes" type="ShaderTypeBit" constructor="= ShaderTypeBit::NONE" />
 			</members>
 		</class>
 	</classes>

+ 0 - 31
src/anki/shader_compiler/ShaderProgramBinaryExtra.h

@@ -44,35 +44,4 @@ public:
 	}
 };
 
-/// Serialize ActiveProgramInputVariableMask
-template<typename TSerializer, typename T>
-void serializeActiveProgramInputVariableMask(T x, TSerializer& s)
-{
-	s.doArray("bitset", 0, &x.getData()[0], x.getData().getSize());
-}
-
-/// Serialize ActiveProgramInputVariableMask
-template<>
-class SerializeFunctor<ActiveProgramInputVariableMask>
-{
-public:
-	template<typename TSerializer>
-	void operator()(const ActiveProgramInputVariableMask& x, TSerializer& serializer)
-	{
-		serializeActiveProgramInputVariableMask<TSerializer, const ActiveProgramInputVariableMask&>(x, serializer);
-	}
-};
-
-/// Deserialize ActiveProgramInputVariableMask
-template<>
-class DeserializeFunctor<ActiveProgramInputVariableMask>
-{
-public:
-	template<typename TDeserializer>
-	void operator()(ActiveProgramInputVariableMask& x, TDeserializer& deserialize)
-	{
-		serializeShaderVariableBlockInfo<TDeserializer, ActiveProgramInputVariableMask&>(x, deserialize);
-	}
-};
-
 } // end namespace anki

+ 231 - 250
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>
@@ -27,12 +28,6 @@ Error ShaderProgramBinaryWrapper::serializeToFile(CString fname) const
 		m_alloc.getMemoryPool().getAllocationCallback(), m_alloc.getMemoryPool().getAllocationCallbackUserData(), 4_KB);
 	ANKI_CHECK(serializer.serialize(*m_binary, tmpAlloc, file));
 
-	if(memcmp(SHADER_BINARY_MAGIC, &m_binary->m_magic[0], 0) != 0)
-	{
-		ANKI_SHADER_COMPILER_LOGE("Corrupted or wrong version of shader binary: %s", fname.cstr());
-		return Error::USER_DATA;
-	}
-
 	return Error::NONE;
 }
 
@@ -48,6 +43,12 @@ Error ShaderProgramBinaryWrapper::deserializeFromFile(CString fname)
 
 	m_singleAllocation = true;
 
+	if(memcmp(SHADER_BINARY_MAGIC, &m_binary->m_magic[0], 0) != 0)
+	{
+		ANKI_SHADER_COMPILER_LOGE("Corrupted or wrong version of shader binary: %s", fname.cstr());
+		return Error::USER_DATA;
+	}
+
 	return Error::NONE;
 }
 
@@ -64,21 +65,42 @@ void ShaderProgramBinaryWrapper::cleanup()
 		{
 			m_alloc.getMemoryPool().free(mutator.m_values.getBegin());
 		}
-
 		m_alloc.getMemoryPool().free(m_binary->m_mutators.getBegin());
-		m_alloc.getMemoryPool().free(m_binary->m_inputVariables.getBegin());
 
-		for(ShaderProgramBinaryCode& code : m_binary->m_codeBlocks)
+		for(ShaderProgramBinaryCodeBlock& code : m_binary->m_codeBlocks)
 		{
 			m_alloc.getMemoryPool().free(code.m_binary.getBegin());
 		}
 		m_alloc.getMemoryPool().free(m_binary->m_codeBlocks.getBegin());
 
+		for(ShaderProgramBinaryMutation& m : m_binary->m_mutations)
+		{
+			m_alloc.getMemoryPool().free(m.m_values.getBegin());
+		}
+		m_alloc.getMemoryPool().free(m_binary->m_mutations.getBegin());
+
 		for(ShaderProgramBinaryVariant& variant : m_binary->m_variants)
 		{
-			m_alloc.getMemoryPool().free(variant.m_mutation.getBegin());
-			m_alloc.getMemoryPool().free(variant.m_blockInfos.getBegin());
-			m_alloc.getMemoryPool().free(variant.m_bindings.getBegin());
+			for(ShaderProgramBinaryBlock& block : variant.m_reflection.m_uniformBlocks)
+			{
+				m_alloc.getMemoryPool().free(block.m_variables.getBegin());
+			}
+
+			for(ShaderProgramBinaryBlock& block : variant.m_reflection.m_storageBlocks)
+			{
+				m_alloc.getMemoryPool().free(block.m_variables.getBegin());
+			}
+
+			if(variant.m_reflection.m_pushConstantBlock)
+			{
+				m_alloc.getMemoryPool().free(variant.m_reflection.m_pushConstantBlock->m_variables.getBegin());
+			}
+
+			m_alloc.getMemoryPool().free(variant.m_reflection.m_uniformBlocks.getBegin());
+			m_alloc.getMemoryPool().free(variant.m_reflection.m_storageBlocks.getBegin());
+			m_alloc.getMemoryPool().free(variant.m_reflection.m_pushConstantBlock);
+			m_alloc.getMemoryPool().free(variant.m_reflection.m_specializationConstants.getBegin());
+			m_alloc.getMemoryPool().free(variant.m_reflection.m_opaques.getBegin());
 		}
 		m_alloc.getMemoryPool().free(m_binary->m_variants.getBegin());
 	}
@@ -127,7 +149,7 @@ static Bool spinDials(DynamicArrayAuto<U32>& dials, ConstWeakArray<ShaderProgram
 static Error compileVariant(ConstWeakArray<MutatorValue> mutation,
 	const ShaderProgramParser& parser,
 	ShaderProgramBinaryVariant& variant,
-	DynamicArrayAuto<ShaderProgramBinaryCode>& codeBlocks,
+	DynamicArrayAuto<ShaderProgramBinaryCodeBlock>& codeBlocks,
 	DynamicArrayAuto<U64>& codeBlockHashes,
 	GenericMemoryPoolAllocator<U8> tmpAlloc,
 	GenericMemoryPoolAllocator<U8> binaryAlloc)
@@ -138,24 +160,13 @@ static Error compileVariant(ConstWeakArray<MutatorValue> mutation,
 	ShaderProgramParserVariant parserVariant;
 	ANKI_CHECK(parser.generateVariant(mutation, parserVariant));
 
-	// Active vars
-	{
-		variant.m_activeVariables = ActiveProgramInputVariableMask(false);
-		for(U32 i = 0; i < parser.getInputs().getSize(); ++i)
-		{
-			if(parserVariant.isInputActive(parser.getInputs()[i]))
-			{
-				variant.m_activeVariables.set(i, true);
-			}
-		}
-	}
-
 	// Compile stages
+	Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirvBinaries;
 	for(ShaderType shaderType = ShaderType::FIRST; shaderType < ShaderType::COUNT; ++shaderType)
 	{
 		if(!(shaderTypeToBit(shaderType) & parser.getShaderTypes()))
 		{
-			variant.m_binaryIndices[shaderType] = MAX_U32;
+			variant.m_codeBlockIndices[shaderType] = MAX_U32;
 			continue;
 		}
 
@@ -172,7 +183,7 @@ static Error compileVariant(ConstWeakArray<MutatorValue> mutation,
 			if(codeBlockHashes[i] == newHash)
 			{
 				// Found it
-				variant.m_binaryIndices[shaderType] = i;
+				variant.m_codeBlockIndices[shaderType] = i;
 				found = true;
 				break;
 			}
@@ -184,61 +195,20 @@ static Error compileVariant(ConstWeakArray<MutatorValue> mutation,
 			U8* code = binaryAlloc.allocate(spirv.getSizeInBytes());
 			memcpy(code, &spirv[0], spirv.getSizeInBytes());
 
-			ShaderProgramBinaryCode block;
+			ShaderProgramBinaryCodeBlock block;
 			block.m_binary.setArray(code, spirv.getSizeInBytes());
 			codeBlocks.emplaceBack(block);
 
 			codeBlockHashes.emplaceBack(newHash);
 
-			variant.m_binaryIndices[shaderType] = codeBlocks.getSize() - 1;
+			variant.m_codeBlockIndices[shaderType] = codeBlocks.getSize() - 1;
 		}
-	}
 
-	// Mutator values
-	variant.m_mutation.setArray(
-		binaryAlloc.newArray<MutatorValue>(parser.getMutators().getSize()), parser.getMutators().getSize());
-	for(U32 i = 0; i < parser.getMutators().getSize(); ++i)
-	{
-		variant.m_mutation[i] = mutation[i];
+		spirvBinaries[shaderType] = codeBlocks[variant.m_codeBlockIndices[shaderType]].m_binary;
 	}
 
-	// Input vars
-	{
-		ShaderVariableBlockInfo defaultInfo;
-		defaultInfo.m_arraySize = -1;
-		defaultInfo.m_arrayStride = -1;
-		defaultInfo.m_matrixStride = -1;
-		defaultInfo.m_offset = -1;
-		variant.m_blockInfos.setArray(
-			binaryAlloc.newArray<ShaderVariableBlockInfo>(parser.getInputs().getSize(), defaultInfo),
-			parser.getInputs().getSize());
-
-		variant.m_bindings.setArray(
-			binaryAlloc.newArray<I16>(parser.getInputs().getSize(), -1), parser.getInputs().getSize());
-
-		for(U32 i = 0; i < parser.getInputs().getSize(); ++i)
-		{
-			const ShaderProgramParserInput& parserInput = parser.getInputs()[i];
-			if(!parserVariant.isInputActive(parserInput))
-			{
-				continue;
-			}
-
-			if(parserInput.inUbo())
-			{
-				variant.m_blockInfos[i] = parserVariant.getBlockInfo(parserInput);
-			}
-
-			if(parserInput.isSampler() || parserInput.isTexture())
-			{
-				variant.m_bindings[i] = I16(parserVariant.getBinding(parserInput));
-			}
-		}
-	}
-
-	// Misc
-	variant.m_blockSize = parserVariant.getBlockSize();
-	variant.m_usesPushConstants = parserVariant.usesPushConstants();
+	// Do reflection
+	ANKI_CHECK(performSpirvReflection(variant.m_reflection, spirvBinaries, tmpAlloc, binaryAlloc));
 
 	return Error::NONE;
 }
@@ -266,35 +236,8 @@ Error compileShaderProgram(CString fname,
 		fname, &fsystem, tempAllocator, pushConstantsSize, backendMinor, backendMajor, gpuVendor);
 	ANKI_CHECK(parser.parse());
 
-	// Inputs
-	if(parser.getInputs().getSize() > 0)
-	{
-		binary.m_inputVariables.setArray(
-			binaryAllocator.newArray<ShaderProgramBinaryInput>(parser.getInputs().getSize()),
-			parser.getInputs().getSize());
-
-		for(U32 i = 0; i < binary.m_inputVariables.getSize(); ++i)
-		{
-			ShaderProgramBinaryInput& out = binary.m_inputVariables[i];
-			const ShaderProgramParserInput& in = parser.getInputs()[i];
-
-			ANKI_ASSERT(in.getName().getLength() < out.m_name.getSize());
-			memcpy(&out.m_name[0], in.getName().cstr(), in.getName().getLength() + 1);
-
-			out.m_firstSpecializationConstantIndex = MAX_U32;
-			in.isConstant(&out.m_firstSpecializationConstantIndex);
-
-			out.m_instanced = in.isInstanced();
-			out.m_dataType = in.getDataType();
-		}
-	}
-	else
-	{
-		ANKI_ASSERT(binary.m_inputVariables.getSize() == 0);
-	}
-
 	// Mutators
-	U32 variantCount = 0;
+	U32 mutationCount = 0;
 	if(parser.getMutators().getSize() > 0)
 	{
 		binary.m_mutators.setArray(binaryAllocator.newArray<ShaderProgramBinaryMutator>(parser.getMutators().getSize()),
@@ -311,10 +254,8 @@ Error compileShaderProgram(CString fname,
 			out.m_values.setArray(binaryAllocator.newArray<I32>(in.getValues().getSize()), in.getValues().getSize());
 			memcpy(out.m_values.getBegin(), in.getValues().getBegin(), in.getValues().getSizeInBytes());
 
-			out.m_instanceCount = in.isInstanceCount();
-
 			// Update the count
-			variantCount = (i == 0) ? out.m_values.getSize() : variantCount * out.m_values.getSize();
+			mutationCount = (i == 0) ? out.m_values.getSize() : mutationCount * out.m_values.getSize();
 		}
 	}
 	else
@@ -326,15 +267,16 @@ Error compileShaderProgram(CString fname,
 	if(parser.getMutators().getSize() > 0)
 	{
 		// Initialize
-		DynamicArrayAuto<MutatorValue> mutation(tempAllocator, parser.getMutators().getSize());
-		DynamicArrayAuto<MutatorValue> mutation2(tempAllocator, parser.getMutators().getSize());
+		DynamicArrayAuto<MutatorValue> originalMutationValues(tempAllocator, parser.getMutators().getSize());
+		DynamicArrayAuto<MutatorValue> rewrittenMutationValues(tempAllocator, parser.getMutators().getSize());
 		DynamicArrayAuto<U32> dials(tempAllocator, parser.getMutators().getSize(), 0);
-		DynamicArrayAuto<ShaderProgramBinaryVariant> variants(binaryAllocator, variantCount);
-		DynamicArrayAuto<ShaderProgramBinaryCode> codeBlocks(binaryAllocator);
+		DynamicArrayAuto<ShaderProgramBinaryVariant> variants(binaryAllocator);
+		DynamicArrayAuto<ShaderProgramBinaryCodeBlock> codeBlocks(binaryAllocator);
+		DynamicArrayAuto<ShaderProgramBinaryMutation> mutations(binaryAllocator, mutationCount);
 		DynamicArrayAuto<U64> codeBlockHashes(tempAllocator);
-		HashMapAuto<U64, U32> mutationToVariantIdx(tempAllocator);
+		HashMapAuto<U64, U32> mutationHashToIdx(tempAllocator);
 
-		variantCount = 0;
+		mutationCount = 0;
 
 		// Spin for all possible combinations of mutators and
 		// - Create the spirv
@@ -344,92 +286,106 @@ Error compileShaderProgram(CString fname,
 			// Create the mutation
 			for(U32 i = 0; i < parser.getMutators().getSize(); ++i)
 			{
-				mutation[i] = parser.getMutators()[i].getValues()[dials[i]];
-				mutation2[i] = mutation[i];
+				originalMutationValues[i] = parser.getMutators()[i].getValues()[dials[i]];
+				rewrittenMutationValues[i] = originalMutationValues[i];
 			}
-			const Bool rewritten =
-				parser.rewriteMutation(WeakArray<MutatorValue>(mutation.getBegin(), mutation.getSize()));
+
+			ShaderProgramBinaryMutation& mutation = mutations[mutationCount++];
+			mutation.m_values.setArray(binaryAllocator.newArray<MutatorValue>(originalMutationValues.getSize()),
+				originalMutationValues.getSize());
+			memcpy(mutation.m_values.getBegin(),
+				originalMutationValues.getBegin(),
+				originalMutationValues.getSizeInBytes());
+
+			mutation.m_hash = computeHash(originalMutationValues.getBegin(), originalMutationValues.getSizeInBytes());
+			ANKI_ASSERT(mutation.m_hash > 0);
+
+			const Bool rewritten = parser.rewriteMutation(
+				WeakArray<MutatorValue>(rewrittenMutationValues.getBegin(), rewrittenMutationValues.getSize()));
 
 			// Create the variant
-			ShaderProgramBinaryVariant& variant = variants[variantCount++];
 			if(!rewritten)
 			{
-				// New and unique variant, add it
-				ANKI_CHECK(compileVariant(
-					mutation, parser, variant, codeBlocks, codeBlockHashes, tempAllocator, binaryAllocator));
+				// New and unique mutation and thus variant, add it
+
+				ShaderProgramBinaryVariant& variant = *variants.emplaceBack();
+
+				ANKI_CHECK(compileVariant(originalMutationValues,
+					parser,
+					variant,
+					codeBlocks,
+					codeBlockHashes,
+					tempAllocator,
+					binaryAllocator));
+
+				mutation.m_variantIndex = variants.getSize() - 1;
 
-				mutationToVariantIdx.emplace(computeHash(&mutation[0], mutation.getSizeInBytes()), variantCount - 1);
+				ANKI_ASSERT(mutationHashToIdx.find(mutation.m_hash) == mutationHashToIdx.getEnd());
+				mutationHashToIdx.emplace(mutation.m_hash, mutationCount - 1);
 			}
 			else
 			{
-				// Check if the original variant exists
-				auto it = mutationToVariantIdx.find(computeHash(&mutation[0], mutation.getSizeInBytes()));
-				U32 originalVariantIdx = (it != mutationToVariantIdx.getEnd()) ? *it : MAX_U32;
+				// Check if the rewritten mutation exists
+				const U64 otherMutationHash =
+					computeHash(rewrittenMutationValues.getBegin(), rewrittenMutationValues.getSizeInBytes());
+				auto it = mutationHashToIdx.find(otherMutationHash);
 
-				if(originalVariantIdx == MAX_U32)
+				ShaderProgramBinaryVariant* variant = nullptr;
+				if(it == mutationHashToIdx.getEnd())
 				{
-					// Original variant not found, create it
-
-					ShaderProgramBinaryVariant& other = variants[variantCount++];
-					originalVariantIdx = variantCount - 1;
-
-					ANKI_CHECK(compileVariant(
-						mutation, parser, other, codeBlocks, codeBlockHashes, tempAllocator, binaryAllocator));
-
-					mutationToVariantIdx.emplace(
-						computeHash(&mutation[0], mutation.getSizeInBytes()), originalVariantIdx);
+					// Rewrite variant not found, create it
+
+					variant = variants.emplaceBack();
+
+					ANKI_CHECK(compileVariant(rewrittenMutationValues,
+						parser,
+						*variant,
+						codeBlocks,
+						codeBlockHashes,
+						tempAllocator,
+						binaryAllocator));
+
+					ShaderProgramBinaryMutation& otherMutation = mutations[mutationCount++];
+					otherMutation.m_values.setArray(
+						binaryAllocator.newArray<MutatorValue>(rewrittenMutationValues.getSize()),
+						rewrittenMutationValues.getSize());
+					memcpy(otherMutation.m_values.getBegin(),
+						rewrittenMutationValues.getBegin(),
+						rewrittenMutationValues.getSizeInBytes());
+
+					mutation.m_hash = otherMutationHash;
+					mutation.m_variantIndex = variants.getSize() - 1;
+
+					it = mutationHashToIdx.emplace(otherMutationHash, mutationCount - 1);
 				}
 
-				// Copy the original variant to the current variant
-				{
-					ANKI_ASSERT(originalVariantIdx < variantCount);
-					const ShaderProgramBinaryVariant& other = variants[originalVariantIdx];
-
-					variant = other;
-
-					variant.m_mutation.setArray(
-						binaryAllocator.newArray<MutatorValue>(other.m_mutation.getSize()), other.m_mutation.getSize());
-					memcpy(
-						variant.m_mutation.getBegin(), other.m_mutation.getBegin(), other.m_mutation.getSizeInBytes());
-
-					if(other.m_blockInfos.getSize())
-					{
-						variant.m_blockInfos.setArray(
-							binaryAllocator.newArray<ShaderVariableBlockInfo>(other.m_blockInfos.getSize()),
-							other.m_blockInfos.getSize());
-						memcpy(variant.m_blockInfos.getBegin(),
-							other.m_blockInfos.getBegin(),
-							other.m_blockInfos.getSizeInBytes());
-
-						variant.m_bindings.setArray(
-							binaryAllocator.newArray<I16>(other.m_bindings.getSize()), other.m_bindings.getSize());
-						memcpy(variant.m_bindings.getBegin(),
-							other.m_bindings.getBegin(),
-							other.m_bindings.getSizeInBytes());
-					}
-
-					mutationToVariantIdx.emplace(
-						computeHash(&mutation2[0], mutation2.getSizeInBytes()), U32(&variant - &variants[0]));
-				}
+				// Setup the new mutation
+				mutation.m_variantIndex = mutations[*it].m_variantIndex;
+
+				mutationHashToIdx.emplace(mutation.m_hash, U32(&mutation - mutations.getBegin()));
 			}
 		} while(!spinDials(dials, parser.getMutators()));
 
-		ANKI_ASSERT(variantCount == variants.getSize());
+		ANKI_ASSERT(mutationCount == mutations.getSize());
 
-		// Store to binary
+		// Store temp containers to binary
 		U32 size, storage;
 		ShaderProgramBinaryVariant* firstVariant;
 		variants.moveAndReset(firstVariant, size, storage);
 		binary.m_variants.setArray(firstVariant, size);
 
-		ShaderProgramBinaryCode* firstCodeBlock;
+		ShaderProgramBinaryCodeBlock* firstCodeBlock;
 		codeBlocks.moveAndReset(firstCodeBlock, size, storage);
 		binary.m_codeBlocks.setArray(firstCodeBlock, size);
+
+		ShaderProgramBinaryMutation* firstMutation;
+		mutations.moveAndReset(firstMutation, size, storage);
+		binary.m_mutations.setArray(firstMutation, size);
 	}
 	else
 	{
 		DynamicArrayAuto<MutatorValue> mutation(tempAllocator);
-		DynamicArrayAuto<ShaderProgramBinaryCode> codeBlocks(binaryAllocator);
+		DynamicArrayAuto<ShaderProgramBinaryCodeBlock> codeBlocks(binaryAllocator);
 		DynamicArrayAuto<U64> codeBlockHashes(tempAllocator);
 
 		binary.m_variants.setArray(binaryAllocator.newInstance<ShaderProgramBinaryVariant>(), 1);
@@ -438,23 +394,47 @@ Error compileShaderProgram(CString fname,
 			mutation, parser, binary.m_variants[0], codeBlocks, codeBlockHashes, tempAllocator, binaryAllocator));
 		ANKI_ASSERT(codeBlocks.getSize() == U32(__builtin_popcount(U32(parser.getShaderTypes()))));
 
-		ShaderProgramBinaryCode* firstCodeBlock;
+		ShaderProgramBinaryCodeBlock* firstCodeBlock;
 		U32 size, storage;
 		codeBlocks.moveAndReset(firstCodeBlock, size, storage);
 		binary.m_codeBlocks.setArray(firstCodeBlock, size);
+
+		binary.m_mutations.setArray(binaryAllocator.newInstance<ShaderProgramBinaryMutation>(), 1);
+		binary.m_mutations[0].m_hash = 1;
+		binary.m_mutations[0].m_variantIndex = 0;
 	}
 
 	// Misc
-	binary.m_descriptorSet = parser.getDescritproSet();
 	binary.m_presentShaderTypes = parser.getShaderTypes();
 
 	return Error::NONE;
 }
 
-void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAuto& humanReadable)
-{
 #define ANKI_TAB "    "
 
+static void disassembleBlock(const ShaderProgramBinaryBlock& block, StringListAuto& lines)
+{
+	lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s set %4u binding %4u size %4u\n",
+		block.m_name.getBegin(),
+		block.m_set,
+		block.m_binding,
+		block.m_size);
+
+	for(const ShaderProgramBinaryVariable& var : block.m_variables)
+	{
+		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(),
+			shaderVariableDataTypeToString(var.m_type).cstr(),
+			var.m_active ? "true" : "false",
+			var.m_blockInfo.m_offset,
+			var.m_blockInfo.m_arraySize,
+			var.m_blockInfo.m_arrayStride,
+			var.m_blockInfo.m_matrixStride);
+	}
+}
+
+void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAuto& humanReadable)
+{
 	GenericMemoryPoolAllocator<U8> alloc = humanReadable.getAllocator();
 	StringListAuto lines(alloc);
 
@@ -463,10 +443,10 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 	{
 		for(const ShaderProgramBinaryMutator& mutator : binary.m_mutators)
 		{
-			lines.pushBackSprintf(ANKI_TAB "\"%s\"", &mutator.m_name[0]);
-			for(MutatorValue value : mutator.m_values)
+			lines.pushBackSprintf(ANKI_TAB "%-32s ", &mutator.m_name[0]);
+			for(U32 i = 0; i < mutator.m_values.getSize(); ++i)
 			{
-				lines.pushBackSprintf(" %d", value);
+				lines.pushBackSprintf((i < mutator.m_values.getSize() - 1) ? "%d," : "%d", mutator.m_values[i]);
 			}
 			lines.pushBack("\n");
 		}
@@ -476,29 +456,9 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 		lines.pushBack(ANKI_TAB "N/A\n");
 	}
 
-	lines.pushBack("\n**INPUT VARIABLES**\n");
-	if(binary.m_inputVariables.getSize() > 0)
-	{
-		for(const ShaderProgramBinaryInput& input : binary.m_inputVariables)
-		{
-			lines.pushBackSprintf(ANKI_TAB "\"%s\" ", &input.m_name[0]);
-			if(input.m_firstSpecializationConstantIndex < MAX_U32)
-			{
-				lines.pushBackSprintf(
-					"firstSpecializationConstant %" PRIu32 " ", input.m_firstSpecializationConstantIndex);
-			}
-			lines.pushBackSprintf("instanced %" PRIu32 " ", U32(input.m_instanced));
-			lines.pushBackSprintf("dataType %" PRIu8 "\n", U8(input.m_dataType));
-		}
-	}
-	else
-	{
-		lines.pushBack(ANKI_TAB "N/A\n");
-	}
-
 	lines.pushBack("\n**BINARIES**\n");
 	U32 count = 0;
-	for(const ShaderProgramBinaryCode& code : binary.m_codeBlocks)
+	for(const ShaderProgramBinaryCodeBlock& code : binary.m_codeBlocks)
 	{
 		spirv_cross::CompilerGLSL::Options options;
 		options.vulkan_semantics = true;
@@ -515,98 +475,119 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 		StringAuto newGlsl(alloc);
 		sourceLines.join("\n" ANKI_TAB ANKI_TAB, newGlsl);
 
-		lines.pushBackSprintf(ANKI_TAB "%" PRIuFAST32 " \n" ANKI_TAB ANKI_TAB "%s\n", count++, newGlsl.cstr());
+		lines.pushBackSprintf(ANKI_TAB "#%u \n" ANKI_TAB ANKI_TAB "%s\n", count++, newGlsl.cstr());
 	}
 
 	lines.pushBack("\n**SHADER VARIANTS**\n");
 	count = 0;
 	for(const ShaderProgramBinaryVariant& variant : binary.m_variants)
 	{
-		lines.pushBackSprintf(ANKI_TAB "%" PRIuFAST32 "\n", count++);
-
-		// Misc
-		ANKI_ASSERT(variant.m_activeVariables.getData().getSize() == 2);
-		lines.pushBackSprintf(ANKI_TAB ANKI_TAB "activeVariables 0b%" ANKI_PRIb64 " 0b%" ANKI_PRIb64 " ",
-			ANKI_FORMAT_U64(variant.m_activeVariables.getData()[1]),
-			ANKI_FORMAT_U64(variant.m_activeVariables.getData()[0]));
-		lines.pushBackSprintf(
-			"blockSize %" PRIu32 " usesPushConstants %" PRIu8 "\n", variant.m_blockSize, variant.m_usesPushConstants);
-
-		// Mutator values
-		lines.pushBack(ANKI_TAB ANKI_TAB "mutatorValues ");
-		if(variant.m_mutation.getSize() > 0)
+		lines.pushBackSprintf(ANKI_TAB "#%u\n", count++);
+
+		// Uniform blocks
+		if(variant.m_reflection.m_uniformBlocks.getSize() > 0)
 		{
-			for(U32 j = 0; j < variant.m_mutation.getSize(); ++j)
+			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Uniform blocks\n");
+			for(const ShaderProgramBinaryBlock& block : variant.m_reflection.m_uniformBlocks)
 			{
-				lines.pushBackSprintf("\"%s\" %" PRId32 " ", &binary.m_mutators[j].m_name[0], variant.m_mutation[j]);
+				disassembleBlock(block, lines);
 			}
 		}
-		else
+
+		// Storage blocks
+		if(variant.m_reflection.m_storageBlocks.getSize() > 0)
 		{
-			lines.pushBack("N/A");
+			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Storage blocks\n");
+			for(const ShaderProgramBinaryBlock& block : variant.m_reflection.m_storageBlocks)
+			{
+				disassembleBlock(block, lines);
+			}
 		}
-		lines.pushBack("\n");
 
-		// Block infos
-		lines.pushBack(ANKI_TAB ANKI_TAB "blockInfos ");
-		if(variant.m_blockInfos.getSize() > 0)
+		// Opaque
+		if(variant.m_reflection.m_opaques.getSize() > 0)
 		{
-			for(const ShaderVariableBlockInfo& inf : variant.m_blockInfos)
+			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Opaque\n");
+			for(const ShaderProgramBinaryOpaque& o : variant.m_reflection.m_opaques)
 			{
-				lines.pushBackSprintf("%" PRIi16 "|%" PRIi16 "|%" PRIi16 "|%" PRIi16 " ",
-					inf.m_offset,
-					inf.m_arraySize,
-					inf.m_arrayStride,
-					inf.m_matrixStride);
+				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s set %4u binding %4u type %12s arraySize %4u\n",
+					o.m_name.getBegin(),
+					o.m_set,
+					o.m_binding,
+					shaderVariableDataTypeToString(o.m_type).cstr(),
+					o.m_arraySize);
 			}
 		}
-		else
+
+		// Push constants
+		if(variant.m_reflection.m_pushConstantBlock)
 		{
-			lines.pushBack("N/A");
+			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Push constants\n");
+			disassembleBlock(*variant.m_reflection.m_pushConstantBlock, lines);
 		}
-		lines.pushBack("\n");
 
-		// Bindings
-		lines.pushBack(ANKI_TAB ANKI_TAB "bindings ");
-		if(variant.m_bindings.getSize() > 0)
+		// Constants
+		if(variant.m_reflection.m_specializationConstants.getSize() > 0)
 		{
-			for(I32 binding : variant.m_bindings)
+			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Specialization constants\n");
+			for(const ShaderProgramBinaryConstant& c : variant.m_reflection.m_specializationConstants)
 			{
-				if(binding < 0)
-				{
-					lines.pushBack("N/A ");
-				}
-				else
-				{
-					lines.pushBackSprintf("%" PRIi32 " ", binding);
-				}
+				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s type %8s id %4u\n",
+					c.m_name.getBegin(),
+					shaderVariableDataTypeToString(c.m_type).cstr(),
+					c.m_constantId);
 			}
 		}
-		else
-		{
-			lines.pushBack("N/A");
-		}
-		lines.pushBack("\n");
 
 		// Binary indices
-		lines.pushBack(ANKI_TAB ANKI_TAB "binaries ");
-		for(ShaderType shaderType = ShaderType::FIRST; shaderType < ShaderType::COUNT; ++shaderType)
+		lines.pushBack(ANKI_TAB ANKI_TAB "Binaries ");
+		for(ShaderType shaderType : EnumIterable<ShaderType>())
 		{
-			if(variant.m_binaryIndices[shaderType] < MAX_U32)
+			if(variant.m_codeBlockIndices[shaderType] < MAX_U32)
 			{
-				lines.pushBackSprintf("%" PRIu32 " ", variant.m_binaryIndices[shaderType]);
+				lines.pushBackSprintf("%u", variant.m_codeBlockIndices[shaderType]);
 			}
 			else
 			{
-				lines.pushBack("N/A ");
+				lines.pushBack("-");
+			}
+
+			if(shaderType != ShaderType::LAST)
+			{
+				lines.pushBack(",");
 			}
 		}
 		lines.pushBack("\n");
 	}
 
+	// Mutations
+	lines.pushBack("\n**MUTATIONS**\n");
+	count = 0;
+	for(const ShaderProgramBinaryMutation& mutation : binary.m_mutations)
+	{
+		lines.pushBackSprintf(ANKI_TAB "#%-4u variantIndex %4u values (", count++, mutation.m_variantIndex);
+		if(mutation.m_values.getSize() > 0)
+		{
+			for(U32 i = 0; i < mutation.m_values.getSize(); ++i)
+			{
+				lines.pushBackSprintf((i < mutation.m_values.getSize() - 1) ? "%s %4d, " : "%s %4d",
+					binary.m_mutators[i].m_name.getBegin(),
+					I32(mutation.m_values[i]));
+			}
+
+			lines.pushBack(")");
+		}
+		else
+		{
+			lines.pushBack("N/A");
+		}
+
+		lines.pushBack("\n");
+	}
+
 	lines.join("", humanReadable);
+}
 
 #undef ANKI_TAB
-}
 
 } // end namespace anki

+ 20 - 682
src/anki/shader_compiler/ShaderProgramParser.cpp

@@ -4,7 +4,6 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/shader_compiler/ShaderProgramParser.h>
-#include <anki/shader_compiler/Glslang.h>
 
 namespace anki
 {
@@ -66,155 +65,32 @@ static const char* SHADER_HEADER = R"(#version 450 core
 #define Mat3x4 mat3x4
 
 #define Bool bool
-)";
 
-static ANKI_USE_RESULT Error computeShaderVariableDataType(const CString& str, ShaderVariableDataType& out)
-{
-	Error err = Error::NONE;
+#define _ANKI_CONCATENATE(a, b) a##b
+#define ANKI_CONCATENATE(a, b) _ANKI_CONCATENATE(a, b)
 
-	if(str == "I32")
-	{
-		out = ShaderVariableDataType::INT;
-	}
-	else if(str == "IVec2")
-	{
-		out = ShaderVariableDataType::IVEC2;
-	}
-	else if(str == "IVec3")
-	{
-		out = ShaderVariableDataType::IVEC3;
-	}
-	else if(str == "IVec4")
-	{
-		out = ShaderVariableDataType::IVEC4;
-	}
-	else if(str == "U32")
-	{
-		out = ShaderVariableDataType::UINT;
-	}
-	else if(str == "UVec2")
-	{
-		out = ShaderVariableDataType::UVEC2;
-	}
-	else if(str == "UVec3")
-	{
-		out = ShaderVariableDataType::UVEC3;
-	}
-	else if(str == "UVec4")
-	{
-		out = ShaderVariableDataType::UVEC4;
-	}
-	else if(str == "F32")
-	{
-		out = ShaderVariableDataType::FLOAT;
-	}
-	else if(str == "Vec2")
-	{
-		out = ShaderVariableDataType::VEC2;
-	}
-	else if(str == "Vec3")
-	{
-		out = ShaderVariableDataType::VEC3;
-	}
-	else if(str == "Vec4")
-	{
-		out = ShaderVariableDataType::VEC4;
-	}
-	else if(str == "Mat3")
-	{
-		out = ShaderVariableDataType::MAT3;
-	}
-	else if(str == "Mat4")
-	{
-		out = ShaderVariableDataType::MAT4;
-	}
-	else if(str == "texture2D")
-	{
-		out = ShaderVariableDataType::TEXTURE_2D;
-	}
-	else if(str == "texture2DArray")
-	{
-		out = ShaderVariableDataType::TEXTURE_2D_ARRAY;
-	}
-	else if(str == "textureCube")
-	{
-		out = ShaderVariableDataType::TEXTURE_CUBE;
-	}
-	else if(str == "sampler")
-	{
-		out = ShaderVariableDataType::SAMPLER;
-	}
-	else
-	{
-		ANKI_SHADER_COMPILER_LOGE("Incorrect variable type %s", &str[0]);
-		err = Error::USER_DATA;
-	}
+#define ANKI_SPECIALIZATION_CONSTANT_I32(x, id, defltVal) layout(constant_id = id) const I32 x = defltVal
 
-	return err;
-}
+#define ANKI_SPECIALIZATION_CONSTANT_IVEC2(x, id, defltVal) \
+	layout(constant_id = id) const I32 ANKI_CONCATENATE(x, _0) = defltVal[0]; \
+	layout(constant_id = id + 1) const I32 ANKI_CONCATENATE(x, _1) = defltVal[1]
 
-static U32 computeSpecConstantIdsRequired(ShaderVariableDataType type)
-{
-	U32 out;
-	if(type >= ShaderVariableDataType::NUMERIC_1_COMPONENT_FIRST
-		&& type <= ShaderVariableDataType::NUMERIC_1_COMPONENT_LAST)
-	{
-		out = 1;
-	}
-	else if(type >= ShaderVariableDataType::NUMERIC_2_COMPONENT_FIRST
-			&& type <= ShaderVariableDataType::NUMERIC_2_COMPONENT_LAST)
-	{
-		out = 2;
-	}
-	else if(type >= ShaderVariableDataType::NUMERIC_3_COMPONENT_FIRST
-			&& type <= ShaderVariableDataType::NUMERIC_3_COMPONENT_LAST)
-	{
-		out = 3;
-	}
-	else if(type >= ShaderVariableDataType::NUMERIC_4_COMPONENT_FIRST
-			&& type <= ShaderVariableDataType::NUMERIC_4_COMPONENT_LAST)
-	{
-		out = 4;
-	}
-	else
-	{
-		out = MAX_U32;
-	}
+#define ANKI_SPECIALIZATION_CONSTANT_IVEC3(x, id, defltVal) \
+	layout(constant_id = id) const I32 ANKI_CONCATENATE(x, _0) = defltVal[0]; \
+	layout(constant_id = id + 1) const I32 ANKI_CONCATENATE(x, _1) = defltVal[1]; \
+	layout(constant_id = id + 2) const I32 ANKI_CONCATENATE(x, _2) = defltVal[2]
 
-	return out;
-}
+#define ANKI_SPECIALIZATION_CONSTANT_F32(x, id, defltVal) layout(constant_id = id) const F32 x = defltVal
 
-/// 0: is int, 1: is uint, 2: is float
-static U32 shaderVariableScalarType(ShaderVariableDataType type)
-{
-	U32 out;
-	switch(type)
-	{
-	case ShaderVariableDataType::INT:
-	case ShaderVariableDataType::IVEC2:
-	case ShaderVariableDataType::IVEC3:
-	case ShaderVariableDataType::IVEC4:
-		out = 0;
-		break;
-	case ShaderVariableDataType::UINT:
-	case ShaderVariableDataType::UVEC2:
-	case ShaderVariableDataType::UVEC3:
-	case ShaderVariableDataType::UVEC4:
-		out = 1;
-		break;
-	case ShaderVariableDataType::FLOAT:
-	case ShaderVariableDataType::VEC2:
-	case ShaderVariableDataType::VEC3:
-	case ShaderVariableDataType::VEC4:
-		out = 2;
-		break;
-	default:
-		ANKI_ASSERT(0);
-		out = MAX_U32;
-		break;
-	}
-	return out;
-}
+#define ANKI_SPECIALIZATION_CONSTANT_VEC2(x, id, defltVal) \
+	layout(constant_id = id) const F32 ANKI_CONCATENATE(x, _0) = defltVal[0]; \
+	layout(constant_id = id + 1) const F32 ANKI_CONCATENATE(x, _1) = defltVal[1]
+
+#define ANKI_SPECIALIZATION_CONSTANT_VEC3(x, id, defltVal) \
+	layout(constant_id = id) const F32 ANKI_CONCATENATE(x, _0) = defltVal[0]; \
+	layout(constant_id = id + 1) const F32 ANKI_CONCATENATE(x, _1) = defltVal[1]; \
+	layout(constant_id = id + 2) const F32 ANKI_CONCATENATE(x, _2) = defltVal[2]
+)";
 
 ShaderProgramParser::ShaderProgramParser(CString fname,
 	ShaderProgramFilesystemInterface* fsystem,
@@ -263,29 +139,6 @@ void ShaderProgramParser::tokenizeLine(CString line, DynamicArrayAuto<StringAuto
 	}
 }
 
-Error ShaderProgramParser::parsePragmaDescriptorSet(
-	const StringAuto* begin, const StringAuto* end, CString line, CString fname)
-{
-	ANKI_ASSERT(begin && end);
-
-	if(begin >= end)
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
-	if(begin->toNumber(m_set))
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
-	if(m_set >= MAX_DESCRIPTOR_SETS)
-	{
-		ANKI_PP_ERROR_MALFORMED_MSG("The descriptor set index is too high");
-	}
-
-	return Error::NONE;
-}
-
 Error ShaderProgramParser::parsePragmaStart(const StringAuto* begin, const StringAuto* end, CString line, CString fname)
 {
 	ANKI_ASSERT(begin && end);
@@ -374,244 +227,6 @@ Error ShaderProgramParser::parsePragmaEnd(const StringAuto* begin, const StringA
 	return Error::NONE;
 }
 
-Error ShaderProgramParser::parsePragmaInput(const StringAuto* begin, const StringAuto* end, CString line, CString fname)
-{
-	ANKI_ASSERT(begin && end);
-
-	if(begin >= end)
-	{
-		ANKI_PP_ERROR_MALFORMED();
-	}
-
-	if(m_insideShader)
-	{
-		ANKI_PP_ERROR_MALFORMED_MSG("Can't have #pragma input inside shader blocks");
-	}
-
-	m_inputs.emplaceBack(m_alloc);
-	Input& input = m_inputs.getBack();
-	input.m_idx = m_inputs.getSize() - 1;
-
-	// const
-	Bool isConst;
-	{
-		if(*begin == "const")
-		{
-			isConst = true;
-			++begin;
-		}
-		else
-		{
-			isConst = false;
-		}
-	}
-
-	// instanced
-	{
-		if(begin >= end)
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		input.m_instanced = false;
-		if(*begin == "instanced")
-		{
-			input.m_instanced = true;
-			m_foundAtLeastOneInstancedInput = true;
-			++begin;
-		}
-	}
-
-	// type
-	const StringAuto& dataTypeStr = *begin;
-	{
-		if(begin >= end)
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		if(computeShaderVariableDataType(*begin, input.m_dataType))
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-		++begin;
-	}
-
-	// name
-	{
-		if(begin >= end)
-		{
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		// Check if there are duplicates
-		for(U32 i = 0; i < m_inputs.getSize() - 1; ++i)
-		{
-			if(m_inputs[i].m_name == *begin)
-			{
-				ANKI_PP_ERROR_MALFORMED_MSG("Duplicate input");
-			}
-		}
-
-		if(begin->getLength() > MAX_SHADER_BINARY_NAME_LENGTH)
-		{
-			ANKI_PP_ERROR_MALFORMED_MSG("Too big name");
-		}
-
-		input.m_name.create(begin->toCString());
-		++begin;
-	}
-
-	// Append to source
-
-	const Bool isSampler = input.m_dataType == ShaderVariableDataType::SAMPLER;
-	const Bool isTexture = input.m_dataType >= ShaderVariableDataType::TEXTURE_FIRST
-						   && input.m_dataType <= ShaderVariableDataType::TEXTURE_LAST;
-
-	if(isConst)
-	{
-		// Const
-
-		if(isSampler || isTexture || input.m_instanced)
-		{
-			// No const samplers or instanced
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		const U32 vecComponents = computeSpecConstantIdsRequired(input.m_dataType);
-		if(vecComponents == MAX_U32)
-		{
-			ANKI_PP_ERROR_MALFORMED_MSG("Type can't be const");
-		}
-
-		const U32 scalarType = shaderVariableScalarType(input.m_dataType);
-
-		// Add an tag for later pre-processing (when trying to see if the variable is present)
-		m_codeLines.pushBackSprintf("#pragma _anki_input_present_%s", input.m_name.cstr());
-
-		m_globalsLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", input.m_name.cstr());
-		m_globalsLines.pushBackSprintf("#define %s_DEFINED 1", input.m_name.cstr());
-
-		const Array<CString, 3> typeNames = {{"I32", "I32", "F32"}};
-
-		input.m_specConstId = m_specConstIdx;
-
-		StringAuto inputDeclaration(m_alloc);
-		for(U32 comp = 0; comp < vecComponents; ++comp)
-		{
-			m_globalsLines.pushBackSprintf("layout(constant_id = %u) const %s _anki_const_%s_%u = %s(0);",
-				m_specConstIdx,
-				typeNames[scalarType].cstr(),
-				input.m_name.cstr(),
-				comp,
-				typeNames[scalarType].cstr());
-
-			if(comp == 0)
-			{
-				inputDeclaration.sprintf("%s %s = %s(_anki_const_%s_%u",
-					dataTypeStr.cstr(),
-					input.m_name.cstr(),
-					dataTypeStr.cstr(),
-					input.m_name.cstr(),
-					comp);
-			}
-			else
-			{
-				StringAuto tmp(m_alloc);
-				tmp.sprintf(", _anki_const_%s_%u", input.m_name.cstr(), comp);
-				inputDeclaration.append(tmp);
-			}
-
-			if(comp == vecComponents - 1)
-			{
-				inputDeclaration.append(");");
-			}
-
-			++m_specConstIdx;
-		}
-
-		m_globalsLines.pushBack(inputDeclaration);
-
-		m_globalsLines.pushBack("#else");
-		m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", input.m_name.cstr());
-		m_globalsLines.pushBack("#endif");
-	}
-	else if(isSampler || isTexture)
-	{
-		// Sampler or texture
-
-		if(input.m_instanced)
-		{
-			// Samplers and textures can't be instanced
-			ANKI_PP_ERROR_MALFORMED();
-		}
-
-		// Add an tag for later pre-processing (when trying to see if the variable is present)
-		m_codeLines.pushBackSprintf("#pragma _anki_input_present_%s", input.m_name.cstr());
-
-		m_globalsLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", input.m_name.cstr());
-		m_globalsLines.pushBackSprintf("#define %s_DEFINED 1", input.m_name.cstr());
-
-		m_globalsLines.pushBackSprintf("layout(set = _ANKI_DSET, binding = _ANKI_%s_BINDING) uniform %s %s;",
-			input.m_name.cstr(),
-			dataTypeStr.cstr(),
-			input.m_name.cstr());
-
-		m_globalsLines.pushBack("#else");
-		m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", input.m_name.cstr());
-		m_globalsLines.pushBack("#endif");
-	}
-	else
-	{
-		// UBO
-
-		const char* name = input.m_name.cstr();
-		const char* type = dataTypeStr.cstr();
-
-		// Add an tag for later pre-processing (when trying to see if the variable is present)
-		m_codeLines.pushBackSprintf("#pragma _anki_input_present_%s", name);
-
-		if(input.m_instanced)
-		{
-			m_uboStructLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", name);
-			m_uboStructLines.pushBack("#if _ANKI_INSTANCE_COUNT > 1");
-			m_uboStructLines.pushBackSprintf("%s _anki_uni_%s[_ANKI_INSTANCE_COUNT];", type, name);
-			m_uboStructLines.pushBack("#else");
-			m_uboStructLines.pushBackSprintf("%s _anki_uni_%s;", type, name);
-			m_uboStructLines.pushBack("#endif");
-			m_uboStructLines.pushBack("#endif");
-
-			m_globalsLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", name);
-			m_globalsLines.pushBack("#ifdef ANKI_VERTEX_SHADER");
-			m_globalsLines.pushBackSprintf("#define %s_DEFINED 1", name);
-			m_globalsLines.pushBack("#if _ANKI_INSTANCE_COUNT > 1");
-			m_globalsLines.pushBackSprintf("const %s %s = _anki_unis._anki_uni_%s[gl_InstanceID];", type, name, name);
-			m_globalsLines.pushBack("#else");
-			m_globalsLines.pushBackSprintf("const %s %s = _anki_unis._anki_uni_%s;", type, name, name);
-			m_globalsLines.pushBack("#endif");
-			m_globalsLines.pushBack("#endif //ANKI_VERTEX_SHADER");
-			m_globalsLines.pushBack("#else");
-			m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", name);
-			m_globalsLines.pushBack("#endif");
-		}
-		else
-		{
-			m_uboStructLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", name);
-			m_uboStructLines.pushBackSprintf("%s _anki_uni_%s;", type, name);
-			m_uboStructLines.pushBack("#endif");
-
-			m_globalsLines.pushBackSprintf("#if _ANKI_ACTIVATE_INPUT_%s", name);
-			m_globalsLines.pushBackSprintf("const %s %s = _anki_unis_._anki_uni_%s;", type, name, name);
-			m_globalsLines.pushBackSprintf("#define %s_DEFINED 1", name);
-			m_globalsLines.pushBack("#else");
-			m_globalsLines.pushBackSprintf("#define %s_DEFINED 0", name);
-			m_globalsLines.pushBack("#endif");
-		}
-	}
-
-	return Error::NONE;
-}
-
 Error ShaderProgramParser::parsePragmaMutator(
 	const StringAuto* begin, const StringAuto* end, CString line, CString fname)
 {
@@ -625,27 +240,6 @@ Error ShaderProgramParser::parsePragmaMutator(
 	m_mutators.emplaceBack(m_alloc);
 	Mutator& mutator = m_mutators.getBack();
 
-	// Instanced
-	{
-		if(*begin == "instanceCount")
-		{
-			mutator.m_instanceCount = true;
-
-			// Check
-			if(m_instancedMutatorIdx != MAX_U32)
-			{
-				ANKI_PP_ERROR_MALFORMED_MSG("Can't have more than one instanced mutators");
-			}
-
-			m_instancedMutatorIdx = m_mutators.getSize() - 1;
-			++begin;
-		}
-		else
-		{
-			mutator.m_instanceCount = false;
-		}
-	}
-
 	// Name
 	{
 		if(begin >= end)
@@ -710,12 +304,6 @@ Error ShaderProgramParser::parsePragmaMutator(
 		}
 	}
 
-	// Update some source
-	if(mutator.m_instanceCount)
-	{
-		m_globalsLines.pushFrontSprintf("#define _ANKI_INSTANCE_COUNT %s", mutator.m_name.cstr());
-	}
-
 	return Error::NONE;
 }
 
@@ -959,10 +547,6 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 			{
 				ANKI_CHECK(parsePragmaMutator(token + 1, end, line, fname));
 			}
-			else if(*token == "input")
-			{
-				ANKI_CHECK(parsePragmaInput(token + 1, end, line, fname));
-			}
 			else if(*token == "start")
 			{
 				ANKI_CHECK(parsePragmaStart(token + 1, end, line, fname));
@@ -971,10 +555,6 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 			{
 				ANKI_CHECK(parsePragmaEnd(token + 1, end, line, fname));
 			}
-			else if(*token == "descriptor_set")
-			{
-				ANKI_CHECK(parsePragmaDescriptorSet(token + 1, end, line, fname));
-			}
 			else if(*token == "rewrite_mutation")
 			{
 				ANKI_CHECK(parsePragmaRewriteMutation(token + 1, end, line, fname));
@@ -1063,12 +643,6 @@ Error ShaderProgramParser::parse()
 
 	// Checks
 	{
-		if(m_foundAtLeastOneInstancedInput != (m_instancedMutatorIdx != MAX_U32))
-		{
-			ANKI_SHADER_COMPILER_LOGE("If there is an instanced mutator there should be at least one instanced input");
-			return Error::USER_DATA;
-		}
-
 		if(!!(m_shaderTypes & ShaderTypeBit::COMPUTE))
 		{
 			if(m_shaderTypes != ShaderTypeBit::COMPUTE)
@@ -1076,12 +650,6 @@ Error ShaderProgramParser::parse()
 				ANKI_SHADER_COMPILER_LOGE("Can't combine compute shader with other types of shaders");
 				return Error::USER_DATA;
 			}
-
-			if(m_instancedMutatorIdx != MAX_U32)
-			{
-				ANKI_SHADER_COMPILER_LOGE("Can't have instanced mutators in compute programs");
-				return Error::USER_DATA;
-			}
 		}
 		else
 		{
@@ -1099,37 +667,9 @@ Error ShaderProgramParser::parse()
 		}
 	}
 
-	// Create the UBO source code
-	{
-		m_uboStructLines.pushFrontSprintf("#define _ANKI_DSET %u", m_set);
-
-		m_uboStructLines.pushFront("struct _AnkiUniforms {");
-		m_uboStructLines.pushBack("};");
-
-		m_uboStructLines.pushBack("#if _ANKI_USE_PUSH_CONSTANTS == 1");
-		m_uboStructLines.pushBackSprintf(
-			"layout(push_constant, std140, row_major) uniform _anki_pc {_AnkiUniforms _anki_unis;};");
-		m_uboStructLines.pushBack("#else");
-		m_uboStructLines.pushBack(
-			"layout(set = _ANKI_DSET, binding = 0, row_major) uniform _anki_ubo {_AnkiUniforms _anki_unis;};");
-		m_uboStructLines.pushBack("#endif\n");
-
-		m_uboStructLines.join("\n", m_uboSource);
-		m_uboStructLines.destroy();
-	}
-
-	// Create the globals source code
-	if(m_globalsLines.getSize() > 0)
-	{
-		m_globalsLines.pushBack("\n");
-		m_globalsLines.join("\n", m_globalsSource);
-		m_globalsLines.destroy();
-	}
-
 	// Create the code lines
 	if(m_codeLines.getSize())
 	{
-		m_codeLines.pushBack("\n");
 		m_codeLines.join("\n", m_codeSource);
 		m_codeLines.destroy();
 	}
@@ -1137,43 +677,6 @@ Error ShaderProgramParser::parse()
 	return Error::NONE;
 }
 
-Error ShaderProgramParser::findActiveInputVars(CString source, BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES>& active) const
-{
-	StringAuto preprocessedSrc(m_alloc);
-	ANKI_CHECK(preprocessGlsl(source, preprocessedSrc));
-
-	StringListAuto lines(m_alloc);
-	lines.splitString(preprocessedSrc, '\n');
-
-	for(const String& line : lines)
-	{
-		const CString prefix = "#pragma _anki_input_present_";
-		if(line.find(prefix) == String::NPOS)
-		{
-			continue;
-		}
-
-		ANKI_ASSERT(line.getLength() > prefix.getLength());
-		const CString varName = line.getBegin() + prefix.getLength();
-
-		// Find the input var
-		Bool found = false;
-		for(const Input& in : m_inputs)
-		{
-			if(in.m_name == varName)
-			{
-				active.set(in.m_idx);
-				found = true;
-				break;
-			}
-		}
-		(void)found;
-		ANKI_ASSERT(found);
-	}
-
-	return Error::NONE;
-}
-
 Error ShaderProgramParser::generateVariant(
 	ConstWeakArray<MutatorValue> mutation, ShaderProgramParserVariant& variant) const
 {
@@ -1188,15 +691,6 @@ Error ShaderProgramParser::generateVariant(
 	// Init variant
 	::new(&variant) ShaderProgramParserVariant();
 	variant.m_alloc = m_alloc;
-	variant.m_bindings.create(m_alloc, m_inputs.getSize(), -1);
-	variant.m_blockInfos.create(m_alloc, m_inputs.getSize());
-
-	// Get instance count, one mutation has it
-	U32 instanceCount = 1;
-	if(m_instancedMutatorIdx != MAX_U32)
-	{
-		instanceCount = mutation[m_instancedMutatorIdx];
-	}
 
 	// Create the mutator defines
 	StringAuto mutatorDefines(m_alloc);
@@ -1214,157 +708,6 @@ Error ShaderProgramParser::generateVariant(
 		MAX_BINDLESS_TEXTURES,
 		MAX_BINDLESS_IMAGES);
 
-	// Find active vars by running the preprocessor
-	StringAuto activeInputs(m_alloc);
-	if(m_inputs.getSize() > 0)
-	{
-		StringAuto src(m_alloc);
-		src.append(header);
-		src.append("#define ANKI_VERTEX_SHADER 1\n"); // Something random to avoid compilation errors
-		src.append(mutatorDefines);
-		src.append(m_codeSource);
-		ANKI_CHECK(findActiveInputVars(src, variant.m_activeInputVarsMask));
-
-		StringListAuto lines(m_alloc);
-		for(const Input& in : m_inputs)
-		{
-			const Bool active = variant.m_activeInputVarsMask.get(in.m_idx);
-			lines.pushBackSprintf("#define _ANKI_ACTIVATE_INPUT_%s %u", in.m_name.cstr(), active);
-		}
-
-		lines.pushBack("\n");
-		lines.join("\n", activeInputs);
-	}
-
-	// Initialize the active vars that are inside a UBO
-	for(const Input& in : m_inputs)
-	{
-		if(!variant.m_activeInputVarsMask.get(in.m_idx))
-		{
-			continue;
-		}
-
-		if(!in.inUbo())
-		{
-			continue;
-		}
-
-		ShaderVariableBlockInfo& blockInfo = variant.m_blockInfos[in.m_idx];
-
-		// std140 rules
-		blockInfo.m_offset = I16(variant.m_uniBlockSize);
-		blockInfo.m_arraySize = (in.m_instanced) ? I16(instanceCount) : 1;
-
-		if(in.m_dataType == ShaderVariableDataType::FLOAT || in.m_dataType == ShaderVariableDataType::INT
-			|| in.m_dataType == ShaderVariableDataType::UINT)
-		{
-			blockInfo.m_arrayStride = sizeof(Vec4);
-
-			if(blockInfo.m_arraySize == 1)
-			{
-				// No need to align the in.m_offset
-				variant.m_uniBlockSize += sizeof(F32);
-			}
-			else
-			{
-				alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
-				variant.m_uniBlockSize += sizeof(Vec4) * blockInfo.m_arraySize;
-			}
-		}
-		else if(in.m_dataType == ShaderVariableDataType::VEC2 || in.m_dataType == ShaderVariableDataType::IVEC2
-				|| in.m_dataType == ShaderVariableDataType::UVEC2)
-		{
-			blockInfo.m_arrayStride = sizeof(Vec4);
-
-			if(blockInfo.m_arraySize == 1)
-			{
-				alignRoundUp(sizeof(Vec2), blockInfo.m_offset);
-				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec2);
-			}
-			else
-			{
-				alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
-				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
-			}
-		}
-		else if(in.m_dataType == ShaderVariableDataType::VEC3 || in.m_dataType == ShaderVariableDataType::IVEC3
-				|| in.m_dataType == ShaderVariableDataType::UVEC3)
-		{
-			alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
-			blockInfo.m_arrayStride = sizeof(Vec4);
-
-			if(blockInfo.m_arraySize == 1)
-			{
-				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec3);
-			}
-			else
-			{
-				variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
-			}
-		}
-		else if(in.m_dataType == ShaderVariableDataType::VEC4 || in.m_dataType == ShaderVariableDataType::IVEC4
-				|| in.m_dataType == ShaderVariableDataType::UVEC4)
-		{
-			blockInfo.m_arrayStride = sizeof(Vec4);
-			alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
-			variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * blockInfo.m_arraySize;
-		}
-		else if(in.m_dataType == ShaderVariableDataType::MAT3)
-		{
-			alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
-			blockInfo.m_arrayStride = sizeof(Vec4) * 3;
-			variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Vec4) * 3 * blockInfo.m_arraySize;
-			blockInfo.m_matrixStride = sizeof(Vec4);
-		}
-		else if(in.m_dataType == ShaderVariableDataType::MAT4)
-		{
-			alignRoundUp(sizeof(Vec4), blockInfo.m_offset);
-			blockInfo.m_arrayStride = sizeof(Mat4);
-			variant.m_uniBlockSize = blockInfo.m_offset + sizeof(Mat4) * blockInfo.m_arraySize;
-			blockInfo.m_matrixStride = sizeof(Vec4);
-		}
-		else
-		{
-			ANKI_ASSERT(0);
-		}
-	}
-
-	// Find if it's using push constants
-	StringAuto pushConstantDefineSrc(m_alloc);
-	{
-		variant.m_usesPushConstants = variant.m_uniBlockSize <= m_pushConstSize;
-		pushConstantDefineSrc.sprintf("#define _ANKI_USE_PUSH_CONSTANTS %u\n", variant.m_usesPushConstants);
-	}
-
-	// Handle the bindings for the textures and samplers
-	StringAuto bindingDefines(m_alloc);
-	{
-		StringListAuto defines(m_alloc);
-		U32 texOrSamplerBinding = variant.m_usesPushConstants ? 0 : 1;
-		for(const Input& in : m_inputs)
-		{
-			if(!variant.m_activeInputVarsMask.get(in.m_idx))
-			{
-				continue;
-			}
-
-			if(!in.isSampler() && !in.isTexture())
-			{
-				continue;
-			}
-
-			defines.pushBackSprintf("#define _ANKI_%s_BINDING %u", in.m_name.cstr(), texOrSamplerBinding);
-			variant.m_bindings[in.m_idx] = I16(texOrSamplerBinding++);
-		}
-
-		defines.pushBack(" ");
-
-		if(!defines.isEmpty())
-		{
-			defines.join("\n", bindingDefines);
-		}
-	}
-
 	// Generate souce per stage
 	for(ShaderType shaderType = ShaderType::FIRST; shaderType < ShaderType::COUNT; ++shaderType)
 	{
@@ -1378,11 +721,6 @@ Error ShaderProgramParser::generateVariant(
 		finalSource.append(header);
 		finalSource.append(mutatorDefines);
 		finalSource.append(StringAuto(m_alloc).sprintf("#define ANKI_%s 1\n", SHADER_STAGE_NAMES[shaderType].cstr()));
-		finalSource.append(activeInputs);
-		finalSource.append(pushConstantDefineSrc);
-		finalSource.append(bindingDefines);
-		finalSource.append(m_uboSource);
-		finalSource.append(m_globalsSource);
 		finalSource.append(m_codeSource);
 
 		// Move the source

+ 1 - 129
src/anki/shader_compiler/ShaderProgramParser.h

@@ -9,7 +9,6 @@
 #include <anki/util/StringList.h>
 #include <anki/util/WeakArray.h>
 #include <anki/util/DynamicArray.h>
-#include <anki/util/BitSet.h>
 #include <anki/gr/utils/Functions.h>
 
 namespace anki
@@ -44,76 +43,9 @@ public:
 		return m_values;
 	}
 
-	Bool isInstanceCount() const
-	{
-		return m_instanceCount;
-	}
-
 private:
 	StringAuto m_name;
 	DynamicArrayAuto<MutatorValue> m_values;
-	Bool m_instanceCount = false;
-};
-
-/// @memberof ShaderProgramParser
-class ShaderProgramParserInput
-{
-	friend ShaderProgramParser;
-	friend ShaderProgramParserVariant;
-
-public:
-	ShaderProgramParserInput(GenericMemoryPoolAllocator<U8> alloc)
-		: m_name(alloc)
-	{
-	}
-
-	CString getName() const
-	{
-		return m_name.toCString();
-	}
-
-	ShaderVariableDataType getDataType() const
-	{
-		return m_dataType;
-	}
-
-	Bool isInstanced() const
-	{
-		return m_instanced;
-	}
-
-	/// @param constantId It's the vulkan spec const index.
-	Bool isConstant(U32* constantId = nullptr) const
-	{
-		if(constantId)
-		{
-			*constantId = m_specConstId;
-		}
-		return m_specConstId != MAX_U32;
-	}
-
-	Bool isTexture() const
-	{
-		return m_dataType >= ShaderVariableDataType::TEXTURE_FIRST
-			   && m_dataType <= ShaderVariableDataType::TEXTURE_LAST;
-	}
-
-	Bool isSampler() const
-	{
-		return m_dataType == ShaderVariableDataType::SAMPLER;
-	}
-
-	Bool inUbo() const
-	{
-		return !isConstant() && !isTexture() && !isSampler();
-	}
-
-private:
-	StringAuto m_name;
-	U32 m_idx = MAX_U32; ///< Index inside an array.
-	U32 m_specConstId = MAX_U32;
-	Bool m_instanced = false;
-	ShaderVariableDataType m_dataType = ShaderVariableDataType::NONE;
 };
 
 /// @memberof ShaderProgramParser
@@ -128,8 +60,6 @@ public:
 		{
 			s.destroy(m_alloc);
 		}
-		m_blockInfos.destroy(m_alloc);
-		m_bindings.destroy(m_alloc);
 	}
 
 	CString getSource(ShaderType type) const
@@ -137,41 +67,9 @@ public:
 		return m_sources[type];
 	}
 
-	Bool isInputActive(const ShaderProgramParserInput& in) const
-	{
-		return m_activeInputVarsMask.get(in.m_idx);
-	}
-
-	const ShaderVariableBlockInfo& getBlockInfo(const ShaderProgramParserInput& in) const
-	{
-		ANKI_ASSERT(in.inUbo() && isInputActive(in));
-		return m_blockInfos[in.m_idx];
-	}
-
-	U32 getBinding(const ShaderProgramParserInput& in) const
-	{
-		ANKI_ASSERT((in.isSampler() || in.isTexture()) && m_bindings[in.m_idx] >= 0);
-		return U32(m_bindings[in.m_idx]);
-	}
-
-	U32 getBlockSize() const
-	{
-		return m_uniBlockSize;
-	}
-
-	Bool usesPushConstants() const
-	{
-		return m_usesPushConstants;
-	}
-
 private:
 	GenericMemoryPoolAllocator<U8> m_alloc;
 	Array<String, U(ShaderType::COUNT)> m_sources;
-	DynamicArray<ShaderVariableBlockInfo> m_blockInfos;
-	DynamicArray<I16> m_bindings;
-	U32 m_uniBlockSize = 0;
-	Bool m_usesPushConstants = false;
-	BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES> m_activeInputVarsMask = {false};
 };
 
 /// This is a special preprocessor that run before the usual preprocessor. Its purpose is to add some meta information
@@ -180,13 +78,11 @@ private:
 /// It supports the following expressions:
 /// #include {<> | ""}
 /// #pragma once
-/// #pragma anki mutator [instanced] NAME VALUE0 [VALUE1 [VALUE2] ...]
+/// #pragma anki mutator NAME VALUE0 [VALUE1 [VALUE2] ...]
 /// #pragma anki rewrite_mutation NAME_A VALUE0 NAME_B VALUE1 [NAME_C VALUE3...] to
 ///                               NAME_A VALUE4 NAME_B VALUE5 [NAME_C VALUE6...]
-/// #pragma anki input [const | instanced] TYPE NAME
 /// #pragma anki start {vert | tessc | tesse | geom | frag | comp}
 /// #pragma anki end
-/// #pragma anki descriptor_set <number>
 ///
 /// Only the "anki input" should be in an ifdef-like guard. For everything else it's ignored.
 class ShaderProgramParser : public NonCopyable
@@ -218,24 +114,13 @@ public:
 		return m_mutators;
 	}
 
-	ConstWeakArray<ShaderProgramParserInput> getInputs() const
-	{
-		return m_inputs;
-	}
-
 	ShaderTypeBit getShaderTypes() const
 	{
 		return m_shaderTypes;
 	}
 
-	U32 getDescritproSet() const
-	{
-		return m_set;
-	}
-
 private:
 	using Mutator = ShaderProgramParserMutator;
-	using Input = ShaderProgramParserInput;
 
 	class MutationRewrite
 	{
@@ -269,26 +154,17 @@ private:
 	ShaderProgramFilesystemInterface* m_fsystem = nullptr;
 
 	StringListAuto m_codeLines = {m_alloc}; ///< The code.
-	StringListAuto m_globalsLines = {m_alloc};
-	StringListAuto m_uboStructLines = {m_alloc};
 	StringAuto m_codeSource = {m_alloc};
-	StringAuto m_globalsSource = {m_alloc};
-	StringAuto m_uboSource = {m_alloc};
 
 	DynamicArrayAuto<Mutator> m_mutators = {m_alloc};
-	DynamicArrayAuto<Input> m_inputs = {m_alloc};
 	DynamicArrayAuto<MutationRewrite> m_mutationRewrites = {m_alloc};
 
 	ShaderTypeBit m_shaderTypes = ShaderTypeBit::NONE;
 	Bool m_insideShader = false;
-	U32 m_set = 0;
-	U32 m_instancedMutatorIdx = MAX_U32;
-	U32 m_specConstIdx = 0;
 	const U32 m_pushConstSize = 0;
 	const U32 m_backendMinor = 1;
 	const U32 m_backendMajor = 1;
 	const GpuVendor m_gpuVendor = GpuVendor::AMD;
-	Bool m_foundAtLeastOneInstancedInput = false;
 
 	ANKI_USE_RESULT Error parseFile(CString fname, U32 depth);
 	ANKI_USE_RESULT Error parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth);
@@ -296,15 +172,11 @@ private:
 		const StringAuto* begin, const StringAuto* end, CString line, CString fname, U32 depth);
 	ANKI_USE_RESULT Error parsePragmaMutator(
 		const StringAuto* begin, const StringAuto* end, CString line, CString fname);
-	ANKI_USE_RESULT Error parsePragmaInput(const StringAuto* begin, const StringAuto* end, CString line, CString fname);
 	ANKI_USE_RESULT Error parsePragmaStart(const StringAuto* begin, const StringAuto* end, CString line, CString fname);
 	ANKI_USE_RESULT Error parsePragmaEnd(const StringAuto* begin, const StringAuto* end, CString line, CString fname);
-	ANKI_USE_RESULT Error parsePragmaDescriptorSet(
-		const StringAuto* begin, const StringAuto* end, CString line, CString fname);
 	ANKI_USE_RESULT Error parsePragmaRewriteMutation(
 		const StringAuto* begin, const StringAuto* end, CString line, CString fname);
 
-	ANKI_USE_RESULT Error findActiveInputVars(CString source, BitSet<MAX_SHADER_PROGRAM_INPUT_VARIABLES>& active) const;
 	void tokenizeLine(CString line, DynamicArrayAuto<StringAuto>& tokens) const;
 
 	static Bool tokenIsComment(CString token)

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

@@ -0,0 +1,621 @@
+// 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, const GenericMemoryPoolAllocator<U8>& tmpAlloc)
+		: spirv_cross::Compiler(ir, wordCount)
+		, m_tmpAlloc(tmpAlloc)
+	{
+	}
+
+	ANKI_USE_RESULT static Error performSpirvReflection(ShaderProgramBinaryReflection& refl,
+		Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
+		GenericMemoryPoolAllocator<U8> tmpAlloc,
+		GenericMemoryPoolAllocator<U8> binaryAlloc);
+
+private:
+	GenericMemoryPoolAllocator<U8> m_tmpAlloc;
+
+	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,
+		CString parentVariable,
+		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, CString(), 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,
+	CString parentVariable,
+	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;
+
+			if(parentVariable.isEmpty())
+			{
+				strncpy(var.m_name.getBegin(), name.c_str(), var.m_name.getSize());
+			}
+			else
+			{
+				StringAuto extendedName(m_tmpAlloc);
+				extendedName.sprintf("%s.%s", parentVariable.cstr(), name.c_str());
+				strncpy(var.m_name.getBegin(), extendedName.cstr(), var.m_name.getSize());
+			}
+
+			var.m_name[var.m_name.getSize() - 1] = '\0';
+		}
+
+		// 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;
+				}
+
+				const Bool specConstantArray = memberType.array_size_literal[0];
+				if(specConstantArray)
+				{
+					var.m_blockInfo.m_arraySize = I16(memberType.array[0]);
+				}
+				else
+				{
+					var.m_blockInfo.m_arraySize = 1;
+				}
+			}
+			else
+			{
+				var.m_blockInfo.m_arraySize = 1;
+			}
+		}
+
+		// Array stride
+		if(has_decoration(type.member_types[i], spv::DecorationArrayStride))
+		{
+			var.m_blockInfo.m_arrayStride = I16(get_decoration(type.member_types[i], spv::DecorationArrayStride));
+		}
+
+		// 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)
+		{
+			if(var.m_blockInfo.m_arraySize == 1)
+			{
+				ANKI_CHECK(blockVariableReflection(memberType, var.m_name.getBegin(), vars));
+			}
+			else
+			{
+				for(U32 i = 0; i < U32(var.m_blockInfo.m_arraySize); ++i)
+				{
+					StringAuto newName(m_tmpAlloc);
+					newName.sprintf("%s[%u]", var.m_name.getBegin(), i);
+					ANKI_CHECK(blockVariableReflection(memberType, newName, vars));
+				}
+			}
+		}
+		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
+		if(var.m_type != ShaderVariableDataType::NONE)
+		{
+			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), tmpAlloc);
+
+		// 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));
+	}
+
+	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

+ 1 - 0
src/anki/ui/Common.h

@@ -10,6 +10,7 @@
 #include <imgui/imgui.h>
 
 #include <anki/util/Allocator.h>
+#include <anki/util/Ptr.h>
 
 namespace anki
 {

+ 0 - 14
src/anki/util/Allocator.h

@@ -8,7 +8,6 @@
 #include <anki/util/Assert.h>
 #include <anki/util/Memory.h>
 #include <anki/util/Logger.h>
-#include <anki/util/Ptr.h>
 #include <cstddef> // For ptrdiff_t
 #include <utility> // For forward
 #include <new> // For placement new
@@ -304,19 +303,6 @@ public:
 		}
 	}
 
-	/// Call the destructor and deallocate an object
-	/// @note This is AnKi specific
-	template<typename Y>
-	void deleteInstance(WeakPtr<Y> ptr)
-	{
-		if(ptr)
-		{
-			typename rebind<Y>::other alloc(*this);
-			alloc.destroy(&ptr[0]);
-			alloc.deallocate(&ptr[0], 1);
-		}
-	}
-
 	/// Call the destructor and deallocate an array of objects
 	/// @note This is AnKi specific
 	template<typename Y>

+ 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;
 };

+ 55 - 0
src/anki/util/Enum.h

@@ -130,4 +130,59 @@ inline TEnum valueToEnum(typename EnumUnderlyingType<TEnum>::Type v)
 {
 	return static_cast<TEnum>(v);
 }
+
+/// @memberof EnumIterable
+template<typename TEnum>
+class EnumIterableIterator
+{
+public:
+	using Type = typename EnumUnderlyingType<TEnum>::Type;
+
+	EnumIterableIterator(TEnum val)
+		: m_val(static_cast<Type>(val))
+	{
+	}
+
+	TEnum operator*() const
+	{
+		return static_cast<TEnum>(m_val);
+	}
+
+	void operator++()
+	{
+		++m_val;
+	}
+
+	bool operator!=(EnumIterableIterator b) const
+	{
+		return m_val != b.m_val;
+	}
+
+private:
+	Type m_val;
+};
+
+/// Allow an enum to be used in a for range loop.
+/// @code
+/// for(SomeEnum type : EnumIterable<SomeEnum>())
+/// {
+/// 	...
+/// }
+/// @endcode
+template<typename TEnum>
+class EnumIterable
+{
+public:
+	using Iterator = EnumIterableIterator<TEnum>;
+
+	static Iterator begin()
+	{
+		return Iterator(TEnum::FIRST);
+	}
+
+	static Iterator end()
+	{
+		return Iterator(TEnum::COUNT);
+	}
+};
 /// @}

+ 40 - 7
src/anki/util/Ptr.h

@@ -5,8 +5,7 @@
 
 #pragma once
 
-#include <anki/util/Assert.h>
-#include <anki/util/StdTypes.h>
+#include <anki/util/Allocator.h>
 
 namespace anki
 {
@@ -266,6 +265,28 @@ public:
 	}
 };
 
+/// UniquePtr alternative deleter.
+template<typename T>
+class AllocatorPtrDeleter
+{
+public:
+	GenericMemoryPoolAllocator<U8> m_allocator;
+
+	AllocatorPtrDeleter()
+	{
+	}
+
+	AllocatorPtrDeleter(GenericMemoryPoolAllocator<U8> alloc)
+		: m_allocator(alloc)
+	{
+	}
+
+	void operator()(T* x)
+	{
+		m_allocator.template deleteInstance<T>(x);
+	}
+};
+
 /// A unique pointer.
 template<typename T, typename TDeleter = DefaultPtrDeleter<T>>
 class UniquePtr : public PtrBase<T>
@@ -279,8 +300,9 @@ public:
 	{
 	}
 
-	explicit UniquePtr(T* ptr)
+	explicit UniquePtr(T* ptr, const Deleter& deleter = Deleter())
 		: Base(ptr)
+		, m_deleter(deleter)
 	{
 	}
 
@@ -311,26 +333,37 @@ public:
 	}
 
 	/// Set a new pointer. Will destroy the previous.
-	void reset(T* ptr)
+	void reset(T* ptr, const Deleter& deleter = Deleter())
 	{
 		destroy();
 		Base::m_ptr = ptr;
+		m_deleter = deleter;
+	}
+
+	/// Move the ownership of the pointer outside the UniquePtr.
+	void moveAndReset(T*& ptr)
+	{
+		ptr = Base::m_ptr;
+		Base::m_ptr = nullptr;
+		m_deleter = Deleter();
 	}
 
 private:
+	Deleter m_deleter;
+
 	void destroy()
 	{
 		if(Base::m_ptr)
 		{
-			TDeleter deleter;
-			deleter(Base::m_ptr);
+			m_deleter(Base::m_ptr);
 			Base::m_ptr = nullptr;
+			m_deleter = Deleter();
 		}
 	}
 
 	void move(UniquePtr& b)
 	{
-		reset(b.m_ptr);
+		reset(b.m_ptr, b.m_deleter);
 		b.m_ptr = nullptr;
 	}
 };

+ 10 - 1
src/anki/util/WeakArray.h

@@ -226,7 +226,7 @@ public:
 	}
 
 	/// Construct from WeakArray.
-	ConstWeakArray(const WeakArray<T>& arr)
+	ConstWeakArray(const WeakArray<T, TSize>& arr)
 		: ConstWeakArray((arr.getSize()) ? &arr[0] : nullptr, arr.getSize())
 	{
 	}
@@ -270,6 +270,15 @@ public:
 		return *this;
 	}
 
+	/// Copy from a WeakArray.
+	template<typename Y>
+	ConstWeakArray& operator=(const WeakArray<Y, TSize>& b)
+	{
+		m_data = (b.getSize()) ? b.getBegin() : nullptr;
+		m_size = b.getSize();
+		return *this;
+	}
+
 	/// Move.
 	ConstWeakArray& operator=(ConstWeakArray&& b)
 	{

+ 5 - 5
src/anki/util/serializer.py

@@ -30,7 +30,7 @@ class MemberInfo:
     def __init__(self):
         self.name = None
         self.base_type = None
-        self.array_size = 1
+        self.array_size = "1"
         self.comment = None
         self.pointer = False
         self.constructor = None
@@ -40,10 +40,10 @@ class MemberInfo:
             return False
 
         for member in member_arr:
-            if member.name == str(member.array_size):
-                return False
+            if member.name == self.array_size:
+                return True
 
-        return True
+        return False
 
     def is_pointer(self, member_arr):
         return self.pointer and not self.is_dynamic_array(member_arr)
@@ -132,7 +132,7 @@ def gen_class(root_el):
             comment = ""
 
         if member.constructor:
-            constructor = "= {%s}" % member.constructor
+            constructor = " %s" % member.constructor
         else:
             constructor = ""
 

+ 45 - 42
tests/shader_compiler/ShaderProgramCompiler.cpp

@@ -9,7 +9,7 @@
 ANKI_TEST(ShaderCompiler, ShaderProgramCompiler)
 {
 	const CString sourceCode = R"(
-#pragma anki mutator instanceCount INSTANCE_COUNT 1 2 4 8 16 32 64
+#pragma anki mutator INSTANCE_COUNT 1 2 4 8 16 32 64
 #pragma anki mutator LOD 0 1 2
 #pragma anki mutator PASS 0 1 2 3
 #pragma anki mutator DIFFUSE_TEX 0 1
@@ -50,59 +50,60 @@ ANKI_TEST(ShaderCompiler, ShaderProgramCompiler)
 #pragma anki rewrite_mutation PASS 2 VELOCITY 1 to PASS 2 VELOCITY 0
 #pragma anki rewrite_mutation PASS 3 VELOCITY 1 to PASS 2 VELOCITY 0
 
-#pragma anki input instanced Mat4 mvp
+layout(set = 0, binding = 0) uniform ankiMaterial
+{
+	Mat4 u_ankiMvp[INSTANCE_COUNT];
+
 #if PASS == 0
-#	pragma anki input instanced Mat3 rotationMat
+	Mat3 u_ankiRotationMat[INSTANCE_COUNT];
 #endif
+
 #if PASS == 0 && PARALLAX == 1
-#	pragma anki input instanced Mat4 modelViewMat
+	Mat4 u_ankiModelViewMat[INSTANCE_COUNT];
 #endif
+
 #if PASS == 0 && VELOCITY == 1
-#	pragma anki input instanced Mat4 prevMvp
+	Mat4 u_ankiPrevMvp[INSTANCE_COUNT];
 #endif
+};
 
-#if DIFFUSE_TEX == 0 && PASS == 0
-#	pragma anki input const Vec3 diffColor
-#endif
-#if SPECULAR_TEX == 0 && PASS == 0
-#	pragma anki input const Vec3 specColor
-#endif
-#if ROUGHNESS_TEX == 0 && PASS == 0
-#	pragma anki input const F32 roughness
-#endif
-#if METAL_TEX == 0 && PASS == 0
-#	pragma anki input const F32 metallic
-#endif
-#if EMISSIVE_TEX == 0 && PASS == 0
-#	pragma anki input const Vec3 emission
-#endif
-#if PARALLAX == 1 && PASS == 0 && LOD == 0
-#	pragma anki input const F32 heightMapScale
-#endif
 #if PASS == 0
-#	pragma anki input const F32 subsurface
-#endif
-#pragma anki input sampler globalSampler
-#if DIFFUSE_TEX == 1 && PASS == 0
-#	pragma anki input texture2D diffTex
+
+#if DIFFUSE_TEX == 0
+ANKI_SPECIALIZATION_CONSTANT_VEC3(diffColor, 0, Vec3(0));
+#else
+layout(set = 0, binding = 1) uniform texture2D diffTex;
 #endif
-#if SPECULAR_TEX == 1 && PASS == 0
-#	pragma anki input texture2D specTex
+
+#if SPECULAR_TEX == 0
+ANKI_SPECIALIZATION_CONSTANT_VEC3(specColor, 3, Vec3(0));
+#else
+layout(set = 0, binding = 2) uniform texture2D specTex;
 #endif
-#if ROUGHNESS_TEX == 1 && PASS == 0
-#	pragma anki input texture2D roughnessTex
+
+#if ROUGHNESS_TEX == 0
+ANKI_SPECIALIZATION_CONSTANT_F32(roughness, 6, 0.0);
+#else
+layout(set = 0, binding = 3) uniform texture2D roughnessTex;
 #endif
-#if METAL_TEX == 1 && PASS == 0
-#	pragma anki input texture2D metallicTex
+
+#if METAL_TEX == 0
+ANKI_SPECIALIZATION_CONSTANT_F32(metallic, 7, 0.0);
+#else
+layout(set = 0, binding = 4) uniform texture2D metallicTex;
 #endif
-#if NORMAL_TEX == 1 && PASS == 0 && LOD < 2
-#	pragma anki input texture2D normalTex
+
+#if EMISSIVE_TEX == 0
+ANKI_SPECIALIZATION_CONSTANT_VEC3(emission, 8, Vec3(0.0));
+#else
+layout(set = 0, binding = 5) uniform texture2D emissiveTex;
 #endif
-#if PARALLAX == 1 && PASS == 0 && LOD == 0
-#	pragma anki input texture2D heightTex
+
+#if PARALLAX == 1 && LOD == 0
+ANKI_SPECIALIZATION_CONSTANT_F32(heightMapScale, 11, 0.0);
+layout(set = 0, binding = 6) uniform texture2D heightTex;
 #endif
-#if EMISSIVE_TEX == 1 && PASS == 0
-#	pragma anki input texture2D emissiveTex
+
 #endif
 
 #pragma anki start vert
@@ -150,7 +151,9 @@ void main()
 	ShaderProgramBinaryWrapper binary(alloc);
 	ANKI_TEST_EXPECT_NO_ERR(compileShaderProgram("test.glslp", fsystem, alloc, 128, 1, 1, GpuVendor::AMD, binary));
 
-	/*StringAuto dis(alloc);
+#if 0
+	StringAuto dis(alloc);
 	disassembleShaderProgramBinary(binary.getBinary(), dis);
-	ANKI_LOGI("Binary disassembly:\n%s\n", dis.cstr());*/
+	ANKI_LOGI("Binary disassembly:\n%s\n", dis.cstr());
+#endif
 }

+ 0 - 13
tests/shader_compiler/ShaderProgramParser.cpp

@@ -25,14 +25,6 @@ ANKI_TEST(ShaderCompiler, ShaderCompilerParser)
 
 #pragma anki rewrite_mutation M0 2 M1 4 to M0 1 M1 3
 
-#if M0 == 1
-#pragma anki input Vec3 var0
-#endif
-
-#if M1 == 4
-#pragma anki input const Vec3 var1
-#endif
-
 #pragma anki start vert
 
 // vert
@@ -56,16 +48,11 @@ ANKI_TEST(ShaderCompiler, ShaderCompilerParser)
 	ShaderProgramParser parser("filename0", &interface, alloc, 128, 1, 1, GpuVendor::AMD);
 	ANKI_TEST_EXPECT_NO_ERR(parser.parse());
 
-	// Check inputs
-	ANKI_TEST_EXPECT_EQ(parser.getInputs().getSize(), 2);
-
 	// Test a variant
 	Array<MutatorValue, 2> mutation = {{2, 4}};
 
 	ShaderProgramParserVariant variant;
 	ANKI_TEST_EXPECT_NO_ERR(parser.generateVariant(mutation, variant));
-	ANKI_TEST_EXPECT_EQ(variant.isInputActive(parser.getInputs()[0]), false);
-	ANKI_TEST_EXPECT_EQ(variant.isInputActive(parser.getInputs()[1]), true);
 
 	// Test rewrite
 	ANKI_TEST_EXPECT_EQ(parser.rewriteMutation(mutation), true);

+ 1 - 1
thirdparty

@@ -1 +1 @@
-Subproject commit 34601bca2fa9ea48123392eb19172774f09e7056
+Subproject commit b9353c88a4684fb71975f547afbc5bcdc469465a