Browse Source

Add the shader reflection code

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
0798c8406c

+ 10 - 10
src/anki/gr/Enums.h

@@ -478,6 +478,7 @@ inline ShaderTypeBit shaderTypeToBit(ShaderType type)
 enum class ShaderVariableDataType : U8
 {
 	NONE,
+
 	INT,
 	UINT,
 	FLOAT,
@@ -492,16 +493,18 @@ enum class ShaderVariableDataType : U8
 	VEC4,
 	MAT3,
 	MAT4,
-	COMBINED_TEXTURE_SAMPLER_2D,
-	COMBINED_TEXTURE_SAMPLER_3D,
-	COMBINED_TEXTURE_SAMPLER_2D_ARRAY,
-	COMBINED_TEXTURE_SAMPLER_CUBE,
+
+	TEXTURE_1D,
+	TEXTURE_1D_ARRAY,
 	TEXTURE_2D,
-	TEXTURE_3D,
 	TEXTURE_2D_ARRAY,
+	TEXTURE_3D,
 	TEXTURE_CUBE,
+	TEXTURE_CUBE_ARRAY,
+
 	SAMPLER,
 
+	// Combined
 	NUMERICS_FIRST = INT,
 	NUMERICS_LAST = MAT4,
 
@@ -517,11 +520,8 @@ 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_2D,
-	TEXTURE_LAST = TEXTURE_CUBE,
+	TEXTURE_FIRST = TEXTURE_1D,
+	TEXTURE_LAST = TEXTURE_CUBE_ARRAY,
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderVariableDataType, inline)
 

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

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

@@ -56,6 +56,7 @@ public:
 	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)
@@ -65,6 +66,7 @@ public:
 		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>
@@ -88,6 +90,7 @@ public:
 	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)
@@ -97,6 +100,7 @@ public:
 		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>
@@ -144,31 +148,40 @@ public:
 	}
 };
 
-/// Shader program mutator.
-class ShaderProgramBinaryMutator
+/// ShaderProgramBinaryReflection class.
+class ShaderProgramBinaryReflection
 {
 public:
-	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name = {};
-	WeakArray<MutatorValue> m_values;
+	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.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_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, ShaderProgramBinaryMutator&>(deserializer, *this);
+		serializeCommon<TDeserializer, ShaderProgramBinaryReflection&>(deserializer, *this);
 	}
 
 	template<typename TSerializer>
 	void serialize(TSerializer& serializer) const
 	{
-		serializeCommon<TSerializer, const ShaderProgramBinaryMutator&>(serializer, *this);
+		serializeCommon<TSerializer, const ShaderProgramBinaryReflection&>(serializer, *this);
 	}
 };
 
@@ -176,24 +189,13 @@ public:
 class ShaderProgramBinaryVariant
 {
 public:
-	WeakArray<ShaderProgramBinaryBlock> m_uniformBlocks;
-	WeakArray<ShaderProgramBinaryBlock> m_storageBlocks;
-	ShaderProgramBinaryBlock* m_pushConstantBlock = nullptr;
-	WeakArray<ShaderProgramBinaryOpaque> m_opaques;
-	WeakArray<ShaderProgramBinaryConstant> m_specializationConstants;
+	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_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_specializationConstants",
-			offsetof(ShaderProgramBinaryVariant, m_specializationConstants),
-			self.m_specializationConstants);
+		s.doValue("m_reflection", offsetof(ShaderProgramBinaryVariant, m_reflection), self.m_reflection);
 		s.doArray("m_codeBlockIndices",
 			offsetof(ShaderProgramBinaryVariant, m_codeBlockIndices),
 			&self.m_codeBlockIndices[0],
@@ -213,6 +215,34 @@ public:
 	}
 };
 
+/// Shader program mutator.
+class ShaderProgramBinaryMutator
+{
+public:
+	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name = {};
+	WeakArray<MutatorValue> m_values;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		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);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryMutator&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryMutator&>(serializer, *this);
+	}
+};
+
 /// Contains the IR (SPIR-V).
 class ShaderProgramBinaryCodeBlock
 {

+ 16 - 8
src/anki/shader_compiler/ShaderProgramBinary.xml

@@ -21,6 +21,7 @@
 				<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>
 
@@ -30,6 +31,7 @@
 				<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>
 
@@ -41,24 +43,30 @@
 			</members>
 		</class>
 
-		<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="ShaderProgramBinaryVariant">
+		<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_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="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;" />

+ 342 - 35
src/anki/shader_compiler/ShaderProgramCompiler.cpp

@@ -80,10 +80,11 @@ void ShaderProgramBinaryWrapper::cleanup()
 
 		for(ShaderProgramBinaryVariant& variant : m_binary->m_variants)
 		{
-			m_alloc.getMemoryPool().free(variant.m_uniformBlocks.getBegin());
-			m_alloc.getMemoryPool().free(variant.m_storageBlocks.getBegin());
-			m_alloc.getMemoryPool().free(variant.m_pushConstantBlock);
-			m_alloc.getMemoryPool().free(variant.m_specializationConstants.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());
 	}
@@ -129,12 +130,308 @@ static Bool spinDials(DynamicArrayAuto<U32>& dials, ConstWeakArray<ShaderProgram
 	return done;
 }
 
-static Error performSpirvReflection(ShaderProgramBinaryVariant& variant,
+/// Populates the reflection info.
+class SpirvReflector : public spirv_cross::Compiler
+{
+public:
+	SpirvReflector(const U32* ir, PtrSize wordCount)
+		: spirv_cross::Compiler(ir, wordCount)
+	{
+	}
+
+	ANKI_USE_RESULT static Error performSpirvReflection(ShaderProgramBinaryReflection& refl,
+		Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
+		GenericMemoryPoolAllocator<U8> tmpAlloc,
+		GenericMemoryPoolAllocator<U8> binaryAlloc);
+
+private:
+	ANKI_USE_RESULT Error spirvTypeToAnki(const spirv_cross::SPIRType& type, ShaderVariableDataType& out) const;
+
+	ANKI_USE_RESULT Error blockReflection(
+		const spirv_cross::Resource& res, Bool isStorage, DynamicArrayAuto<ShaderProgramBinaryBlock>& blocks) const;
+
+	ANKI_USE_RESULT Error opaqueReflection(
+		const spirv_cross::Resource& res, DynamicArrayAuto<ShaderProgramBinaryOpaque>& opaques) const;
+};
+
+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("Too big of a name: %s", name.c_str());
+			return Error::USER_DATA;
+		}
+		memcpy(newBlock.m_name.getBegin(), name.c_str(), name.length() + 1);
+	}
+
+	// Set
+	if(!isPushConstant)
+	{
+		newBlock.m_set = get_decoration(res.id, spv::DecorationDescriptorSet);
+		if(newBlock.m_set >= MAX_DESCRIPTOR_SETS)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Too high descriptor set: %u", newBlock.m_set);
+			return Error::USER_DATA;
+		}
+	}
+
+	// Binding
+	if(!isPushConstant)
+	{
+		newBlock.m_binding = get_decoration(res.id, spv::DecorationBinding);
+	}
+
+	// Size
+	newBlock.m_size = U32(get_declared_struct_size(get_type(res.base_type_id)));
+	ANKI_ASSERT(isStorage || newBlock.m_size > 0);
+
+	// Add it
+	Bool found = false;
+	for(const ShaderProgramBinaryBlock& other : blocks)
+	{
+		const Bool bindingSame = other.m_set == newBlock.m_set && other.m_binding == newBlock.m_binding;
+		const Bool nameSame = strcmp(other.m_name.getBegin(), newBlock.m_name.getBegin()) == 0;
+		const Bool sizeSame = other.m_size == newBlock.m_size;
+		const Bool varsSame = other.m_variables.getSize() == newBlock.m_variables.getSize();
+
+		const Bool err0 = bindingSame && (!nameSame || !sizeSame || !varsSame);
+		const Bool err1 = nameSame && (!bindingSame || !sizeSame || !varsSame);
+		if(err0 || err1)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Linking error");
+			return Error::USER_DATA;
+		}
+
+		if(bindingSame)
+		{
+			found = true;
+			break;
+		}
+	}
+
+	if(!found)
+	{
+		blocks.emplaceBack(newBlock);
+	}
+
+	return Error::NONE;
+}
+
+Error SpirvReflector::spirvTypeToAnki(const spirv_cross::SPIRType& type, ShaderVariableDataType& out) const
+{
+	switch(type.basetype)
+	{
+	case spirv_cross::SPIRType::Image:
+	case spirv_cross::SPIRType::SampledImage:
+	{
+		switch(type.image.dim)
+		{
+		case spv::Dim1D:
+			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_1D_ARRAY : ShaderVariableDataType::TEXTURE_1D;
+			break;
+		case spv::Dim2D:
+			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_2D_ARRAY : ShaderVariableDataType::TEXTURE_2D;
+			break;
+		case spv::Dim3D:
+			out = ShaderVariableDataType::TEXTURE_3D;
+			break;
+		case spv::DimCube:
+			out = (type.image.arrayed) ? ShaderVariableDataType::TEXTURE_CUBE_ARRAY
+									   : ShaderVariableDataType::TEXTURE_CUBE;
+			break;
+		default:
+			ANKI_ASSERT(0);
+		}
+
+		break;
+	}
+	case spirv_cross::SPIRType::Sampler:
+		out = ShaderVariableDataType::SAMPLER;
+		break;
+	default:
+		ANKI_SHADER_COMPILER_LOGE("Can't determine the type");
+		return Error::USER_DATA;
+	}
+
+	return Error::NONE;
+}
+
+Error SpirvReflector::opaqueReflection(
+	const spirv_cross::Resource& res, DynamicArrayAuto<ShaderProgramBinaryOpaque>& opaques) const
+{
+	ShaderProgramBinaryOpaque newOpaque;
+	const spirv_cross::SPIRType type = get_type(res.type_id);
+	const spirv_cross::Bitset decorationMask = get_decoration_bitset(res.id);
+
+	const spirv_cross::ID fallbackId = spirv_cross::ID(res.id);
+
+	// Name
+	const std::string name = (!res.name.empty()) ? res.name : get_fallback_name(fallbackId);
+	if(name.length() == 0 || name.length() > MAX_SHADER_BINARY_NAME_LENGTH)
+	{
+		ANKI_SHADER_COMPILER_LOGE("Too big of a name: %s", 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::performSpirvReflection(ShaderProgramBinaryReflection& refl,
 	Array<ConstWeakArray<U8, PtrSize>, U32(ShaderType::COUNT)> spirv,
 	GenericMemoryPoolAllocator<U8> tmpAlloc,
 	GenericMemoryPoolAllocator<U8> binaryAlloc)
 {
-	// TODO
+	DynamicArrayAuto<ShaderProgramBinaryBlock> uniformBlocks(binaryAlloc);
+	DynamicArrayAuto<ShaderProgramBinaryBlock> storageBlocks(binaryAlloc);
+	DynamicArrayAuto<ShaderProgramBinaryBlock> pushConstantBlock(binaryAlloc);
+	DynamicArrayAuto<ShaderProgramBinaryOpaque> opaques(binaryAlloc);
+	DynamicArrayAuto<ShaderProgramBinaryConstant> specializationConstants(binaryAlloc);
+
+	// Perform reflection for each stage
+	for(const ShaderType type : EnumIterable<ShaderType>())
+	{
+		if(spirv[type].getSize() == 0)
+		{
+			continue;
+		}
+
+		// Parse SPIR-V
+		const unsigned int* spvb = reinterpret_cast<const unsigned int*>(spirv[type].getBegin());
+		SpirvReflector compiler(spvb, spirv[type].getSizeInBytes() / sizeof(unsigned int));
+
+		// Uniform blocks
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().uniform_buffers)
+		{
+			ANKI_CHECK(compiler.blockReflection(res, false, uniformBlocks));
+		}
+
+		// Sorage blocks
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().storage_buffers)
+		{
+			ANKI_CHECK(compiler.blockReflection(res, true, storageBlocks));
+		}
+
+		// Push constants
+		if(compiler.get_shader_resources().push_constant_buffers.size() == 1)
+		{
+			ANKI_CHECK(compiler.blockReflection(
+				compiler.get_shader_resources().push_constant_buffers[0], false, pushConstantBlock));
+		}
+		else if(compiler.get_shader_resources().push_constant_buffers.size() > 1)
+		{
+			ANKI_SHADER_COMPILER_LOGE("Expecting only a single push constants block");
+			return Error::USER_DATA;
+		}
+
+		// Opaque
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().separate_images)
+		{
+			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
+		}
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().storage_images)
+		{
+			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
+		}
+		for(const spirv_cross::Resource& res : compiler.get_shader_resources().separate_samplers)
+		{
+			ANKI_CHECK(compiler.opaqueReflection(res, opaques));
+		}
+
+		// TODO
+	}
+
+	ShaderProgramBinaryBlock* firstBlock;
+	U32 size, storage;
+	uniformBlocks.moveAndReset(firstBlock, size, storage);
+	refl.m_uniformBlocks.setArray(firstBlock, size);
+
+	storageBlocks.moveAndReset(firstBlock, size, storage);
+	refl.m_storageBlocks.setArray(firstBlock, size);
+
+	if(pushConstantBlock.getSize() == 1)
+	{
+		pushConstantBlock.moveAndReset(firstBlock, size, storage);
+		refl.m_pushConstantBlock = firstBlock;
+	}
+
+	ShaderProgramBinaryOpaque* firstOpaque;
+	opaques.moveAndReset(firstOpaque, size, storage);
+	refl.m_opaques.setArray(firstOpaque, size);
+
 	return Error::NONE;
 }
 
@@ -200,7 +497,7 @@ static Error compileVariant(ConstWeakArray<MutatorValue> mutation,
 	}
 
 	// Do reflection
-	ANKI_CHECK(performSpirvReflection(variant, spirvBinaries, tmpAlloc, binaryAlloc));
+	ANKI_CHECK(SpirvReflector::performSpirvReflection(variant.m_reflection, spirvBinaries, tmpAlloc, binaryAlloc));
 
 	return Error::NONE;
 }
@@ -406,18 +703,18 @@ Error compileShaderProgram(CString fname,
 
 static void disassembleBlock(const ShaderProgramBinaryBlock& block, StringListAuto& lines)
 {
-	lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "\"%s\" set %" PRIu32 " binding %" PRIu32 "\n",
+	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_binding,
+		block.m_size);
 
 	for(const ShaderProgramBinaryVariable& var : block.m_variables)
 	{
-		lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB ANKI_TAB
-			"\"%s\" type %" PRIu32 " active %" PRIu8 " blockInfo %" PRIi16 "|%" PRIi16 "|%" PRIi16 "|%" PRIi16 "\n",
+		lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB ANKI_TAB "\"%s\" type: %u active: %s blockInfo: %d,%d,%d,%d\n",
 			var.m_name.getBegin(),
 			U32(var.m_type),
-			U8(var.m_active),
+			var.m_active ? "true" : "false",
 			var.m_blockInfo.m_offset,
 			var.m_blockInfo.m_arraySize,
 			var.m_blockInfo.m_arrayStride,
@@ -435,10 +732,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");
 		}
@@ -477,53 +774,54 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 		lines.pushBackSprintf(ANKI_TAB "#%u\n", count++);
 
 		// Uniform blocks
-		if(variant.m_uniformBlocks.getSize() > 0)
+		if(variant.m_reflection.m_uniformBlocks.getSize() > 0)
 		{
 			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Uniform blocks\n");
-			for(const ShaderProgramBinaryBlock& block : variant.m_uniformBlocks)
+			for(const ShaderProgramBinaryBlock& block : variant.m_reflection.m_uniformBlocks)
 			{
 				disassembleBlock(block, lines);
 			}
 		}
 
 		// Storage blocks
-		if(variant.m_storageBlocks.getSize() > 0)
+		if(variant.m_reflection.m_storageBlocks.getSize() > 0)
 		{
 			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Storage blocks\n");
-			for(const ShaderProgramBinaryBlock& block : variant.m_storageBlocks)
+			for(const ShaderProgramBinaryBlock& block : variant.m_reflection.m_storageBlocks)
 			{
 				disassembleBlock(block, lines);
 			}
 		}
 
 		// Opaque
-		if(variant.m_opaques.getSize() > 0)
+		if(variant.m_reflection.m_opaques.getSize() > 0)
 		{
 			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Opaque\n");
-			for(const ShaderProgramBinaryOpaque& o : variant.m_opaques)
+			for(const ShaderProgramBinaryOpaque& o : variant.m_reflection.m_opaques)
 			{
-				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "\"%s\" set %u binding %u type %u\n",
+				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%-32s set %4u binding %4u type %4u arraySize %4u\n",
 					o.m_name.getBegin(),
 					o.m_set,
 					o.m_binding,
-					o.m_type);
+					o.m_type,
+					o.m_arraySize);
 			}
 		}
 
 		// Push constants
-		if(variant.m_pushConstantBlock)
+		if(variant.m_reflection.m_pushConstantBlock)
 		{
 			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Push constants\n");
-			disassembleBlock(*variant.m_pushConstantBlock, lines);
+			disassembleBlock(*variant.m_reflection.m_pushConstantBlock, lines);
 		}
 
 		// Constants
-		if(variant.m_specializationConstants.getSize() > 0)
+		if(variant.m_reflection.m_specializationConstants.getSize() > 0)
 		{
 			lines.pushBackSprintf(ANKI_TAB ANKI_TAB "Specialization constants\n");
-			for(const ShaderProgramBinaryConstant& c : variant.m_specializationConstants)
+			for(const ShaderProgramBinaryConstant& c : variant.m_reflection.m_specializationConstants)
 			{
-				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "\"%s\" type %u id %u\n",
+				lines.pushBackSprintf(ANKI_TAB ANKI_TAB ANKI_TAB "%16s type %4u id %4u\n",
 					c.m_name.getBegin(),
 					U32(c.m_type),
 					c.m_constantId);
@@ -531,16 +829,21 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 		}
 
 		// 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_codeBlockIndices[shaderType] < MAX_U32)
 			{
-				lines.pushBackSprintf("%u ", variant.m_codeBlockIndices[shaderType]);
+				lines.pushBackSprintf("%u", variant.m_codeBlockIndices[shaderType]);
 			}
 			else
 			{
-				lines.pushBack("N/A ");
+				lines.pushBack("-");
+			}
+
+			if(shaderType != ShaderType::LAST)
+			{
+				lines.pushBack(",");
 			}
 		}
 		lines.pushBack("\n");
@@ -551,13 +854,17 @@ void disassembleShaderProgramBinary(const ShaderProgramBinary& binary, StringAut
 	count = 0;
 	for(const ShaderProgramBinaryMutation& mutation : binary.m_mutations)
 	{
-		lines.pushBackSprintf(ANKI_TAB "#%u variantIndex %u values ", count++, mutation.m_variantIndex);
+		lines.pushBackSprintf(ANKI_TAB "#%-4u variantIndex %4u values (", count++, mutation.m_variantIndex);
 		if(mutation.m_values.getSize() > 0)
 		{
-			for(MutatorValue val : mutation.m_values)
+			for(U32 i = 0; i < mutation.m_values.getSize(); ++i)
 			{
-				lines.pushBackSprintf("%d ", I32(val));
+				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
 		{

+ 14 - 0
src/anki/shader_compiler/ShaderProgramParser.cpp

@@ -65,6 +65,20 @@ static const char* SHADER_HEADER = R"(#version 450 core
 #define Mat3x4 mat3x4
 
 #define Bool bool
+
+#define _ANKI_CONCATENATE(a, b) a##b
+#define ANKI_CONCATENATE(a, b) _ANKI_CONCATENATE(a, b)
+
+#define ANKI_SPECIALIZATION_CONSTANT_I32(x, id, defltVal) layout(constant_id = id) const I32 x = defltVal
+
+#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]
+
+#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]
 )";
 
 ShaderProgramParser::ShaderProgramParser(CString fname,

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

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

+ 28 - 87
tests/shader_compiler/ShaderProgramCompiler.cpp

@@ -9,101 +9,36 @@
 ANKI_TEST(ShaderCompiler, ShaderProgramCompiler)
 {
 	const CString sourceCode = R"(
-#pragma anki mutator instanceCount 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
-#pragma anki mutator SPECULAR_TEX 0 1
-#pragma anki mutator ROUGHNESS_TEX 0 1
-#pragma anki mutator METAL_TEX 0 1
-#pragma anki mutator NORMAL_TEX 0 1
-#pragma anki mutator PARALLAX 0 1
-#pragma anki mutator EMISSIVE_TEX 0 1
-#pragma anki mutator BONES 0 1
-#pragma anki mutator VELOCITY 0 1
 
 #pragma anki rewrite_mutation PASS 1 DIFFUSE_TEX 1 to PASS 1 DIFFUSE_TEX 0
 #pragma anki rewrite_mutation PASS 2 DIFFUSE_TEX 1 to PASS 2 DIFFUSE_TEX 0
 #pragma anki rewrite_mutation PASS 3 DIFFUSE_TEX 1 to PASS 2 DIFFUSE_TEX 0
 
-#pragma anki rewrite_mutation PASS 1 SPECULAR_TEX 1 to PASS 1 SPECULAR_TEX 0
-#pragma anki rewrite_mutation PASS 2 SPECULAR_TEX 1 to PASS 2 SPECULAR_TEX 0
-#pragma anki rewrite_mutation PASS 3 SPECULAR_TEX 1 to PASS 2 SPECULAR_TEX 0
+ANKI_SPECIALIZATION_CONSTANT_I32(INSTANCE_COUNT, 0, 1);
 
-#pragma anki rewrite_mutation PASS 1 ROUGHNESS_TEX 1 to PASS 1 ROUGHNESS_TEX 0
-#pragma anki rewrite_mutation PASS 2 ROUGHNESS_TEX 1 to PASS 2 ROUGHNESS_TEX 0
-#pragma anki rewrite_mutation PASS 3 ROUGHNESS_TEX 1 to PASS 2 ROUGHNESS_TEX 0
-
-#pragma anki rewrite_mutation PASS 1 METAL_TEX 1 to PASS 1 METAL_TEX 0
-#pragma anki rewrite_mutation PASS 2 METAL_TEX 1 to PASS 2 METAL_TEX 0
-#pragma anki rewrite_mutation PASS 3 METAL_TEX 1 to PASS 2 METAL_TEX 0
-
-#pragma anki rewrite_mutation PASS 1 NORMAL_TEX 1 to PASS 1 NORMAL_TEX 0
-#pragma anki rewrite_mutation PASS 2 NORMAL_TEX 1 to PASS 2 NORMAL_TEX 0
-#pragma anki rewrite_mutation PASS 3 NORMAL_TEX 1 to PASS 2 NORMAL_TEX 0
-
-#pragma anki rewrite_mutation PASS 1 EMISSIVE_TEX 1 to PASS 1 EMISSIVE_TEX 0
-#pragma anki rewrite_mutation PASS 2 EMISSIVE_TEX 1 to PASS 2 EMISSIVE_TEX 0
-#pragma anki rewrite_mutation PASS 3 EMISSIVE_TEX 1 to PASS 2 EMISSIVE_TEX 0
-
-#pragma anki rewrite_mutation PASS 1 VELOCITY 1 to PASS 1 VELOCITY 0
-#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
-#if PASS == 0
-#	pragma anki input instanced Mat3 rotationMat
-#endif
-#if PASS == 0 && PARALLAX == 1
-#	pragma anki input instanced Mat4 modelViewMat
-#endif
-#if PASS == 0 && VELOCITY == 1
-#	pragma anki input instanced Mat4 prevMvp
+layout(set = 1, binding = 0) uniform u_
+{
+	Mat4 u_mvp[INSTANCE_COUNT];
+#if PASS > 1
+	Mat3 u_normalMat[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
-#endif
-#if SPECULAR_TEX == 1 && PASS == 0
-#	pragma anki input texture2D specTex
-#endif
-#if ROUGHNESS_TEX == 1 && PASS == 0
-#	pragma anki input texture2D roughnessTex
-#endif
-#if METAL_TEX == 1 && PASS == 0
-#	pragma anki input texture2D metallicTex
-#endif
-#if NORMAL_TEX == 1 && PASS == 0 && LOD < 2
-#	pragma anki input texture2D normalTex
-#endif
-#if PARALLAX == 1 && PASS == 0 && LOD == 0
-#	pragma anki input texture2D heightTex
+layout(set = 1, binding = 1) uniform u2_
+{
+	Mat4 u_mvp2[INSTANCE_COUNT];
+#if PASS > 1
+	Mat3 u_normalMat2[INSTANCE_COUNT];
 #endif
-#if EMISSIVE_TEX == 1 && PASS == 0
-#	pragma anki input texture2D emissiveTex
+};
+
+#if DIFFUSE_TEX == 1
+layout(set = 0, binding = 0) uniform texture2D u_tex[3];
 #endif
+layout(set = 0, binding = 1) uniform sampler u_sampler;
 
 #pragma anki start vert
 out gl_PerVertex
@@ -113,16 +48,20 @@ out gl_PerVertex
 
 void main()
 {
-	gl_Position = Vec4(gl_VertexID);
+	gl_Position = u_mvp[gl_InstanceID] * u_mvp2[gl_InstanceID] * Vec4(gl_VertexID);
 }
 #pragma anki end
 
 #pragma anki start frag
-layout(location = 0) out Vec3 out_color;
+layout(location = 0) out Vec4 out_color;
 
 void main()
 {
-	out_color = Vec3(0.0);
+#if DIFFUSE_TEX == 1
+	out_color = texture(sampler2D(u_tex[0], u_sampler), Vec2(0));
+#else
+	out_color = Vec4(0);
+#endif
 }
 #pragma anki end
 	)";
@@ -150,7 +89,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 1
+	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
 }

+ 1 - 1
thirdparty

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