Browse Source

MaterialLoader: Fixing bugs and tests

Panagiotis Christopoulos Charitos 10 năm trước cách đây
mục cha
commit
df3769e759

+ 3 - 3
include/anki/resource/Material.h

@@ -227,9 +227,9 @@ private:
 ///					<input>
 ///						<name>xx</name>
 ///						<type>any glsl type</type>
-///						<value> (2)
+///						[<value> (2)
 ///							[a_series_of_numbers | path/to/image.ankitex]
-///						</value>
+///						</value>]
 ///						[<const>0 | 1</const>] (3)
 /// 					[<inShadow>0 | 1</inShadow>] (4)
 ///					</input>
@@ -260,7 +260,7 @@ private:
 /// </material>
 /// @endcode
 /// (1): AKA uniforms
-/// (2): The \<value\> can be left empty for build-in variables
+/// (2): The \<value\> can be omitted for build-in variables
 /// (3): The \<const\> will mark a variable as constant and it cannot be changed
 ///      at all. Default is 0
 /// (4): Optimization. Set to 1 if the var will be used in shadow passes as well

+ 35 - 24
include/anki/resource/MaterialLoader.h

@@ -20,27 +20,39 @@ class XmlElement;
 class MaterialLoaderInputVariable: public NonCopyable
 {
 public:
-	TempResourceAllocator<U8> m_alloc;
+	GenericMemoryPoolAllocator<U8> m_alloc;
+
 	String m_name;
-	String m_typeStr;
-	ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
 	StringList m_value;
-	Bool8 m_constant = false;
-	Bool8 m_instanced = false;
+	String m_line;
+
+	ShaderVariableDataType m_type = ShaderVariableDataType::NONE;
 	BuiltinMaterialVariableId m_builtin = BuiltinMaterialVariableId::NONE;
 
-	String m_line;
-	ShaderTypeBit m_shaderDefinedMask = ShaderTypeBit::NONE; ///< Defined in
-	/// Referenced by
-	ShaderTypeBit m_shaderReferencedMask = ShaderTypeBit::NONE;
-	Bool8 m_inBlock = true;
+	// Flags
+	class Flags
+	{
+	public:
+		Bool8 m_inBlock = false;
+		Bool8 m_texture = false;
+		Bool8 m_builtin = false;
+		Bool8 m_const = false;
+		Bool8 m_instanced = false;
+		Bool8 m_inShadow = false;
+		Bool8 m_specialBuiltin = false;
+
+		Bool operator==(const Flags& b) const
+		{
+			return memcmp(this, &b, sizeof(*this)) == 0;
+		}
+	} m_flags;
 
 	I16 m_binding = -1; ///< Texture unit
-	U16 m_index = 666;
-
+	I16 m_index = -1;
 	ShaderVariableBlockInfo m_blockInfo;
 
-	Bool8 m_inShadow = true;
+	ShaderTypeBit m_shaderDefinedMask = ShaderTypeBit::NONE; ///< Defined in
+	ShaderTypeBit m_shaderReferencedMask = ShaderTypeBit::NONE; ///< Referenced
 
 	MaterialLoaderInputVariable()
 	{}
@@ -53,13 +65,11 @@ public:
 	~MaterialLoaderInputVariable()
 	{
 		m_name.destroy(m_alloc);
-		m_typeStr.destroy(m_alloc);
 		m_value.destroy(m_alloc);
 		m_line.destroy(m_alloc);
 	}
 
-	MaterialLoaderInputVariable& operator=(
-		MaterialLoaderInputVariable&& b)
+	MaterialLoaderInputVariable& operator=(MaterialLoaderInputVariable&& b)
 	{
 		move(b);
 		return *this;
@@ -70,13 +80,13 @@ public:
 	Bool duplicate(const MaterialLoaderInputVariable& b) const
 	{
 		return b.m_name == m_name
-			&& b.m_type == m_type
 			&& b.m_value == m_value
-			&& b.m_constant == m_constant
-			&& b.m_instanced == m_instanced
+			&& b.m_type == m_type
 			&& b.m_builtin == m_builtin
-			&& b.m_inShadow == m_inShadow;
+			&& b.m_flags == m_flags;
 	}
+
+	CString typeStr() const;
 };
 
 /// Creator of shader programs. This class parses between
@@ -90,7 +100,7 @@ class MaterialLoader
 public:
 	using Input = MaterialLoaderInputVariable;
 
-	explicit MaterialLoader(TempResourceAllocator<U8> alloc);
+	explicit MaterialLoader(GenericMemoryPoolAllocator<U8> alloc);
 
 	~MaterialLoader();
 
@@ -113,7 +123,7 @@ public:
 	{
 		for(const Input& in : m_inputs)
 		{
-			if(!in.m_constant)
+			if(!in.m_flags.m_const && !in.m_flags.m_specialBuiltin)
 			{
 				ANKI_CHECK(func(in));
 			}
@@ -152,11 +162,10 @@ public:
 	}
 
 private:
-	TempResourceAllocator<char> m_alloc;
+	GenericMemoryPoolAllocator<char> m_alloc;
 	Array<StringList, 5> m_source; ///< Shader program final source
 	Array<String, 5> m_sourceBaked; ///< Final source baked
 	List<Input> m_inputs;
-	StringList m_uniformBlock;
 	ShaderTypeBit m_uniformBlockReferencedMask = ShaderTypeBit::NONE;
 	U32 m_blockSize = 0;
 	Bool8 m_instanced = false;
@@ -179,6 +188,8 @@ private:
 	/// Parse what is within the @code <inputs></inputs> @endcode
 	ANKI_USE_RESULT Error parseInputsTag(const XmlElement& programEl);
 
+	void processInputs();
+
 	/// Parse what is within the @code <operation></operation> @endcode
 	ANKI_USE_RESULT Error parseOperationTag(
 		const XmlElement& el, ShaderType glshader,

+ 3 - 3
src/resource/Material.cpp

@@ -63,7 +63,7 @@ Error MaterialVariableTemplate<T>::init(U idx,
 	m_varType = in.m_type;
 	m_name.create(mtl.getAllocator(), in.m_name);
 	m_builtin = in.m_builtin;
-	m_instanced = in.m_instanced;
+	m_instanced = in.m_flags.m_instanced;
 
 	// Set value
 	if(in.m_value.getSize() > 0)
@@ -173,12 +173,12 @@ Error MaterialVariant::init(const RenderingKey& key2, Material& mtl,
 		const MaterialLoader::Input& in) -> Error
 	{
 		m_varActive[count] = true;
-		if(!in.m_inShadow && key.m_pass == Pass::SM)
+		if(!in.m_flags.m_inShadow && key.m_pass == Pass::SM)
 		{
 			m_varActive[count] = false;
 		}
 
-		if(in.m_inBlock && m_varActive[count])
+		if(in.m_flags.m_inBlock && m_varActive[count])
 		{
 			m_blockInfo[count] = in.m_blockInfo;
 		}

+ 295 - 179
src/resource/MaterialLoader.cpp

@@ -113,6 +113,47 @@ static ANKI_USE_RESULT Error computeShaderVariableDataType(
 	return err;
 }
 
+//==============================================================================
+static CString toString(ShaderVariableDataType in)
+{
+	CString out;
+
+	switch(in)
+	{
+	case ShaderVariableDataType::FLOAT:
+		out = "float";
+		break;
+	case ShaderVariableDataType::VEC2:
+		out = "vec2";
+		break;
+	case ShaderVariableDataType::VEC3:
+		out = "vec3";
+		break;
+	case ShaderVariableDataType::VEC4:
+		out = "vec4";
+		break;
+	case ShaderVariableDataType::MAT4:
+		out = "mat4";
+		break;
+	case ShaderVariableDataType::MAT3:
+		out = "mat3";
+		break;
+	case ShaderVariableDataType::SAMPLER_2D:
+		out = "sampler2D";
+		break;
+	case ShaderVariableDataType::SAMPLER_2D_ARRAY:
+		out = "sampler2DArray";
+		break;
+	case ShaderVariableDataType::SAMPLER_CUBE:
+		out = "samplerCube";
+		break;
+	default:
+		ANKI_ASSERT(0);
+	};
+
+	return out;
+}
+
 //==============================================================================
 static ANKI_USE_RESULT Error computeBuiltin(const CString& name,
 	BuiltinMaterialVariableId& out)
@@ -158,6 +199,42 @@ static ANKI_USE_RESULT Error computeBuiltin(const CString& name,
 	return ErrorCode::NONE;
 }
 
+//==============================================================================
+static ShaderVariableDataType getBuiltinType(BuiltinMaterialVariableId in)
+{
+	ShaderVariableDataType out = ShaderVariableDataType::SAMPLER_2D;
+
+	switch(in)
+	{
+	case BuiltinMaterialVariableId::MVP_MATRIX:
+		out = ShaderVariableDataType::MAT4;
+		break;
+	case BuiltinMaterialVariableId::MV_MATRIX:
+		out = ShaderVariableDataType::MAT4;
+		break;
+	case BuiltinMaterialVariableId::VP_MATRIX:
+		out = ShaderVariableDataType::MAT4;
+		break;
+	case BuiltinMaterialVariableId::NORMAL_MATRIX:
+		out = ShaderVariableDataType::MAT3;
+		break;
+	case BuiltinMaterialVariableId::BILLBOARD_MVP_MATRIX:
+		out = ShaderVariableDataType::MAT4;
+		break;
+	case BuiltinMaterialVariableId::MAX_TESS_LEVEL:
+		out = ShaderVariableDataType::FLOAT;
+		break;
+	case BuiltinMaterialVariableId::MS_DEPTH_MAP:
+		out = ShaderVariableDataType::SAMPLER_2D;
+		break;
+	default:
+		ANKI_ASSERT(0);
+		break;
+	}
+
+	return out;
+}
+
 //==============================================================================
 // MaterialLoaderInputVariable                                                 =
 //==============================================================================
@@ -166,21 +243,26 @@ static ANKI_USE_RESULT Error computeBuiltin(const CString& name,
 void MaterialLoaderInputVariable::move(MaterialLoaderInputVariable& b)
 {
 	m_alloc = std::move(b.m_alloc);
+
 	m_name = std::move(b.m_name);
-	m_typeStr = std::move(b.m_typeStr);
-	m_type = b.m_type;
 	m_value = std::move(b.m_value);
-	m_constant = b.m_constant;
-	m_instanced = b.m_instanced;
-	m_builtin = b.m_builtin;
 	m_line = std::move(b.m_line);
-	m_shaderDefinedMask = b.m_shaderDefinedMask;
-	m_shaderReferencedMask = b.m_shaderReferencedMask;
-	m_inBlock = b.m_inBlock;
+
+	m_type = b.m_type;
+	m_builtin = b.m_builtin;
+	m_flags = b.m_flags;
+
 	m_binding = b.m_binding;
 	m_index = b.m_index;
-	m_blockInfo = b.m_blockInfo;
-	m_inShadow = b.m_inShadow;
+
+	m_shaderDefinedMask = b.m_shaderDefinedMask;
+	m_shaderReferencedMask = b.m_shaderReferencedMask;
+}
+
+//==============================================================================
+CString MaterialLoaderInputVariable::typeStr() const
+{
+	return toString(m_type);
 }
 
 //==============================================================================
@@ -188,7 +270,7 @@ void MaterialLoaderInputVariable::move(MaterialLoaderInputVariable& b)
 //==============================================================================
 
 //==============================================================================
-MaterialLoader::MaterialLoader(TempResourceAllocator<U8> alloc)
+MaterialLoader::MaterialLoader(GenericMemoryPoolAllocator<U8> alloc)
 	: m_alloc(alloc)
 {}
 
@@ -206,7 +288,6 @@ MaterialLoader::~MaterialLoader()
 	}
 
 	m_inputs.destroy(m_alloc);
-	m_uniformBlock.destroy(m_alloc);
 }
 
 //==============================================================================
@@ -276,11 +357,7 @@ Error MaterialLoader::parseProgramsTag(const XmlElement& el)
 		ANKI_CHECK(programEl.getNextSiblingElement("program", programEl));
 	} while(programEl);
 
-	// Sort them by name to decrease the change of creating unique shaders
-	m_inputs.sort([](const Input& a, const Input& b)
-	{
-		return a.m_name < b.m_name;
-	});
+	processInputs();
 
 	//
 	// Then parse the includes, operations and other parts of the program
@@ -335,6 +412,17 @@ Error MaterialLoader::parseProgramTag(
 		m_tessellation = true;
 	}
 
+	// Some instancing crap
+	lines.pushBackSprintf(m_alloc,
+		"\n#if INSTANCE_COUNT > 1\n"
+		"#define INSTANCE_ID [%s]\n"
+		"#define INSTANCED [INSTANCE_COUNT]\n"
+		"#else\n"
+		"#define INSTANCE_ID\n"
+		"#define INSTANCED\n"
+		"#endif\n",
+		glshader == ShaderType::VERTEX ? "gl_InstanceID" : "in_instanceId");
+
 	// <includes></includes>
 	XmlElement includesEl;
 	ANKI_CHECK(programEl.getChildElement("includes", includesEl));
@@ -353,16 +441,18 @@ Error MaterialLoader::parseProgramTag(
 	// Inputs
 
 	// Block
-	if(!m_uniformBlock.isEmpty()
-		&& (m_uniformBlockReferencedMask & glshaderbit) != ShaderTypeBit::NONE)
+	if((m_uniformBlockReferencedMask & glshaderbit) != ShaderTypeBit::NONE)
 	{
 		lines.pushBackSprintf(m_alloc,
 			"\nlayout(UBO_BINDING(0, 0), std140, row_major) "
 			"uniform _ublk00\n{");
 
-		for(auto& str : m_uniformBlock)
+		for(Input& in : m_inputs)
 		{
-			lines.pushBackSprintf(m_alloc, &str[0]);
+			if(in.m_flags.m_inBlock)
+			{
+				lines.pushBackSprintf(m_alloc, &in.m_line[0]);
+			}
 		}
 
 		lines.pushBackSprintf(m_alloc, "};");
@@ -371,8 +461,9 @@ Error MaterialLoader::parseProgramTag(
 	// Other variables
 	for(Input& in : m_inputs)
 	{
-		if(!in.m_inBlock
-			&& (in.m_shaderDefinedMask & glshaderbit) != ShaderTypeBit::NONE)
+		if(!in.m_flags.m_inBlock
+			&& (in.m_shaderDefinedMask & glshaderbit) != ShaderTypeBit::NONE
+			&& !in.m_flags.m_specialBuiltin)
 		{
 			lines.pushBackSprintf(m_alloc, &in.m_line[0]);
 		}
@@ -424,51 +515,46 @@ Error MaterialLoader::parseInputsTag(const XmlElement& programEl)
 	ANKI_CHECK(inputsEl.getChildElement("input", inputEl));
 	do
 	{
-		Input inpvar;
-		inpvar.m_alloc = m_alloc;
+		Input in;
+		in.m_alloc = m_alloc;
 
 		// <name>
 		ANKI_CHECK(inputEl.getChildElement("name", el));
 		ANKI_CHECK(el.getText(cstr));
-		inpvar.m_name.create(m_alloc, cstr);
+		in.m_name.create(m_alloc, cstr);
 
-		ANKI_CHECK(computeBuiltin(inpvar.m_name.toCString(), inpvar.m_builtin));
+		ANKI_CHECK(computeBuiltin(in.m_name.toCString(), in.m_builtin));
+
+		if(in.m_builtin != BuiltinMaterialVariableId::NONE)
+		{
+			in.m_flags.m_builtin = true;
+			if(in.m_builtin == BuiltinMaterialVariableId::MS_DEPTH_MAP)
+			{
+				in.m_flags.m_specialBuiltin = true;
+			}
+		}
 
 		// Check if instanced
-		if(inpvar.m_builtin == BuiltinMaterialVariableId::MVP_MATRIX
-			|| inpvar.m_builtin == BuiltinMaterialVariableId::NORMAL_MATRIX
-			|| inpvar.m_builtin
+		if(in.m_builtin == BuiltinMaterialVariableId::MVP_MATRIX
+			|| in.m_builtin == BuiltinMaterialVariableId::NORMAL_MATRIX
+			|| in.m_builtin
 				== BuiltinMaterialVariableId::BILLBOARD_MVP_MATRIX)
 		{
-			inpvar.m_instanced = true;
+			in.m_flags.m_instanced = true;
 			m_instanced = true;
 		}
 
 		// <type>
 		ANKI_CHECK(inputEl.getChildElement("type", el));
 		ANKI_CHECK(el.getText(cstr));
-		inpvar.m_typeStr.create(m_alloc, cstr);
-		ANKI_CHECK(computeShaderVariableDataType(cstr, inpvar.m_type));
+		ANKI_CHECK(computeShaderVariableDataType(cstr, in.m_type));
 
-		// <value>
-		ANKI_CHECK(inputEl.getChildElement("value", el));
-		ANKI_CHECK(el.getText(cstr));
-		if(cstr)
-		{
-			inpvar.m_value.splitString(m_alloc, cstr, ' ');
-
-			if(inpvar.m_builtin != BuiltinMaterialVariableId::NONE)
-			{
-				ANKI_LOGE("Builtins cannot have value %s", &inpvar.m_name[0]);
-				return ErrorCode::USER_DATA;
-			}
-		}
-		else
+		if(in.m_flags.m_builtin)
 		{
-			if(inpvar.m_builtin == BuiltinMaterialVariableId::NONE)
+			if(getBuiltinType(in.m_builtin) != in.m_type)
 			{
-				ANKI_LOGE("Non-builtins should have a value %s",
-					&inpvar.m_name[0]);
+				ANKI_LOGE("Builtin variable %s cannot be of type %s",
+					&in.m_name[0], &cstr[0]);
 				return ErrorCode::USER_DATA;
 			}
 		}
@@ -479,18 +565,43 @@ Error MaterialLoader::parseInputsTag(const XmlElement& programEl)
 		{
 			I64 tmp;
 			ANKI_CHECK(el.getI64(tmp));
-			inpvar.m_constant = tmp;
+			in.m_flags.m_const = tmp;
 		}
-		else
+
+		if(in.m_flags.m_builtin && in.m_flags.m_const)
 		{
-			inpvar.m_constant = false;
+			ANKI_LOGE("Builtins cannot be consts: %s", &in.m_name[0]);
+			return ErrorCode::USER_DATA;
 		}
 
-		if(inpvar.m_builtin != BuiltinMaterialVariableId::NONE
-			&& inpvar.m_constant)
+		// <value>
+		ANKI_CHECK(inputEl.getChildElementOptional("value", el));
+		if(el)
 		{
-			ANKI_LOGE("Builtins cannot be consts %s", &inpvar.m_name[0]);
-			return ErrorCode::USER_DATA;
+			ANKI_CHECK(el.getText(cstr));
+
+			if(in.m_flags.m_builtin)
+			{
+				ANKI_LOGE("Builtins cannot have value: %s", &in.m_name[0]);
+				return ErrorCode::USER_DATA;
+			}
+
+			in.m_value.splitString(m_alloc, cstr, ' ');
+		}
+		else
+		{
+			if(!in.m_flags.m_builtin)
+			{
+				ANKI_LOGE("Non-builtins should have a value: %s",
+					&in.m_name[0]);
+				return ErrorCode::USER_DATA;
+			}
+
+			if(in.m_flags.m_const)
+			{
+				ANKI_LOGE("Consts should have a value: %s", &in.m_name[0]);
+				return ErrorCode::USER_DATA;
+			}
 		}
 
 		// <inShadow>
@@ -499,19 +610,31 @@ Error MaterialLoader::parseInputsTag(const XmlElement& programEl)
 		{
 			I64 tmp;
 			ANKI_CHECK(el.getI64(tmp));
-			inpvar.m_inShadow = tmp;
+			in.m_flags.m_inShadow = tmp;
 		}
 		else
 		{
-			inpvar.m_inShadow = true;
+			in.m_flags.m_inShadow = true;
+		}
+
+		// Set some stuff
+		if(in.m_type >= ShaderVariableDataType::SAMPLERS_FIRST
+			&& in.m_type <= ShaderVariableDataType::SAMPLERS_LAST)
+		{
+			in.m_flags.m_texture = true;
+		}
+		else if(!in.m_flags.m_const)
+		{
+			in.m_flags.m_inBlock = true;
+			m_uniformBlockReferencedMask |= glshaderbit;
 		}
 
 		// Now you have the info to check if duplicate
 		Input* duplicateInp = nullptr;
 		Error err = m_inputs.iterateForward(
-			[&duplicateInp, &inpvar](Input& in) -> Error
+			[&duplicateInp, &in](Input& inp) -> Error
 		{
-			if(in.m_name == inpvar.m_name)
+			if(in.m_name == inp.m_name)
 			{
 				duplicateInp = &in;
 				return ErrorCode::NONE;
@@ -524,100 +647,104 @@ Error MaterialLoader::parseInputsTag(const XmlElement& programEl)
 		if(duplicateInp != nullptr)
 		{
 			// Duplicate. Make sure it's the same as the other shader
-			Bool same = duplicateInp->duplicate(inpvar);
+			Bool same = duplicateInp->duplicate(in);
 
 			if(!same)
 			{
 				ANKI_LOGE("Variable defined differently between "
-					"shaders: %s", &inpvar.m_name[0]);
+					"shaders: %s", &in.m_name[0]);
 				return ErrorCode::USER_DATA;
 			}
 
 			duplicateInp->m_shaderDefinedMask |= glshaderbit;
 
-			if(duplicateInp->m_inBlock)
+			if(duplicateInp->m_flags.m_inBlock)
 			{
 				m_uniformBlockReferencedMask |= glshaderbit;
 			}
+		}
+		else
+		{
+			in.m_shaderDefinedMask = glshaderbit;
 
-			goto advance;
+			m_inputs.emplaceBack(m_alloc);
+			m_inputs.getBack().move(in);
 		}
 
-		if(!inpvar.m_constant)
+		// Advance
+		ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
+	} while(inputEl);
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+void MaterialLoader::processInputs()
+{
+	// Sort the variables to decrease the change of creating unique shaders
+	m_inputs.sort([](const Input& a, const Input& b)
+	{
+		return a.m_name < b.m_name;
+	});
+
+	for(auto& in : m_inputs)
+	{
+		if(!in.m_flags.m_const)
 		{
 			// Handle NON-consts
 
-			if(inpvar.m_type >= ShaderVariableDataType::SAMPLERS_FIRST
-				&& inpvar.m_type <= ShaderVariableDataType::SAMPLERS_LAST)
+			if(in.m_flags.m_texture)
 			{
 				// Not in block
 
-				inpvar.m_inBlock = false;
-				inpvar.m_binding = m_texBinding++;
+				if(!in.m_flags.m_specialBuiltin)
+				{
+					in.m_binding = m_texBinding++;
 
-				inpvar.m_line.sprintf(
-					m_alloc, "layout(TEX_BINDING(0, %u)) uniform tex%u",
-					inpvar.m_binding, inpvar.m_binding);
+					in.m_line.sprintf(
+						m_alloc, "layout(TEX_BINDING(0, %u)) uniform tex%u",
+						in.m_binding, in.m_binding);
+				}
 			}
-			else
+			else if(in.m_flags.m_inBlock)
 			{
 				// In block
 
-				inpvar.m_inBlock = true;
-				inpvar.m_index = m_nextIndex++;
-				m_uniformBlockReferencedMask |= glshaderbit;
+				in.m_index = m_nextIndex++;
 
-				inpvar.m_line.sprintf(m_alloc,
+				in.m_line.sprintf(m_alloc,
 					"#if %s\n"
-					"%s var%u %s\n;"
+					"%s var%u%s;\n"
 					"#endif",
-					inpvar.m_inShadow ? "SHADOW" : "1",
-					&inpvar.m_typeStr[0],
-					inpvar.m_index,
-					inpvar.m_instanced ? "[INSTANCE_COUNT]" : " ");
-
-				String tmp;
-				tmp.create(m_alloc, inpvar.m_line);
-				m_uniformBlock.emplaceBack(m_alloc, std::move(tmp));
+					!in.m_flags.m_inShadow ? "PASS == COLOR" : "1",
+					&in.typeStr()[0],
+					in.m_index,
+					in.m_flags.m_instanced ? " INSTANCED" : "");
+			}
+			else
+			{
+				ANKI_ASSERT(0);
 			}
 		}
 		else
 		{
 			// Handle consts
 
-			if(inpvar.m_value.isEmpty())
-			{
-				ANKI_LOGE("Empty value and const is illogical");
-				return ErrorCode::USER_DATA;
-			}
-
-			inpvar.m_inBlock = false;
-			inpvar.m_index = m_nextIndex++;
+			in.m_index = m_nextIndex++;
 
 			String initList;
-			inpvar.m_value.join(m_alloc, ", ", initList);
+			in.m_value.join(m_alloc, ", ", initList);
 
-			inpvar.m_line.sprintf(m_alloc,
+			in.m_line.sprintf(m_alloc,
 				"const %s var%u = %s(%s);",
-				&inpvar.m_typeStr[0],
-				&inpvar.m_index,
-				&inpvar.m_typeStr[0],
+				&in.typeStr()[0],
+				in.m_index,
+				&in.typeStr()[0],
 				&initList[0]);
 
 			initList.destroy(m_alloc);
 		}
-
-		inpvar.m_shaderDefinedMask = glshaderbit;
-
-		m_inputs.emplaceBack(m_alloc);
-		m_inputs.getBack().move(inpvar);
-
-advance:
-		// Advance
-		ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
-	} while(inputEl);
-
-	return ErrorCode::NONE;
+	}
 }
 
 //==============================================================================
@@ -635,22 +762,22 @@ Error MaterialLoader::parseOperationTag(
 	CString funcName;
 	StringListAuto argsList(m_alloc);
 
-	// <id></id>
+	// <id>
 	I64 id;
 	ANKI_CHECK(operationTag.getChildElement("id", el));
 	ANKI_CHECK(el.getI64(id));
 
-	// <returnType></returnType>
+	// <returnType>
 	XmlElement retTypeEl;
 	ANKI_CHECK(operationTag.getChildElement("returnType", retTypeEl));
 	ANKI_CHECK(retTypeEl.getText(cstr));
 	Bool retTypeVoid = cstr == "void";
 
-	// <function>functionName</function>
+	// <function>
 	ANKI_CHECK(operationTag.getChildElement("function", el));
 	ANKI_CHECK(el.getText(funcName));
 
-	// <arguments></arguments>
+	// <arguments>
 	XmlElement argsEl;
 	ANKI_CHECK(operationTag.getChildElementOptional("arguments", argsEl));
 
@@ -681,7 +808,7 @@ Error MaterialLoader::parseOperationTag(
 			// The argument should be an input variable or an outXX or global
 			if(input == nullptr)
 			{
-				if(arg.find(OUT) != 0 && arg.find("anki_") != 0)
+				if(arg.find(OUT) != 0)
 				{
 					ANKI_LOGE("Incorrect argument: %s", &arg[0]);
 					return ErrorCode::USER_DATA;
@@ -689,31 +816,24 @@ Error MaterialLoader::parseOperationTag(
 			}
 
 			// Add to a list and do something special if instanced
-			if(input && input->m_instanced)
+			if(input)
 			{
-				ANKI_CHECK(argEl.getText(cstr));
-
-				if(glshader == ShaderType::VERTEX
-					|| glshader == ShaderType::FRAGMENT)
+				if(!input->m_flags.m_specialBuiltin)
 				{
 					argsList.pushBackSprintf(
-						"%s%u\n"
-						"#if INSTANCE_COUNT > 1\n"
-						"[%s]\n"
-						"#endif",
-						input->m_binding >= 0 ? "tex" : "var",
-						input->m_binding >= 0
-							? input->m_binding : input->m_index,
-						(glshader == ShaderType::VERTEX)
-							? "gl_InstanceID" : "vInstanceId");
-
-					m_instanceIdMask |= glshaderbit;
+						"%s%d%s",
+						input->m_flags.m_texture ? "tex" : "var",
+						!input->m_flags.m_texture
+							? input->m_index : input->m_binding,
+						input->m_flags.m_instanced ? " INSTANCE_ID" : "");
 				}
 				else
 				{
-					ANKI_LOGE("Cannot access the instance ID in all shaders");
-					return ErrorCode::USER_DATA;
+					ANKI_CHECK(argEl.getText(cstr));
+					argsList.pushBackSprintf(&cstr[0]);
 				}
+
+				m_instanceIdMask |= glshaderbit;
 			}
 			else
 			{
@@ -744,12 +864,8 @@ Error MaterialLoader::parseOperationTag(
 	{
 		ANKI_CHECK(retTypeEl.getText(cstr));
 		lines.pushBackSprintf(
-			"#\tdefine out%u_DEFINED\n"
-			"\t%s out%u = ", id, &cstr[0], id);
-	}
-	else
-	{
-		lines.pushBackSprintf("\t");
+			"#define out%u_DEFINED\n"
+			"%s out%u = ", id, &cstr[0], id);
 	}
 
 	// write the "func(args...)" of "blah = func(args...)"
@@ -765,7 +881,7 @@ Error MaterialLoader::parseOperationTag(
 		(argsStr.isEmpty()) ? "" : &argsStr[0]);
 
 	// Bake final string
-	lines.join(m_alloc, " ", out);
+	lines.join(m_alloc, "", out);
 
 	return ErrorCode::NONE;
 }
@@ -791,88 +907,88 @@ void MaterialLoader::mutate(const RenderingKey& key)
 
 	// Compute the block info for each var
 	m_blockSize = 0;
-	for(Input& inpvar : m_inputs)
+	for(Input& in : m_inputs)
 	{
 		// Invalidate the var's block info
-		inpvar.m_blockInfo = ShaderVariableBlockInfo();
+		in.m_blockInfo = ShaderVariableBlockInfo();
 
-		if(!inpvar.m_inBlock)
+		if(!in.m_flags.m_inBlock)
 		{
 			continue;
 		}
 
-		if(pass == Pass::SM && !inpvar.m_inShadow)
+		if(pass == Pass::SM && !in.m_flags.m_inShadow)
 		{
 			continue;
 		}
 
 		// std140 rules
-		inpvar.m_blockInfo.m_offset = m_blockSize;
-		inpvar.m_blockInfo.m_arraySize = inpvar.m_instanced ? instanceCount : 1;
+		in.m_blockInfo.m_offset = m_blockSize;
+		in.m_blockInfo.m_arraySize = in.m_flags.m_instanced ? instanceCount : 1;
 
-		if(inpvar.m_type == ShaderVariableDataType::FLOAT)
+		if(in.m_type == ShaderVariableDataType::FLOAT)
 		{
-			inpvar.m_blockInfo.m_arrayStride = sizeof(Vec4);
+			in.m_blockInfo.m_arrayStride = sizeof(Vec4);
 
-			if(inpvar.m_blockInfo.m_arraySize == 1)
+			if(in.m_blockInfo.m_arraySize == 1)
 			{
-				// No need to align the inpvar.m_offset
+				// No need to align the in.m_offset
 				m_blockSize += sizeof(F32);
 			}
 			else
 			{
-				alignRoundUp(sizeof(Vec4), inpvar.m_blockInfo.m_offset);
-				m_blockSize += sizeof(Vec4) * inpvar.m_blockInfo.m_arraySize;
+				alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
+				m_blockSize += sizeof(Vec4) * in.m_blockInfo.m_arraySize;
 			}
 		}
-		else if(inpvar.m_type == ShaderVariableDataType::VEC2)
+		else if(in.m_type == ShaderVariableDataType::VEC2)
 		{
-			inpvar.m_blockInfo.m_arrayStride = sizeof(Vec4);
+			in.m_blockInfo.m_arrayStride = sizeof(Vec4);
 
-			if(inpvar.m_blockInfo.m_arraySize == 1)
+			if(in.m_blockInfo.m_arraySize == 1)
 			{
-				alignRoundUp(sizeof(Vec2), inpvar.m_blockInfo.m_offset);
+				alignRoundUp(sizeof(Vec2), in.m_blockInfo.m_offset);
 				m_blockSize += sizeof(Vec2);
 			}
 			else
 			{
-				alignRoundUp(sizeof(Vec4), inpvar.m_blockInfo.m_offset);
-				m_blockSize += sizeof(Vec4) * inpvar.m_blockInfo.m_arraySize;
+				alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
+				m_blockSize += sizeof(Vec4) * in.m_blockInfo.m_arraySize;
 			}
 		}
-		else if(inpvar.m_type == ShaderVariableDataType::VEC3)
+		else if(in.m_type == ShaderVariableDataType::VEC3)
 		{
-			alignRoundUp(sizeof(Vec4), inpvar.m_blockInfo.m_offset);
-			inpvar.m_blockInfo.m_arrayStride = sizeof(Vec4);
+			alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
+			in.m_blockInfo.m_arrayStride = sizeof(Vec4);
 
-			if(inpvar.m_blockInfo.m_arraySize == 1)
+			if(in.m_blockInfo.m_arraySize == 1)
 			{
 				m_blockSize += sizeof(Vec3);
 			}
 			else
 			{
-				m_blockSize += sizeof(Vec4) * inpvar.m_blockInfo.m_arraySize;
+				m_blockSize += sizeof(Vec4) * in.m_blockInfo.m_arraySize;
 			}
 		}
-		else if(inpvar.m_type == ShaderVariableDataType::VEC4)
+		else if(in.m_type == ShaderVariableDataType::VEC4)
 		{
-			inpvar.m_blockInfo.m_arrayStride = sizeof(Vec4);
-			alignRoundUp(sizeof(Vec4), inpvar.m_blockInfo.m_offset);
-			m_blockSize += sizeof(Vec4) * inpvar.m_blockInfo.m_arraySize;
+			in.m_blockInfo.m_arrayStride = sizeof(Vec4);
+			alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
+			m_blockSize += sizeof(Vec4) * in.m_blockInfo.m_arraySize;
 		}
-		else if(inpvar.m_type == ShaderVariableDataType::MAT3)
+		else if(in.m_type == ShaderVariableDataType::MAT3)
 		{
-			alignRoundUp(sizeof(Vec4), inpvar.m_blockInfo.m_offset);
-			inpvar.m_blockInfo.m_arrayStride = sizeof(Vec4) * 3;
-			m_blockSize += sizeof(Vec4) * 3 * inpvar.m_blockInfo.m_arraySize;
-			inpvar.m_blockInfo.m_matrixStride = sizeof(Vec4);
+			alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
+			in.m_blockInfo.m_arrayStride = sizeof(Vec4) * 3;
+			m_blockSize += sizeof(Vec4) * 3 * in.m_blockInfo.m_arraySize;
+			in.m_blockInfo.m_matrixStride = sizeof(Vec4);
 		}
-		else if(inpvar.m_type == ShaderVariableDataType::MAT4)
+		else if(in.m_type == ShaderVariableDataType::MAT4)
 		{
-			alignRoundUp(sizeof(Vec4), inpvar.m_blockInfo.m_offset);
-			inpvar.m_blockInfo.m_arrayStride = sizeof(Mat4);
-			m_blockSize += sizeof(Mat4) * inpvar.m_blockInfo.m_arraySize;
-			inpvar.m_blockInfo.m_matrixStride = sizeof(Vec4);
+			alignRoundUp(sizeof(Vec4), in.m_blockInfo.m_offset);
+			in.m_blockInfo.m_arrayStride = sizeof(Mat4);
+			m_blockSize += sizeof(Mat4) * in.m_blockInfo.m_arraySize;
+			in.m_blockInfo.m_matrixStride = sizeof(Vec4);
 		}
 		else
 		{

+ 117 - 0
tests/resource/MaterialLoader.cpp

@@ -5,12 +5,129 @@
 
 #include "tests/framework/Framework.h"
 #include "anki/resource/MaterialLoader.h"
+#include "anki/misc/Xml.h"
+#include <unordered_map>
 
 namespace anki {
 
+static const char* MTL = R"(
+<material>
+	<levelsOfDetail>3</levelsOfDetail>
+	<shadow>1</shadow>
+
+	<programs>
+		<program>
+			<type>vert</type>
+			<inputs>
+				<input>
+					<name>anki_mvp</name>
+					<type>mat4</type>
+				</input>
+				<input>
+					<name>c</name>
+					<type>float</type>
+					<value>1.0</value>
+					<const>1</const>
+				</input>
+				<input>
+					<name>anki_msDepthRt</name>
+					<type>sampler2D</type>
+				</input>
+				<input>
+					<name>sampl</name>
+					<type>sampler2D</type>
+					<value>aasdfasdf/asdfasdf</value>
+				</input>
+				<input>
+					<name>mouse</name>
+					<type>float</type>
+					<value>1.0</value>
+					<inShadow>0</inShadow>
+				</input>
+				<input>
+					<name>cat</name>
+					<type>vec3</type>
+					<value>1.0 1.0 1.0</value>
+					<inShadow>1</inShadow>
+				</input>
+			</inputs>
+
+			<includes><include>file.glsl</include></includes>
+
+			<operations>
+				<operation>
+					<id>12</id>
+					<returnType>vec3</returnType>
+					<function>ha</function>
+				</operation>
+				<operation>
+					<id>123</id>
+					<returnType>vec3</returnType>
+					<function>foo</function>
+					<arguments>
+						<argument>anki_mvp</argument>
+						<argument>c</argument>
+						<argument>cat</argument>
+						<argument>out12</argument>
+						<argument>anki_msDepthRt</argument>
+					</arguments>
+				</operation>
+				<operation>
+					<id>124</id>
+					<returnType>void</returnType>
+					<function>boo</function>
+					<arguments>
+						<argument>out123</argument>
+						<argument>mouse</argument>
+						<argument>sampl</argument>
+					</arguments>
+				</operation>
+			</operations>
+		</program>
+	</programs>
+</material>
+)";
+
 //==============================================================================
 ANKI_TEST(Resource, MaterialLoader)
 {
+	XmlDocument doc;
+	HeapAllocator<U8> alloc(allocAligned, nullptr);
+	MaterialLoader loader(alloc);
+
+	ANKI_TEST_EXPECT_NO_ERR(doc.parse(MTL, alloc));
+	ANKI_TEST_EXPECT_NO_ERR(loader.parseXmlDocument(doc));
+
+	{
+		RenderingKey key(Pass::SM, 1, false, 3);
+		loader.mutate(key);
+		printf("%s\n", &loader.getShaderSource(ShaderType::VERTEX)[0]);
+
+		std::unordered_map<std::string, Bool> map;
+		map["anki_mvp"] = true;
+		map["c"] = false;
+		map["anki_msDepthRt"] = false;
+		map["sampl"] = true;
+		map["mouse"] = true;
+		map["cat"] = true;
+
+		Error err = loader.iterateAllInputVariables([&](
+			const MaterialLoaderInputVariable& in) -> Error
+		{
+			ANKI_TEST_EXPECT_EQ(map[&in.m_name.toCString()[0]], true);
+
+			if(in.m_flags.m_inBlock)
+			{
+				printf("var in block: %s %d %d %d %d\n", &in.m_name[0],
+					in.m_blockInfo.m_offset, in.m_blockInfo.m_arraySize,
+					in.m_blockInfo.m_arrayStride,
+					in.m_blockInfo.m_matrixStride);
+			}
+
+			return ErrorCode::NONE;
+		});
+		(void)err;
+	}
 }
 
 } // end namespace anki