Browse Source

Finalize the struct SPIRV reflection

Panagiotis Christopoulos Charitos 4 years ago
parent
commit
e2da055d22

+ 65 - 0
AnKi/ShaderCompiler/ShaderProgramBinary.h

@@ -240,6 +240,69 @@ public:
 	}
 };
 
+/// A member of a ShaderProgramBinaryStruct.
+class ShaderProgramBinaryStructMember
+{
+public:
+	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name = {};
+	ShaderVariableDataType m_type =
+		ShaderVariableDataType::NONE; ///< If the value is ShaderVariableDataType::NONE then it's a struct.
+	U32 m_structIndex = MAX_U32;
+	U32 m_offset = MAX_U32;
+	U32 m_arraySize = MAX_U32;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doArray("m_name", offsetof(ShaderProgramBinaryStructMember, m_name), &self.m_name[0], self.m_name.getSize());
+		s.doValue("m_type", offsetof(ShaderProgramBinaryStructMember, m_type), self.m_type);
+		s.doValue("m_structIndex", offsetof(ShaderProgramBinaryStructMember, m_structIndex), self.m_structIndex);
+		s.doValue("m_offset", offsetof(ShaderProgramBinaryStructMember, m_offset), self.m_offset);
+		s.doValue("m_arraySize", offsetof(ShaderProgramBinaryStructMember, m_arraySize), self.m_arraySize);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryStructMember&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryStructMember&>(serializer, *this);
+	}
+};
+
+/// Structures.
+class ShaderProgramBinaryStruct
+{
+public:
+	Array<char, MAX_SHADER_BINARY_NAME_LENGTH + 1> m_name;
+	WeakArray<ShaderProgramBinaryStructMember> m_members;
+	U32 m_size = MAX_U32;
+
+	template<typename TSerializer, typename TClass>
+	static void serializeCommon(TSerializer& s, TClass self)
+	{
+		s.doArray("m_name", offsetof(ShaderProgramBinaryStruct, m_name), &self.m_name[0], self.m_name.getSize());
+		s.doValue("m_members", offsetof(ShaderProgramBinaryStruct, m_members), self.m_members);
+		s.doValue("m_size", offsetof(ShaderProgramBinaryStruct, m_size), self.m_size);
+	}
+
+	template<typename TDeserializer>
+	void deserialize(TDeserializer& deserializer)
+	{
+		serializeCommon<TDeserializer, ShaderProgramBinaryStruct&>(deserializer, *this);
+	}
+
+	template<typename TSerializer>
+	void serialize(TSerializer& serializer) const
+	{
+		serializeCommon<TSerializer, const ShaderProgramBinaryStruct&>(serializer, *this);
+	}
+};
+
 /// ShaderProgramBinaryVariant class.
 class ShaderProgramBinaryVariant
 {
@@ -382,6 +445,7 @@ public:
 	ShaderProgramBinaryBlock* m_pushConstantBlock = nullptr;
 	WeakArray<ShaderProgramBinaryOpaque> m_opaques;
 	WeakArray<ShaderProgramBinaryConstant> m_constants;
+	WeakArray<ShaderProgramBinaryStruct> m_structs;
 	ShaderTypeBit m_presentShaderTypes = ShaderTypeBit::NONE;
 	Array<char, 64> m_libraryName = {}; ///< The name of the shader library. Mainly for RT shaders.
 	U32 m_rayType = MAX_U32; ///< An arbitary number indicating the type of the ray.
@@ -400,6 +464,7 @@ public:
 					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_structs", offsetof(ShaderProgramBinary, m_structs), self.m_structs);
 		s.doValue("m_presentShaderTypes", offsetof(ShaderProgramBinary, m_presentShaderTypes),
 				  self.m_presentShaderTypes);
 		s.doArray("m_libraryName", offsetof(ShaderProgramBinary, m_libraryName), &self.m_libraryName[0],

+ 19 - 0
AnKi/ShaderCompiler/ShaderProgramBinary.xml

@@ -67,6 +67,24 @@
 			</members>
 		</class>
 
+		<class name="ShaderProgramBinaryStructMember" comment="A member of a ShaderProgramBinaryStruct">
+			<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" comment="If the value is ShaderVariableDataType::NONE then it's a struct" />
+				<member name="m_structIndex" type="U32" constructor="= MAX_U32" />
+				<member name="m_offset" type="U32" constructor="= MAX_U32" />
+				<member name="m_arraySize" type="U32" constructor="= MAX_U32" />
+			</members>
+		</class>
+
+		<class name="ShaderProgramBinaryStruct" comment="Structures">
+			<members>
+				<member name="m_name" type="char" array_size="MAX_SHADER_BINARY_NAME_LENGTH + 1" />
+				<member name="m_members" type="WeakArray&lt;ShaderProgramBinaryStructMember&gt;" />
+				<member name="m_size" type="U32" constructor="= MAX_U32" />
+			</members>
+		</class>
+
 		<class name="ShaderProgramBinaryVariant">
 			<members>
 				<member name="m_codeBlockIndices" type="U32" array_size="U32(ShaderType::COUNT)" comment="Index in ShaderProgramBinary::m_codeBlocks. MAX_U32 means no shader" constructor="= {}" />
@@ -114,6 +132,7 @@
 				<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_structs" type="WeakArray&lt;ShaderProgramBinaryStruct&gt;" />
 				<member name="m_presentShaderTypes" type="ShaderTypeBit" constructor="= ShaderTypeBit::NONE" />
 				<member name="m_libraryName" type="char" array_size="64" constructor="= {}" comment="The name of the shader library. Mainly for RT shaders" />
 				<member name="m_rayType" type="U32" constructor="= MAX_U32" comment="An arbitary number indicating the type of the ray" />

+ 168 - 31
AnKi/ShaderCompiler/ShaderProgramCompiler.cpp

@@ -13,9 +13,6 @@
 namespace anki
 {
 
-static const char* SHADER_BINARY_MAGIC = "ANKISDR5"; ///< @warning If changed change SHADER_BINARY_VERSION
-const U32 SHADER_BINARY_VERSION = 5;
-
 Error ShaderProgramBinaryWrapper::serializeToFile(CString fname) const
 {
 	ANKI_ASSERT(m_binary);
@@ -60,74 +57,83 @@ void ShaderProgramBinaryWrapper::cleanup()
 		return;
 	}
 
+	BaseMemoryPool& mempool = m_alloc.getMemoryPool();
+
 	if(!m_singleAllocation)
 	{
 		for(ShaderProgramBinaryMutator& mutator : m_binary->m_mutators)
 		{
-			m_alloc.getMemoryPool().free(mutator.m_values.getBegin());
+			mempool.free(mutator.m_values.getBegin());
 		}
-		m_alloc.getMemoryPool().free(m_binary->m_mutators.getBegin());
+		mempool.free(m_binary->m_mutators.getBegin());
 
 		for(ShaderProgramBinaryCodeBlock& code : m_binary->m_codeBlocks)
 		{
-			m_alloc.getMemoryPool().free(code.m_binary.getBegin());
+			mempool.free(code.m_binary.getBegin());
 		}
-		m_alloc.getMemoryPool().free(m_binary->m_codeBlocks.getBegin());
+		mempool.free(m_binary->m_codeBlocks.getBegin());
 
 		for(ShaderProgramBinaryMutation& m : m_binary->m_mutations)
 		{
-			m_alloc.getMemoryPool().free(m.m_values.getBegin());
+			mempool.free(m.m_values.getBegin());
 		}
-		m_alloc.getMemoryPool().free(m_binary->m_mutations.getBegin());
+		mempool.free(m_binary->m_mutations.getBegin());
 
 		for(ShaderProgramBinaryBlock& block : m_binary->m_uniformBlocks)
 		{
-			m_alloc.getMemoryPool().free(block.m_variables.getBegin());
+			mempool.free(block.m_variables.getBegin());
 		}
-		m_alloc.getMemoryPool().free(m_binary->m_uniformBlocks.getBegin());
+		mempool.free(m_binary->m_uniformBlocks.getBegin());
 
 		for(ShaderProgramBinaryBlock& block : m_binary->m_storageBlocks)
 		{
-			m_alloc.getMemoryPool().free(block.m_variables.getBegin());
+			mempool.free(block.m_variables.getBegin());
 		}
-		m_alloc.getMemoryPool().free(m_binary->m_storageBlocks.getBegin());
+		mempool.free(m_binary->m_storageBlocks.getBegin());
 
 		if(m_binary->m_pushConstantBlock)
 		{
-			m_alloc.getMemoryPool().free(m_binary->m_pushConstantBlock->m_variables.getBegin());
-			m_alloc.getMemoryPool().free(m_binary->m_pushConstantBlock);
+			mempool.free(m_binary->m_pushConstantBlock->m_variables.getBegin());
+			mempool.free(m_binary->m_pushConstantBlock);
 		}
 
-		m_alloc.getMemoryPool().free(m_binary->m_opaques.getBegin());
-		m_alloc.getMemoryPool().free(m_binary->m_constants.getBegin());
+		mempool.free(m_binary->m_opaques.getBegin());
+		mempool.free(m_binary->m_constants.getBegin());
+
+		for(ShaderProgramBinaryStruct& s : m_binary->m_structs)
+		{
+			mempool.free(s.m_members.getBegin());
+		}
+
+		mempool.free(m_binary->m_structs.getBegin());
 
 		for(ShaderProgramBinaryVariant& variant : m_binary->m_variants)
 		{
 			for(ShaderProgramBinaryBlockInstance& block : variant.m_uniformBlocks)
 			{
-				m_alloc.getMemoryPool().free(block.m_variables.getBegin());
+				mempool.free(block.m_variables.getBegin());
 			}
 
 			for(ShaderProgramBinaryBlockInstance& block : variant.m_storageBlocks)
 			{
-				m_alloc.getMemoryPool().free(block.m_variables.getBegin());
+				mempool.free(block.m_variables.getBegin());
 			}
 
 			if(variant.m_pushConstantBlock)
 			{
-				m_alloc.getMemoryPool().free(variant.m_pushConstantBlock->m_variables.getBegin());
+				mempool.free(variant.m_pushConstantBlock->m_variables.getBegin());
 			}
 
-			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_constants.getBegin());
-			m_alloc.getMemoryPool().free(variant.m_opaques.getBegin());
+			mempool.free(variant.m_uniformBlocks.getBegin());
+			mempool.free(variant.m_storageBlocks.getBegin());
+			mempool.free(variant.m_pushConstantBlock);
+			mempool.free(variant.m_constants.getBegin());
+			mempool.free(variant.m_opaques.getBegin());
 		}
-		m_alloc.getMemoryPool().free(m_binary->m_variants.getBegin());
+		mempool.free(m_binary->m_variants.getBegin());
 	}
 
-	m_alloc.getMemoryPool().free(m_binary);
+	mempool.free(m_binary);
 	m_binary = nullptr;
 	m_singleAllocation = false;
 }
@@ -334,6 +340,10 @@ public:
 
 	DynamicArrayAuto<ShaderProgramBinaryOpaque> m_opaque = {m_alloc};
 	DynamicArrayAuto<ShaderProgramBinaryConstant> m_consts = {m_alloc};
+
+	DynamicArrayAuto<ShaderProgramBinaryStruct> m_structs = {m_alloc};
+	/// [structIndex][memberIndex]
+	DynamicArrayAuto<DynamicArrayAuto<ShaderProgramBinaryStructMember>> m_structMembers = {m_alloc};
 	/// @}
 
 	/// Will be stored in a variant
@@ -532,15 +542,126 @@ public:
 		return Error::NONE;
 	}
 
-	Error visitStruct(U32 idx, CString name, U32 memberCount) final
+	ANKI_USE_RESULT Bool findStruct(CString name, U32& idx) const
+	{
+		idx = MAX_U32;
+
+		for(U32 i = 0; i < m_structs.getSize(); ++i)
+		{
+			const ShaderProgramBinaryStruct& s = m_structs[i];
+			if(s.m_name.getBegin() == name)
+			{
+				idx = i;
+				break;
+			}
+		}
+
+		return idx != MAX_U32;
+	}
+
+	Error visitStruct(U32 idx, CString name, U32 memberCount, U32 size) final
 	{
-		// TODO
+		// Init the block
+		U32 structIdx;
+		const Bool structFound = findStruct(name, structIdx);
+		if(structFound)
+		{
+			if(memberCount != m_structMembers[structIdx].getSize())
+			{
+				ANKI_SHADER_COMPILER_LOGE("The number of members can't differ between shader variants for struct: %s",
+										  name.cstr());
+				return Error::USER_DATA;
+			}
+
+			if(size != m_structs[structIdx].m_size)
+			{
+				ANKI_SHADER_COMPILER_LOGE("The size can't differ between shader variants for struct: %s", name.cstr());
+				return Error::USER_DATA;
+			}
+		}
+		else
+		{
+			// Create a new struct
+			ShaderProgramBinaryStruct& s = *m_structs.emplaceBack();
+			ANKI_CHECK(setName(name, s.m_name));
+			s.m_size = size;
+
+			// Allocate members
+			m_structMembers.emplaceBack(m_alloc);
+			m_structMembers.getBack().create(memberCount);
+		}
+
 		return Error::NONE;
 	}
 
-	Error visitStructMember(U32 memberIdx, CString name, ShaderVariableDataType type) final
+	Error visitStructMember(U32 structIdx, CString structName, U32 memberIdx, CString memberName,
+							ShaderVariableDataType type, CString typeStructName, U32 offset, U32 arraySize) final
 	{
-		// TODO
+		// Refresh the structIdx because we have a different global mapping
+		const Bool structFound = findStruct(structName, structIdx);
+		ANKI_ASSERT(structFound);
+		(void)structFound;
+		const ShaderProgramBinaryStruct& s = m_structs[structIdx];
+		DynamicArrayAuto<ShaderProgramBinaryStructMember>& members = m_structMembers[structIdx];
+
+		// Find member
+		Bool found = false;
+		for(U32 i = 0; i < members.getSize(); ++i)
+		{
+			if(memberName == &members[i].m_name[0])
+			{
+				if(members[i].m_type != type)
+				{
+					ANKI_SHADER_COMPILER_LOGE("Member %s of struct %s has different type between variants",
+											  memberName.cstr(), &s.m_name[0]);
+					return Error::USER_DATA;
+				}
+
+				if(i != memberIdx)
+				{
+					ANKI_SHADER_COMPILER_LOGE("Member %s of struct %s is in different place between variants",
+											  memberName.cstr(), &s.m_name[0]);
+					return Error::USER_DATA;
+				}
+
+				if(members[i].m_offset != offset)
+				{
+					ANKI_SHADER_COMPILER_LOGE("Member %s of struct %s has different offset between variants",
+											  memberName.cstr(), &s.m_name[0]);
+					return Error::USER_DATA;
+				}
+
+				if(members[i].m_arraySize != arraySize)
+				{
+					ANKI_SHADER_COMPILER_LOGE("Member %s of struct %s has different array size between variants",
+											  memberName.cstr(), &s.m_name[0]);
+					return Error::USER_DATA;
+				}
+
+				found = true;
+				break;
+			}
+		}
+
+		// Create if not found
+		if(!found)
+		{
+			ShaderProgramBinaryStructMember& member = members[memberIdx];
+			ANKI_CHECK(setName(memberName, member.m_name));
+			member.m_type = type;
+			member.m_offset = offset;
+			member.m_arraySize = arraySize;
+
+			if(type == ShaderVariableDataType::NONE)
+			{
+				// Type is a struct, find the right index
+
+				const Bool structFound = findStruct(typeStructName, member.m_structIndex);
+				ANKI_ASSERT(structFound);
+				(void)structFound;
+			}
+		}
+
 		return Error::NONE;
 	}
 
@@ -788,6 +909,22 @@ static Error doReflection(const StringList& symbolsToReflect, ShaderProgramBinar
 		binary.m_constants.setArray(consts, size);
 	}
 
+	if(refl.m_structs.getSize())
+	{
+		ShaderProgramBinaryStruct* storage;
+		U32 size, storageSize;
+		refl.m_structs.moveAndReset(storage, size, storageSize);
+		binary.m_structs.setArray(storage, size);
+
+		for(U32 i = 0; i < size; ++i)
+		{
+			ShaderProgramBinaryStructMember* memberStorage;
+			U32 memberSize, memberStorageSize;
+			refl.m_structMembers[i].moveAndReset(memberStorage, memberSize, memberStorageSize);
+			binary.m_structs[i].m_members.setArray(memberStorage, memberSize);
+		}
+	}
+
 	return Error::NONE;
 }
 

+ 2 - 1
AnKi/ShaderCompiler/ShaderProgramCompiler.h

@@ -15,7 +15,8 @@ namespace anki
 /// @addtogroup shader_compiler
 /// @{
 
-extern const U32 SHADER_BINARY_VERSION;
+constexpr const char* SHADER_BINARY_MAGIC = "ANKISDR6"; ///< @warning If changed change SHADER_BINARY_VERSION
+constexpr U32 SHADER_BINARY_VERSION = 6;
 
 /// A wrapper over the POD ShaderProgramBinary class.
 /// @memberof ShaderProgramCompiler

+ 22 - 0
AnKi/ShaderCompiler/ShaderProgramDump.cpp

@@ -142,6 +142,28 @@ void dumpShaderProgramBinary(const ShaderProgramBinary& binary, StringAuto& huma
 		lines.pushBack(ANKI_TAB "N/A\n");
 	}
 
+	lines.pushBack("\n**STRUCTS**\n");
+	if(binary.m_structs.getSize() > 0)
+	{
+		for(const ShaderProgramBinaryStruct& s : binary.m_structs)
+		{
+			lines.pushBackSprintf(ANKI_TAB "%-32s size %4u\n", s.m_name.getBegin(), s.m_size);
+
+			for(const ShaderProgramBinaryStructMember& member : s.m_members)
+			{
+				CString typeStr = (member.m_type == ShaderVariableDataType::NONE)
+									  ? &binary.m_structs[member.m_structIndex].m_name[0]
+									  : shaderVariableDataTypeToString(member.m_type);
+				lines.pushBackSprintf(ANKI_TAB ANKI_TAB "%-32s type %24s offset %4u arraySize %4u\n",
+									  member.m_name.getBegin(), typeStr.cstr(), member.m_offset, member.m_arraySize);
+			}
+		}
+	}
+	else
+	{
+		lines.pushBack(ANKI_TAB "N/A\n");
+	}
+
 	lines.pushBack("\n**BINARIES**\n");
 	U32 count = 0;
 	for(const ShaderProgramBinaryCodeBlock& code : binary.m_codeBlocks)

+ 98 - 19
AnKi/ShaderCompiler/ShaderProgramReflection.cpp

@@ -133,6 +133,8 @@ private:
 		StringAuto m_name;
 		ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
 		U32 m_structIndex = MAX_U32; ///< The member is actually a struct.
+		U32 m_offset = MAX_U32;
+		U32 m_arraySize = MAX_U32;
 
 		StructMember(const GenericMemoryPoolAllocator<U8>& alloc)
 			: m_name(alloc)
@@ -145,6 +147,8 @@ private:
 	public:
 		StringAuto m_name;
 		DynamicArrayAuto<StructMember> m_members;
+		U32 m_size = 0;
+		U32 m_alignment = 0;
 
 		Struct(const GenericMemoryPoolAllocator<U8>& alloc)
 			: m_name(alloc)
@@ -174,7 +178,7 @@ private:
 
 	ANKI_USE_RESULT Error structsReflection(DynamicArrayAuto<Struct>& structs) const;
 
-	ANKI_USE_RESULT Error structReflection(uint32_t id, const spirv_cross::SPIRType& type, Bool trySkipType,
+	ANKI_USE_RESULT Error structReflection(uint32_t id, const spirv_cross::SPIRType& type, U32 depth, Bool& skipped,
 										   DynamicArrayAuto<Struct>& structs, U32& structIndexInStructsArr) const;
 };
 
@@ -188,27 +192,34 @@ Error SpirvReflector::structsReflection(DynamicArrayAuto<Struct>& structs) const
 			return;
 		}
 
-		if(!(type.basetype == spirv_cross::SPIRType::Struct && !type.pointer && type.array.empty()))
+		if(type.basetype != spirv_cross::SPIRType::Struct || type.pointer || !type.array.empty()
+		   || has_decoration(type.self, spv::DecorationBlock))
 		{
 			return;
 		}
 
 		U32 idx;
-		err = structReflection(id, type, true, structs, idx);
+		Bool skipped;
+		err = structReflection(id, type, 0, skipped, structs, idx);
 	});
 
 	return err;
 }
 
-Error SpirvReflector::structReflection(uint32_t id, const spirv_cross::SPIRType& type, Bool trySkipType,
+Error SpirvReflector::structReflection(uint32_t id, const spirv_cross::SPIRType& type, U32 depth, Bool& skipped,
 									   DynamicArrayAuto<Struct>& structs, U32& structIndexInStructsArr) const
 {
+	skipped = false;
+
 	// Name
 	std::string name = to_name(id);
 
-	if(trySkipType && m_interface->skipSymbol(name.c_str()))
+	// Skip GL builtins, SPIRV-Cross things and symbols that should be skipped
+	if(CString(name.c_str()).find("gl_") == 0 || CString(name.c_str()).find("_") == 0
+	   || (depth == 0 && m_interface->skipSymbol(name.c_str())))
 	{
-		// return Error::NONE;
+		skipped = true;
+		return Error::NONE;
 	}
 
 	// Check if the struct is already there
@@ -224,17 +235,17 @@ Error SpirvReflector::structReflection(uint32_t id, const spirv_cross::SPIRType&
 	}
 
 	// Create new struct
-	structIndexInStructsArr = structs.getSize();
 	GenericMemoryPoolAllocator<U8> alloc = structs.getAllocator();
-	structs.emplaceBack(alloc);
-	structs[structIndexInStructsArr].m_name = name.c_str();
-
-	// printf("%s\n", s.m_name.cstr());
+	Struct cstruct(alloc);
+	cstruct.m_name = name.c_str();
+	U32 membersOffset = 0;
+	Bool aMemberWasSkipped = false;
 
 	// Members
 	for(U32 i = 0; i < type.member_types.size(); ++i)
 	{
-		StructMember& member = *structs[structIndexInStructsArr].m_members.emplaceBack(alloc);
+		StructMember& member = *cstruct.m_members.emplaceBack(alloc);
+		const spirv_cross::SPIRType& memberType = get<spirv_cross::SPIRType>(type.member_types[i]);
 
 		// Get name
 		const spirv_cross::Meta* meta = ir.find_meta(type.self);
@@ -243,12 +254,39 @@ Error SpirvReflector::structReflection(uint32_t id, const spirv_cross::SPIRType&
 		ANKI_ASSERT(!meta->members[i].alias.empty());
 		member.m_name = meta->members[i].alias.c_str();
 
+		// 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 notSpecConstantArraySize = memberType.array_size_literal[0];
+			if(notSpecConstantArraySize)
+			{
+				// Have a min to acount for unsized arrays of SSBOs
+				member.m_arraySize = max(memberType.array[0], 1u);
+			}
+			else
+			{
+				ANKI_SHADER_COMPILER_LOGE("Arrays with spec constant size are not allowed: %s", member.m_name.cstr());
+				return Error::FUNCTION_FAILED;
+			}
+		}
+		else
+		{
+			member.m_arraySize = 1;
+		}
+
 		// Type
-		const spirv_cross::SPIRType& memberType = get<spirv_cross::SPIRType>(type.member_types[i]);
 		const ShaderVariableDataType baseType = spirvcrossBaseTypeToAnki(memberType.basetype);
 		const Bool isNumeric = baseType != ShaderVariableDataType::NONE;
-
 		ShaderVariableDataType actualType = ShaderVariableDataType::NONE;
+		U32 memberSize = 0;
+		U32 memberAlignment = 0;
+
 		if(isNumeric)
 		{
 			const Bool isMatrix = memberType.columns > 1;
@@ -261,10 +299,14 @@ Error SpirvReflector::structReflection(uint32_t id, const spirv_cross::SPIRType&
 			&& memberType.columns == rowCount) \
 	{ \
 		actualType = ShaderVariableDataType::capital; \
+		memberSize = sizeof(type); \
+		memberAlignment = alignof(baseType_); \
 	} \
 	else if(ShaderVariableDataType::baseType_ == baseType && !isMatrix && memberType.vecsize == rowCount) \
 	{ \
 		actualType = ShaderVariableDataType::capital; \
+		memberSize = sizeof(type); \
+		memberAlignment = alignof(baseType_); \
 	}
 #include <AnKi/Gr/ShaderVariableDataTypeDefs.h>
 #undef ANKI_SVDT_MACRO
@@ -274,16 +316,50 @@ Error SpirvReflector::structReflection(uint32_t id, const spirv_cross::SPIRType&
 		else if(memberType.basetype == spirv_cross::SPIRType::Struct)
 		{
 			U32 idx = MAX_U32;
-			ANKI_CHECK(structReflection(type.member_types[i], memberType, false, structs, idx));
+			Bool memberSkipped = false;
+			ANKI_CHECK(structReflection(type.member_types[i], memberType, depth + 1, memberSkipped, structs, idx));
 
-			ANKI_ASSERT(idx < structs.getSize());
-			member.m_structIndex = idx;
+			if(memberSkipped)
+			{
+				aMemberWasSkipped = true;
+				break;
+			}
+			else
+			{
+				ANKI_ASSERT(idx < structs.getSize());
+				member.m_structIndex = idx;
+				memberSize = structs[idx].m_size;
+				memberAlignment = structs[idx].m_alignment;
+			}
 		}
 		else
 		{
 			ANKI_SHADER_COMPILER_LOGE("Unhandled base type for member: %s", name.c_str());
 			return Error::FUNCTION_FAILED;
 		}
+
+		// Update offsets and alignments
+		memberSize *= member.m_arraySize;
+		member.m_offset = getAlignedRoundUp(memberAlignment, membersOffset);
+
+		cstruct.m_alignment = max(cstruct.m_alignment, memberAlignment);
+		cstruct.m_size = member.m_offset + memberSize;
+
+		membersOffset = member.m_offset + memberSize;
+	}
+
+	if(!aMemberWasSkipped)
+	{
+		// Now you can create the struct
+
+		alignRoundUp(cstruct.m_alignment, cstruct.m_size);
+
+		Struct& newStruct = *structs.emplaceBack(alloc);
+		newStruct = std::move(cstruct);
+	}
+	else
+	{
+		skipped = true;
 	}
 
 	return Error::NONE;
@@ -928,12 +1004,15 @@ Error SpirvReflector::performSpirvReflection(Array<ConstWeakArray<U8>, U32(Shade
 	for(U32 i = 0; i < structs.getSize(); ++i)
 	{
 		const Struct& s = structs[i];
-		ANKI_CHECK(interface.visitStruct(i, s.m_name, s.m_members.getSize()));
+		ANKI_CHECK(interface.visitStruct(i, s.m_name, s.m_members.getSize(), s.m_size));
 
 		for(U32 j = 0; j < s.m_members.getSize(); ++j)
 		{
 			const StructMember& sm = s.m_members[j];
-			ANKI_CHECK(interface.visitStructMember(j, sm.m_name, sm.m_type));
+			ANKI_CHECK(interface.visitStructMember(i, s.m_name, j, sm.m_name, sm.m_type,
+												   (sm.m_structIndex != MAX_U32) ? structs[sm.m_structIndex].m_name
+																				 : CString(),
+												   sm.m_offset, sm.m_arraySize));
 		}
 	}
 

+ 4 - 2
AnKi/ShaderCompiler/ShaderProgramReflection.h

@@ -47,9 +47,11 @@ public:
 
 	virtual ANKI_USE_RESULT Error visitConstant(U32 idx, CString name, ShaderVariableDataType type, U32 constantId) = 0;
 
-	virtual ANKI_USE_RESULT Error visitStruct(U32 idx, CString name, U32 memberCount) = 0;
+	virtual ANKI_USE_RESULT Error visitStruct(U32 idx, CString name, U32 memberCount, U32 size) = 0;
 
-	virtual ANKI_USE_RESULT Error visitStructMember(U32 memberIdx, CString name, ShaderVariableDataType type) = 0;
+	virtual ANKI_USE_RESULT Error visitStructMember(U32 structIdx, CString structName, U32 memberIdx,
+													CString memberName, ShaderVariableDataType type,
+													CString typeStructName, U32 offset, U32 arraySize) = 0;
 
 	virtual ANKI_USE_RESULT Bool skipSymbol(CString symbol) const = 0;
 };