Browse Source

New material is feature complete

Panagiotis Christopoulos Charitos 5 years ago
parent
commit
a675e9d1e5

+ 804 - 0
src/anki/resource/MaterialResource2.cpp

@@ -4,3 +4,807 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/resource/MaterialResource2.h>
+#include <anki/resource/ResourceManager.h>
+#include <anki/resource/TextureResource.h>
+#include <anki/util/Xml.h>
+
+namespace anki
+{
+
+class BuiltinVarInfo
+{
+public:
+	const char* m_name;
+	ShaderVariableDataType m_type;
+	Bool m_instanced;
+};
+
+static const Array<BuiltinVarInfo, U(BuiltinMaterialVariableId2::COUNT)> BUILTIN_INFOS = {
+	{{"NONE", ShaderVariableDataType::NONE, false},
+		{"m_ankiMvp", ShaderVariableDataType::MAT4, true},
+		{"m_ankiViewMatrix", ShaderVariableDataType::MAT4, true},
+		{"m_ankiModelMatrix", ShaderVariableDataType::MAT4, true},
+		{"m_ankiProjectionMatrix", ShaderVariableDataType::MAT4, false},
+		{"m_ankiViewMatrix", ShaderVariableDataType::MAT4, false},
+		{"m_ankiNormalMatrix", ShaderVariableDataType::MAT3, true},
+		{"m_ankiRotationMatrix", ShaderVariableDataType::MAT3, true},
+		{"m_ankiCameraRotationMatrix", ShaderVariableDataType::MAT3, false},
+		{"m_ankiCameraPosition", ShaderVariableDataType::VEC3, false},
+		{"m_ankiPreviousMvp", ShaderVariableDataType::MAT4, true},
+		{"m_ankiGlobalSampler", ShaderVariableDataType::SAMPLER, false}}};
+
+MaterialVariable2::MaterialVariable2()
+{
+	m_mat4 = Mat4::getZero();
+	m_mat4(3, 3) = NO_VALUE; // Add a random value
+}
+
+MaterialVariable2::~MaterialVariable2()
+{
+}
+
+MaterialResource2::MaterialResource2(ResourceManager* manager)
+	: ResourceObject(manager)
+{
+}
+
+MaterialResource2::~MaterialResource2()
+{
+	// TODO
+}
+
+Error MaterialResource2::load(const ResourceFilename& filename, Bool async)
+{
+	XmlDocument doc;
+	XmlElement el;
+	Bool present = false;
+	ANKI_CHECK(openFileParseXml(filename, doc));
+
+	// <material>
+	XmlElement rootEl;
+	ANKI_CHECK(doc.getChildElement("material", rootEl));
+
+	// shaderProgram
+	CString fname;
+	ANKI_CHECK(rootEl.getAttributeText("shaderProgram", fname));
+	ANKI_CHECK(getManager().loadResource(fname, m_prog, async));
+
+	// Good time to create the vars
+	ANKI_CHECK(createVars());
+
+	// shadow
+	ANKI_CHECK(rootEl.getAttributeNumberOptional("shadow", m_shadow, present));
+	m_shadow = m_shadow != 0;
+
+	// forwardShading
+	ANKI_CHECK(rootEl.getAttributeNumberOptional("forwardShading", m_forwardShading, present));
+	m_forwardShading = m_forwardShading != 0;
+
+	// <mutators>
+	XmlElement mutatorsEl;
+	ANKI_CHECK(rootEl.getChildElementOptional("mutators", mutatorsEl));
+	if(mutatorsEl)
+	{
+		ANKI_CHECK(parseMutators(mutatorsEl));
+	}
+
+	// <inputs>
+	ANKI_CHECK(rootEl.getChildElementOptional("inputs", el));
+	if(el)
+	{
+		ANKI_CHECK(parseInputs(el, async));
+	}
+
+	// Check that all non-builtin vars have a value
+	for(const MaterialVariable2& var : m_vars)
+	{
+		if(!var.isBuildin() && !var.valueSetByMaterial())
+		{
+			ANKI_RESOURCE_LOGE("Missing value for variable %s", var.m_name.cstr());
+			return Error::USER_DATA;
+		}
+	}
+
+	return Error::NONE;
+}
+
+Error MaterialResource2::parseMutators(XmlElement mutatorsEl)
+{
+	XmlElement mutatorEl;
+	ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
+
+	//
+	// Process the non-builtin mutators
+	//
+	U32 mutatorCount = 0;
+	ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
+	++mutatorCount;
+	ANKI_ASSERT(mutatorCount > 0);
+	m_nonBuiltinsMutation.create(getAllocator(), mutatorCount);
+	mutatorCount = 0;
+
+	do
+	{
+		SubMutation& smutation = m_nonBuiltinsMutation[mutatorCount];
+
+		// name
+		CString mutatorName;
+		ANKI_CHECK(mutatorEl.getAttributeText("name", mutatorName));
+		if(mutatorName.isEmpty())
+		{
+			ANKI_RESOURCE_LOGE("Mutator name is empty");
+			return Error::USER_DATA;
+		}
+
+		if(mutatorName == "ANKI_INSTANCE_COUNT" || mutatorName == "ANKI_PASS" || mutatorName == "ANKI_LOD"
+			|| mutatorName == "ANKI_BONES" || mutatorName == "ANKI_VELOCITY")
+		{
+			ANKI_RESOURCE_LOGE("Cannot list builtin mutator \"%s\"", &mutatorName[0]);
+			return Error::USER_DATA;
+		}
+
+		// value
+		ANKI_CHECK(mutatorEl.getAttributeNumber("value", smutation.m_value));
+
+		// Find mutator
+		smutation.m_mutator = m_prog->tryFindMutator(mutatorName);
+
+		if(!smutation.m_mutator)
+		{
+			ANKI_RESOURCE_LOGE("Mutator not found in program %s", &mutatorName[0]);
+			return Error::USER_DATA;
+		}
+
+		if(!smutation.m_mutator->valueExists(smutation.m_value))
+		{
+			ANKI_RESOURCE_LOGE("Value %d is not part of the mutator %s", smutation.m_value, &mutatorName[0]);
+			return Error::USER_DATA;
+		}
+
+		// Advance
+		++mutatorCount;
+		ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
+	} while(mutatorEl);
+
+	ANKI_ASSERT(mutatorCount == m_nonBuiltinsMutation.getSize());
+
+	//
+	// Find the builtin mutators
+	//
+	U builtinMutatorCount = 0;
+
+	m_instancingMutator = m_prog->tryFindMutator("ANKI_INSTANCE_COUNT");
+	if(m_instancingMutator)
+	{
+		if(m_instancingMutator->m_values.getSize() != MAX_INSTANCE_GROUPS)
+		{
+			ANKI_RESOURCE_LOGE("Mutator ANKI_INSTANCE_COUNT should have %u values in the program", MAX_INSTANCE_GROUPS);
+			return Error::USER_DATA;
+		}
+
+		for(U32 i = 0; i < MAX_INSTANCE_GROUPS; ++i)
+		{
+			if(m_instancingMutator->m_values[i] != (1 << i))
+			{
+				ANKI_RESOURCE_LOGE("Values of the ANKI_INSTANCE_COUNT mutator in the program are not the expected");
+				return Error::USER_DATA;
+			}
+		}
+
+		++builtinMutatorCount;
+	}
+
+	m_passMutator = m_prog->tryFindMutator("ANKI_PASS");
+	if(m_passMutator)
+	{
+		if(m_passMutator->m_values.getSize() != U32(Pass::COUNT))
+		{
+			ANKI_RESOURCE_LOGE("Mutator ANKI_PASS should have %u values in the program", U(Pass::COUNT));
+			return Error::USER_DATA;
+		}
+
+		for(U32 i = 0; i < U(Pass::COUNT); ++i)
+		{
+			if(m_passMutator->m_values[i] != I(i))
+			{
+				ANKI_RESOURCE_LOGE("Values of the ANKI_PASS mutator in the program are not the expected");
+				return Error::USER_DATA;
+			}
+		}
+
+		++builtinMutatorCount;
+	}
+
+	if(!m_forwardShading && !m_passMutator)
+	{
+		ANKI_RESOURCE_LOGE("ANKI_PASS mutator is required");
+		return Error::USER_DATA;
+	}
+
+	m_lodMutator = m_prog->tryFindMutator("ANKI_LOD");
+	if(m_lodMutator)
+	{
+		if(m_lodMutator->m_values.getSize() > MAX_LOD_COUNT)
+		{
+			ANKI_RESOURCE_LOGE("Mutator ANKI_LOD should have at least %u values in the program", U(MAX_LOD_COUNT));
+			return Error::USER_DATA;
+		}
+
+		for(U32 i = 0; i < m_lodMutator->m_values.getSize(); ++i)
+		{
+			if(m_lodMutator->m_values[i] != I(i))
+			{
+				ANKI_RESOURCE_LOGE("Values of the ANKI_LOD mutator in the program are not the expected");
+				return Error::USER_DATA;
+			}
+		}
+
+		m_lodCount = U8(m_lodMutator->m_values.getSize());
+		++builtinMutatorCount;
+	}
+
+	m_bonesMutator = m_prog->tryFindMutator("ANKI_BONES");
+	if(m_bonesMutator)
+	{
+		if(m_bonesMutator->m_values.getSize() != 2)
+		{
+			ANKI_RESOURCE_LOGE("Mutator ANKI_BONES should have 2 values in the program");
+			return Error::USER_DATA;
+		}
+
+		for(U32 i = 0; i < m_bonesMutator->m_values.getSize(); ++i)
+		{
+			if(m_bonesMutator->m_values[i] != I(i))
+			{
+				ANKI_RESOURCE_LOGE("Values of the BONES mutator in the program are not the expected");
+				return Error::USER_DATA;
+			}
+		}
+
+		++builtinMutatorCount;
+	}
+
+	m_velocityMutator = m_prog->tryFindMutator("ANKI_VELOCITY");
+	if(m_velocityMutator)
+	{
+		if(m_velocityMutator->m_values.getSize() != 2)
+		{
+			ANKI_RESOURCE_LOGE("Mutator ANKI_VELOCITY should have 2 values in the program");
+			return Error::USER_DATA;
+		}
+
+		for(U32 i = 0; i < m_velocityMutator->m_values.getSize(); ++i)
+		{
+			if(m_velocityMutator->m_values[i] != I(i))
+			{
+				ANKI_RESOURCE_LOGE("Values of the ANKI_VELOCITY mutator in the program are not the expected");
+				return Error::USER_DATA;
+			}
+		}
+
+		++builtinMutatorCount;
+	}
+
+	if(m_nonBuiltinsMutation.getSize() + builtinMutatorCount != m_prog->getMutators().getSize())
+	{
+		ANKI_RESOURCE_LOGE("Some mutatators are unacounted for");
+		return Error::USER_DATA;
+	}
+
+	return Error::NONE;
+}
+
+Error MaterialResource2::parseVariable(CString fullVarName, Bool& instanced, U32& idx, CString& name)
+{
+	if(fullVarName.find("u_ankiPerDraw") == CString::NPOS)
+	{
+		instanced = false;
+	}
+	else if(fullVarName.find("u_ankiPerInstance") == CString::NPOS)
+	{
+		instanced = true;
+	}
+	else
+	{
+		ANKI_RESOURCE_LOGE("Wrong variable name: %s", fullVarName.cstr());
+		return Error::USER_DATA;
+	}
+
+	if(instanced)
+	{
+		const PtrSize leftBracket = fullVarName.find("[");
+		const PtrSize rightBracket = fullVarName.find("]");
+
+		if(leftBracket == CString::NPOS || rightBracket == CString::NPOS || rightBracket <= leftBracket)
+		{
+			ANKI_RESOURCE_LOGE("Wrong variable name: %s", fullVarName.cstr());
+			return Error::USER_DATA;
+		}
+
+		Array<char, 8> idxStr;
+		ANKI_ASSERT(rightBracket - leftBracket < idxStr.getSize() - 1);
+		for(PtrSize i = leftBracket; i <= rightBracket; ++i)
+		{
+			idxStr[i - leftBracket] = fullVarName[i];
+		}
+		idxStr[rightBracket - leftBracket + 1] = '\0';
+
+		ANKI_CHECK(CString(idxStr.getBegin()).toNumber(idx));
+	}
+
+	const PtrSize dot = fullVarName.find(".");
+	if(dot == CString::NPOS)
+	{
+		ANKI_RESOURCE_LOGE("Wrong variable name: %s", fullVarName.cstr());
+		return Error::USER_DATA;
+	}
+
+	name = fullVarName.getBegin() + dot;
+
+	return Error::NONE;
+}
+
+Error MaterialResource2::createVars()
+{
+	const ShaderProgramBinary& binary = m_prog->getBinary();
+
+	// Create the uniform vars
+	U32 descriptorSet = MAX_U32;
+	U32 maxDescriptorSet = 0;
+	for(const ShaderProgramBinaryBlock& block : binary.m_uniformBlocks)
+	{
+		maxDescriptorSet = max(maxDescriptorSet, block.m_set);
+
+		if(block.m_name.getBegin() != CString("b_ankiMaterial"))
+		{
+			continue;
+		}
+
+		descriptorSet = block.m_set;
+		m_uboIdx = U32(&block - binary.m_uniformBlocks.getBegin());
+
+		for(const ShaderProgramBinaryVariable& var : block.m_variables)
+		{
+			Bool instanced;
+			U32 idx;
+			CString name;
+			ANKI_CHECK(parseVariable(var.m_name.getBegin(), instanced, idx, name));
+
+			if(idx > 0)
+			{
+				continue;
+			}
+
+			MaterialVariable2& in = *m_vars.emplaceBack(getAllocator());
+			in.m_name.create(getAllocator(), name);
+			in.m_index = m_vars.getSize() - 1;
+			in.m_indexInBinary = U32(&var - block.m_variables.getBegin());
+			in.m_constant = false;
+			in.m_instanced = instanced;
+			in.m_dataType = var.m_type;
+
+			// Check if it's builtin
+			for(BuiltinMaterialVariableId2 id : EnumIterable<BuiltinMaterialVariableId2>())
+			{
+				if(id == BuiltinMaterialVariableId2::NONE)
+				{
+					continue;
+				}
+
+				if(BUILTIN_INFOS[id].m_name == name)
+				{
+					in.m_builtin = id;
+
+					if(BUILTIN_INFOS[id].m_type != in.m_dataType)
+					{
+						ANKI_RESOURCE_LOGE("Incorect type for builtin: %s", name.cstr());
+						return Error::USER_DATA;
+					}
+
+					if(BUILTIN_INFOS[id].m_instanced == instanced)
+					{
+						ANKI_RESOURCE_LOGE("Variable %s be instanced: %s",
+							(BUILTIN_INFOS[id].m_instanced) ? "should" : "shouldn't",
+							name.cstr());
+						return Error::USER_DATA;
+					}
+
+					break;
+				}
+			}
+		}
+	}
+
+	if(descriptorSet == MAX_U32)
+	{
+		ANKI_RESOURCE_LOGE("The b_ankiMaterial UBO is missing");
+		return Error::USER_DATA;
+	}
+
+	// Continue with the opaque if it's a material shader program
+	for(const ShaderProgramBinaryOpaque& o : binary.m_opaques)
+	{
+		maxDescriptorSet = max(maxDescriptorSet, o.m_set);
+
+		if(o.m_set != descriptorSet)
+		{
+			continue;
+		}
+
+		MaterialVariable2& in = *m_vars.emplaceBack(getAllocator());
+		in.m_name.create(getAllocator(), o.m_name.getBegin());
+		in.m_index = m_vars.getSize() - 1;
+		in.m_indexInBinary = U32(&o - binary.m_opaques.getBegin());
+		in.m_constant = false;
+		in.m_instanced = false;
+		in.m_dataType = o.m_type;
+	}
+
+	if(descriptorSet != maxDescriptorSet)
+	{
+		ANKI_RESOURCE_LOGE("All bindings of a material shader should be in the highest descriptor set");
+		return Error::USER_DATA;
+	}
+
+	m_descriptorSetIdx = U8(descriptorSet);
+
+	// Consts
+	for(const ShaderProgramResourceConstant2& c : m_prog->getConstants())
+	{
+		MaterialVariable2& in = *m_vars.emplaceBack(getAllocator());
+		in.m_name.create(getAllocator(), c.m_name);
+		in.m_index = m_vars.getSize() - 1;
+		in.m_constant = true;
+		in.m_instanced = false;
+		in.m_dataType = c.m_dataType;
+	}
+
+	return Error::NONE;
+}
+
+Error MaterialResource2::parseInputs(XmlElement inputsEl, Bool async)
+{
+	// Connect the input variables
+	XmlElement inputEl;
+	ANKI_CHECK(inputsEl.getChildElementOptional("input", inputEl));
+	while(inputEl)
+	{
+		// Get var name
+		CString varName;
+		ANKI_CHECK(inputEl.getAttributeText("shaderVar", varName));
+
+		// Try find var
+		MaterialVariable2* foundVar = nullptr;
+		for(MaterialVariable2& v : m_vars)
+		{
+			if(v.m_name == varName)
+			{
+				foundVar = &v;
+				break;
+			}
+		}
+
+		if(foundVar == nullptr)
+		{
+			ANKI_RESOURCE_LOGE("Variable \"%s\" not found", varName.cstr());
+			return Error::USER_DATA;
+		}
+
+		if(foundVar->m_builtin == BuiltinMaterialVariableId2::NONE)
+		{
+			ANKI_RESOURCE_LOGE("Shouldn't list builtin vars: %s", varName.cstr());
+			return Error::USER_DATA;
+		}
+
+		// Process var
+		if(foundVar->isConstant())
+		{
+			// Const
+
+			switch(foundVar->getDataType())
+			{
+			case ShaderVariableDataType::INT:
+				ANKI_CHECK(inputEl.getAttributeNumber("value", foundVar->m_int));
+				break;
+			case ShaderVariableDataType::IVEC2:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_ivec2));
+				break;
+			case ShaderVariableDataType::IVEC3:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_ivec3));
+				break;
+			case ShaderVariableDataType::IVEC4:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_ivec4));
+				break;
+			case ShaderVariableDataType::UINT:
+				ANKI_CHECK(inputEl.getAttributeNumber("value", foundVar->m_uint));
+				break;
+			case ShaderVariableDataType::UVEC2:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_uvec2));
+				break;
+			case ShaderVariableDataType::UVEC3:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_uvec3));
+				break;
+			case ShaderVariableDataType::UVEC4:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_uvec4));
+				break;
+			case ShaderVariableDataType::FLOAT:
+				ANKI_CHECK(inputEl.getAttributeNumber("value", foundVar->m_float));
+				break;
+			case ShaderVariableDataType::VEC2:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_vec2));
+				break;
+			case ShaderVariableDataType::VEC3:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_vec3));
+				break;
+			case ShaderVariableDataType::VEC4:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_vec4));
+				break;
+			default:
+				ANKI_ASSERT(0);
+				break;
+			}
+		}
+		else
+		{
+			// Not built-in
+
+			if(foundVar->isInstanced())
+			{
+				ANKI_RESOURCE_LOGE("Only some builtin variables can be instanced: %s", foundVar->getName().cstr());
+				return Error::USER_DATA;
+			}
+
+			switch(foundVar->getDataType())
+			{
+			case ShaderVariableDataType::INT:
+				ANKI_CHECK(inputEl.getAttributeNumber("value", foundVar->m_int));
+				break;
+			case ShaderVariableDataType::IVEC2:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_ivec2));
+				break;
+			case ShaderVariableDataType::IVEC3:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_ivec3));
+				break;
+			case ShaderVariableDataType::IVEC4:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_ivec4));
+				break;
+			case ShaderVariableDataType::UINT:
+				ANKI_CHECK(inputEl.getAttributeNumber("value", foundVar->m_uint));
+				break;
+			case ShaderVariableDataType::UVEC2:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_uvec2));
+				break;
+			case ShaderVariableDataType::UVEC3:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_uvec3));
+				break;
+			case ShaderVariableDataType::UVEC4:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_uvec4));
+				break;
+			case ShaderVariableDataType::FLOAT:
+				ANKI_CHECK(inputEl.getAttributeNumber("value", foundVar->m_float));
+				break;
+			case ShaderVariableDataType::VEC2:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_vec2));
+				break;
+			case ShaderVariableDataType::VEC3:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_vec3));
+				break;
+			case ShaderVariableDataType::VEC4:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_vec4));
+				break;
+			case ShaderVariableDataType::MAT3:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_mat3));
+				break;
+			case ShaderVariableDataType::MAT4:
+				ANKI_CHECK(inputEl.getAttributeNumbers("value", foundVar->m_mat4));
+				break;
+			case ShaderVariableDataType::TEXTURE_2D:
+			case ShaderVariableDataType::TEXTURE_2D_ARRAY:
+			case ShaderVariableDataType::TEXTURE_3D:
+			case ShaderVariableDataType::TEXTURE_CUBE:
+			{
+				CString texfname;
+				ANKI_CHECK(inputEl.getAttributeText("value", texfname));
+				ANKI_CHECK(getManager().loadResource(texfname, foundVar->m_tex, async));
+				break;
+			}
+
+			default:
+				ANKI_ASSERT(0);
+				break;
+			}
+		}
+
+		// Advance
+		ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
+	}
+
+	return Error::NONE;
+}
+
+const MaterialVariant2& MaterialResource2::getOrCreateVariant(const RenderingKey& key_) const
+{
+	RenderingKey key = key_;
+	key.setLod(min<U32>(m_lodCount - 1, key.getLod()));
+
+	if(!isInstanced())
+	{
+		ANKI_ASSERT(key.getInstanceCount() == 1);
+	}
+
+	ANKI_ASSERT(!key.isSkinned() || m_bonesMutator);
+	ANKI_ASSERT(!key.hasVelocity() || m_velocityMutator);
+
+	key.setInstanceCount(1 << getInstanceGroupIdx(key.getInstanceCount()));
+
+	MaterialVariant2& variant = m_variantMatrix[key.getPass()][key.getLod()][getInstanceGroupIdx(
+		key.getInstanceCount())][key.isSkinned()][key.hasVelocity()];
+
+	// Check if it's initialized
+	{
+		RLockGuard<RWMutex> lock(m_variantMatrixMtx);
+		if(variant.m_prog.isCreated())
+		{
+			return variant;
+		}
+	}
+
+	// Not initialized, init it
+	WLockGuard<RWMutex> lock(m_variantMatrixMtx);
+
+	ShaderProgramResourceVariantInitInfo2 initInfo(m_prog);
+
+	for(const SubMutation& m : m_nonBuiltinsMutation)
+	{
+		initInfo.addMutation(m.m_mutator->m_name, m.m_value);
+	}
+
+	if(m_instancingMutator)
+	{
+		initInfo.addMutation(m_instancingMutator->m_name, key.getInstanceCount());
+	}
+
+	if(m_passMutator)
+	{
+		initInfo.addMutation(m_passMutator->m_name, MutatorValue(key.getPass()));
+	}
+
+	if(m_lodMutator)
+	{
+		initInfo.addMutation(m_lodMutator->m_name, MutatorValue(key.getLod()));
+	}
+
+	if(m_bonesMutator)
+	{
+		initInfo.addMutation(m_bonesMutator->m_name, key.isSkinned() != 0);
+	}
+
+	if(m_velocityMutator)
+	{
+		initInfo.addMutation(m_velocityMutator->m_name, key.hasVelocity() != 0);
+	}
+
+	for(const MaterialVariable2& var : m_vars)
+	{
+		if(!var.isConstant())
+		{
+			continue;
+		}
+
+		switch(var.m_dataType)
+		{
+		case ShaderVariableDataType::INT:
+			initInfo.addConstant(var.getName(), var.getValue<I32>());
+			break;
+		case ShaderVariableDataType::IVEC2:
+			initInfo.addConstant(var.getName(), var.getValue<IVec2>());
+			break;
+		case ShaderVariableDataType::IVEC3:
+			initInfo.addConstant(var.getName(), var.getValue<IVec3>());
+			break;
+		case ShaderVariableDataType::IVEC4:
+			initInfo.addConstant(var.getName(), var.getValue<IVec4>());
+			break;
+		case ShaderVariableDataType::FLOAT:
+			initInfo.addConstant(var.getName(), var.getValue<F32>());
+			break;
+		case ShaderVariableDataType::VEC2:
+			initInfo.addConstant(var.getName(), var.getValue<Vec2>());
+			break;
+		case ShaderVariableDataType::VEC3:
+			initInfo.addConstant(var.getName(), var.getValue<Vec3>());
+			break;
+		case ShaderVariableDataType::VEC4:
+			initInfo.addConstant(var.getName(), var.getValue<Vec4>());
+			break;
+		default:
+			ANKI_ASSERT(0);
+		}
+	}
+
+	const ShaderProgramResourceVariant2* progVariant;
+	m_prog->getOrCreateVariant(initInfo, progVariant);
+
+	// Init the variant
+	initVariant(*progVariant, variant);
+
+	return variant;
+}
+
+void MaterialResource2::initVariant(const ShaderProgramResourceVariant2& progVariant, MaterialVariant2& variant) const
+{
+	// Find the block instance
+	const ShaderProgramBinary& binary = m_prog->getBinary();
+	const ShaderProgramBinaryVariant& binaryVariant = progVariant.getBinaryVariant();
+	const ShaderProgramBinaryBlockInstance* binaryBlockInstance = nullptr;
+	for(const ShaderProgramBinaryBlockInstance& instance : binaryVariant.m_uniformBlocks)
+	{
+		if(instance.m_index == m_uboIdx)
+		{
+			binaryBlockInstance = &instance;
+			break;
+		}
+	}
+
+	if(binaryBlockInstance == nullptr)
+	{
+		ANKI_RESOURCE_LOGF(
+			"The uniform block doesn't appear to be active for variant. Material: %s", getFilename().cstr());
+	}
+
+	// Some init
+	variant.m_prog = progVariant.getProgram();
+	variant.m_blockInfos.create(getAllocator(), m_vars.getSize());
+	variant.m_opaqueBindings.create(getAllocator(), m_vars.getSize(), -1);
+	variant.m_uniBlockSize = binaryBlockInstance->m_size;
+
+	// Initialize the block infos, active vars and bindings
+	for(const MaterialVariable2& var : m_vars)
+	{
+		if(var.m_constant)
+		{
+			for(const ShaderProgramResourceConstant2& c : m_prog->getConstants())
+			{
+				if(c.m_name == var.m_name)
+				{
+					variant.m_activeVars.set(var.m_index, progVariant.isConstantActive(c));
+				}
+			}
+		}
+		else if(var.inBlock())
+		{
+			for(const ShaderProgramBinaryVariableInstance& instance : binaryBlockInstance->m_variables)
+			{
+				if(instance.m_index == var.m_indexInBinary)
+				{
+					variant.m_activeVars.set(var.m_index, true);
+					variant.m_blockInfos[var.m_index] = instance.m_blockInfo;
+					break;
+				}
+			}
+		}
+		else
+		{
+			ANKI_ASSERT(var.isSampler() || var.isTexture());
+			for(const ShaderProgramBinaryOpaqueInstance& instance : binaryVariant.m_opaques)
+			{
+				if(instance.m_index == var.m_indexInBinary)
+				{
+					variant.m_activeVars.set(var.m_index, true);
+					variant.m_opaqueBindings[var.m_index] = I16(binary.m_opaques[instance.m_index].m_binding);
+					break;
+				}
+			}
+		}
+	}
+}
+
+U32 MaterialResource2::getInstanceGroupIdx(U32 instanceCount)
+{
+	ANKI_ASSERT(instanceCount > 0);
+	instanceCount = nextPowerOfTwo(instanceCount);
+	ANKI_ASSERT(instanceCount <= MAX_INSTANCES);
+	return U32(std::log2(F32(instanceCount)));
+}
+
+} // end namespace anki

+ 100 - 7
src/anki/resource/MaterialResource2.h

@@ -9,10 +9,14 @@
 #include <anki/resource/RenderingKey.h>
 #include <anki/resource/ShaderProgramResource2.h>
 #include <anki/Math.h>
+#include <anki/util/Enum.h>
 
 namespace anki
 {
 
+// Forward
+class XmlElement;
+
 /// @addtogroup resource
 /// @{
 
@@ -31,15 +35,42 @@ enum class BuiltinMaterialVariableId2 : U8
 	CAMERA_POSITION,
 	PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX,
 	GLOBAL_SAMPLER,
-	COUNT
+
+	COUNT,
+	FIRST = 0,
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(BuiltinMaterialVariableId2, inline)
 
 /// Holds the shader variables. It's a container for shader program variables that share the same name.
 class MaterialVariable2 : public NonCopyable
 {
 	friend class MaterialVariant2;
+	friend class MaterialResource2;
 
 public:
+	MaterialVariable2();
+
+	MaterialVariable2(MaterialVariable2&& b)
+	{
+		*this = std::move(b);
+	}
+
+	~MaterialVariable2();
+
+	MaterialVariable2& operator=(MaterialVariable2&& b)
+	{
+		m_name = std::move(b.m_name);
+		m_index = b.m_index;
+		m_indexInBinary = b.m_indexInBinary;
+		m_constant = b.m_constant;
+		m_instanced = b.m_instanced;
+		m_dataType = b.m_dataType;
+		m_builtin = b.m_builtin;
+		m_mat4 = b.m_mat4;
+		m_tex = std::move(b.m_tex);
+		return *this;
+	}
+
 	/// Get the builtin info.
 	BuiltinMaterialVariableId2 getBuiltin() const
 	{
@@ -70,14 +101,40 @@ public:
 		return !m_constant && !isTexture() && !isSampler();
 	}
 
+	Bool isConstant() const
+	{
+		return m_constant;
+	}
+
+	Bool isInstanced() const
+	{
+		return m_instanced;
+	}
+
+	Bool isBuildin() const
+	{
+		return m_builtin != BuiltinMaterialVariableId2::NONE;
+	}
+
+	ShaderVariableDataType getDataType() const
+	{
+		ANKI_ASSERT(m_dataType != ShaderVariableDataType::NONE);
+		return m_dataType;
+	}
+
 protected:
+	static constexpr F32 NO_VALUE = 1234.5678f;
+
 	String m_name;
 	U32 m_index = MAX_U32;
+	U32 m_indexInBinary = MAX_U32;
 	Bool m_constant = false;
 	Bool m_instanced = false;
 	ShaderVariableDataType m_dataType = ShaderVariableDataType::NONE;
 	BuiltinMaterialVariableId2 m_builtin = BuiltinMaterialVariableId2::NONE;
 
+	/// Values for non-builtins
+	/// @{
 	union
 	{
 		I32 m_int;
@@ -97,6 +154,12 @@ protected:
 	};
 
 	TextureResourcePtr m_tex;
+	/// @}
+
+	Bool valueSetByMaterial() const
+	{
+		return m_tex.isCreated() || m_mat4(3, 3) != NO_VALUE;
+	}
 };
 
 // Specialize the MaterialVariable::getValue
@@ -137,6 +200,8 @@ inline const TextureResourcePtr& MaterialVariable2::getValue() const
 /// Material variant.
 class MaterialVariant2 : public NonCopyable
 {
+	friend class MaterialResource2;
+
 public:
 	/// Return true of the the variable is active.
 	Bool isVariableActive(const MaterialVariable2& var) const
@@ -189,7 +254,7 @@ private:
 ///		</mutators>]
 ///
 ///		[<inputs>
-///			<input shaderInput="name to shaderProg input" value="values"/> (1)
+///			<input shaderVar="name to shaderProg var" value="values"/> (1)
 ///		</inputs>]
 /// </material>
 /// @endcode
@@ -226,11 +291,9 @@ public:
 
 	Bool isInstanced() const
 	{
-		return m_instanced;
+		return m_instancingMutator != nullptr;
 	}
 
-	const MaterialVariant2& getOrCreateVariant(const RenderingKey& key) const;
-
 	ConstWeakArray<MaterialVariable2> getVariables() const
 	{
 		return m_vars;
@@ -241,20 +304,50 @@ public:
 		return m_descriptorSetIdx;
 	}
 
+	const MaterialVariant2& getOrCreateVariant(const RenderingKey& key) const;
+
 private:
+	class SubMutation
+	{
+	public:
+		const ShaderProgramResourceMutator2* m_mutator;
+		MutatorValue m_value;
+	};
+
 	ShaderProgramResource2Ptr m_prog;
 
+	const ShaderProgramResourceMutator2* m_instancingMutator = nullptr;
+	const ShaderProgramResourceMutator2* m_passMutator = nullptr;
+	const ShaderProgramResourceMutator2* m_lodMutator = nullptr;
+	const ShaderProgramResourceMutator2* m_bonesMutator = nullptr;
+	const ShaderProgramResourceMutator2* m_velocityMutator = nullptr;
+
 	Bool m_shadow = true;
 	Bool m_forwardShading = false;
-	Bool m_instanced = false;
 	U8 m_lodCount = 1;
-	U8 m_descriptorSetIdx = 0; ///< Cache the value from the m_prog;
+	U8 m_descriptorSetIdx = 0; ///< The material set.
+	U32 m_uboIdx = MAX_U32; ///< The b_ankiMaterial UBO inside the binary.
 
 	/// Matrix of variants.
 	mutable Array5d<MaterialVariant2, U(Pass::COUNT), MAX_LOD_COUNT, MAX_INSTANCE_GROUPS, 2, 2> m_variantMatrix;
 	mutable RWMutex m_variantMatrixMtx;
 
 	DynamicArray<MaterialVariable2> m_vars;
+
+	DynamicArray<SubMutation> m_nonBuiltinsMutation;
+
+	ANKI_USE_RESULT Error createVars();
+
+	static ANKI_USE_RESULT Error parseVariable(CString fullVarName, Bool& instanced, U32& idx, CString& name);
+
+	/// Parse whatever is inside the <inputs> tag.
+	ANKI_USE_RESULT Error parseInputs(XmlElement inputsEl, Bool async);
+
+	ANKI_USE_RESULT Error parseMutators(XmlElement mutatorsEl);
+
+	static U32 getInstanceGroupIdx(U32 instanceCount);
+
+	void initVariant(const ShaderProgramResourceVariant2& progVariant, MaterialVariant2& variant) const;
 };
 /// @}
 

+ 1 - 0
src/anki/resource/ShaderProgramResource2.cpp

@@ -239,6 +239,7 @@ void ShaderProgramResource2::initVariant(
 		binaryVariant = &binary.m_variants[0];
 	}
 	ANKI_ASSERT(binaryVariant);
+	variant.m_binaryVariant = binaryVariant;
 
 	// Set the constannt values
 	Array2d<ShaderSpecializationConstValue, U(ShaderType::COUNT), 64> constValues;

+ 24 - 0
src/anki/resource/ShaderProgramResource2.h

@@ -30,6 +30,18 @@ class ShaderProgramResourceMutator2 : public NonCopyable
 public:
 	CString m_name;
 	ConstWeakArray<MutatorValue> m_values;
+
+	Bool valueExists(MutatorValue v) const
+	{
+		for(MutatorValue x : m_values)
+		{
+			if(v == x)
+			{
+				return true;
+			}
+		}
+		return false;
+	}
 };
 
 /// Shader program resource variant.
@@ -62,8 +74,15 @@ public:
 		return m_activeConsts.get(var.m_index);
 	}
 
+	const ShaderProgramBinaryVariant& getBinaryVariant() const
+	{
+		ANKI_ASSERT(m_binaryVariant);
+		return *m_binaryVariant;
+	}
+
 private:
 	ShaderProgramPtr m_prog;
+	const ShaderProgramBinaryVariant* m_binaryVariant = nullptr;
 	BitSet<128, U64> m_activeConsts = {false};
 };
 
@@ -121,6 +140,11 @@ public:
 		return m_shaderStages;
 	}
 
+	const ShaderProgramBinary& getBinary() const
+	{
+		return m_binary.getBinary();
+	}
+
 	/// Get or create a graphics shader program variant.
 	/// @note It's thread-safe.
 	void getOrCreateVariant(