Browse Source

Start another rewrite

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
04a9e5b60a

+ 3 - 2
AnKi/Gr/Enums.cpp

@@ -10,8 +10,9 @@ namespace anki {
 
 /// @warning Don't use Array because the compilers can't handle it for some reason.
 static constexpr ShaderVariableDataTypeInfo SVD_INFOS[] = {
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) {ANKI_STRINGIZE(type), sizeof(type), false},
-#define ANKI_SVDT_MACRO_OPAQUE(capital, type) {ANKI_STRINGIZE(type), MAX_U32, true},
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) \
+	{ANKI_STRINGIZE(type), sizeof(type), false, isIntagralType},
+#define ANKI_SVDT_MACRO_OPAQUE(capital, type) {ANKI_STRINGIZE(type), MAX_U32, true, false},
 #include <AnKi/Gr/ShaderVariableDataType.defs.h>
 #undef ANKI_SVDT_MACRO
 #undef ANKI_SVDT_MACRO_OPAQUE

+ 2 - 1
AnKi/Gr/Enums.h

@@ -318,7 +318,7 @@ enum class ShaderVariableDataType : U8
 {
 	NONE,
 
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) capital,
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) capital,
 #define ANKI_SVDT_MACRO_OPAQUE(capital, type) capital,
 #include <AnKi/Gr/ShaderVariableDataType.defs.h>
 #undef ANKI_SVDT_MACRO
@@ -356,6 +356,7 @@ public:
 	const Char* m_name;
 	U32 m_size; ///< Size of the type.
 	Bool m_opaque;
+	Bool m_isIntegral; ///< If true is integral type. Else it's float.
 };
 
 ANKI_PURE const ShaderVariableDataTypeInfo& getShaderVariableDataTypeInfo(ShaderVariableDataType type);

+ 39 - 38
AnKi/Gr/ShaderVariableDataType.defs.h

@@ -5,51 +5,52 @@
 
 // ShaderVariableDataType defines
 
-// ANKI_SVDT_MACRO(capital, varType, baseType, rowCount, columnCount)
+// ANKI_SVDT_MACRO(capital, varType, baseType, rowCount, columnCount, isIntegralType)
 #if defined(ANKI_SVDT_MACRO)
-ANKI_SVDT_MACRO(I8, I8, I8, 1, 1)
-ANKI_SVDT_MACRO(U8, U8, U8, 1, 1)
-ANKI_SVDT_MACRO(I16, I16, I16, 1, 1)
-ANKI_SVDT_MACRO(U16, U16, U16, 1, 1)
-ANKI_SVDT_MACRO(I32, I32, I32, 1, 1)
-ANKI_SVDT_MACRO(U32, U32, U32, 1, 1)
-ANKI_SVDT_MACRO(I64, I64, I64, 1, 1)
-ANKI_SVDT_MACRO(U64, U64, U64, 1, 1)
-ANKI_SVDT_MACRO(F16, F16, F16, 1, 1)
-ANKI_SVDT_MACRO(F32, F32, F32, 1, 1)
+ANKI_SVDT_MACRO(I8, I8, I8, 1, 1, true)
+ANKI_SVDT_MACRO(U8, U8, U8, 1, 1, true)
+ANKI_SVDT_MACRO(I16, I16, I16, 1, 1, true)
+ANKI_SVDT_MACRO(U16, U16, U16, 1, 1, true)
+ANKI_SVDT_MACRO(I32, I32, I32, 1, 1, true)
+ANKI_SVDT_MACRO(U32, U32, U32, 1, 1, true)
+ANKI_SVDT_MACRO(I64, I64, I64, 1, 1, true)
+ANKI_SVDT_MACRO(U64, U64, U64, 1, 1, true)
+ANKI_SVDT_MACRO(F16, F16, F16, 1, 1, false)
+ANKI_SVDT_MACRO(F32, F32, F32, 1, 1, false)
 
-ANKI_SVDT_MACRO(I8VEC2, I8Vec2, I8, 2, 1)
-ANKI_SVDT_MACRO(U8VEC2, U8Vec2, U8, 2, 1)
-ANKI_SVDT_MACRO(I16VEC2, I16Vec2, I16, 2, 1)
-ANKI_SVDT_MACRO(U16VEC2, U16Vec2, U16, 2, 1)
-ANKI_SVDT_MACRO(IVEC2, IVec2, I32, 2, 1)
-ANKI_SVDT_MACRO(UVEC2, UVec2, U32, 2, 1)
-ANKI_SVDT_MACRO(HVEC2, HVec2, F16, 2, 1)
-ANKI_SVDT_MACRO(VEC2, Vec2, F32, 2, 1)
+ANKI_SVDT_MACRO(I8VEC2, I8Vec2, I8, 2, 1, true)
+ANKI_SVDT_MACRO(U8VEC2, U8Vec2, U8, 2, 1, true)
+ANKI_SVDT_MACRO(I16VEC2, I16Vec2, I16, 2, 1, true)
+ANKI_SVDT_MACRO(U16VEC2, U16Vec2, U16, 2, 1, true)
+ANKI_SVDT_MACRO(IVEC2, IVec2, I32, 2, 1, true)
+ANKI_SVDT_MACRO(UVEC2, UVec2, U32, 2, 1, true)
+ANKI_SVDT_MACRO(HVEC2, HVec2, F16, 2, 1, false)
+ANKI_SVDT_MACRO(VEC2, Vec2, F32, 2, 1, false)
 
-ANKI_SVDT_MACRO(I8VEC3, I8Vec3, I8, 3, 1)
-ANKI_SVDT_MACRO(U8VEC3, U8Vec3, U8, 3, 1)
-ANKI_SVDT_MACRO(I16VEC3, I16Vec3, I16, 3, 1)
-ANKI_SVDT_MACRO(U16VEC3, U16Vec3, U16, 3, 1)
-ANKI_SVDT_MACRO(IVEC3, IVec3, I32, 3, 1)
-ANKI_SVDT_MACRO(UVEC3, UVec3, U32, 3, 1)
-ANKI_SVDT_MACRO(HVEC3, HVec3, F16, 3, 1)
-ANKI_SVDT_MACRO(VEC3, Vec3, F32, 3, 1)
+ANKI_SVDT_MACRO(I8VEC3, I8Vec3, I8, 3, 1, true)
+ANKI_SVDT_MACRO(U8VEC3, U8Vec3, U8, 3, 1, true)
+ANKI_SVDT_MACRO(I16VEC3, I16Vec3, I16, 3, 1, true)
+ANKI_SVDT_MACRO(U16VEC3, U16Vec3, U16, 3, 1, true)
+ANKI_SVDT_MACRO(IVEC3, IVec3, I32, 3, 1, true)
+ANKI_SVDT_MACRO(UVEC3, UVec3, U32, 3, 1, true)
+ANKI_SVDT_MACRO(HVEC3, HVec3, F16, 3, 1, false)
+ANKI_SVDT_MACRO(VEC3, Vec3, F32, 3, 1, false)
 
-ANKI_SVDT_MACRO(I8VEC4, I8Vec4, I8, 4, 1)
-ANKI_SVDT_MACRO(U8VEC4, U8Vec4, U8, 4, 1)
-ANKI_SVDT_MACRO(I16VEC4, I16Vec4, I16, 4, 1)
-ANKI_SVDT_MACRO(U16VEC4, U16Vec4, U16, 4, 1)
-ANKI_SVDT_MACRO(IVEC4, IVec4, I32, 4, 1)
-ANKI_SVDT_MACRO(UVEC4, UVec4, U32, 4, 1)
-ANKI_SVDT_MACRO(HVEC4, HVec4, F16, 4, 1)
-ANKI_SVDT_MACRO(VEC4, Vec4, F32, 4, 1)
+ANKI_SVDT_MACRO(I8VEC4, I8Vec4, I8, 4, 1, true)
+ANKI_SVDT_MACRO(U8VEC4, U8Vec4, U8, 4, 1, true)
+ANKI_SVDT_MACRO(I16VEC4, I16Vec4, I16, 4, 1, true)
+ANKI_SVDT_MACRO(U16VEC4, U16Vec4, U16, 4, 1, true)
+ANKI_SVDT_MACRO(IVEC4, IVec4, I32, 4, 1, true)
+ANKI_SVDT_MACRO(UVEC4, UVec4, U32, 4, 1, true)
+ANKI_SVDT_MACRO(HVEC4, HVec4, F16, 4, 1, false)
+ANKI_SVDT_MACRO(VEC4, Vec4, F32, 4, 1, false)
 
-ANKI_SVDT_MACRO(MAT3, Mat3, F32, 3, 3)
-ANKI_SVDT_MACRO(MAT3X4, Mat3x4, F32, 3, 4)
-ANKI_SVDT_MACRO(MAT4, Mat4, F32, 4, 4)
+ANKI_SVDT_MACRO(MAT3, Mat3, F32, 3, 3, false)
+ANKI_SVDT_MACRO(MAT3X4, Mat3x4, F32, 3, 4, false)
+ANKI_SVDT_MACRO(MAT4, Mat4, F32, 4, 4, false)
 #endif
 
+// ANKI_SVDT_MACRO_OPAQUE(capital, varType)
 #if defined(ANKI_SVDT_MACRO_OPAQUE)
 ANKI_SVDT_MACRO_OPAQUE(TEXTURE_1D, texture1D)
 ANKI_SVDT_MACRO_OPAQUE(TEXTURE_1D_ARRAY, texture1DArray)

+ 2 - 2
AnKi/Gr/Utils/Functions.cpp

@@ -86,7 +86,7 @@ public:
 	static constexpr Bool VALUE = false;
 };
 
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) \
 	template<> \
 	class IsShaderVarDataTypeAMatrix<type> \
 	{ \
@@ -126,7 +126,7 @@ void writeShaderBlockMemory(ShaderVariableDataType type, const ShaderVariableBlo
 {
 	switch(type)
 	{
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) \
 	case ShaderVariableDataType::capital: \
 		WriteShaderBlockMemory<type>()(varBlkInfo, elements, elementsCount, buffBegin, buffEnd); \
 		break;

+ 1 - 1
AnKi/Gr/Utils/Functions.h

@@ -31,7 +31,7 @@ inline Bool blendingDisabled(BlendFactor srcFactorRgb, BlendFactor dstFactorRgb,
 template<typename T>
 ShaderVariableDataType getShaderVariableTypeFromTypename();
 
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) \
 	template<> \
 	inline ShaderVariableDataType getShaderVariableTypeFromTypename<type>() \
 	{ \

+ 9 - 39
AnKi/Importer/GltfImporterMaterial.cpp

@@ -11,10 +11,9 @@ namespace anki {
 
 const char* MATERIAL_TEMPLATE = R"(<!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="%diffTexMutator%"/>
 				<mutator name="SPECULAR_TEX" value="%specTexMutator%"/>
 				<mutator name="ROUGHNESS_TEX" value="%roughnessTexMutator%"/>
@@ -24,38 +23,9 @@ const char* MATERIAL_TEMPLATE = R"(<!-- This file is auto generated by ImporterM
 				<mutator name="EMISSIVE_TEX" value="%emissiveTexMutator%"/>
 				<mutator name="ALPHA_TEST" value="%alphaTestMutator%"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="%diffTexMutator%"/>
-				<mutator name="SPECULAR_TEX" value="%specTexMutator%"/>
-				<mutator name="ROUGHNESS_TEX" value="%roughnessTexMutator%"/>
-				<mutator name="METAL_TEX" value="%metalTexMutator%"/>
-				<mutator name="NORMAL_TEX" value="%normalTexMutator%"/>
-				<mutator name="PARALLAX" value="%parallaxMutator%"/>
-				<mutator name="EMISSIVE_TEX" value="%emissiveTexMutator%"/>
-				<mutator name="ALPHA_TEST" value="%alphaTestMutator%"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="%diffTexMutator%"/>
-				<mutator name="SPECULAR_TEX" value="%specTexMutator%"/>
-				<mutator name="ROUGHNESS_TEX" value="%roughnessTexMutator%"/>
-				<mutator name="METAL_TEX" value="%metalTexMutator%"/>
-				<mutator name="NORMAL_TEX" value="%normalTexMutator%"/>
-				<mutator name="PARALLAX" value="%parallaxMutator%"/>
-				<mutator name="EMISSIVE_TEX" value="%emissiveTexMutator%"/>
-				<mutator name="ALPHA_TEST" value="%alphaTestMutator%"/>
-			</mutation>
-		</technique>
-
+		</shaderProgram>
 		%rayTracing%
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>
 		%parallaxInput%
@@ -72,11 +42,11 @@ const char* MATERIAL_TEMPLATE = R"(<!-- This file is auto generated by ImporterM
 )";
 
 const char* RT_MATERIAL_TEMPLATE = R"(
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
+			<mutation>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
+			</mutation>
+		</shaderProgram>
 )";
 
 static CString getTextureUri(const cgltf_texture_view& view)

+ 110 - 101
AnKi/Resource/MaterialResource.cpp

@@ -11,7 +11,7 @@
 namespace anki {
 
 static const Array<CString, U32(BuiltinMutatorId::COUNT)> BUILTIN_MUTATOR_NAMES = {
-	{"NONE", "ANKI_LOD", "ANKI_BONES", "ANKI_VELOCITY"}};
+	{"NONE", "ANKI_TECHNIQUE", "ANKI_LOD", "ANKI_BONES", "ANKI_VELOCITY"}};
 
 static const Array<CString, U(RenderingTechnique::COUNT)> TECHNIQUE_NAMES = {
 	{"GBuffer", "GBufferEarlyZ", "Shadow", "Forward", "RtShadow"}};
@@ -26,7 +26,7 @@ public:
 	static constexpr Bool VALUE = false;
 };
 
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) \
 	template<> \
 	class IsShaderVarDataTypeAnArray<type> \
 	{ \
@@ -70,13 +70,14 @@ MaterialVariable::~MaterialVariable()
 MaterialResource::MaterialResource(ResourceManager* manager)
 	: ResourceObject(manager)
 {
+	memset(m_techniqueToProgram.getBegin(), 0xFF, m_techniqueToProgram.getSizeInBytes());
 }
 
 MaterialResource::~MaterialResource()
 {
-	for(Technique& t : m_techniques)
+	for(Program& p : m_programs)
 	{
-		t.m_partialMutation.destroy(getAllocator());
+		p.m_partialMutation.destroy(getAllocator());
 	}
 
 	m_textures.destroy(getAllocator());
@@ -87,6 +88,7 @@ MaterialResource::~MaterialResource()
 	}
 
 	m_vars.destroy(getAllocator());
+	m_programs.destroy(getAllocator());
 }
 
 const MaterialVariable* MaterialResource::tryFindVariableInternal(CString name) const
@@ -112,16 +114,16 @@ Error MaterialResource::load(const ResourceFilename& filename, Bool async)
 	XmlElement rootEl;
 	ANKI_CHECK(doc.getChildElement("material", rootEl));
 
-	// <techniques>
-	XmlElement techniquesEl;
-	ANKI_CHECK(rootEl.getChildElement("techniques", techniquesEl));
-	XmlElement techniqueEl;
-	ANKI_CHECK(techniquesEl.getChildElement("technique", techniqueEl));
+	// <shaderPrograms>
+	XmlElement shaderProgramsEl;
+	ANKI_CHECK(rootEl.getChildElement("shaderPrograms", shaderProgramsEl));
+	XmlElement shaderProgramEl;
+	ANKI_CHECK(shaderProgramsEl.getChildElement("shaderProgram", shaderProgramEl));
 	do
 	{
-		ANKI_CHECK(parseTechnique(techniqueEl, async));
-		ANKI_CHECK(techniqueEl.getNextSiblingElement("technique", techniqueEl));
-	} while(techniqueEl);
+		ANKI_CHECK(parseShaderProgram(shaderProgramEl, async));
+		ANKI_CHECK(shaderProgramEl.getNextSiblingElement("shaderProgram", shaderProgramEl));
+	} while(shaderProgramEl);
 
 	ANKI_ASSERT(!!m_techniquesMask);
 
@@ -151,68 +153,41 @@ Error MaterialResource::load(const ResourceFilename& filename, Bool async)
 	return Error::NONE;
 }
 
-Error MaterialResource::parseTechnique(XmlElement techniqueEl, Bool async)
+Error MaterialResource::parseShaderProgram(XmlElement shaderProgramEl, Bool async)
 {
-	// name
-	CString name;
-	ANKI_CHECK(techniqueEl.getAttributeText("name", name));
-
-	RenderingTechnique techniqueId = RenderingTechnique::FIRST;
-	for(; techniqueId < RenderingTechnique::COUNT; ++techniqueId)
-	{
-		if(TECHNIQUE_NAMES[techniqueId] == name)
-		{
-			break;
-		}
-	}
-
-	if(techniqueId == RenderingTechnique::COUNT)
-	{
-		ANKI_RESOURCE_LOGE("Unrecognized technique name: %s", name.cstr());
-		return Error::USER_DATA;
-	}
-
-	const RenderingTechniqueBit mask = RenderingTechniqueBit(1 << techniqueId);
-	if(!!(mask & m_techniquesMask))
-	{
-		ANKI_RESOURCE_LOGE("Technique set more than once: %s", name.cstr());
-		return Error::USER_DATA;
-	}
+	// filename
+	CString fname;
+	ANKI_CHECK(shaderProgramEl.getAttributeText("filename", fname));
 
-	if(!!(mask & RenderingTechniqueBit::ALL_RT)
-	   && !getManager().getGrManager().getDeviceCapabilities().m_rayTracingEnabled)
+	if(fname.find("/Rt") != CString::NPOS && !getManager().getGrManager().getDeviceCapabilities().m_rayTracingEnabled)
 	{
+		// Skip RT programs when RT is disabled
 		return Error::NONE;
 	}
 
-	Technique& technique = m_techniques[techniqueId];
-
-	// shaderProgram
-	CString fname;
-	ANKI_CHECK(techniqueEl.getAttributeText("shaderProgram", fname));
-	ANKI_CHECK(getManager().loadResource(fname, technique.m_prog, async));
+	Program& prog = *m_programs.emplaceBack(getAllocator());
+	ANKI_CHECK(getManager().loadResource(fname, prog.m_prog, async));
 
 	// <mutation>
 	XmlElement mutatorsEl;
-	ANKI_CHECK(techniqueEl.getChildElementOptional("mutation", mutatorsEl));
+	ANKI_CHECK(shaderProgramEl.getChildElementOptional("mutation", mutatorsEl));
 	if(mutatorsEl)
 	{
-		ANKI_CHECK(parseMutators(mutatorsEl, technique));
+		ANKI_CHECK(parseMutators(mutatorsEl, prog));
 	}
 
 	// And find the builtin mutators
-	ANKI_CHECK(findBuiltinMutators(technique));
+	ANKI_CHECK(findBuiltinMutators(prog));
 
 	// Create the vars
-	ANKI_CHECK(createVars(technique));
+	ANKI_CHECK(createVars(prog));
 
-	m_techniquesMask |= mask;
 	return Error::NONE;
 }
 
-Error MaterialResource::createVars(Technique& technique)
+Error MaterialResource::createVars(Program& prog)
 {
-	const ShaderProgramBinary& binary = technique.m_prog->getBinary();
+	const ShaderProgramBinary& binary = prog.m_prog->getBinary();
 
 	// Find struct
 	const ShaderProgramBinaryStruct* localUniformsStruct = nullptr;
@@ -224,18 +199,18 @@ Error MaterialResource::createVars(Technique& technique)
 			break;
 		}
 
-		++technique.m_localUniformsStructIdx;
+		++prog.m_localUniformsStructIdx;
 	}
 
 	if(localUniformsStruct == nullptr)
 	{
-		technique.m_localUniformsStructIdx = MAX_U32;
+		prog.m_localUniformsStructIdx = MAX_U32;
 	}
 
 	// Iterate all variants of builtin mutators to gather the vars
-	ShaderProgramResourceVariantInitInfo initInfo(technique.m_prog);
+	ShaderProgramResourceVariantInitInfo initInfo(prog.m_prog);
 
-	for(const PartialMutation& m : technique.m_partialMutation)
+	for(const PartialMutation& m : prog.m_partialMutation)
 	{
 		initInfo.addMutation(m.m_mutator->m_name, m.m_value);
 	}
@@ -243,7 +218,7 @@ Error MaterialResource::createVars(Technique& technique)
 	Array<const ShaderProgramResourceMutator*, U(BuiltinMutatorId::COUNT)> mutatorPtrs = {};
 	for(BuiltinMutatorId id : EnumIterable<BuiltinMutatorId>())
 	{
-		mutatorPtrs[id] = technique.m_prog->tryFindMutator(BUILTIN_MUTATOR_NAMES[id]);
+		mutatorPtrs[id] = prog.m_prog->tryFindMutator(BUILTIN_MUTATOR_NAMES[id]);
 	}
 
 #define ANKI_LOOP(builtIn) \
@@ -260,30 +235,22 @@ Error MaterialResource::createVars(Technique& technique)
 
 #define ANKI_LOOP_END() }
 
+	ANKI_LOOP(TECHNIQUE)
 	ANKI_LOOP(LOD)
 	ANKI_LOOP(BONES)
 	ANKI_LOOP(VELOCITY)
 	{
 		const ShaderProgramResourceVariant* variant;
-		technique.m_prog->getOrCreateVariant(initInfo, variant);
+		prog.m_prog->getOrCreateVariant(initInfo, variant);
 		for(const ShaderProgramBinaryStructInstance& instance : variant->getBinaryVariant().m_structs)
 		{
-			if(technique.m_localUniformsStructIdx != instance.m_index)
+			if(prog.m_localUniformsStructIdx != instance.m_index)
 			{
 				continue;
 			}
 
 			// Update the size
-			if(m_localUniformsSize != 0 && instance.m_size != 0 && m_localUniformsSize != instance.m_size)
-			{
-				ANKI_RESOURCE_LOGE("The size of the AnKiLocalUniforms doesn't match with the rest of the techniques");
-				return Error::USER_DATA;
-			}
-
-			if(m_localUniformsSize == 0)
-			{
-				m_localUniformsSize = instance.m_size;
-			}
+			m_localUniformsSize = max(instance.m_size, m_localUniformsSize);
 
 			// Update the variables
 			for(const ShaderProgramBinaryStructMemberInstance& mInstance : instance.m_memberInstances)
@@ -393,6 +360,7 @@ Error MaterialResource::createVars(Technique& technique)
 	ANKI_LOOP_END()
 	ANKI_LOOP_END()
 	ANKI_LOOP_END()
+	ANKI_LOOP_END()
 
 #undef ANKI_LOOP
 #undef ANKI_LOOP_END
@@ -400,7 +368,7 @@ Error MaterialResource::createVars(Technique& technique)
 	return Error::NONE;
 }
 
-Error MaterialResource::parseMutators(XmlElement mutatorsEl, Technique& technique)
+Error MaterialResource::parseMutators(XmlElement mutatorsEl, Program& prog)
 {
 	XmlElement mutatorEl;
 	ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
@@ -409,12 +377,12 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl, Technique& techniqu
 	ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
 	++mutatorCount;
 	ANKI_ASSERT(mutatorCount > 0);
-	technique.m_partialMutation.create(getAllocator(), mutatorCount);
+	prog.m_partialMutation.create(getAllocator(), mutatorCount);
 	mutatorCount = 0;
 
 	do
 	{
-		PartialMutation& pmutation = technique.m_partialMutation[mutatorCount];
+		PartialMutation& pmutation = prog.m_partialMutation[mutatorCount];
 
 		// name
 		CString mutatorName;
@@ -449,7 +417,7 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl, Technique& techniqu
 		ANKI_CHECK(mutatorEl.getAttributeNumber("value", pmutation.m_value));
 
 		// Find mutator
-		pmutation.m_mutator = technique.m_prog->tryFindMutator(mutatorName);
+		pmutation.m_mutator = prog.m_prog->tryFindMutator(mutatorName);
 
 		if(!pmutation.m_mutator)
 		{
@@ -468,18 +436,56 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl, Technique& techniqu
 		ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
 	} while(mutatorEl);
 
-	ANKI_ASSERT(mutatorCount == technique.m_partialMutation.getSize());
+	ANKI_ASSERT(mutatorCount == prog.m_partialMutation.getSize());
 
 	return Error::NONE;
 }
 
-Error MaterialResource::findBuiltinMutators(Technique& technique)
+Error MaterialResource::findBuiltinMutators(Program& prog)
 {
 	U builtinMutatorCount = 0;
 
+	// ANKI_TECHNIQUE
+	CString techniqueMutatorName = BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::TECHNIQUE];
+	const ShaderProgramResourceMutator* techniqueMutator = prog.m_prog->tryFindMutator(techniqueMutatorName);
+	if(techniqueMutator)
+	{
+		for(U32 i = 0; i < techniqueMutator->m_values.getSize(); ++i)
+		{
+			const MutatorValue mvalue = techniqueMutator->m_values[i];
+			if(mvalue >= MutatorValue(RenderingTechnique::COUNT) || mvalue < MutatorValue(RenderingTechnique::FIRST))
+			{
+				ANKI_RESOURCE_LOGE("Mutator %s has a wrong value %d", techniqueMutatorName.cstr(), mvalue);
+				return Error::USER_DATA;
+			}
+
+			const RenderingTechnique techniqueId = RenderingTechnique(mvalue);
+			const PtrSize progIdx = &prog - m_programs.getBegin();
+			ANKI_ASSERT(progIdx < m_programs.getSize());
+			m_techniqueToProgram[techniqueId] = U8(progIdx);
+
+			const RenderingTechniqueBit mask = RenderingTechniqueBit(1 << techniqueId);
+			if(!!(m_techniquesMask & mask))
+			{
+				ANKI_RESOURCE_LOGE("The %s technique appeared more than once", TECHNIQUE_NAMES[mvalue].cstr());
+				return Error::USER_DATA;
+			}
+			m_techniquesMask |= mask;
+		}
+
+		++builtinMutatorCount;
+		prog.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::TECHNIQUE);
+	}
+	else
+	{
+		ANKI_RESOURCE_LOGE("Mutator %s should be present in every shader program referenced by a material",
+						   techniqueMutatorName.cstr());
+		return Error::USER_DATA;
+	}
+
 	// ANKI_LOD
 	CString lodMutatorName = BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::LOD];
-	const ShaderProgramResourceMutator* lodMutator = technique.m_prog->tryFindMutator(lodMutatorName);
+	const ShaderProgramResourceMutator* lodMutator = prog.m_prog->tryFindMutator(lodMutatorName);
 	if(lodMutator)
 	{
 		if(lodMutator->m_values.getSize() > MAX_LOD_COUNT)
@@ -499,14 +505,14 @@ Error MaterialResource::findBuiltinMutators(Technique& technique)
 			}
 		}
 
-		technique.m_lodCount = U8(lodMutator->m_values.getSize());
+		prog.m_lodCount = U8(lodMutator->m_values.getSize());
 		++builtinMutatorCount;
-		technique.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::LOD);
+		prog.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::LOD);
 	}
 
 	// ANKI_BONES
 	CString bonesMutatorName = BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::BONES];
-	const ShaderProgramResourceMutator* bonesMutator = technique.m_prog->tryFindMutator(bonesMutatorName);
+	const ShaderProgramResourceMutator* bonesMutator = prog.m_prog->tryFindMutator(bonesMutatorName);
 	if(bonesMutator)
 	{
 		if(bonesMutator->m_values.getSize() != 2)
@@ -528,7 +534,7 @@ Error MaterialResource::findBuiltinMutators(Technique& technique)
 		++builtinMutatorCount;
 
 		// Find if the bindings are present
-		ConstWeakArray<ShaderProgramBinaryBlock> storageBlocks = technique.m_prog->getBinary().m_storageBlocks;
+		ConstWeakArray<ShaderProgramBinaryBlock> storageBlocks = prog.m_prog->getBinary().m_storageBlocks;
 		U foundCount = 0;
 		for(U32 i = 0; i < storageBlocks.getSize(); ++i)
 		{
@@ -549,12 +555,12 @@ Error MaterialResource::findBuiltinMutators(Technique& technique)
 		}
 
 		m_supportsSkinning = true;
-		technique.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::BONES);
+		prog.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::BONES);
 	}
 
 	// VELOCITY
 	CString velocityMutatorName = BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::VELOCITY];
-	const ShaderProgramResourceMutator* velocityMutator = technique.m_prog->tryFindMutator(velocityMutatorName);
+	const ShaderProgramResourceMutator* velocityMutator = prog.m_prog->tryFindMutator(velocityMutatorName);
 	if(velocityMutator)
 	{
 		if(velocityMutator->m_values.getSize() != 2)
@@ -574,10 +580,10 @@ Error MaterialResource::findBuiltinMutators(Technique& technique)
 		}
 
 		++builtinMutatorCount;
-		technique.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::VELOCITY);
+		prog.m_presentBuildinMutators |= U32(1 << BuiltinMutatorId::VELOCITY);
 	}
 
-	if(technique.m_partialMutation.getSize() + builtinMutatorCount != technique.m_prog->getMutators().getSize())
+	if(prog.m_partialMutation.getSize() + builtinMutatorCount != prog.m_prog->getMutators().getSize())
 	{
 		ANKI_RESOURCE_LOGE("Some mutatators are unacounted for");
 		return Error::USER_DATA;
@@ -622,7 +628,7 @@ Error MaterialResource::parseInput(XmlElement inputEl, Bool async, BitSet<128>&
 	{
 		switch(foundVar->m_dataType)
 		{
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) \
 	case ShaderVariableDataType::capital: \
 		ANKI_CHECK(GetAttribute<type>()(inputEl, foundVar->ANKI_CONCATENATE(m_, type))); \
 		break;
@@ -656,7 +662,7 @@ void MaterialResource::prefillLocalUniforms()
 
 		switch(var.m_dataType)
 		{
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) \
 	case ShaderVariableDataType::capital: \
 		ANKI_ASSERT(var.m_offsetInLocalUniforms + sizeof(type) <= m_localUniformsSize); \
 		memcpy(static_cast<U8*>(m_prefilledLocalUniforms) + var.m_offsetInLocalUniforms, &var.m_##type, sizeof(type)); \
@@ -673,19 +679,20 @@ void MaterialResource::prefillLocalUniforms()
 const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey& key_) const
 {
 	RenderingKey key = key_;
-	const Technique& technique = m_techniques[key.getRenderingTechnique()];
-	ANKI_ASSERT(technique.m_prog.isCreated());
+	ANKI_ASSERT(m_techniqueToProgram[key.getRenderingTechnique()] != MAX_U8);
+	const Program& prog = m_programs[m_techniqueToProgram[key.getRenderingTechnique()]];
 
-	key.setLod(min<U32>(technique.m_lodCount - 1, key.getLod()));
+	key.setLod(min<U32>(prog.m_lodCount - 1, key.getLod()));
 
-	ANKI_ASSERT(!key.getSkinned() || !!(technique.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::BONES)));
-	ANKI_ASSERT(!key.getVelocity() || !!(technique.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::VELOCITY)));
+	ANKI_ASSERT(!key.getSkinned() || !!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::BONES)));
+	ANKI_ASSERT(!key.getVelocity() || !!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::VELOCITY)));
 
-	MaterialVariant& variant = technique.m_variantMatrix[key.getLod()][key.getSkinned()][key.getVelocity()];
+	MaterialVariant& variant =
+		prog.m_variantMatrix[key.getRenderingTechnique()][key.getLod()][key.getSkinned()][key.getVelocity()];
 
 	// Check if it's initialized
 	{
-		RLockGuard<RWMutex> lock(technique.m_variantMatrixMtx);
+		RLockGuard<RWMutex> lock(prog.m_variantMatrixMtx);
 		if(ANKI_LIKELY(variant.m_prog.isCreated()))
 		{
 			return variant;
@@ -693,7 +700,7 @@ const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey&
 	}
 
 	// Not initialized, init it
-	WLockGuard<RWMutex> lock(technique.m_variantMatrixMtx);
+	WLockGuard<RWMutex> lock(prog.m_variantMatrixMtx);
 
 	// Check again
 	if(variant.m_prog.isCreated())
@@ -701,30 +708,32 @@ const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey&
 		return variant;
 	}
 
-	ShaderProgramResourceVariantInitInfo initInfo(technique.m_prog);
+	ShaderProgramResourceVariantInitInfo initInfo(prog.m_prog);
 
-	for(const PartialMutation& m : technique.m_partialMutation)
+	for(const PartialMutation& m : prog.m_partialMutation)
 	{
 		initInfo.addMutation(m.m_mutator->m_name, m.m_value);
 	}
 
-	if(!!(technique.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::LOD)))
+	initInfo.addMutation(BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::TECHNIQUE], MutatorValue(key.getRenderingTechnique()));
+
+	if(!!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::LOD)))
 	{
 		initInfo.addMutation(BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::LOD], MutatorValue(key.getLod()));
 	}
 
-	if(!!(technique.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::BONES)))
+	if(!!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::BONES)))
 	{
 		initInfo.addMutation(BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::BONES], MutatorValue(key.getSkinned()));
 	}
 
-	if(!!(technique.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::VELOCITY)))
+	if(!!(prog.m_presentBuildinMutators & U32(1 << BuiltinMutatorId::VELOCITY)))
 	{
 		initInfo.addMutation(BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::VELOCITY], MutatorValue(key.getVelocity()));
 	}
 
 	const ShaderProgramResourceVariant* progVariant;
-	technique.m_prog->getOrCreateVariant(initInfo, progVariant);
+	prog.m_prog->getOrCreateVariant(initInfo, progVariant);
 
 	variant.m_prog = progVariant->getProgram();
 

+ 70 - 18
AnKi/Resource/MaterialResource.h

@@ -25,6 +25,7 @@ class XmlElement;
 enum class BuiltinMutatorId : U8
 {
 	NONE = 0,
+	TECHNIQUE,
 	LOD,
 	BONES,
 	VELOCITY,
@@ -113,7 +114,7 @@ protected:
 	/// @{
 	union
 	{
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) type ANKI_CONCATENATE(m_, type);
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) type ANKI_CONCATENATE(m_, type);
 #include <AnKi/Gr/ShaderVariableDataType.defs.h>
 #undef ANKI_SVDT_MACRO
 	};
@@ -131,7 +132,7 @@ protected:
 		return var_; \
 	}
 
-#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type, baseType, rowCount, columnCount, isIntagralType) \
 	ANKI_SPECIALIZE_GET_VALUE(type, ANKI_CONCATENATE(m_, type), capital)
 #include <AnKi/Gr/ShaderVariableDataType.defs.h>
 #undef ANKI_SVDT_MACRO
@@ -171,6 +172,18 @@ public:
 private:
 	ShaderProgramPtr m_prog;
 	U32 m_rtShaderGroupHandleIndex = MAX_U32;
+
+	MaterialVariant(MaterialVariant&& b)
+	{
+		*this = std::move(b);
+	}
+
+	MaterialVariant& operator=(MaterialVariant&& b)
+	{
+		m_prog = std::move(b.m_prog);
+		m_rtShaderGroupHandleIndex = b.m_rtShaderGroupHandleIndex;
+		return *this;
+	}
 };
 
 /// Material resource.
@@ -178,15 +191,17 @@ private:
 /// Material XML file format:
 /// @code
 ///	<material [shadows="0|1"]>
-///		<shaderProgram filename="filename">
-///			[<mutation>
-///				<mutator name="str" value="value"/>
-///			</mutation>]
-///		</shaderProgram>
+/// 	<shaderPrograms>
+///			<shaderProgram filename="filename">
+///				[<mutation>
+///					<mutator name="str" value="value"/>
+///				</mutation>]
+///			</shaderProgram>
 ///
-///		[<shaderProgram ...>
-///			...
-///		</shaderProgram>]
+///			[<shaderProgram ...>
+///				...
+///			</shaderProgram>]
+/// 	</shaderPrograms>
 ///
 ///		[<inputs>
 ///			<input name="name in AnKiMaterialUniforms struct or opaque type" value="value(s)"/> (1)
@@ -249,22 +264,59 @@ private:
 		MutatorValue m_value;
 	};
 
-	class Technique
+	class Program
 	{
 	public:
 		ShaderProgramResourcePtr m_prog;
-		U8 m_lodCount = 1;
 
-		mutable Array3d<MaterialVariant, MAX_LOD_COUNT, 2, 2> m_variantMatrix; ///< Matrix of variants.
+		mutable Array4d<MaterialVariant, U(RenderingTechnique::COUNT), MAX_LOD_COUNT, 2, 2> m_variantMatrix;
 		mutable RWMutex m_variantMatrixMtx;
 
 		DynamicArray<PartialMutation> m_partialMutation; ///< Only with the non-builtins.
 
 		U32 m_presentBuildinMutators = 0;
 		U32 m_localUniformsStructIdx = 0; ///< Struct index in the program binary.
+
+		U8 m_lodCount = 1;
+
+		Program() = default;
+
+		Program(const Program&) = delete; // Non-copyable
+
+		Program(Program&& b)
+		{
+			*this = std::move(b);
+		}
+
+		Program& operator=(const Program& b) = delete; // Non-copyable
+
+		Program& operator=(Program&& b)
+		{
+			m_prog = std::move(b.m_prog);
+			for(RenderingTechnique t : EnumIterable<RenderingTechnique>())
+			{
+				for(U32 l = 0; l < MAX_LOD_COUNT; ++l)
+				{
+					for(U32 skin = 0; skin < 2; ++skin)
+					{
+						for(U32 vel = 0; vel < 2; ++vel)
+						{
+							m_variantMatrix[t][skin][skin][vel] = std::move(b.m_variantMatrix[t][skin][skin][vel]);
+						}
+					}
+				}
+			}
+			m_partialMutation = std::move(b.m_partialMutation);
+			m_presentBuildinMutators = b.m_presentBuildinMutators;
+			m_localUniformsStructIdx = b.m_localUniformsStructIdx;
+			m_lodCount = b.m_lodCount;
+			return *this;
+		}
 	};
 
-	Array<Technique, U(RenderingTechnique::COUNT)> m_techniques;
+	DynamicArray<Program> m_programs;
+
+	Array<U8, U(RenderingTechnique::COUNT)> m_techniqueToProgram;
 	RenderingTechniqueBit m_techniquesMask = RenderingTechniqueBit::NONE;
 
 	DynamicArray<MaterialVariable> m_vars;
@@ -276,11 +328,11 @@ private:
 	void* m_prefilledLocalUniforms = nullptr;
 	U32 m_localUniformsSize = 0;
 
-	ANKI_USE_RESULT Error parseMutators(XmlElement mutatorsEl, Technique& technique);
-	ANKI_USE_RESULT Error parseTechnique(XmlElement techniqueEl, Bool async);
+	ANKI_USE_RESULT Error parseMutators(XmlElement mutatorsEl, Program& prog);
+	ANKI_USE_RESULT Error parseShaderProgram(XmlElement techniqueEl, Bool async);
 	ANKI_USE_RESULT Error parseInput(XmlElement inputEl, Bool async, BitSet<128>& varsSet);
-	ANKI_USE_RESULT Error findBuiltinMutators(Technique& technique);
-	ANKI_USE_RESULT Error createVars(Technique& technique);
+	ANKI_USE_RESULT Error findBuiltinMutators(Program& prog);
+	ANKI_USE_RESULT Error createVars(Program& prog);
 	void prefillLocalUniforms();
 
 	const MaterialVariable* tryFindVariableInternal(CString name) const;

+ 2 - 2
AnKi/Scene/Components/RenderComponent.cpp

@@ -59,12 +59,12 @@ void RenderComponent::allocateAndSetupUniforms(const MaterialResourcePtr& mtl, c
 	StagingGpuMemoryToken token;
 	U8* const localUniformsBegin =
 		(localUniformsUboSize != 0)
-			? static_cast<U8*>(alloc.allocateFrame(localUniformsUboSize, StagingGpuMemoryType::UNIFORM, token))
+			? static_cast<U8*>(alloc.allocateFrame(localUniformsUboSize, StagingGpuMemoryType::STORAGE, token))
 			: nullptr;
 
 	if(localUniformsUboSize)
 	{
-		cmdb->bindUniformBuffer(set, MATERIAL_BINDING_LOCAL_UNIFORMS, token.m_buffer, token.m_offset, token.m_range);
+		cmdb->bindStorageBuffer(set, MATERIAL_BINDING_LOCAL_UNIFORMS, token.m_buffer, token.m_offset, token.m_range);
 	}
 
 	// Iterate variables

+ 1 - 1
AnKi/ShaderCompiler/Glslang.cpp

@@ -21,7 +21,7 @@
 #	pragma GCC diagnostic pop
 #endif
 
-#define ANKI_GLSLANG_DUMP 1
+#define ANKI_GLSLANG_DUMP 0
 
 namespace anki {
 

+ 187 - 0
AnKi/ShaderCompiler/ShaderProgramParser.cpp

@@ -810,32 +810,57 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 
 			if(*token == "mutator")
 			{
+				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaMutator(token + 1, end, line, fname));
 			}
 			else if(*token == "start")
 			{
+				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaStart(token + 1, end, line, fname));
 			}
 			else if(*token == "end")
 			{
+				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaEnd(token + 1, end, line, fname));
 			}
 			else if(*token == "rewrite_mutation")
 			{
+				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaRewriteMutation(token + 1, end, line, fname));
 			}
 			else if(*token == "library")
 			{
+				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaLibraryName(token + 1, end, line, fname));
 			}
 			else if(*token == "ray_type")
 			{
+				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaRayType(token + 1, end, line, fname));
 			}
 			else if(*token == "reflect")
 			{
+				ANKI_CHECK(checkNoActiveStruct());
 				ANKI_CHECK(parsePragmaReflect(token + 1, end, line, fname));
 			}
+			else if(*token == "struct")
+			{
+				if(*(token + 1) == "end")
+				{
+					ANKI_CHECK(checkActiveStruct());
+					ANKI_CHECK(parsePragmaStructEnd(token + 1, end, line, fname));
+				}
+				else
+				{
+					ANKI_CHECK(checkNoActiveStruct());
+					ANKI_CHECK(parsePragmaStructBegin(token + 1, end, line, fname));
+				}
+			}
+			else if(*token == "member")
+			{
+				ANKI_CHECK(checkActiveStruct());
+				ANKI_CHECK(parsePragmaMember(token + 1, end, line, fname));
+			}
 			else
 			{
 				ANKI_PP_ERROR_MALFORMED();
@@ -860,6 +885,168 @@ Error ShaderProgramParser::parseLine(CString line, CString fname, Bool& foundPra
 	return Error::NONE;
 }
 
+Error ShaderProgramParser::parsePragmaStructBegin(const StringAuto* begin, const StringAuto* end, CString line,
+												  CString fname)
+{
+	const U tokenCount = U(end - begin);
+	if(tokenCount != 1)
+	{
+		ANKI_PP_ERROR_MALFORMED();
+	}
+
+	m_activeStruct.create(*begin);
+	m_codeLines.pushBackSprintf("struct %s {", begin->cstr());
+
+	return Error::NONE;
+}
+
+Error ShaderProgramParser::parsePragmaMember(const StringAuto* begin, const StringAuto* end, CString line,
+											 CString fname)
+{
+	const U tokenCount = U(end - begin);
+	if(tokenCount != 2 && tokenCount != 3)
+	{
+		ANKI_PP_ERROR_MALFORMED();
+	}
+
+	Bool relaxed = false;
+	if(*begin == "ANKI_RP")
+	{
+		if(tokenCount != 3)
+		{
+			ANKI_PP_ERROR_MALFORMED();
+		}
+
+		relaxed = true;
+		++begin;
+	}
+
+	const CString typeStr = *begin;
+	ShaderVariableDataType type = ShaderVariableDataType::NONE;
+	if(typeStr == "F32")
+	{
+		type = ShaderVariableDataType::F32;
+	}
+	else if(typeStr == "Vec2")
+	{
+		type = ShaderVariableDataType::VEC2;
+	}
+	else if(typeStr == "Vec3")
+	{
+		type = ShaderVariableDataType::VEC3;
+	}
+	else if(typeStr == "Vec4")
+	{
+		type = ShaderVariableDataType::VEC4;
+	}
+
+	if(type == ShaderVariableDataType::NONE)
+	{
+		ANKI_PP_ERROR_MALFORMED_MSG("Unrecognized type");
+	}
+
+	const CString memberName = begin[1];
+
+	// Code
+	m_codeLines.pushBackSprintf("#define %s_%s_DEFINED 1", m_activeStruct.cstr(), memberName.cstr());
+	m_codeLines.pushBackSprintf("\t%s %s %s;", (relaxed) ? "ANKI_RP" : "", typeStr.cstr(), memberName.cstr());
+
+	// Add it
+	Member& m = *m_members.emplaceBack(m_alloc);
+	m.m_name.create(memberName);
+	m.m_type = type;
+
+	return Error::NONE;
+}
+
+Error ShaderProgramParser::parsePragmaStructEnd(const StringAuto* begin, const StringAuto* end, CString line,
+												CString fname)
+{
+	ANKI_ASSERT(!m_activeStruct.isEmpty());
+
+	const U tokenCount = U(end - begin);
+	if(tokenCount != 1)
+	{
+		ANKI_PP_ERROR_MALFORMED();
+	}
+
+	if(m_members.isEmpty())
+	{
+		ANKI_PP_ERROR_MALFORMED_MSG("The struct doesn't have any members");
+	}
+
+	m_codeLines.pushBack("};");
+
+	for(U32 i = 0; i < m_members.getSize(); ++i)
+	{
+		const Member& m = m_members[i];
+
+		// #define XXX_OFFSETOF
+		if(i == 0)
+		{
+			m_codeLines.pushBackSprintf("#define %s_%s_OFFSETOF 0u", m_activeStruct.cstr(), m.m_name.cstr());
+		}
+		else
+		{
+			const Member& prev = m_members[i - 1];
+			m_codeLines.pushBackSprintf("#define %s_%s_OFFSETOF (%s_%s_OFFSETOF + %s_%s_SIZEOF)", m_activeStruct.cstr(),
+										m.m_name.cstr(), m_activeStruct.cstr(), prev.m_name.cstr(),
+										m_activeStruct.cstr(), prev.m_name.cstr());
+		}
+
+		// #if XXX_DEFINED
+		m_codeLines.pushBackSprintf("#if defined(%s_%s_DEFINED)", m_activeStruct.cstr(), m.m_name.cstr());
+
+		// #	define XXX_SIZEOF
+		m_codeLines.pushBackSprintf("#\tdefine %s_%s_SIZEOF %uu", m_activeStruct.cstr(), m.m_name.cstr(),
+									getShaderVariableDataTypeInfo(m.m_type).m_size / 4);
+
+		// #	define XXX_LOAD()
+		const Bool isIntegral = getShaderVariableDataTypeInfo(m.m_type).m_isIntegral;
+		const U32 componentCount = getShaderVariableDataTypeInfo(m.m_type).m_size / sizeof(U32);
+		StringAuto values(m_alloc);
+		for(U32 j = 0; j < componentCount; ++j)
+		{
+			StringAuto tmp(m_alloc);
+			tmp.sprintf("%s(ssbo[%s_%s_OFFSETOF + offset + %uu])%s", (isIntegral) ? "" : "uintBitsToFloat",
+						m_activeStruct.cstr(), m.m_name.cstr(), j, (j != componentCount - 1) ? "," : "");
+
+			values.append(tmp);
+		}
+
+		m_codeLines.pushBackSprintf("#\tdefine %s_%s_LOAD(ssbo, offset) %s(%s)%s", m_activeStruct.cstr(),
+									m.m_name.cstr(), getShaderVariableDataTypeInfo(m.m_type).m_name, values.cstr(),
+									(i != m_members.getSize() - 1) ? "," : "");
+
+		// #else
+		m_codeLines.pushBack("#else");
+
+		// #	define XXX_SIZEOF 0
+		m_codeLines.pushBackSprintf("#\tdefine %s_%s_SIZEOF 0u", m_activeStruct.cstr(), m.m_name.cstr());
+
+		// #	define XXX_LOAD()
+		m_codeLines.pushBackSprintf("#\tdefine %s_%s_LOAD(ssbo, offset)", m_activeStruct.cstr(), m.m_name.cstr());
+
+		// #endif
+		m_codeLines.pushBack("#endif");
+	}
+
+	// Now define the structure LOAD
+	m_codeLines.pushBackSprintf("#define load%s(ssbo, offset) %s( \\", m_activeStruct.cstr(), m_activeStruct.cstr());
+	for(U32 i = 0; i < m_members.getSize(); ++i)
+	{
+		const Member& m = m_members[i];
+		m_codeLines.pushBackSprintf("\t%s_%s_LOAD(ssbo, offset) \\", m_activeStruct.cstr(), m.m_name.cstr());
+	}
+	m_codeLines.pushBack(")");
+
+	// Cleanup
+	m_activeStruct.destroy();
+	m_members.destroy();
+
+	return Error::NONE;
+}
+
 Error ShaderProgramParser::parseFile(CString fname, U32 depth)
 {
 	// First check the depth

+ 47 - 1
AnKi/ShaderCompiler/ShaderProgramParser.h

@@ -86,7 +86,12 @@ private:
 /// #pragma anki ray_type NUMBER
 /// #pragma anki reflect NAME
 ///
-/// Only the "anki input" should be in an ifdef-like guard. For everything else it's ignored.
+/// #pragma anki struct NAME
+/// #	pragma anki member [ANKI_RP] TYPE NAME [if MUTATOR_NAME is MUTATOR_VALUE]
+/// 	...
+/// #pragma anki struct end
+///
+/// None of the pragmas should be in an ifdef-like guard. It's ignored.
 class ShaderProgramParser
 {
 public:
@@ -173,6 +178,18 @@ private:
 		}
 	};
 
+	class Member
+	{
+	public:
+		StringAuto m_name;
+		ShaderVariableDataType m_type;
+
+		Member(GenericMemoryPoolAllocator<U8> alloc)
+			: m_name(alloc)
+		{
+		}
+	};
+
 	static const U32 MAX_INCLUDE_DEPTH = 8;
 
 	GenericMemoryPoolAllocator<U8> m_alloc;
@@ -195,6 +212,9 @@ private:
 
 	StringListAuto m_symbolsToReflect = {m_alloc};
 
+	StringAuto m_activeStruct = {m_alloc};
+	DynamicArrayAuto<Member> m_members = {m_alloc};
+
 	ANKI_USE_RESULT Error parseFile(CString fname, U32 depth);
 	ANKI_USE_RESULT Error parseLine(CString line, CString fname, Bool& foundPragmaOnce, U32 depth);
 	ANKI_USE_RESULT Error parseInclude(const StringAuto* begin, const StringAuto* end, CString line, CString fname,
@@ -211,6 +231,12 @@ private:
 											 CString fname);
 	ANKI_USE_RESULT Error parsePragmaReflect(const StringAuto* begin, const StringAuto* end, CString line,
 											 CString fname);
+	ANKI_USE_RESULT Error parsePragmaStructBegin(const StringAuto* begin, const StringAuto* end, CString line,
+												 CString fname);
+	ANKI_USE_RESULT Error parsePragmaStructEnd(const StringAuto* begin, const StringAuto* end, CString line,
+											   CString fname);
+	ANKI_USE_RESULT Error parsePragmaMember(const StringAuto* begin, const StringAuto* end, CString line,
+											CString fname);
 
 	void tokenizeLine(CString line, DynamicArrayAuto<StringAuto>& tokens) const;
 
@@ -220,6 +246,26 @@ private:
 	}
 
 	static Bool mutatorHasValue(const ShaderProgramParserMutator& mutator, MutatorValue value);
+
+	ANKI_USE_RESULT Error checkNoActiveStruct() const
+	{
+		if(!m_activeStruct.isEmpty())
+		{
+			ANKI_SHADER_COMPILER_LOGE("Unsupported \"pragma anki\" inside \"pragma anki struct\"");
+			return Error::USER_DATA;
+		}
+		return Error::NONE;
+	}
+
+	ANKI_USE_RESULT Error checkActiveStruct() const
+	{
+		if(m_activeStruct.isEmpty())
+		{
+			ANKI_SHADER_COMPILER_LOGE("Expected a \"pragma anki struct\" to open");
+			return Error::USER_DATA;
+		}
+		return Error::NONE;
+	}
 };
 /// @}
 

+ 2 - 2
AnKi/ShaderCompiler/ShaderProgramReflection.cpp

@@ -293,7 +293,7 @@ Error SpirvReflector::structReflection(uint32_t id, const spirv_cross::SPIRType&
 			if(0)
 			{
 			}
-#define ANKI_SVDT_MACRO(capital, type, baseType_, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type, baseType_, rowCount, columnCount, isIntagralType) \
 	else if(ShaderVariableDataType::baseType_ == baseType && isMatrix && memberType.vecsize == rowCount \
 			&& memberType.columns == columnCount) \
 	{ \
@@ -496,7 +496,7 @@ Error SpirvReflector::blockVariableReflection(const spirv_cross::SPIRType& type,
 			if(0)
 			{
 			}
-#define ANKI_SVDT_MACRO(capital, type_, baseType_, rowCount, columnCount) \
+#define ANKI_SVDT_MACRO(capital, type_, baseType_, rowCount, columnCount, isIntagralType) \
 	else if(ShaderVariableDataType::baseType_ == baseType && isMatrix && memberType.vecsize == rowCount \
 			&& memberType.columns == columnCount) \
 	{ \

+ 46 - 44
AnKi/Shaders/ForwardShadingParticles.ankiprog

@@ -3,32 +3,20 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
+#pragma anki mutator ANKI_TECHNIQUE 3
 #pragma anki mutator ANIMATED_TEXTURE 0 1
 #pragma anki mutator LIGHT 0 1
 
 #include <AnKi/Shaders/ForwardShadingCommon.glsl>
 
-#pragma anki reflect AnKiLocalUniforms
-struct AnKiLocalUniforms
-{
-#if ANIMATED_TEXTURE == 1
-	F32 m_animationPeriod;
-#endif
-	ANKI_RP Vec4 m_colorScale;
-	ANKI_RP Vec4 m_colorBias;
-};
+#pragma anki start vert
 
-layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_RENDERABLE_GPU_VIEW, row_major,
-	   scalar) uniform b_renderableGpuViews
-{
-	RenderableGpuView u_renderableGpuViews[1];
-};
+layout(location = VERTEX_ATTRIBUTE_ID_SCALE) in F32 in_scale;
+layout(location = VERTEX_ATTRIBUTE_ID_ALPHA) in F32 in_alpha;
 
-layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_LOCAL_UNIFORMS, row_major,
-	   scalar) uniform b_localUniforms
-{
-	AnKiLocalUniforms u_localUniforms;
-};
+layout(location = 0) flat out ANKI_RP F32 out_alpha;
+layout(location = 1) out Vec2 out_uv;
+layout(location = 2) out Vec3 out_worldPos;
 
 layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_GLOBAL_UNIFORMS, row_major,
 	   scalar) uniform b_ankiGlobalUniforms
@@ -36,26 +24,11 @@ layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_GLOBAL_UNIFORMS,
 	MaterialGlobalUniforms u_ankiGlobals;
 };
 
-layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_GLOBAL_SAMPLER) uniform sampler u_ankiGlobalSampler;
-#if ANIMATED_TEXTURE == 0
-#	pragma anki reflect u_diffuseMap
-layout(set = MATERIAL_SET_EXTERNAL,
-	   binding = MATERIAL_BINDING_FIRST_NON_STANDARD) uniform ANKI_RP texture2D u_diffuseMap;
-#endif
-#if ANIMATED_TEXTURE == 1
-#	pragma anki reflect u_diffuseMapArr
-layout(set = MATERIAL_SET_EXTERNAL,
-	   binding = MATERIAL_BINDING_FIRST_NON_STANDARD) uniform ANKI_RP texture2DArray u_diffuseMapArr;
-#endif
-
-#pragma anki start vert
-
-layout(location = VERTEX_ATTRIBUTE_ID_SCALE) in F32 in_scale;
-layout(location = VERTEX_ATTRIBUTE_ID_ALPHA) in F32 in_alpha;
-
-layout(location = 0) flat out ANKI_RP F32 out_alpha;
-layout(location = 1) out Vec2 out_uv;
-layout(location = 2) out Vec3 out_worldPos;
+layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_RENDERABLE_GPU_VIEW, row_major,
+	   scalar) uniform b_renderableGpuViews
+{
+	RenderableGpuView u_renderableGpuViews[1];
+};
 
 void main()
 {
@@ -75,21 +48,50 @@ layout(location = 0) flat in ANKI_RP F32 in_alpha;
 layout(location = 1) in Vec2 in_uv;
 layout(location = 2) in Vec3 in_worldPos;
 
+#pragma anki reflect AnKiLocalUniforms
+#pragma anki struct AnKiLocalUniforms
+#if ANIMATED_TEXTURE == 1
+#	pragma anki member F32 m_animationPeriod
+#endif
+#pragma anki member ANKI_RP Vec4 m_colorScale
+#pragma anki member ANKI_RP Vec4 m_colorBias
+#pragma anki struct end
+
+layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_LOCAL_UNIFORMS, row_major, scalar) buffer b_localUniforms
+{
+	U32 u_localUniforms[];
+};
+ANKI_FAKE_LOCAL_UNIFORMS_BUFFER()
+
+layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_GLOBAL_SAMPLER) uniform sampler u_globalSampler;
+#if ANIMATED_TEXTURE == 0
+#	pragma anki reflect u_diffuseMap
+layout(set = MATERIAL_SET_EXTERNAL,
+	   binding = MATERIAL_BINDING_FIRST_NON_STANDARD) uniform ANKI_RP texture2D u_diffuseMap;
+#endif
+#if ANIMATED_TEXTURE == 1
+#	pragma anki reflect u_diffuseMapArr
+layout(set = MATERIAL_SET_EXTERNAL,
+	   binding = MATERIAL_BINDING_FIRST_NON_STANDARD) uniform ANKI_RP texture2DArray u_diffuseMapArr;
+#endif
+
 void main()
 {
+	const AnKiLocalUniforms localUniforms = loadAnKiLocalUniforms(u_localUniforms, 0u);
+
 #if ANIMATED_TEXTURE == 1
-	ANKI_RP Vec4 texCol = readAnimatedTextureRgba(u_diffuseMapArr, u_ankiGlobalSampler,
-												  u_localUniforms.m_animationPeriod, in_uv, u_clusteredShading.m_time);
+	ANKI_RP Vec4 texCol = readAnimatedTextureRgba(u_diffuseMapArr, u_globalSampler, localUniforms.m_animationPeriod,
+												  in_uv, u_clusteredShading.m_time);
 #else
-	ANKI_RP Vec4 texCol = texture(u_diffuseMap, u_ankiGlobalSampler, in_uv);
+	ANKI_RP Vec4 texCol = texture(u_diffuseMap, u_globalSampler, in_uv);
 #endif
 
 #if LIGHT
 	texCol.rgb = computeLightColorLow(texCol.rgb, in_worldPos);
 #endif
 
-	ANKI_RP Vec4 colScale = u_localUniforms.m_colorScale;
+	ANKI_RP Vec4 colScale = localUniforms.m_colorScale;
 	colScale.a *= in_alpha;
-	particleAlpha(texCol, colScale, u_localUniforms.m_colorBias);
+	particleAlpha(texCol, colScale, localUniforms.m_colorBias);
 }
 #pragma anki end

+ 24 - 18
AnKi/Shaders/GBufferGeneric.ankiprog

@@ -86,34 +86,38 @@ layout(set = MATERIAL_SET_EXTERNAL,
 #endif
 
 #pragma anki reflect AnKiLocalUniforms
-struct AnKiLocalUniforms
-{
+#pragma anki struct AnKiLocalUniforms
 #if !DIFFUSE_TEX
-	Vec3 m_diffColor;
+#	pragma anki member Vec3 m_diffColor
 #endif
 #if !ROUGHNESS_TEX
-	F32 m_roughness;
+#	pragma anki member F32 m_roughness
 #endif
 #if !SPECULAR_TEX
-	Vec3 m_specColor;
+#	pragma anki member Vec3 m_specColor
 #endif
 #if !METAL_TEX
-	F32 m_metallic;
+#	pragma anki member F32 m_metallic
 #endif
 #if !EMISSIVE_TEX
-	Vec3 m_emission;
+#	pragma anki member Vec3 m_emission
 #endif
 #if PARALLAX
-	F32 m_heightmapScale;
+#	pragma anki member F32 m_heightmapScale
 #endif
+#pragma anki member F32 m_subsurface
+#pragma anki struct end
 
-	F32 m_subsurface;
+layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_LOCAL_UNIFORMS, row_major, scalar) buffer b_localUniforms
+{
+	U32 u_localUniforms[];
 };
 
+// A trick to avoid spirv-opt optimizing away the AnKiLocalUniforms
 layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_LOCAL_UNIFORMS, row_major,
-	   scalar) uniform b_localUniforms
+	   scalar) buffer b_localUniformsFake
 {
-	AnKiLocalUniforms u_localUniforms;
+	AnKiLocalUniforms u_localUniformsFake[];
 };
 
 layout(set = MATERIAL_SET_EXTERNAL, binding = MATERIAL_BINDING_RENDERABLE_GPU_VIEW, row_major,
@@ -362,9 +366,11 @@ void doAlphaText(F32 alpha)
 
 void main()
 {
+	const AnKiLocalUniforms localUniforms = loadAnKiLocalUniforms(u_localUniforms, 0u);
+
 #if ANKI_TECHNIQUE == RENDERING_TECHNIQUE_GBUFFER
 #	if REALLY_USING_PARALLAX
-	const Vec2 uv = computeTextureCoordParallax(u_heightTex, u_globalSampler, in_uv, u_localUniforms.m_heightmapScale);
+	const Vec2 uv = computeTextureCoordParallax(u_heightTex, u_globalSampler, in_uv, localUniforms.m_heightmapScale);
 #	else
 	const Vec2 uv = in_uv;
 #	endif
@@ -378,25 +384,25 @@ void main()
 	const ANKI_RP Vec3 diffColor = texture(u_diffTex, u_globalSampler, uv).rgb;
 #		endif
 #	else
-	const ANKI_RP Vec3 diffColor = u_localUniforms.m_diffColor;
+	const ANKI_RP Vec3 diffColor = localUniforms.m_diffColor;
 #	endif
 
 #	if SPECULAR_TEX
 	const ANKI_RP Vec3 specColor = texture(u_specTex, u_globalSampler, uv).rgb;
 #	else
-	const ANKI_RP Vec3 specColor = u_localUniforms.m_specColor;
+	const ANKI_RP Vec3 specColor = localUniforms.m_specColor;
 #	endif
 
 #	if ROUGHNESS_TEX
 	const ANKI_RP F32 roughness = texture(u_roughnessTex, u_globalSampler, uv).g;
 #	else
-	const ANKI_RP F32 roughness = u_localUniforms.m_roughness;
+	const ANKI_RP F32 roughness = localUniforms.m_roughness;
 #	endif
 
 #	if METAL_TEX
 	const ANKI_RP F32 metallic = texture(u_metallicTex, u_globalSampler, uv).b;
 #	else
-	const ANKI_RP F32 metallic = u_localUniforms.m_metallic;
+	const ANKI_RP F32 metallic = localUniforms.m_metallic;
 #	endif
 
 #	if NORMAL_TEX
@@ -408,7 +414,7 @@ void main()
 #	if EMISSIVE_TEX
 	const ANKI_RP Vec3 emission = texture(u_emissiveTex, u_globalSampler, uv).rgb;
 #	else
-	const ANKI_RP Vec3 emission = u_localUniforms.m_emission;
+	const ANKI_RP Vec3 emission = localUniforms.m_emission;
 #	endif
 
 #	if ANKI_VELOCITY || ANKI_BONES
@@ -417,7 +423,7 @@ void main()
 	const Vec2 velocity = Vec2(1.0);
 #	endif
 
-	packGBuffer(diffColor, normal, specColor, roughness, u_localUniforms.m_subsurface, emission, metallic, velocity);
+	packGBuffer(diffColor, normal, specColor, roughness, localUniforms.m_subsurface, emission, metallic, velocity);
 #elif ANKI_TECHNIQUE == RENDERING_TECHNIQUE_GBUFFER_EZ
 	out_gbuffer0 = Vec4(0.0);
 	out_gbuffer1 = Vec4(0.0);

+ 8 - 38
Samples/SimpleScene/Assets/backWall_361f28d87a6738d3.ankimtl

@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="0"/>
 				<mutator name="SPECULAR_TEX" value="0"/>
 				<mutator name="ROUGHNESS_TEX" value="0"/>
@@ -14,44 +13,15 @@
 				<mutator name="EMISSIVE_TEX" value="0"/>
 				<mutator name="ALPHA_TEST" value="0"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+		</shaderProgram>
+		
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
 			</mutation>
-		</technique>
-
-		
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		</shaderProgram>
 
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>
 		

+ 8 - 38
Samples/SimpleScene/Assets/ceiling_3fd94cde277a48e1.ankimtl

@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="0"/>
 				<mutator name="SPECULAR_TEX" value="0"/>
 				<mutator name="ROUGHNESS_TEX" value="0"/>
@@ -14,44 +13,15 @@
 				<mutator name="EMISSIVE_TEX" value="0"/>
 				<mutator name="ALPHA_TEST" value="0"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+		</shaderProgram>
+		
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
 			</mutation>
-		</technique>
-
-		
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		</shaderProgram>
 
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>
 		

+ 8 - 38
Samples/SimpleScene/Assets/floor_71cbd2644e53ab8c.ankimtl

@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="0"/>
 				<mutator name="SPECULAR_TEX" value="0"/>
 				<mutator name="ROUGHNESS_TEX" value="0"/>
@@ -14,44 +13,15 @@
 				<mutator name="EMISSIVE_TEX" value="0"/>
 				<mutator name="ALPHA_TEST" value="0"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+		</shaderProgram>
+		
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
 			</mutation>
-		</technique>
-
-		
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		</shaderProgram>
 
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>
 		

+ 8 - 38
Samples/SimpleScene/Assets/leftWall_fe97b196ed148fca.ankimtl

@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="0"/>
 				<mutator name="SPECULAR_TEX" value="0"/>
 				<mutator name="ROUGHNESS_TEX" value="0"/>
@@ -14,44 +13,15 @@
 				<mutator name="EMISSIVE_TEX" value="0"/>
 				<mutator name="ALPHA_TEST" value="0"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+		</shaderProgram>
+		
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
 			</mutation>
-		</technique>
-
-		
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		</shaderProgram>
 
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>
 		

+ 8 - 38
Samples/SimpleScene/Assets/light_1544a10dffc35038.ankimtl

@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="0"/>
 				<mutator name="SPECULAR_TEX" value="0"/>
 				<mutator name="ROUGHNESS_TEX" value="0"/>
@@ -14,44 +13,15 @@
 				<mutator name="EMISSIVE_TEX" value="0"/>
 				<mutator name="ALPHA_TEST" value="0"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+		</shaderProgram>
+		
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
 			</mutation>
-		</technique>
-
-		
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		</shaderProgram>
 
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>
 		

+ 8 - 38
Samples/SimpleScene/Assets/rightWall_d627c19b8205864.ankimtl

@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="0"/>
 				<mutator name="SPECULAR_TEX" value="0"/>
 				<mutator name="ROUGHNESS_TEX" value="0"/>
@@ -14,44 +13,15 @@
 				<mutator name="EMISSIVE_TEX" value="0"/>
 				<mutator name="ALPHA_TEST" value="0"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+		</shaderProgram>
+		
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
 			</mutation>
-		</technique>
-
-		
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		</shaderProgram>
 
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>
 		

+ 8 - 38
Samples/SimpleScene/Assets/shortBox_122467965d493dab.ankimtl

@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="0"/>
 				<mutator name="SPECULAR_TEX" value="0"/>
 				<mutator name="ROUGHNESS_TEX" value="0"/>
@@ -14,44 +13,15 @@
 				<mutator name="EMISSIVE_TEX" value="0"/>
 				<mutator name="ALPHA_TEST" value="0"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+		</shaderProgram>
+		
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
 			</mutation>
-		</technique>
-
-		
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		</shaderProgram>
 
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>
 		

+ 8 - 38
Samples/SimpleScene/Assets/tallBox_15316a0c94bdf7f6.ankimtl

@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!-- This file is auto generated by ImporterMaterial.cpp -->
 <material shadows="1">
-	<techniques>
-		<technique name="GBuffer" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+	<shaderPrograms>
+		<shaderProgram filename="AnKi/Shaders/GBufferGeneric.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="0"/>
 				<mutator name="DIFFUSE_TEX" value="0"/>
 				<mutator name="SPECULAR_TEX" value="0"/>
 				<mutator name="ROUGHNESS_TEX" value="0"/>
@@ -14,44 +13,15 @@
 				<mutator name="EMISSIVE_TEX" value="0"/>
 				<mutator name="ALPHA_TEST" value="0"/>
 			</mutation>
-		</technique>
-
-		<technique name="GBufferEarlyZ" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
-			<mutation>
-				<mutator name="TECHNIQUE" value="1"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
-			</mutation>
-		</technique>
-
-		<technique name="Shadow" shaderProgram="AnKi/Shaders/GBufferGeneric.ankiprog">
+		</shaderProgram>
+		
+		<shaderProgram filename="AnKi/Shaders/RtShadowsHit.ankiprog">
 			<mutation>
-				<mutator name="TECHNIQUE" value="2"/>
-				<mutator name="DIFFUSE_TEX" value="0"/>
-				<mutator name="SPECULAR_TEX" value="0"/>
-				<mutator name="ROUGHNESS_TEX" value="0"/>
-				<mutator name="METAL_TEX" value="0"/>
-				<mutator name="NORMAL_TEX" value="0"/>
-				<mutator name="PARALLAX" value="0"/>
-				<mutator name="EMISSIVE_TEX" value="0"/>
-				<mutator name="ALPHA_TEST" value="0"/>
+				<mutator name="ALPHA_TEXTURE" value="0"/>
 			</mutation>
-		</technique>
-
-		
-	<technique name="RtShadow" shaderProgram="AnKi/Shaders/RtShadowsHit.ankiprog">
-		<mutation>
-			<mutator name="ALPHA_TEXTURE" value="0"/>
-		</mutation>
-	</technique>
+		</shaderProgram>
 
-	</techniques>
+	</shaderPrograms>
 
 	<inputs>