Browse Source

Refactor the shader binary again

Panagiotis Christopoulos Charitos 5 years ago
parent
commit
c0cf749635

+ 15 - 0
src/anki/gr/Common.h

@@ -80,6 +80,21 @@ ANKI_GR_CLASS(RenderGraph)
 	template<typename, typename> \
 	friend class GenericPoolAllocator;
 
+/// Shader block information.
+class ShaderVariableBlockInfo
+{
+public:
+	I16 m_offset = -1; ///< Offset inside the block
+
+	I16 m_arraySize = -1; ///< Number of elements.
+
+	/// Stride between the each array element if the variable is array.
+	I16 m_arrayStride = -1;
+
+	/// Identifying the stride between columns of a column-major matrix or rows of a row-major matrix.
+	I16 m_matrixStride = -1;
+};
+
 /// Knowing the vendor allows some optimizations
 enum class GpuVendor : U8
 {

+ 0 - 15
src/anki/gr/utils/Functions.h

@@ -48,21 +48,6 @@ ShaderVariableDataType getShaderVariableTypeFromTypename();
 #include <anki/gr/ShaderVariableDataTypeDefs.h>
 #undef ANKI_SVDT_MACRO
 
-/// Shader block information.
-class ShaderVariableBlockInfo
-{
-public:
-	I16 m_offset = -1; ///< Offset inside the block
-
-	I16 m_arraySize = -1; ///< Number of elements.
-
-	/// Stride between the each array element if the variable is array.
-	I16 m_arrayStride = -1;
-
-	/// Identifying the stride between columns of a column-major matrix or rows of a row-major matrix.
-	I16 m_matrixStride = -1;
-};
-
 /// Populate the memory of a variable that is inside a shader block.
 void writeShaderBlockMemory(ShaderVariableDataType type,
 	const ShaderVariableBlockInfo& varBlkInfo,

+ 122 - 39
src/anki/shader_compiler/ShaderProgramBinary.h

@@ -19,17 +19,13 @@ class ShaderProgramBinaryVariable
 {
 public:
 	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], self.m_name.getSize());
-		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>
@@ -45,6 +41,91 @@ public:
 	}
 };
 
+/// Storage or uniform variable per variant.
+class ShaderProgramBinaryVariableInstance
+{
+public:
+	U32 m_index = MAX_U32; ///< Points to ShaderProgramBinary::m_variables.
+	ShaderVariableBlockInfo m_blockInfo;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doValue("m_index", offsetof(ShaderProgramBinaryVariableInstance, m_index), self.m_index);
+		s.doValue("m_blockInfo", offsetof(ShaderProgramBinaryVariableInstance, m_blockInfo), self.m_blockInfo);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryVariableInstance&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryVariableInstance&>(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;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doArray("m_name", offsetof(ShaderProgramBinaryOpaque, m_name), &self.m_name[0], self.m_name.getSize());
+		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);
+	}
+
+	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);
+	}
+};
+
+/// Sampler or texture or image per variant.
+class ShaderProgramBinaryOpaqueInstance
+{
+public:
+	U32 m_index = MAX_U32; ///< Points to ShaderProgramBinary::m_opaques.
+	U32 m_arraySize = MAX_U32;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doValue("m_index", offsetof(ShaderProgramBinaryOpaqueInstance, m_index), self.m_index);
+		s.doValue("m_arraySize", offsetof(ShaderProgramBinaryOpaqueInstance, m_arraySize), self.m_arraySize);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryOpaqueInstance&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryOpaqueInstance&>(serializer, *this);
+	}
+};
+
 /// Storage or uniform block.
 class ShaderProgramBinaryBlock
 {
@@ -78,36 +159,30 @@ public:
 	}
 };
 
-/// Sampler or texture or image.
-class ShaderProgramBinaryOpaque
+/// Storage or uniform block per variant.
+class ShaderProgramBinaryBlockInstance
 {
 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;
+	U32 m_index = MAX_U32; ///< Points to ShaderProgramBinary::m_uniformBlocks or m_storageBlocks.
+	U32 m_size = 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], self.m_name.getSize());
-		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);
+		s.doValue("m_index", offsetof(ShaderProgramBinaryBlockInstance, m_index), self.m_index);
+		s.doValue("m_size", offsetof(ShaderProgramBinaryBlockInstance, m_size), self.m_size);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryOpaque&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryBlockInstance&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryOpaque&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryBlockInstance&>(serializer, *this);
 	}
 };
 
@@ -142,40 +217,28 @@ public:
 	}
 };
 
-/// ShaderProgramBinaryReflection class.
-class ShaderProgramBinaryReflection
+/// Specialization constant per variant.
+class ShaderProgramBinaryConstantInstance
 {
 public:
-	WeakArray<ShaderProgramBinaryBlock> m_uniformBlocks;
-	WeakArray<ShaderProgramBinaryBlock> m_storageBlocks;
-	ShaderProgramBinaryBlock* m_pushConstantBlock = nullptr;
-	WeakArray<ShaderProgramBinaryOpaque> m_opaques;
-	WeakArray<ShaderProgramBinaryConstant> m_specializationConstants;
+	U32 m_index = MAX_U32; ///< Points to ShaderProgramBinary::m_constants.
 
 	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);
+		s.doValue("m_index", offsetof(ShaderProgramBinaryConstantInstance, m_index), self.m_index);
 	}
 
 	template<typename TDeserializer>
 	void deserialize(TDeserializer& deserializer)
 	{
-		serializeCommon<TDeserializer, ShaderProgramBinaryReflection&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryConstantInstance&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryReflection&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryConstantInstance&>(serializer, *this);
 	}
 };
 
@@ -183,17 +246,26 @@ public:
 class ShaderProgramBinaryVariant
 {
 public:
-	ShaderProgramBinaryReflection m_reflection;
 	Array<U32, U32(ShaderType::COUNT)> m_codeBlockIndices = {}; ///< Index in ShaderProgramBinary::m_codeBlocks.
+	WeakArray<ShaderProgramBinaryBlockInstance> m_uniformBlocks;
+	WeakArray<ShaderProgramBinaryBlockInstance> m_storageBlocks;
+	ShaderProgramBinaryBlockInstance* m_pushConstantBlock = nullptr;
+	WeakArray<ShaderProgramBinaryOpaqueInstance> m_opaques;
+	WeakArray<ShaderProgramBinaryConstantInstance> m_constants;
 
 	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],
 			self.m_codeBlockIndices.getSize());
+		s.doValue("m_uniformBlocks", offsetof(ShaderProgramBinaryVariant, m_uniformBlocks), self.m_uniformBlocks);
+		s.doValue("m_storageBlocks", offsetof(ShaderProgramBinaryVariant, m_storageBlocks), self.m_storageBlocks);
+		s.doPointer(
+			"m_pushConstantBlock", offsetof(ShaderProgramBinaryVariant, m_pushConstantBlock), self.m_pushConstantBlock);
+		s.doValue("m_opaques", offsetof(ShaderProgramBinaryVariant, m_opaques), self.m_opaques);
+		s.doValue("m_constants", offsetof(ShaderProgramBinaryVariant, m_constants), self.m_constants);
 	}
 
 	template<typename TDeserializer>
@@ -298,7 +370,12 @@ public:
 	WeakArray<ShaderProgramBinaryMutator> m_mutators;
 	WeakArray<ShaderProgramBinaryCodeBlock> m_codeBlocks;
 	WeakArray<ShaderProgramBinaryVariant> m_variants;
-	WeakArray<ShaderProgramBinaryMutation> m_mutations; ///< I'ts sorted using the mutation's hash.
+	WeakArray<ShaderProgramBinaryMutation> m_mutations; ///< It's sorted using the mutation's hash.
+	WeakArray<ShaderProgramBinaryBlock> m_uniformBlocks;
+	WeakArray<ShaderProgramBinaryBlock> m_storageBlocks;
+	ShaderProgramBinaryBlock* m_pushConstantBlock = nullptr;
+	WeakArray<ShaderProgramBinaryOpaque> m_opaques;
+	WeakArray<ShaderProgramBinaryConstant> m_constants;
 	ShaderTypeBit m_presentShaderTypes = ShaderTypeBit::NONE;
 
 	template<typename TSerializer, typename TClass>
@@ -309,6 +386,12 @@ public:
 		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_mutations", offsetof(ShaderProgramBinary, m_mutations), self.m_mutations);
+		s.doValue("m_uniformBlocks", offsetof(ShaderProgramBinary, m_uniformBlocks), self.m_uniformBlocks);
+		s.doValue("m_storageBlocks", offsetof(ShaderProgramBinary, m_storageBlocks), self.m_storageBlocks);
+		s.doPointer(
+			"m_pushConstantBlock", offsetof(ShaderProgramBinary, m_pushConstantBlock), self.m_pushConstantBlock);
+		s.doValue("m_opaques", offsetof(ShaderProgramBinary, m_opaques), self.m_opaques);
+		s.doValue("m_constants", offsetof(ShaderProgramBinary, m_constants), self.m_constants);
 		s.doValue(
 			"m_presentShaderTypes", offsetof(ShaderProgramBinary, m_presentShaderTypes), self.m_presentShaderTypes);
 	}

+ 38 - 15
src/anki/shader_compiler/ShaderProgramBinary.xml

@@ -9,9 +9,30 @@
 		<class name="ShaderProgramBinaryVariable" comment="Storage or uniform variable">
 			<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" />
+			</members>
+		</class>
+
+		<class name="ShaderProgramBinaryVariableInstance" comment="Storage or uniform variable per variant">
+			<members>
+				<member name="m_index" type="U32" constructor="= MAX_U32" comment="Points to ShaderProgramBinary::m_variables" />
 				<member name="m_blockInfo" type="ShaderVariableBlockInfo" />
+			</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_active" type="Bool" constructor="= true" />
+				<member name="m_binding" type="U32" constructor="= MAX_U32" />
+				<member name="m_set" type="U32" constructor="= MAX_U32" />
+			</members>
+		</class>
+
+		<class name="ShaderProgramBinaryOpaqueInstance" comment="Sampler or texture or image per variant">
+			<members>
+				<member name="m_index" type="U32" constructor="= MAX_U32" comment="Points to ShaderProgramBinary::m_opaques" />
+				<member name="m_arraySize" type="U32" constructor="= MAX_U32" />
 			</members>
 		</class>
 
@@ -25,13 +46,10 @@
 			</members>
 		</class>
 
-		<class name="ShaderProgramBinaryOpaque" comment="Sampler or texture or image">
+		<class name="ShaderProgramBinaryBlockInstance" comment="Storage or uniform block per variant">
 			<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" />
+				<member name="m_index" type="U32" constructor="= MAX_U32" comment="Points to ShaderProgramBinary::m_uniformBlocks or m_storageBlocks" />
+				<member name="m_size" type="U32" constructor="= MAX_U32" />
 			</members>
 		</class>
 
@@ -44,20 +62,20 @@
 			</members>
 		</class>
 
-		<class name="ShaderProgramBinaryReflection">
+		<class name="ShaderProgramBinaryConstantInstance" comment="Specialization constant per variant">
 			<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;" />
+				<member name="m_index" type="U32" constructor="= MAX_U32" comment="Points to ShaderProgramBinary::m_constants" />
 			</members>
 		</class>
 
 		<class name="ShaderProgramBinaryVariant">
 			<members>
-				<member name="m_reflection" type="ShaderProgramBinaryReflection" />
 				<member name="m_codeBlockIndices" type="U32" array_size="U32(ShaderType::COUNT)" comment="Index in ShaderProgramBinary::m_codeBlocks" constructor="= {}" />
+				<member name="m_uniformBlocks" type="WeakArray&lt;ShaderProgramBinaryBlockInstance&gt;" />
+				<member name="m_storageBlocks" type="WeakArray&lt;ShaderProgramBinaryBlockInstance&gt;" />
+				<member name="m_pushConstantBlock" type="ShaderProgramBinaryBlockInstance" pointer="true" constructor="= nullptr" />
+				<member name="m_opaques" type="WeakArray&lt;ShaderProgramBinaryOpaqueInstance&gt;" />
+				<member name="m_constants" type="WeakArray&lt;ShaderProgramBinaryConstantInstance&gt;" />
 			</members>
 		</class>
 
@@ -88,7 +106,12 @@
 				<member name="m_mutators" type="WeakArray&lt;ShaderProgramBinaryMutator&gt;" />
 				<member name="m_codeBlocks" type="WeakArray&lt;ShaderProgramBinaryCodeBlock&gt;" />
 				<member name="m_variants" type="WeakArray&lt;ShaderProgramBinaryVariant&gt;" />
-				<member name="m_mutations" type="WeakArray&lt;ShaderProgramBinaryMutation&gt;" comment="I'ts sorted using the mutation's hash" />
+				<member name="m_mutations" type="WeakArray&lt;ShaderProgramBinaryMutation&gt;" comment="It's sorted using the mutation's hash" />
+				<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_constants" type="WeakArray&lt;ShaderProgramBinaryConstant&gt;" />
 				<member name="m_presentShaderTypes" type="ShaderTypeBit" constructor="= ShaderTypeBit::NONE" />
 			</members>
 		</class>

+ 1 - 1
src/anki/shader_compiler/ShaderProgramCompiler.cpp

@@ -233,7 +233,7 @@ Error compileShaderProgram(CString fname,
 	ShaderProgramParser parser(fname, &fsystem, tempAllocator, gpuCapabilities, bindlessLimits);
 	ANKI_CHECK(parser.parse());
 
-	// Mutators
+	// Get mutators
 	U32 mutationCount = 0;
 	if(parser.getMutators().getSize() > 0)
 	{

+ 168 - 95
src/anki/shader_compiler/ShaderProgramReflection.cpp

@@ -4,7 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/shader_compiler/ShaderProgramReflection.h>
-#include <anki/shader_compiler/ShaderProgramBinary.h>
+#include <anki/gr/utils/Functions.h>
 #include <SPIRV-Cross/spirv_glsl.hpp>
 
 namespace anki
@@ -16,39 +16,92 @@ 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)
+		, m_alloc(tmpAlloc)
 	{
 	}
 
-	ANKI_USE_RESULT static Error performSpirvReflection(ShaderProgramBinaryReflection& refl,
+	ANKI_USE_RESULT static Error performSpirvReflection(
 		Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
 		GenericMemoryPoolAllocator<U8> tmpAlloc,
-		GenericMemoryPoolAllocator<U8> binaryAlloc);
+		ShaderReflectionVisitorInterface& interface);
 
 private:
-	GenericMemoryPoolAllocator<U8> m_tmpAlloc;
+	class Var
+	{
+	public:
+		StringAuto m_name;
+		ShaderVariableBlockInfo m_blockInfo;
+		ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
+
+		Var(const GenericMemoryPoolAllocator<U8>& alloc)
+			: m_name(alloc)
+		{
+		}
+	};
+
+	class Block
+	{
+	public:
+		StringAuto m_name;
+		DynamicArrayAuto<Var> m_vars;
+		U32 m_binding = MAX_U32;
+		U32 m_set = MAX_U32;
+		U32 m_size = MAX_U32;
+
+		Block(const GenericMemoryPoolAllocator<U8>& alloc)
+			: m_name(alloc)
+			, m_vars(alloc)
+		{
+		}
+	};
+
+	class Opaque
+	{
+	public:
+		StringAuto m_name;
+		ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
+		U32 m_binding = MAX_U32;
+		U32 m_set = MAX_U32;
+		U32 m_arraySize = MAX_U32;
+
+		Opaque(const GenericMemoryPoolAllocator<U8>& alloc)
+			: m_name(alloc)
+		{
+		}
+	};
+
+	class Const
+	{
+	public:
+		StringAuto m_name;
+		ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
+		U32 m_constantId = MAX_U32;
+		ShaderTypeBit m_shaderStages = ShaderTypeBit::NONE;
+
+		Const(const GenericMemoryPoolAllocator<U8>& alloc)
+			: m_name(alloc)
+		{
+		}
+	};
+
+	GenericMemoryPoolAllocator<U8> m_alloc;
 
 	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;
+		const spirv_cross::Resource& res, Bool isStorage, DynamicArrayAuto<Block>& blocks) const;
 
-	ANKI_USE_RESULT Error opaqueReflection(
-		const spirv_cross::Resource& res, DynamicArrayAuto<ShaderProgramBinaryOpaque>& opaques) const;
+	ANKI_USE_RESULT Error opaqueReflection(const spirv_cross::Resource& res, DynamicArrayAuto<Opaque>& opaques) const;
 
-	ANKI_USE_RESULT Error constsReflection(
-		DynamicArrayAuto<ShaderProgramBinaryConstant>& consts, ShaderType stage) const;
+	ANKI_USE_RESULT Error constsReflection(DynamicArrayAuto<Const>& consts, ShaderType stage) const;
 
-	ANKI_USE_RESULT Error blockVariablesReflection(
-		spirv_cross::TypeID resourceId, DynamicArrayAuto<ShaderProgramBinaryVariable>& vars) const;
+	ANKI_USE_RESULT Error blockVariablesReflection(spirv_cross::TypeID resourceId, DynamicArrayAuto<Var>& vars) const;
 
-	ANKI_USE_RESULT Error blockVariableReflection(const spirv_cross::SPIRType& type,
-		CString parentVariable,
-		DynamicArrayAuto<ShaderProgramBinaryVariable>& vars) const;
+	ANKI_USE_RESULT Error blockVariableReflection(
+		const spirv_cross::SPIRType& type, CString parentVariable, DynamicArrayAuto<Var>& vars) const;
 };
 
-Error SpirvReflector::blockVariablesReflection(
-	spirv_cross::TypeID resourceId, DynamicArrayAuto<ShaderProgramBinaryVariable>& vars) const
+Error SpirvReflector::blockVariablesReflection(spirv_cross::TypeID resourceId, DynamicArrayAuto<Var>& vars) const
 {
 	Bool found = false;
 	Error err = Error::NONE;
@@ -78,15 +131,14 @@ Error SpirvReflector::blockVariablesReflection(
 	return Error::NONE;
 }
 
-Error SpirvReflector::blockVariableReflection(const spirv_cross::SPIRType& type,
-	CString parentVariable,
-	DynamicArrayAuto<ShaderProgramBinaryVariable>& vars) const
+Error SpirvReflector::blockVariableReflection(
+	const spirv_cross::SPIRType& type, CString parentVariable, DynamicArrayAuto<Var>& vars) const
 {
 	ANKI_ASSERT(type.basetype == spirv_cross::SPIRType::Struct);
 
 	for(U32 i = 0; i < type.member_types.size(); ++i)
 	{
-		ShaderProgramBinaryVariable var;
+		Var var(m_alloc);
 		const spirv_cross::SPIRType& memberType = get<spirv_cross::SPIRType>(type.member_types[i]);
 
 		// Name
@@ -99,16 +151,12 @@ Error SpirvReflector::blockVariableReflection(const spirv_cross::SPIRType& type,
 
 			if(parentVariable.isEmpty())
 			{
-				strncpy(var.m_name.getBegin(), name.c_str(), var.m_name.getSize());
+				var.m_name.create(name.c_str());
 			}
 			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.sprintf("%s.%s", parentVariable.cstr(), name.c_str());
 			}
-
-			var.m_name[var.m_name.getSize() - 1] = '\0';
 		}
 
 		// Offset
@@ -180,13 +228,13 @@ Error SpirvReflector::blockVariableReflection(const spirv_cross::SPIRType& type,
 		{
 			if(var.m_blockInfo.m_arraySize == 1)
 			{
-				ANKI_CHECK(blockVariableReflection(memberType, var.m_name.getBegin(), vars));
+				ANKI_CHECK(blockVariableReflection(memberType, var.m_name, vars));
 			}
 			else
 			{
 				for(U32 i = 0; i < U32(var.m_blockInfo.m_arraySize); ++i)
 				{
-					StringAuto newName(m_tmpAlloc);
+					StringAuto newName(m_alloc);
 					newName.sprintf("%s[%u]", var.m_name.getBegin(), i);
 					ANKI_CHECK(blockVariableReflection(memberType, newName, vars));
 				}
@@ -236,14 +284,14 @@ Error SpirvReflector::blockVariableReflection(const spirv_cross::SPIRType& type,
 		}
 		else
 		{
-			ANKI_SHADER_COMPILER_LOGE("Unhandled base type for member: %s", var.m_name.getBegin());
+			ANKI_SHADER_COMPILER_LOGE("Unhandled base type for member: %s", var.m_name.cstr());
 			return Error::FUNCTION_FAILED;
 		}
 
 		// Store the member
 		if(var.m_type != ShaderVariableDataType::NONE)
 		{
-			vars.emplaceBack(var);
+			vars.emplaceBack(std::move(var));
 		}
 	}
 
@@ -251,9 +299,9 @@ Error SpirvReflector::blockVariableReflection(const spirv_cross::SPIRType& type,
 }
 
 Error SpirvReflector::blockReflection(
-	const spirv_cross::Resource& res, Bool isStorage, DynamicArrayAuto<ShaderProgramBinaryBlock>& blocks) const
+	const spirv_cross::Resource& res, Bool isStorage, DynamicArrayAuto<Block>& blocks) const
 {
-	ShaderProgramBinaryBlock newBlock;
+	Block newBlock(m_alloc);
 	const spirv_cross::SPIRType type = get_type(res.type_id);
 	const spirv_cross::Bitset decorationMask = get_decoration_bitset(res.id);
 
@@ -267,12 +315,12 @@ Error SpirvReflector::blockReflection(
 	// 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)
+		if(name.length() == 0)
 		{
-			ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
+			ANKI_SHADER_COMPILER_LOGE("Can't accept zero name length");
 			return Error::USER_DATA;
 		}
-		memcpy(newBlock.m_name.getBegin(), name.c_str(), name.length() + 1);
+		newBlock.m_name.create(name.c_str());
 	}
 
 	// Set
@@ -297,16 +345,15 @@ Error SpirvReflector::blockReflection(
 	ANKI_ASSERT(isStorage || newBlock.m_size > 0);
 
 	// Add it
-	Bool found = false;
-	for(const ShaderProgramBinaryBlock& other : blocks)
+	const Block* otherFound = nullptr;
+	for(const Block& 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);
+		const Bool err0 = bindingSame && (!nameSame || !sizeSame);
+		const Bool err1 = nameSame && (!bindingSame || !sizeSame);
 		if(err0 || err1)
 		{
 			ANKI_SHADER_COMPILER_LOGE("Linking error");
@@ -315,24 +362,27 @@ Error SpirvReflector::blockReflection(
 
 		if(bindingSame)
 		{
-			found = true;
+			otherFound = &other;
 			break;
 		}
 	}
 
-	if(!found)
+	if(!otherFound)
 	{
 		// 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);
+		ANKI_CHECK(blockVariablesReflection(res.base_type_id, newBlock.m_vars));
 
 		// Store the block
-		blocks.emplaceBack(newBlock);
+		blocks.emplaceBack(std::move(newBlock));
+	}
+#if ANKI_ASSERTS_ENABLED
+	else
+	{
+		DynamicArrayAuto<Var> vars(m_alloc);
+		ANKI_CHECK(blockVariablesReflection(res.base_type_id, vars));
+		ANKI_ASSERT(vars.getSize() == otherFound->m_vars.getSize() && "Expecting same vars");
 	}
+#endif
 
 	return Error::NONE;
 }
@@ -376,10 +426,9 @@ Error SpirvReflector::spirvTypeToAnki(const spirv_cross::SPIRType& type, ShaderV
 	return Error::NONE;
 }
 
-Error SpirvReflector::opaqueReflection(
-	const spirv_cross::Resource& res, DynamicArrayAuto<ShaderProgramBinaryOpaque>& opaques) const
+Error SpirvReflector::opaqueReflection(const spirv_cross::Resource& res, DynamicArrayAuto<Opaque>& opaques) const
 {
-	ShaderProgramBinaryOpaque newOpaque;
+	Opaque newOpaque(m_alloc);
 	const spirv_cross::SPIRType type = get_type(res.type_id);
 	const spirv_cross::Bitset decorationMask = get_decoration_bitset(res.id);
 
@@ -387,12 +436,12 @@ Error SpirvReflector::opaqueReflection(
 
 	// 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)
+	if(name.length() == 0)
 	{
-		ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
+		ANKI_SHADER_COMPILER_LOGE("Can't accept zero length name");
 		return Error::USER_DATA;
 	}
-	memcpy(newOpaque.m_name.getBegin(), name.c_str(), name.length() + 1);
+	newOpaque.m_name.create(name.c_str());
 
 	// Type
 	ANKI_CHECK(spirvTypeToAnki(type, newOpaque.m_type));
@@ -419,16 +468,16 @@ Error SpirvReflector::opaqueReflection(
 	}
 	else
 	{
-		ANKI_SHADER_COMPILER_LOGE("Can't support multi-dimensional arrays: %s", newOpaque.m_name.getBegin());
+		ANKI_SHADER_COMPILER_LOGE("Can't support multi-dimensional arrays: %s", newOpaque.m_name.cstr());
 		return Error::USER_DATA;
 	}
 
 	// Add it
 	Bool found = false;
-	for(const ShaderProgramBinaryOpaque& other : opaques)
+	for(const Opaque& 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 nameSame = other.m_name == newOpaque.m_name;
 		const Bool sizeSame = other.m_arraySize == newOpaque.m_arraySize;
 		const Bool typeSame = other.m_type == newOpaque.m_type;
 
@@ -449,29 +498,29 @@ Error SpirvReflector::opaqueReflection(
 
 	if(!found)
 	{
-		opaques.emplaceBack(newOpaque);
+		opaques.emplaceBack(std::move(newOpaque));
 	}
 
 	return Error::NONE;
 }
 
-Error SpirvReflector::constsReflection(DynamicArrayAuto<ShaderProgramBinaryConstant>& consts, ShaderType stage) const
+Error SpirvReflector::constsReflection(DynamicArrayAuto<Const>& consts, ShaderType stage) const
 {
 	spirv_cross::SmallVector<spirv_cross::SpecializationConstant> specConsts = get_specialization_constants();
 	for(const spirv_cross::SpecializationConstant& c : specConsts)
 	{
-		ShaderProgramBinaryConstant newConst;
+		Const newConst(m_alloc);
 
 		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)
+		if(name.length() == 0)
 		{
-			ANKI_SHADER_COMPILER_LOGE("Wrong name length: %s", name.length() ? name.c_str() : " ");
+			ANKI_SHADER_COMPILER_LOGE("Can't accept zero legth name");
 			return Error::USER_DATA;
 		}
-		memcpy(newConst.m_name.getBegin(), name.c_str(), name.length() + 1);
+		newConst.m_name.create(name.c_str());
 
 		newConst.m_constantId = c.constant_id;
 
@@ -490,10 +539,10 @@ Error SpirvReflector::constsReflection(DynamicArrayAuto<ShaderProgramBinaryConst
 		}
 
 		// Search for it
-		ShaderProgramBinaryConstant* foundConst = nullptr;
-		for(ShaderProgramBinaryConstant& other : consts)
+		Const* foundConst = nullptr;
+		for(Const& other : consts)
 		{
-			const Bool nameSame = strcmp(other.m_name.getBegin(), newConst.m_name.getBegin()) == 0;
+			const Bool nameSame = other.m_name == newConst.m_name;
 			const Bool typeSame = other.m_type == newConst.m_type;
 			const Bool idSame = other.m_constantId == newConst.m_constantId;
 
@@ -515,7 +564,7 @@ Error SpirvReflector::constsReflection(DynamicArrayAuto<ShaderProgramBinaryConst
 		// Add it or update it
 		if(foundConst == nullptr)
 		{
-			consts.emplaceBack(newConst);
+			consts.emplaceBack(std::move(newConst));
 		}
 		else
 		{
@@ -527,16 +576,15 @@ Error SpirvReflector::constsReflection(DynamicArrayAuto<ShaderProgramBinaryConst
 	return Error::NONE;
 }
 
-Error SpirvReflector::performSpirvReflection(ShaderProgramBinaryReflection& refl,
-	Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
+Error SpirvReflector::performSpirvReflection(Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
 	GenericMemoryPoolAllocator<U8> tmpAlloc,
-	GenericMemoryPoolAllocator<U8> binaryAlloc)
+	ShaderReflectionVisitorInterface& interface)
 {
-	DynamicArrayAuto<ShaderProgramBinaryBlock> uniformBlocks(binaryAlloc);
-	DynamicArrayAuto<ShaderProgramBinaryBlock> storageBlocks(binaryAlloc);
-	DynamicArrayAuto<ShaderProgramBinaryBlock> pushConstantBlock(binaryAlloc);
-	DynamicArrayAuto<ShaderProgramBinaryOpaque> opaques(binaryAlloc);
-	DynamicArrayAuto<ShaderProgramBinaryConstant> specializationConstants(binaryAlloc);
+	DynamicArrayAuto<Block> uniformBlocks(tmpAlloc);
+	DynamicArrayAuto<Block> storageBlocks(tmpAlloc);
+	DynamicArrayAuto<Block> pushConstantBlock(tmpAlloc);
+	DynamicArrayAuto<Opaque> opaques(tmpAlloc);
+	DynamicArrayAuto<Const> specializationConstants(tmpAlloc);
 
 	// Perform reflection for each stage
 	for(const ShaderType type : EnumIterable<ShaderType>())
@@ -592,37 +640,62 @@ Error SpirvReflector::performSpirvReflection(ShaderProgramBinaryReflection& refl
 		ANKI_CHECK(compiler.constsReflection(specializationConstants, type));
 	}
 
-	ShaderProgramBinaryBlock* firstBlock;
-	U32 size, storage;
-	uniformBlocks.moveAndReset(firstBlock, size, storage);
-	refl.m_uniformBlocks.setArray(firstBlock, size);
+	// Inform through the interface
+	interface.setUniformBlockCount(uniformBlocks.getSize());
+	for(const Block& block : uniformBlocks)
+	{
+		interface.visitUniformBlock(block.m_name, block.m_set, block.m_binding, block.m_size);
+
+		interface.setUniformBlockVariableCount(block.m_vars.getSize());
+		for(const Var& var : block.m_vars)
+		{
+			interface.visitUniformVariable(var.m_name, var.m_type, var.m_blockInfo);
+		}
+	}
 
-	storageBlocks.moveAndReset(firstBlock, size, storage);
-	refl.m_storageBlocks.setArray(firstBlock, size);
+	interface.setStorageBlockCount(storageBlocks.getSize());
+	for(const Block& block : storageBlocks)
+	{
+		interface.visitStorageBlock(block.m_name, block.m_set, block.m_binding, block.m_size);
+
+		interface.setStorageBlockVariableCount(block.m_vars.getSize());
+		for(const Var& var : block.m_vars)
+		{
+			interface.visitStorageVariable(var.m_name, var.m_type, var.m_blockInfo);
+		}
+	}
 
 	if(pushConstantBlock.getSize() == 1)
 	{
-		pushConstantBlock.moveAndReset(firstBlock, size, storage);
-		refl.m_pushConstantBlock = firstBlock;
+		interface.visitPushConstantsBlock(
+			pushConstantBlock[0].m_name, pushConstantBlock[0].m_size, pushConstantBlock[0].m_vars.getSize());
+
+		for(const Var& var : pushConstantBlock[0].m_vars)
+		{
+			interface.visitPushConstant(var.m_name, var.m_type, var.m_blockInfo);
+		}
 	}
 
-	ShaderProgramBinaryOpaque* firstOpaque;
-	opaques.moveAndReset(firstOpaque, size, storage);
-	refl.m_opaques.setArray(firstOpaque, size);
+	interface.setOpaqueCount(opaques.getSize());
+	for(const Opaque& o : opaques)
+	{
+		interface.visitOpaque(o.m_name, o.m_type, o.m_set, o.m_binding, o.m_arraySize);
+	}
 
-	ShaderProgramBinaryConstant* firstConst;
-	specializationConstants.moveAndReset(firstConst, size, storage);
-	refl.m_specializationConstants.setArray(firstConst, size);
+	interface.setConstantCount(specializationConstants.getSize());
+	for(const Const& c : specializationConstants)
+	{
+		interface.visitConstant(c.m_name, c.m_type, c.m_constantId, c.m_shaderStages);
+	}
 
 	return Error::NONE;
 }
 
-Error performSpirvReflection(ShaderProgramBinaryReflection& refl,
-	Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
+Error performSpirvReflection(Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
 	GenericMemoryPoolAllocator<U8> tmpAlloc,
-	GenericMemoryPoolAllocator<U8> binaryAlloc)
+	ShaderReflectionVisitorInterface& interface)
 {
-	return SpirvReflector::performSpirvReflection(refl, spirv, tmpAlloc, binaryAlloc);
+	return SpirvReflector::performSpirvReflection(spirv, tmpAlloc, interface);
 }
 
 } // end namespace anki

+ 40 - 6
src/anki/shader_compiler/ShaderProgramReflection.h

@@ -7,21 +7,55 @@
 
 #include <anki/shader_compiler/Common.h>
 #include <anki/gr/Enums.h>
+#include <anki/gr/Common.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,
+/// A visitor class that will be used to populate reflection information.
+class ShaderReflectionVisitorInterface
+{
+public:
+	virtual void setUniformBlockCount(U32 count) = 0;
+
+	virtual void visitUniformBlock(CString name, U32 set, U32 binding, U32 size) = 0;
+
+	virtual void setUniformBlockVariableCount(U32 count) = 0;
+
+	virtual void visitUniformVariable(
+		CString name, ShaderVariableDataType type, const ShaderVariableBlockInfo& blockInfo) = 0;
+
+	virtual void setStorageBlockCount(U32 count) = 0;
+
+	virtual void visitStorageBlock(CString name, U32 set, U32 binding, U32 size) = 0;
+
+	virtual void setStorageBlockVariableCount(U32 count) = 0;
+
+	virtual void visitStorageVariable(
+		CString name, ShaderVariableDataType type, const ShaderVariableBlockInfo& blockInfo) = 0;
+
+	virtual void visitPushConstantsBlock(CString name, U32 size, U32 varCount) = 0;
+
+	virtual void visitPushConstant(
+		CString name, ShaderVariableDataType type, const ShaderVariableBlockInfo& blockInfo) = 0;
+
+	virtual void setOpaqueCount(U32 count) = 0;
+
+	virtual void visitOpaque(CString name, ShaderVariableDataType type, U32 set, U32 binding, U32 arraySize) = 0;
+
+	virtual void setConstantCount(U32 count) = 0;
+
+	virtual void visitConstant(CString name, ShaderVariableDataType type, U32 constantId, ShaderTypeBit stages) = 0;
+};
+
+/// Does reflection using SPIR-V.
+ANKI_USE_RESULT Error performSpirvReflection(Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
 	GenericMemoryPoolAllocator<U8> tmpAlloc,
-	GenericMemoryPoolAllocator<U8> binaryAlloc);
+	ShaderReflectionVisitorInterface& interface);
 /// @}
 
 } // end namespace anki

+ 73 - 0
tests/shader_compiler/ShaderProgramCompiler.cpp

@@ -6,6 +6,79 @@
 #include <tests/framework/Framework.h>
 #include <anki/shader_compiler/ShaderProgramCompiler.h>
 
+ANKI_TEST(ShaderCompiler, ShaderProgramCompilerSimple)
+{
+	const CString sourceCode = R"(
+#pragma anki mutator INSTANCE_COUNT 1 2 4 8 16 32 64
+
+struct Instanced
+{
+	Mat4 m_ankiMvp;
+	Mat3 m_ankiRotationMat;
+	Mat4 m_ankiModelViewMat;
+	Mat4 m_ankiPrevMvp;
+};
+
+layout(set = 0, binding = 0) uniform ankiMaterial
+{
+	Instanced u_instanced[INSTANCE_COUNT];
+};
+
+#pragma anki start vert
+out gl_PerVertex
+{
+	Vec4 gl_Position;
+};
+
+void main()
+{
+	gl_Position = u_instanced[gl_InstanceID].m_ankiMvp * Vec4(gl_VertexID);
+}
+#pragma anki end
+
+#pragma anki start frag
+layout(location = 0) out Vec3 out_color;
+
+void main()
+{
+	out_color = Vec3(0.0) + u_color.xyz;
+}
+#pragma anki end
+	)";
+
+	// Write the file
+	{
+		File file;
+		ANKI_TEST_EXPECT_NO_ERR(file.open("test.glslp", FileOpenFlag::WRITE));
+		ANKI_TEST_EXPECT_NO_ERR(file.writeText(sourceCode));
+	}
+
+	class Fsystem : public ShaderProgramFilesystemInterface
+	{
+	public:
+		Error readAllText(CString filename, StringAuto& txt) final
+		{
+			File file;
+			ANKI_CHECK(file.open(filename, FileOpenFlag::READ));
+			ANKI_CHECK(file.readAllText(txt));
+			return Error::NONE;
+		}
+	} fsystem;
+
+	HeapAllocator<U8> alloc(allocAligned, nullptr);
+	ShaderProgramBinaryWrapper binary(alloc);
+	BindlessLimits bindlessLimits;
+	GpuDeviceCapabilities gpuCapabilities;
+	ANKI_TEST_EXPECT_NO_ERR(
+		compileShaderProgram("test.glslp", fsystem, alloc, gpuCapabilities, bindlessLimits, binary));
+
+#if 0
+	StringAuto dis(alloc);
+	dumpShaderProgramBinary(binary.getBinary(), dis);
+	ANKI_LOGI("Binary disassembly:\n%s\n", dis.cstr());
+#endif
+}
+
 ANKI_TEST(ShaderCompiler, ShaderProgramCompiler)
 {
 	const CString sourceCode = R"(