Browse Source

GPU particles: Add the resource

Panagiotis Christopoulos Charitos 1 month ago
parent
commit
f264344eee

+ 1 - 6
AnKi/Editor/EditorUi.h

@@ -7,12 +7,7 @@
 
 #include <AnKi/Ui.h>
 #include <AnKi/Editor/ImageViewerUi.h>
-
-namespace std {
-namespace filesystem {
-class path;
-}
-} // namespace std
+#include <filesystem>
 
 namespace anki {
 

+ 0 - 2
AnKi/Gr/BackendCommon/Functions.cpp

@@ -95,7 +95,6 @@ public:
 		static constexpr Bool kValue = rowCount * columnCount > 4; \
 	};
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
 
 template<typename T, Bool isMatrix = IsShaderVarDataTypeAMatrix<T>::kValue>
 class WriteShaderBlockMemory
@@ -130,7 +129,6 @@ void writeShaderBlockMemory(ShaderVariableDataType type, const ShaderVariableBlo
 		WriteShaderBlockMemory<type>()(varBlkInfo, elements, elementsCount, buffBegin, buffEnd); \
 		break;
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
 
 	default:
 		ANKI_ASSERT(0);

+ 0 - 2
AnKi/Gr/BackendCommon/Functions.h

@@ -41,9 +41,7 @@ ShaderVariableDataType getShaderVariableTypeFromTypename();
 	{ \
 		return ShaderVariableDataType::k##type; \
 	}
-
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
 
 /// Populate the memory of a variable that is inside a shader block.
 void writeShaderBlockMemory(ShaderVariableDataType type, const ShaderVariableBlockInfo& varBlkInfo, const void* elements, U32 elementsCount,

+ 0 - 2
AnKi/Gr/Common.cpp

@@ -14,8 +14,6 @@ inline constexpr ShaderVariableDataTypeInfo kShaderVariableDataTypeInfos[] = {
 #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) {ANKI_STRINGIZE(type), sizeof(type), false, isIntagralType},
 #define ANKI_SVDT_MACRO_OPAQUE(constant, type) {ANKI_STRINGIZE(type), kMaxU32, true, false},
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
-#undef ANKI_SVDT_MACRO_OPAQUE
 };
 
 const ShaderVariableDataTypeInfo& getShaderVariableDataTypeInfo(ShaderVariableDataType type)

+ 0 - 2
AnKi/Gr/Common.h

@@ -607,8 +607,6 @@ enum class ShaderVariableDataType : U8
 #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) k##type,
 #define ANKI_SVDT_MACRO_OPAQUE(constant, type) k##constant,
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
-#undef ANKI_SVDT_MACRO_OPAQUE
 
 	// Derived
 	kCount,

+ 4 - 0
AnKi/Gr/ShaderVariableDataType.def.h

@@ -48,6 +48,8 @@ ANKI_SVDT_MACRO(Vec4, F32, 4, 1, false)
 ANKI_SVDT_MACRO(Mat3, F32, 3, 3, false)
 ANKI_SVDT_MACRO(Mat3x4, F32, 3, 4, false)
 ANKI_SVDT_MACRO(Mat4, F32, 4, 4, false)
+
+#	undef ANKI_SVDT_MACRO
 #endif
 
 // ANKI_SVDT_MACRO_OPAQUE(const, varType)
@@ -69,4 +71,6 @@ ANKI_SVDT_MACRO_OPAQUE(ImageCube, imageCube)
 ANKI_SVDT_MACRO_OPAQUE(ImageCubeArray, imageCubeArray)
 
 ANKI_SVDT_MACRO_OPAQUE(Sampler, sampler)
+
+#	undef ANKI_SVDT_MACRO_OPAQUE
 #endif

+ 0 - 3
AnKi/Resource/MaterialResource.cpp

@@ -31,7 +31,6 @@ public:
 		static constexpr Bool kValue = rowCount * columnCount > 1; \
 	};
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
 
 template<typename T, Bool isArray = IsShaderVarDataTypeAnArray<T>::kValue>
 class GetAttribute
@@ -477,7 +476,6 @@ Error MaterialResource::parseInput(XmlElement inputEl, Bool async, BitSet<128>&
 		ANKI_CHECK(GetAttribute<type>()(inputEl, foundVar->ANKI_CONCATENATE(m_, type))); \
 		break;
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
 		default:
 			ANKI_ASSERT(0);
 			break;
@@ -507,7 +505,6 @@ void MaterialResource::prefillLocalConstants()
 		memcpy(static_cast<U8*>(m_prefilledLocalConstants) + var.m_offsetInLocalConstants, &var.m_##type, sizeof(type)); \
 		break;
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
 		default:
 			ANKI_ASSERT(0);
 			break;

+ 0 - 4
AnKi/Resource/MaterialResource.h

@@ -99,7 +99,6 @@ protected:
 	{
 #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) type ANKI_CONCATENATE(m_, type);
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
 	};
 
 	ImageResourcePtr m_image;
@@ -114,11 +113,8 @@ protected:
 		ANKI_ASSERT(m_dataType == ShaderVariableDataType::k##type); \
 		return member; \
 	}
-
 #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) ANKI_SPECIALIZE_GET_VALUE(type, ANKI_CONCATENATE(m_, type))
 #include <AnKi/Gr/ShaderVariableDataType.def.h>
-#undef ANKI_SVDT_MACRO
-
 #undef ANKI_SPECIALIZE_GET_VALUE
 
 template<>

+ 245 - 0
AnKi/Resource/ParticleEmitterResource2.cpp

@@ -0,0 +1,245 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Resource/ParticleEmitterResource2.h>
+#include <AnKi/Resource/ResourceManager.h>
+#include <AnKi/Resource/ShaderProgramResource.h>
+#include <AnKi/Util/Xml.h>
+#include <AnKi/Gr/GrManager.h>
+
+namespace anki {
+
+static Bool mutatorValueExists(const ShaderBinaryMutator& m, MutatorValue val)
+{
+	for(MutatorValue v : m.m_values)
+	{
+		if(v == val)
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
+Error ParticleEmitterResource2::load(const ResourceFilename& filename, Bool async)
+{
+	ResourceXmlDocument doc;
+	XmlElement el;
+	ANKI_CHECK(openFileParseXml(filename, doc));
+
+	// <material>
+	XmlElement rootEl;
+	ANKI_CHECK(doc.getChildElement("particleEmitter", rootEl));
+
+	// <shaderPrograms>
+	XmlElement shaderProgramEl;
+	ANKI_CHECK(rootEl.getChildElement("shaderProgram", shaderProgramEl));
+	ANKI_CHECK(parseShaderProgram(shaderProgramEl, async));
+
+	// Inputs
+	ANKI_CHECK(rootEl.getChildElementOptional("inputs", el));
+	if(el)
+	{
+		XmlElement inputEl;
+		ANKI_CHECK(el.getChildElement("input", inputEl));
+		do
+		{
+			ANKI_CHECK(parseInput(inputEl));
+			ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
+		} while(inputEl);
+	}
+
+	return Error::kNone;
+}
+
+Error ParticleEmitterResource2::parseShaderProgram(XmlElement shaderProgramEl, Bool async)
+{
+	// name
+	CString shaderName;
+	ANKI_CHECK(shaderProgramEl.getAttributeText("name", shaderName));
+
+	ResourceString fname;
+	fname.sprintf("ShaderBinaries/%s.ankiprogbin", shaderName.cstr());
+
+	ANKI_CHECK(ResourceManager::getSingleton().loadResource(fname, m_prog, async));
+
+	// <mutation>
+	ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
+	XmlElement mutatorsEl;
+	ANKI_CHECK(shaderProgramEl.getChildElementOptional("mutation", mutatorsEl));
+	if(mutatorsEl)
+	{
+		ANKI_CHECK(parseMutators(mutatorsEl, variantInitInfo));
+	}
+
+	// Create the GR prog
+	variantInitInfo.addMutation("ANKI_WAVE_SIZE", GrManager::getSingleton().getDeviceCapabilities().m_maxWaveSize);
+	const ShaderProgramResourceVariant* variant;
+	m_prog->getOrCreateVariant(variantInitInfo, variant);
+
+	if(!variant)
+	{
+		ANKI_RESOURCE_LOGE("Shader variant creation failed");
+		return Error::kUserData;
+	}
+
+	m_grProg.reset(&variant->getProgram());
+
+	// Create the properties
+	{
+		// Find struct
+		const ShaderBinary& binary = m_prog->getBinary();
+		const ShaderBinaryStruct* propsStruct = nullptr;
+		for(const ShaderBinaryStruct& strct : binary.m_structs)
+		{
+			if(CString(strct.m_name.getBegin()) == "AnKiParticleEmitterProperties")
+			{
+				propsStruct = &strct;
+				break;
+			}
+		}
+
+		if(!propsStruct)
+		{
+			ANKI_RESOURCE_LOGE("AnKiParticleEmitterProperties struct not found");
+			return Error::kUserData;
+		}
+
+		m_prefilledAnKiParticleEmitterProperties.resize(propsStruct->m_size, 0_U8);
+
+		for(U32 i = 0; i < propsStruct->m_members.getSize(); ++i)
+		{
+			const ShaderBinaryStructMember& member = propsStruct->m_members[i];
+			const U32 memberSize = getShaderVariableDataTypeInfo(member.m_type).m_size;
+
+			ANKI_ASSERT(member.m_offset + memberSize <= propsStruct->m_size);
+			memcpy(m_prefilledAnKiParticleEmitterProperties.getBegin() + member.m_offset, member.m_defaultValues.getBegin(), memberSize);
+		}
+	}
+
+	return Error::kNone;
+}
+
+Error ParticleEmitterResource2::parseMutators(XmlElement mutatorsEl, ShaderProgramResourceVariantInitInfo& variantInitInfo)
+{
+	XmlElement mutatorEl;
+	ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
+
+	U32 mutatorCount = 0;
+	ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
+	++mutatorCount;
+	ANKI_ASSERT(mutatorCount > 0);
+
+	do
+	{
+		// name
+		CString mutatorName;
+		ANKI_CHECK(mutatorEl.getAttributeText("name", mutatorName));
+		if(mutatorName.isEmpty())
+		{
+			ANKI_RESOURCE_LOGE("Mutator name is empty");
+			return Error::kUserData;
+		}
+
+		if(mutatorName.find("ANKI_") == 0)
+		{
+			ANKI_RESOURCE_LOGE("Mutators can't start with ANKI_: %s", mutatorName.cstr());
+			return Error::kUserData;
+		}
+
+		// value
+		MutatorValue mval;
+		ANKI_CHECK(mutatorEl.getAttributeNumber("value", mval));
+
+		// Find mutator
+		const ShaderBinaryMutator* mutator = m_prog->tryFindMutator(mutatorName);
+
+		if(!mutator)
+		{
+			ANKI_RESOURCE_LOGE("Mutator not found in program %s", &mutatorName[0]);
+			return Error::kUserData;
+		}
+
+		if(!mutatorValueExists(*mutator, mval))
+		{
+			ANKI_RESOURCE_LOGE("Value %d is not part of the mutator %s", mval, mutatorName.cstr());
+			return Error::kUserData;
+		}
+
+		variantInitInfo.addMutation(mutatorName, mval);
+
+		// Advance
+		ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
+	} while(mutatorEl);
+
+	return Error::kNone;
+}
+
+Error ParticleEmitterResource2::parseInput(XmlElement inputEl)
+{
+	// Get var name
+	CString varName;
+	ANKI_CHECK(inputEl.getAttributeText("name", varName));
+
+	// Find struct
+	const ShaderBinary& binary = m_prog->getBinary();
+	const ShaderBinaryStruct* propsStruct = nullptr;
+	for(const ShaderBinaryStruct& strct : binary.m_structs)
+	{
+		if(CString(strct.m_name.getBegin()) == "AnKiParticleEmitterProperties")
+		{
+			propsStruct = &strct;
+			break;
+		}
+	}
+	ANKI_ASSERT(propsStruct);
+
+	// Find the member
+	const ShaderBinaryStructMember* foundMember = nullptr;
+	for(U32 i = 0; i < propsStruct->m_members.getSize(); ++i)
+	{
+		const ShaderBinaryStructMember& member = propsStruct->m_members[i];
+		const CString memberName = member.m_name.getBegin();
+		if(memberName == varName)
+		{
+			foundMember = &member;
+			break;
+		}
+	}
+
+	if(!foundMember)
+	{
+		ANKI_RESOURCE_LOGE("Input not found in AnKiParticleEmitterProperties: %s", varName.cstr());
+		return Error::kUserData;
+	}
+
+	const U32 memberSize = getShaderVariableDataTypeInfo(foundMember->m_type).m_size;
+	const U32 memberOffset = foundMember->m_offset;
+
+	// Set the value
+	switch(foundMember->m_type)
+	{
+#define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \
+	case ShaderVariableDataType::k##type: \
+	{ \
+		Array<baseType, rowCount * columnCount> arr; \
+		ANKI_CHECK(inputEl.getNumbers(arr)); \
+		ANKI_ASSERT(memberOffset + memberSize <= m_prefilledAnKiParticleEmitterProperties.getSize()); \
+		ANKI_ASSERT(memberSize == sizeof(arr)); \
+		memcpy(m_prefilledAnKiParticleEmitterProperties.getBegin() + memberOffset, arr.getBegin(), memberSize); \
+		break; \
+	}
+#include <AnKi/Gr/ShaderVariableDataType.def.h>
+
+	default:
+		ANKI_ASSERT(0);
+		break;
+	}
+
+	return Error::kNone;
+}
+
+} // end namespace anki

+ 103 - 0
AnKi/Resource/ParticleEmitterResource2.h

@@ -0,0 +1,103 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Resource/ResourceObject.h>
+#include <AnKi/Math.h>
+
+namespace anki {
+
+class XmlElement;
+class ShaderProgramResourceVariantInitInfo;
+
+/// @addtogroup resource
+/// @{
+
+/// @memberof ParticleEmitterResource2
+class ParticleEmitterProperty
+{
+public:
+	ParticleEmitterProperty() = default;
+
+	ParticleEmitterProperty(const ParticleEmitterProperty&) = delete; // Non-copyable
+
+	ParticleEmitterProperty& operator=(const ParticleEmitterProperty&) = delete; // Non-copyable
+
+	CString getName() const
+	{
+		return m_name;
+	}
+
+	template<typename T>
+	const T& getValue() const;
+
+private:
+	ResourceString m_name;
+	U32 m_offsetInAnKiParticleEmitterProperties = kMaxU32;
+	ShaderVariableDataType m_dataType = ShaderVariableDataType::kNone;
+
+	union
+	{
+#define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) type ANKI_CONCATENATE(m_, type);
+#include <AnKi/Gr/ShaderVariableDataType.def.h>
+	};
+};
+
+// Specialize the ParticleEmitterProperty::getValue
+#define ANKI_SPECIALIZE_GET_VALUE(type, member) \
+	template<> \
+	inline const type& ParticleEmitterProperty::getValue<type>() const \
+	{ \
+		ANKI_ASSERT(m_dataType == ShaderVariableDataType::k##type); \
+		return member; \
+	}
+#define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) ANKI_SPECIALIZE_GET_VALUE(type, ANKI_CONCATENATE(m_, type))
+#include <AnKi/Gr/ShaderVariableDataType.def.h>
+#undef ANKI_SPECIALIZE_GET_VALUE
+
+/// This is the a particle emitter resource containing shader and properties.
+/// XML format:
+/// @code
+///	<particleEmitter>
+/// 	<shaderProgram name="name of the shader" />
+///			[<mutation>
+///				<mutator name="str" value="value"/>
+///			</mutation>]
+///		</shaderProgram>
+///
+///		[<inputs>
+///			<input name="name in AnKiParticleEmitterProperties struct" value="value(s)"/>
+///		</inputs>]
+///	</particleEmitter>
+/// @endcode
+class ParticleEmitterResource2 : public ResourceObject
+{
+public:
+	ParticleEmitterResource2(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
+
+	~ParticleEmitterResource2() = default;
+
+	/// Load it
+	Error load(const ResourceFilename& filename, Bool async);
+
+private:
+	ResourceDynamicArray<U8> m_prefilledAnKiParticleEmitterProperties;
+
+	ShaderProgramResourcePtr m_prog;
+	ShaderProgramPtr m_grProg;
+
+	Error parseShaderProgram(XmlElement shaderProgramEl, Bool async);
+
+	Error parseMutators(XmlElement mutatorsEl, ShaderProgramResourceVariantInitInfo& variantInitInfo);
+
+	Error parseInput(XmlElement inputEl);
+};
+/// @}
+
+} // end namespace anki

+ 1 - 0
AnKi/Resource/ResourceManager.cpp

@@ -17,6 +17,7 @@
 #include <AnKi/Resource/ScriptResource.h>
 #include <AnKi/Resource/DummyResource.h>
 #include <AnKi/Resource/ParticleEmitterResource.h>
+#include <AnKi/Resource/ParticleEmitterResource2.h>
 #include <AnKi/Resource/ImageResource.h>
 #include <AnKi/Resource/GenericResource.h>
 #include <AnKi/Resource/ImageAtlasResource.h>

+ 2 - 0
AnKi/Resource/Resources.def.h

@@ -21,6 +21,8 @@ ANKI_INSTANTIATE_RESOURCE(SkeletonResource)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(ParticleEmitterResource)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(ParticleEmitterResource2)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(ScriptResource)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(DummyResource)

+ 2 - 0
AnKi/ShaderCompiler/ShaderBinary.h

@@ -20,6 +20,7 @@ public:
 	Array<Char, kMaxShaderBinaryNameLength + 1> m_name = {};
 	U32 m_offset = kMaxU32;
 	ShaderVariableDataType m_type = ShaderVariableDataType::kNone;
+	Array<U8, 16> m_defaultValues = {};
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
@@ -27,6 +28,7 @@ public:
 		s.doArray("m_name", offsetof(ShaderBinaryStructMember, m_name), &self.m_name[0], self.m_name.getSize());
 		s.doValue("m_offset", offsetof(ShaderBinaryStructMember, m_offset), self.m_offset);
 		s.doValue("m_type", offsetof(ShaderBinaryStructMember, m_type), self.m_type);
+		s.doArray("m_defaultValues", offsetof(ShaderBinaryStructMember, m_defaultValues), &self.m_defaultValues[0], self.m_defaultValues.getSize());
 	}
 
 	template<typename TDeserializer>

+ 3 - 2
AnKi/ShaderCompiler/ShaderBinary.xml

@@ -11,6 +11,7 @@
 				<member name="m_name" type="Char" array_size="kMaxShaderBinaryNameLength + 1" constructor="= {}" />
 				<member name="m_offset" type="U32" constructor="= kMaxU32" />
 				<member name="m_type" type="ShaderVariableDataType" constructor="= ShaderVariableDataType::kNone" />
+				<member name="m_defaultValues" type="U8" array_size="16" constructor="= {}" />
 			</members>
 		</class>
 
@@ -21,7 +22,7 @@
 				<member name="m_size" type="U32" />
 			</members>
 		</class>
-		
+
 		<class name="ShaderBinaryCodeBlock" comment="Contains the IR (SPIR-V or DXIL)">
 			<members>
 				<member name="m_binary" type="WeakArray&lt;U8&gt;" />
@@ -49,7 +50,7 @@
 				<member name="m_codeBlockIndices" type="U32" array_size="U32(ShaderType::kCount)" constructor="= {}" comment="Points to ShaderBinary::m_codeBlocks. If the shader type is not present the value is kMaxU32" />
 			</members>
 		</class>
-		
+
 		<class name="ShaderBinaryVariant">
 			<members>
 				<member name="m_techniqueCodeBlocks" type="WeakArray&lt;ShaderBinaryTechniqueCodeBlocks&gt;" comment="One entry per technique" />

+ 2 - 0
AnKi/ShaderCompiler/ShaderCompiler.cpp

@@ -526,6 +526,8 @@ static Error compileShaderProgramInternal(CString fname, Bool spirv, Bool debugI
 			memcpy(outm.m_name.getBegin(), inm.m_name.cstr(), inm.m_name.getLength() + 1);
 			outm.m_offset = inm.m_offset;
 			outm.m_type = inm.m_type;
+			static_assert(sizeof(outm.m_defaultValues) == sizeof(inm.m_defaultValues));
+			memcpy(outm.m_defaultValues.getBegin(), inm.m_defaultValues.getBegin(), sizeof(inm.m_defaultValues));
 		}
 
 		out.m_size = in.m_members.getBack().m_offset + getShaderVariableDataTypeInfo(in.m_members.getBack().m_type).m_size;

+ 40 - 1
AnKi/ShaderCompiler/ShaderParser.cpp

@@ -556,7 +556,7 @@ Error ShaderParser::parsePragmaMember(const ShaderCompilerString* begin, const S
 {
 	ANKI_ASSERT(m_insideStruct);
 	const U tokenCount = U(end - begin);
-	if(tokenCount != 2)
+	if(tokenCount < 2)
 	{
 		ANKI_PP_ERROR_MALFORMED();
 	}
@@ -597,6 +597,45 @@ Error ShaderParser::parsePragmaMember(const ShaderCompilerString* begin, const S
 	++begin;
 	member.m_name = *begin;
 
+	// Default values
+	++begin;
+	if(begin != end)
+	{
+		const ShaderVariableDataTypeInfo& typeInfo = getShaderVariableDataTypeInfo(member.m_type);
+		if(U32(end - begin) != typeInfo.m_size / sizeof(U32))
+		{
+			ANKI_PP_ERROR_MALFORMED_MSG("Incorrect number of initial values");
+		}
+
+		U32 offset = 0;
+		while(begin != end)
+		{
+			F32 f;
+			U32 u;
+			if(typeInfo.m_isIntegral)
+			{
+				if(begin->toNumber(u))
+				{
+					ANKI_PP_ERROR_MALFORMED_MSG("Type conversion failed");
+				}
+				memcpy(member.m_defaultValues.getBegin() + offset, &u, sizeof(u));
+				offset += sizeof(u);
+			}
+			else
+			{
+				if(begin->toNumber(f))
+				{
+					ANKI_PP_ERROR_MALFORMED_MSG("Type conversion failed");
+				}
+				memcpy(member.m_defaultValues.getBegin() + offset, &f, sizeof(f));
+				offset += sizeof(f);
+			}
+
+			++begin;
+		}
+		ANKI_ASSERT(offset == typeInfo.m_size);
+	}
+
 	// Rest
 	member.m_offset = (structure.m_members.getSize())
 						  ? structure.m_members.getBack().m_offset + getShaderVariableDataTypeInfo(structure.m_members.getBack().m_type).m_size

+ 2 - 1
AnKi/ShaderCompiler/ShaderParser.h

@@ -33,6 +33,7 @@ public:
 	ShaderCompilerString m_name;
 	ShaderVariableDataType m_type;
 	U32 m_offset = kMaxU32;
+	Array<U8, 16> m_defaultValues = {};
 };
 
 /// @memberof ShaderParser
@@ -65,7 +66,7 @@ public:
 /// #pragma anki extra_compiler_args ARG0 [ARG1 [ARG2...]]
 ///
 /// #pragma anki struct NAME
-/// #	pragma anki member TYPE NAME
+/// #	pragma anki member TYPE NAME [DEFAULT_VALUE0 [DEFAULT_VALUE1 ...]]
 /// 	...
 /// #pragma anki struct_end
 ///

+ 20 - 20
AnKi/Shaders/GpuParticlesGass.ankiprog

@@ -13,34 +13,34 @@
 
 #include <AnKi/Shaders/GpuParticlesCommon.hlsl>
 
+// clang-format off
 #pragma anki struct AnKiParticleEmitterProperties
+#	pragma anki member F32 m_minParticleLife 1.0
+#	pragma anki member F32 m_maxParticleLife 1.0
 
-#pragma anki member F32 m_minParticleLife
-#pragma anki member F32 m_maxParticleLife
+#	pragma anki member Vec3 m_minGravityDirection 0.0 -1.0 0.0
+#	pragma anki member Vec3 m_maxGravityDirection 0.0 -1.0 0.0
 
-#pragma anki member Vec3 m_minGravityDirection
-#pragma anki member Vec3 m_maxGravityDirection
+#	pragma anki member F32 m_minGravityMagnitude 9.8
+#	pragma anki member F32 m_maxGravityMagnitude 9.8
 
-#pragma anki member F32 m_minGravityMagnitude
-#pragma anki member F32 m_maxGravityMagnitude
+#	pragma anki member F32 m_minMass 1.0
+#	pragma anki member F32 m_maxMass 1.0
 
-#pragma anki member F32 m_minMass
-#pragma anki member F32 m_maxMass
+#	pragma anki member F32 m_minInitialSize 1.0
+#	pragma anki member F32 m_maxInitialSize 1.0
+#	pragma anki member F32 m_minFinalSize 1.0
+#	pragma anki member F32 m_maxFinalSize 1.0
 
-#pragma anki member Vec3 m_minInitialSize
-#pragma anki member Vec3 m_maxInitialSize
-#pragma anki member Vec3 m_minFinalSize
-#pragma anki member Vec3 m_maxFinalSize
-
-#pragma anki member F32 m_minInitialAlpha
-#pragma anki member F32 m_maxInitialAlpha
-#pragma anki member F32 m_minFinalAlpha
-#pragma anki member F32 m_maxFinalAlpha
-
-#pragma anki member Vec3 m_minStartingPosition
-#pragma anki member Vec3 m_maxStartingPosition
+#	pragma anki member F32 m_minInitialAlpha 1.0
+#	pragma anki member F32 m_maxInitialAlpha 1.0
+#	pragma anki member F32 m_minFinalAlpha 1.0
+#	pragma anki member F32 m_maxFinalAlpha 1.0
 
+#	pragma anki member Vec3 m_minStartingPosition 0.0 0.0 0.0
+#	pragma anki member Vec3 m_maxStartingPosition 0.0 0.0 0.0
 #pragma anki struct_end
+// clang-format on
 
 #define GRAVITY_PROP ParticleProperty::kUserDefined0
 #define ALPHA_PROP ParticleProperty::kUserDefined1 // Initial alpha, final alpha, current alpha