Browse Source

GPU particles: More work

Panagiotis Christopoulos Charitos 1 month ago
parent
commit
12db512f4c

+ 2 - 2
AnKi/Editor/EditorUi.cpp

@@ -479,7 +479,7 @@ void EditorUi::sceneNodePropertiesWindow()
 			{
 				switch(SceneComponentType(state.m_selectedSceneComponentType))
 				{
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) \
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) \
 	case SceneComponentType::k##name: \
 		node.newComponent<name##Component>(); \
 		break;
@@ -536,7 +536,7 @@ void EditorUi::sceneNodePropertiesWindow()
 					CString icon = ICON_MDI_TOY_BRICK;
 					switch(comp.getType())
 					{
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon_) \
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon_) \
 	case SceneComponentType::k##name: \
 		icon = ANKI_CONCATENATE(ICON_MDI_, icon_); \
 		break;

+ 1 - 1
AnKi/Editor/EditorUi.h

@@ -13,7 +13,7 @@ namespace anki {
 
 // Forward
 class SceneNode;
-#define ANKI_DEFINE_SCENE_COMPONENT(class_, weight, icon) class class_##Component;
+#define ANKI_DEFINE_SCENE_COMPONENT(class_, weight, sceneNodeCanHaveMany, icon) class class_##Component;
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 
 /// @addtogroup editor

+ 1 - 1
AnKi/Scene/Components/MeshComponent.cpp

@@ -139,7 +139,7 @@ void MeshComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool
 			if(GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled)
 			{
 				const U64 address = mesh.getBottomLevelAccelerationStructure(l, submeshIdx)->getGpuAddress();
-				memcpy(&meshLod.m_blasAddress, &address, sizeof(meshLod.m_blasAddress));
+				memcpy(&meshLod.m_blasAddress[0], &address, sizeof(meshLod.m_blasAddress));
 
 				meshLod.m_tlasInstanceMask = 0xFFFFFFFF;
 			}

+ 85 - 7
AnKi/Scene/Components/ParticleEmitter2Component.cpp

@@ -5,8 +5,10 @@
 
 #include <AnKi/Scene/Components/ParticleEmitter2Component.h>
 #include <AnKi/Scene/Components/MeshComponent.h>
+#include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Resource/ParticleEmitterResource2.h>
 #include <AnKi/Resource/ResourceManager.h>
+#include <AnKi/Resource/MeshResource.h>
 #include <AnKi/GpuMemory/RebarTransientMemoryPool.h>
 
 namespace anki {
@@ -19,10 +21,18 @@ public:
 	UnifiedGeometryBufferAllocation m_quadUvs;
 	UnifiedGeometryBufferAllocation m_quadIndices;
 
+	GpuSceneArrays::MeshLod::Allocation m_gpuSceneMeshLods;
+
 	U32 m_userCount = 0;
 	SpinLock m_mtx;
 
 	void init()
+	{
+		initGeom();
+		initGpuScene();
+	}
+
+	void initGeom()
 	{
 		// Allocate and populate a quad
 		const U32 vertCount = 4;
@@ -74,11 +84,32 @@ public:
 		GrManager::getSingleton().submit(cmdb.get());
 	}
 
+	void initGpuScene()
+	{
+		GpuSceneMeshLod meshLod = {};
+		meshLod.m_vertexOffsets[U32(VertexStreamId::kPosition)] =
+			m_quadPositions.getOffset() / getFormatInfo(kMeshRelatedVertexStreamFormats[VertexStreamId::kPosition]).m_texelSize;
+		meshLod.m_vertexOffsets[U32(VertexStreamId::kUv)] =
+			m_quadUvs.getOffset() / getFormatInfo(kMeshRelatedVertexStreamFormats[VertexStreamId::kUv]).m_texelSize;
+		meshLod.m_indexCount = 6;
+		meshLod.m_firstIndex = m_quadIndices.getOffset() / sizeof(U16);
+		meshLod.m_positionScale = 1.0f;
+		meshLod.m_positionTranslation = Vec3(-0.5f, -0.5f, 0.0f);
+
+		Array<GpuSceneMeshLod, kMaxLodCount> meshLods;
+		meshLods.fill(meshLod);
+
+		m_gpuSceneMeshLods.allocate();
+		m_gpuSceneMeshLods.uploadToGpuScene(meshLods);
+	}
+
 	void deinit()
 	{
 		UnifiedGeometryBuffer::getSingleton().deferredFree(m_quadPositions);
 		UnifiedGeometryBuffer::getSingleton().deferredFree(m_quadUvs);
 		UnifiedGeometryBuffer::getSingleton().deferredFree(m_quadIndices);
+
+		m_gpuSceneMeshLods.free();
 	}
 
 	void addUser()
@@ -153,14 +184,34 @@ void ParticleEmitter2Component::onOtherComponentRemovedOrAdded(SceneComponent* o
 
 	if(other->getType() == SceneComponentType::kMesh)
 	{
-		bookkeepComponent(m_meshComponents, other, added, m_meshComponentDirty);
+		bookkeepComponent(m_meshComponent, other, added);
+		m_meshComponentDirty = true;
 	}
 }
 
+Bool ParticleEmitter2Component::isValid() const
+{
+	Bool valid = !!m_particleEmitterResource;
+
+	if(m_geomType == ParticleGeometryType::kMeshComponent)
+	{
+		valid = valid && m_meshComponent->isValid();
+	}
+
+	return valid;
+}
+
 void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
-	if(!isValid())
+	if(!isValid()) [[unlikely]]
 	{
+		for(auto& s : m_gpuScene.m_particleStreams)
+		{
+			GpuSceneBuffer::getSingleton().deferredFree(s);
+		}
+		GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_aliveParticleIndices);
+		GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_anKiParticleEmitterProperties);
+		m_gpuScene.m_gpuSceneParticleEmitter.free();
 		return;
 	}
 
@@ -172,6 +223,7 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 	// From now on it's dirty and needs some kind of update
 
 	updated = true;
+	const ParticleEmitterResourceCommonProperties& commonProps = m_particleEmitterResource->getCommonProperties();
 
 	// Streams
 	if(m_resourceDirty)
@@ -179,14 +231,14 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 		for(ParticleProperty prop : EnumIterable<ParticleProperty>())
 		{
 			GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_particleStreams[prop]);
-			m_gpuScene.m_particleStreams[prop] = GpuSceneBuffer::getSingleton().allocate(kParticlePropertySize[prop], alignof(U32));
+			m_gpuScene.m_particleStreams[prop] =
+				GpuSceneBuffer::getSingleton().allocate(commonProps.m_particleCount * kParticlePropertySize[prop], alignof(U32));
 		}
 	}
 
 	// Alive particles index buffer
 	if(m_resourceDirty)
 	{
-		const ParticleEmitterResourceCommonProperties& commonProps = m_particleEmitterResource->getCommonProperties();
 		GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_aliveParticleIndices);
 		m_gpuScene.m_aliveParticleIndices = GpuSceneBuffer::getSingleton().allocate(commonProps.m_particleCount * sizeof(U32), alignof(U32));
 	}
@@ -211,8 +263,6 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 			m_gpuScene.m_gpuSceneParticleEmitter.allocate();
 		}
 
-		const ParticleEmitterResourceCommonProperties& commonProps = m_particleEmitterResource->getCommonProperties();
-
 		GpuSceneParticleEmitter2 emitter;
 		zeroMemory(emitter);
 
@@ -227,7 +277,35 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 		emitter.m_particlesPerEmission = commonProps.m_particlesPerEmission;
 		emitter.m_particleEmitterPropertiesOffset = m_gpuScene.m_anKiParticleEmitterProperties.getOffset();
 
-		// TODO
+		if(m_geomType == ParticleGeometryType::kMeshComponent)
+		{
+			const MeshResource& meshr = m_meshComponent->getMeshResource();
+			emitter.m_particleAabbMin = meshr.getBoundingShape().getMin().xyz();
+			emitter.m_particleAabbMax = meshr.getBoundingShape().getMax().xyz();
+		}
+		else
+		{
+			emitter.m_particleAabbMin = Vec3(-0.5f);
+			emitter.m_particleAabbMax = Vec3(+0.5f);
+		}
+
+		emitter.m_reinitializeOnNextUpdate = 1;
+		emitter.m_worldTransformsIndex = info.m_node->getFirstComponentOfType<MoveComponent>().getGpuSceneTransforms().getIndex();
+
+		m_gpuScene.m_gpuSceneParticleEmitter.uploadToGpuScene(emitter);
+	}
+}
+
+U32 ParticleEmitter2Component::getGpuSceneMeshLodIndex(U32 submeshIdx) const
+{
+	ANKI_ASSERT(isValid());
+	if(m_geomType == ParticleGeometryType::kQuad)
+	{
+		return ParticleEmitterQuadGeometry::getSingleton().m_gpuSceneMeshLods.getIndex() * kMaxLodCount;
+	}
+	else
+	{
+		return m_meshComponent->getGpuSceneMeshLods(submeshIdx).getIndex();
 	}
 }
 

+ 5 - 8
AnKi/Scene/Components/ParticleEmitter2Component.h

@@ -52,27 +52,24 @@ public:
 		return m_geomType;
 	}
 
-	Bool isValid() const
-	{
-		Bool invalid = !m_particleEmitterResource;
-		invalid = invalid || (m_geomType == ParticleGeometryType::kMeshComponent && m_meshComponents.getSize() == 0);
-		return !invalid;
-	}
+	Bool isValid() const;
+
+	ANKI_INTERNAL U32 getGpuSceneMeshLodIndex(U32 submeshIdx) const;
 
 private:
 	class ParticleEmitterQuadGeometry;
 
 	ParticleEmitterResource2Ptr m_particleEmitterResource;
 
-	SceneDynamicArray<MeshComponent*> m_meshComponents;
+	MeshComponent* m_meshComponent = nullptr;
 
 	class
 	{
 	public:
-		GpuSceneArrays::ParticleEmitter2::Allocation m_gpuSceneParticleEmitter;
 		Array<GpuSceneBufferAllocation, U32(ParticleProperty::kCount)> m_particleStreams;
 		GpuSceneBufferAllocation m_aliveParticleIndices;
 		GpuSceneBufferAllocation m_anKiParticleEmitterProperties;
+		GpuSceneArrays::ParticleEmitter2::Allocation m_gpuSceneParticleEmitter;
 	} m_gpuScene;
 
 	ParticleGeometryType m_geomType = ParticleGeometryType::kQuad;

+ 35 - 17
AnKi/Scene/Components/SceneComponent.h

@@ -13,13 +13,9 @@
 
 namespace anki {
 
-/// @addtogroup scene
-/// @{
-
-/// @memberof SceneComponent
 enum class SceneComponentType : U8
 {
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) k##name,
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) k##name,
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 
 	kCount,
@@ -27,12 +23,11 @@ enum class SceneComponentType : U8
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(SceneComponentType)
 
-/// @memberof SceneComponent
 enum class SceneComponentTypeMask : U32
 {
 	kNone = 0,
 
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) k##name = 1 << U32(SceneComponentType::k##name),
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) k##name = 1 << U32(SceneComponentType::k##name),
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(SceneComponentTypeMask)
@@ -40,7 +35,8 @@ ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(SceneComponentTypeMask)
 class SceneComponentType2
 {
 public:
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) static constexpr SceneComponentType k##name##Component = SceneComponentType::k##name;
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) \
+	static constexpr SceneComponentType k##name##Component = SceneComponentType::k##name;
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 };
 
@@ -50,15 +46,21 @@ public: \
 \
 private:
 
-/// Component names
+// Component names
 inline Array<const Char*, U32(SceneComponentType::kCount)> kSceneComponentTypeName = {
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) ANKI_STRINGIZE(name)
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) ANKI_STRINGIZE(name)
+#define ANKI_SCENE_COMPONENT_SEPARATOR ,
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+};
+
+// Just a flag per component
+inline Array<Bool, U32(SceneComponentType::kCount)> kSceneComponentSceneNodeCanHaveMany = {
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) sceneNodeCanHaveMany
 #define ANKI_SCENE_COMPONENT_SEPARATOR ,
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 };
 
-/// Passed to SceneComponent::update.
-/// @memberof SceneComponent
+// Passed to SceneComponent::update.
 class SceneComponentUpdateInfo
 {
 	friend class SceneGraph;
@@ -91,11 +93,10 @@ private:
 	Vec3 m_sceneMax = Vec3(kMinF32);
 };
 
-/// Scene node component.
+// Scene node component.
 class SceneComponent
 {
 public:
-	/// Construct the scene component.
 	SceneComponent(SceneNode* node, SceneComponentType type);
 
 	virtual ~SceneComponent() = default;
@@ -137,7 +138,7 @@ public:
 	{
 	}
 
-	/// Don't call it directly.
+	// Don't call it directly.
 	ANKI_INTERNAL void setTimestamp(Timestamp timestamp)
 	{
 		ANKI_ASSERT(timestamp > 0);
@@ -191,6 +192,24 @@ protected:
 		}
 	}
 
+	// A convenience function for components to keep tabs on other components of a SceneNode
+	template<typename TComponent>
+	static void bookkeepComponent(TComponent*& ptr, SceneComponent* other, Bool added)
+	{
+		ANKI_ASSERT(other);
+		ANKI_ASSERT(other->getType() == TComponent::kClassType);
+		if(added)
+		{
+			ANKI_ASSERT(ptr == nullptr);
+			ptr = static_cast<TComponent*>(other);
+		}
+		else
+		{
+			ANKI_ASSERT(ptr == other);
+			ptr = nullptr;
+		}
+	}
+
 private:
 	Timestamp m_timestamp = 1; ///< Indicates when an update happened
 	U32 m_uuid = 0;
@@ -199,11 +218,10 @@ private:
 	U32 m_type : 8 = 0; ///< Cache the type ID.
 
 	static constexpr Array<F32, U32(SceneComponentType::kCount)> m_updateOrderWeights = {
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) weight
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) weight
 #define ANKI_SCENE_COMPONENT_SEPARATOR ,
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 	};
 };
-/// @}
 
 } // end namespace anki

+ 22 - 20
AnKi/Scene/Components/SceneComponentClasses.def.h

@@ -7,52 +7,54 @@
 #	define ANKI_SCENE_COMPONENT_SEPARATOR
 #endif
 
-ANKI_DEFINE_SCENE_COMPONENT(Script, 0.0f, LANGUAGE_LUA)
+// ANKI_DEFINE_SCENE_COMPONENT(className, weight, sceneNodeCanHaveMany, icon)
+
+ANKI_DEFINE_SCENE_COMPONENT(Script, 0.0f, true, LANGUAGE_LUA)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
-ANKI_DEFINE_SCENE_COMPONENT(Body, 10.0f, CUBE_SEND)
+ANKI_DEFINE_SCENE_COMPONENT(Body, 10.0f, false, CUBE_SEND)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(PlayerController, 10.0f, HUMAN)
+ANKI_DEFINE_SCENE_COMPONENT(PlayerController, 10.0f, false, HUMAN)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
-ANKI_DEFINE_SCENE_COMPONENT(Move, 30.0f, AXIS_ARROW)
+ANKI_DEFINE_SCENE_COMPONENT(Move, 30.0f, false, AXIS_ARROW)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(Skin, 30.0f, BONE)
+ANKI_DEFINE_SCENE_COMPONENT(Skin, 30.0f, false, BONE)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
-ANKI_DEFINE_SCENE_COMPONENT(Joint, 35.0f, CONNECTION)
+ANKI_DEFINE_SCENE_COMPONENT(Joint, 35.0f, true, CONNECTION)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
-ANKI_DEFINE_SCENE_COMPONENT(Trigger, 40.0f, LIGHT_SWITCH_OFF)
+ANKI_DEFINE_SCENE_COMPONENT(Trigger, 40.0f, true, LIGHT_SWITCH_OFF)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
-ANKI_DEFINE_SCENE_COMPONENT(Mesh, 50.0f, VECTOR_POLYGON)
+ANKI_DEFINE_SCENE_COMPONENT(Mesh, 50.0f, false, VECTOR_POLYGON)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
-ANKI_DEFINE_SCENE_COMPONENT(ParticleEmitter2, 60.0f, FOUNTAIN)
+ANKI_DEFINE_SCENE_COMPONENT(ParticleEmitter2, 60.0f, false, FOUNTAIN)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
-ANKI_DEFINE_SCENE_COMPONENT(Material, 100.0f, TEXTURE_BOX)
+ANKI_DEFINE_SCENE_COMPONENT(Material, 100.0f, true, TEXTURE_BOX)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(ParticleEmitter, 100.0f, FOUNTAIN)
+ANKI_DEFINE_SCENE_COMPONENT(ParticleEmitter, 100.0f, false, FOUNTAIN)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(Decal, 100.0f, LIQUID_SPOT)
+ANKI_DEFINE_SCENE_COMPONENT(Decal, 100.0f, false, LIQUID_SPOT)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(Camera, 100.0f, CAMERA)
+ANKI_DEFINE_SCENE_COMPONENT(Camera, 100.0f, false, CAMERA)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(FogDensity, 100.0f, CLOUD)
+ANKI_DEFINE_SCENE_COMPONENT(FogDensity, 100.0f, false, CLOUD)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(GlobalIlluminationProbe, 100.0f, SPHERE)
+ANKI_DEFINE_SCENE_COMPONENT(GlobalIlluminationProbe, 100.0f, false, SPHERE)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(ReflectionProbe, 100.0f, SPHERE)
+ANKI_DEFINE_SCENE_COMPONENT(ReflectionProbe, 100.0f, false, SPHERE)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(Skybox, 100.0f, EARTH)
+ANKI_DEFINE_SCENE_COMPONENT(Skybox, 100.0f, false, EARTH)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(Ui, 100.0f, WINDOW_RESTORE)
+ANKI_DEFINE_SCENE_COMPONENT(Ui, 100.0f, true, WINDOW_RESTORE)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(LensFlare, 100.0f, FLARE)
+ANKI_DEFINE_SCENE_COMPONENT(LensFlare, 100.0f, true, FLARE)
 ANKI_SCENE_COMPONENT_SEPARATOR
-ANKI_DEFINE_SCENE_COMPONENT(Light, 100.0f, LIGHTBULB)
+ANKI_DEFINE_SCENE_COMPONENT(Light, 100.0f, false, LIGHTBULB)
 
 #undef ANKI_DEFINE_SCENE_COMPONENT
 #undef ANKI_SCENE_COMPONENT_SEPARATOR

+ 1 - 1
AnKi/Scene/Forward.h

@@ -9,7 +9,7 @@ namespace anki {
 
 // Components
 class SceneComponent;
-#define ANKI_DEFINE_SCENE_COMPONENT(name, updateOrder, icon) class name##Component;
+#define ANKI_DEFINE_SCENE_COMPONENT(name, updateOrder, sceneNodeCanHaveMany, icon) class name##Component;
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 
 // Nodes

+ 2 - 2
AnKi/Scene/SceneGraph.h

@@ -38,7 +38,7 @@ ANKI_CVAR(NumericCVar<U32>, Scene, MinGpuSceneRenderables, 10 * 1024, 8, 100 * 1
 class SceneComponentArrays
 {
 public:
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) \
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) \
 	SceneBlockArray<name##Component>& get##name##s() \
 	{ \
 		return m_##name##Array; \
@@ -46,7 +46,7 @@ public:
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 
 private:
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) SceneBlockArray<name##Component> m_##name##Array;
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) SceneBlockArray<name##Component> m_##name##Array;
 #include <AnKi/Scene/Components/SceneComponentClasses.def.h>
 };
 

+ 8 - 2
AnKi/Scene/SceneNode.cpp

@@ -32,10 +32,16 @@
 namespace anki {
 
 // Specialize newComponent(). Do that first
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) \
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) \
 	template<> \
 	name##Component* SceneNode::newComponent<name##Component>() \
 	{ \
+		if(hasComponent<name##Component>() && !kSceneComponentSceneNodeCanHaveMany[SceneComponentType::k##name]) \
+		{ \
+			ANKI_SCENE_LOGE("Can't have many %s components in scene node %s", kSceneComponentTypeName[SceneComponentType::k##name], \
+							getName().cstr()); \
+			return nullptr; \
+		} \
 		auto it = SceneGraph::getSingleton().getComponentArrays().get##name##s().emplace(this); \
 		it->setArrayIndex(it.getArrayIndex()); \
 		newComponentInternal(&(*it)); \
@@ -63,7 +69,7 @@ SceneNode::~SceneNode()
 
 		switch(comp->getType())
 		{
-#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, icon) \
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) \
 	case SceneComponentType::k##name: \
 		SceneGraph::getSingleton().getComponentArrays().get##name##s().erase(comp->getArrayIndex()); \
 		break;

+ 13 - 0
AnKi/Shaders/Common.hlsl

@@ -185,6 +185,19 @@ U32 checkStructuredBuffer(T buff, U32 idx)
 // Safely access a structured buffer. Throw an assertion if it's out of bounds
 #define SBUFF(buff, idx) buff[checkStructuredBuffer(buff, idx)]
 
+template<typename TStruct, typename TBab>
+U32 checkBab(TBab bab, U32 offset)
+{
+	U32 babSize;
+	bab.GetDimensions(babSize);
+	ANKI_ASSERT(offset + sizeof(TStruct) <= babSize);
+	return offset;
+}
+
+// Savely access a ByteAddressBuffer
+#define BAB_LOAD(bab, type, offset) bab.Load<type>(checkBab<type>(bab, offset))
+#define BAB_STORE(bab, type, offset, data) bab.Store<type>(checkBab<type>(bab, offset), data)
+
 #define CHECK_TEXTURE_3D(textureType) \
 	UVec3 checkTexture(textureType tex, UVec3 coords) \
 	{ \

+ 2 - 2
AnKi/Shaders/ForwardShadingParticles.ankiprog

@@ -14,7 +14,7 @@
 
 struct VertIn
 {
-	UVec4 m_gpuSceneRenderable : MISC0;
+	UVec4 m_instanceData : MISC0;
 	U32 m_svVertexId : SV_VERTEXID;
 };
 
@@ -37,7 +37,7 @@ struct VertOut
 #if ANKI_VERTEX_SHADER
 VertOut main(VertIn input)
 {
-	const GpuSceneRenderableInstance instance = unpackGpuSceneRenderableVertex(input.m_gpuSceneRenderable);
+	const GpuSceneRenderableInstance instance = unpackGpuSceneRenderableVertex(input.m_instanceData);
 	const GpuSceneParticleEmitter particles = g_particleEmitters[instance.m_boneTransformsOffsetOrParticleEmitterIndex];
 	const GpuSceneMeshLod meshLod = g_meshLods[instance.m_meshLodIndex];
 

+ 1 - 1
AnKi/Shaders/GBufferGeneric.ankiprog

@@ -275,7 +275,7 @@ VertOut main(VertIn input)
 	output.m_meshletIndex = instance.m_meshletGeometryDescriptorIndex;
 #		endif
 
-#	else
+#	else // SW_MESHLETS
 	const GpuSceneRenderableInstance instance = unpackGpuSceneRenderableVertex(input.m_instanceData);
 	const GpuSceneMeshLod mesh = g_meshLods[instance.m_meshLodIndex];
 	UnpackedMeshVertex vert = loadVertex(mesh, input.m_svVertexId, ANKI_BONES);

+ 3 - 0
AnKi/Shaders/GpuParticlesCommon.hlsl

@@ -23,6 +23,9 @@ RWStructuredBuffer<GpuSceneParticleEmitter2> g_gpuSceneParticleEmitters :
 
 RWStructuredBuffer<ParticleSimulationScratch> g_scratch : register(ANKI_CONCATENATE(u, ANKI_PARTICLE_SIM_SCRATCH));
 
+// It's an array of offsets to GpuSceneRenderableBoundingVolume in the GPU scene
+StructuredBuffer<U32> g_gpuSceneBoundingVolumeList : register(ANKI_CONCATENATE(t, ANKI_PARTICLE_SIM_GPU_SCENE_BOUNDING_VOLUME_LIST));
+
 static U32 g_particleIdx;
 static U32 g_randomNumber;
 

+ 16 - 10
AnKi/Shaders/GpuParticlesGass.ankiprog

@@ -133,9 +133,9 @@ void initializeParticle(AnKiParticleEmitterProperties props, GpuSceneParticleEmi
 {
 	particlesInitGlobals(svDispatchThreadId.x);
 
-	GpuSceneParticleEmitter2 emitter = g_gpuSceneParticleEmitters[g_consts.m_gpuSceneParticleEmitterIndex];
+	GpuSceneParticleEmitter2 emitter = SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex);
 	const AnKiParticleEmitterProperties props = loadAnKiParticleEmitterProperties(g_gpuScene, emitter.m_particleEmitterPropertiesOffset);
-	const Mat3x4 emitterTrf = g_gpuSceneTransforms[emitter.m_worldTransformsIndex];
+	const Mat3x4 emitterTrf = SBUFF(g_gpuSceneTransforms, emitter.m_worldTransformsIndex);
 
 	const Bool reinit = emitter.m_reinitializeOnNextUpdate;
 	const Bool canEmitThisFrame = emitter.m_timeLeftForNextEmission - g_consts.m_dt <= 0.0;
@@ -207,12 +207,18 @@ void initializeParticle(AnKiParticleEmitterProperties props, GpuSceneParticleEmi
 			const Vec3 center = (aabbMax + aabbMin) / 2.0;
 			const F32 radius = length(aabbMax - center);
 
-			GpuSceneRenderableBoundingVolume bvol = g_gpuScene.Load<GpuSceneRenderableBoundingVolume>(emitter.m_boundingVolumeOffset);
-			bvol.m_aabbMin = aabbMin;
-			bvol.m_aabbMax = aabbMax;
-			bvol.m_sphereRadius = radius;
+			const U32 bvolumeCount = getStructuredBufferElementCount(g_gpuSceneBoundingVolumeList);
+			for(U32 i = 0; i < bvolumeCount; ++i)
+			{
+				const U32 offset = SBUFF(g_gpuSceneBoundingVolumeList, i);
+
+				GpuSceneRenderableBoundingVolume bvol = BAB_LOAD(g_gpuScene, GpuSceneRenderableBoundingVolume, offset);
+				bvol.m_aabbMin = aabbMin;
+				bvol.m_aabbMax = aabbMax;
+				bvol.m_sphereRadius = radius;
 
-			g_gpuScene.Store<GpuSceneRenderableBoundingVolume>(emitter.m_boundingVolumeOffset, bvol);
+				BAB_STORE(g_gpuScene, GpuSceneRenderableBoundingVolume, offset, bvol);
+			}
 
 			// Reset the scratch stract
 			g_scratch[0].m_aabbMin = kMaxI32;
@@ -223,16 +229,16 @@ void initializeParticle(AnKiParticleEmitterProperties props, GpuSceneParticleEmi
 			// Update the GPU scene emitter
 			if(canEmitThisFrame)
 			{
-				g_gpuSceneParticleEmitters[g_consts.m_gpuSceneParticleEmitterIndex].m_timeLeftForNextEmission = emitter.m_emissionPeriod;
+				SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_timeLeftForNextEmission = emitter.m_emissionPeriod;
 			}
 			else
 			{
-				g_gpuSceneParticleEmitters[g_consts.m_gpuSceneParticleEmitterIndex].m_timeLeftForNextEmission -= g_consts.m_dt;
+				SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_timeLeftForNextEmission -= g_consts.m_dt;
 			}
 
 			if(reinit)
 			{
-				g_gpuSceneParticleEmitters[g_consts.m_gpuSceneParticleEmitterIndex].m_reinitializeOnNextUpdate = 0;
+				SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_reinitializeOnNextUpdate = 0;
 			}
 		}
 	}

+ 48 - 50
AnKi/Shaders/Include/GpuSceneTypes.h

@@ -14,46 +14,46 @@
 
 ANKI_BEGIN_NAMESPACE
 
-/// Some far distance that will make objects not visible. Don't use kMaxF32 because this value will be used in math ops and it might overflow.
+// Some far distance that will make objects not visible. Don't use kMaxF32 because this value will be used in math ops and it might overflow
 constexpr F32 kSomeFarDistance = 100000.0f;
 
-/// @note All offsets in bytes
+// Note: All offsets in bytes
 struct GpuSceneRenderable
 {
-	U32 m_worldTransformsIndex; ///< First index points to the crnt transform and the 2nd to the previous.
+	U32 m_worldTransformsIndex; // First index points to the crnt transform and the 2nd to the previous.
 	U32 m_constantsOffset;
-	U32 m_meshLodsIndex; ///< Points to the array of GpuSceneMeshLod. kMaxLodCount are reserved for each renderable.
-	U32 m_boneTransformsOffset; ///< Array of Mat3x4 or 0 if its not a skin.
-	U32 m_particleEmitterIndex; ///< Index to the GpuSceneParticleEmitter array or kMaxU32 if it's not an emitter.
-	U32 m_rtShadowsShaderHandleIndex; ///< The index of the shader handle in the array of library's handles.
-	U32 m_rtMaterialFetchShaderHandleIndex; ///< The index of the shader handle in the array of library's handles.
+	U32 m_meshLodsIndex; // Points to the array of GpuSceneMeshLod. kMaxLodCount are reserved for each renderable.
+	U32 m_boneTransformsOffset; // Array of Mat3x4 or 0 if its not a skin.
+	U32 m_particleEmitterIndex; // Index to the GpuSceneParticleEmitter array or kMaxU32 if it's not an emitter.
+	U32 m_rtShadowsShaderHandleIndex; // The index of the shader handle in the array of library's handles.
+	U32 m_rtMaterialFetchShaderHandleIndex; // The index of the shader handle in the array of library's handles.
 	U32 m_uuid;
 
-	U32 m_diffuseColor : 24; ///< The average diffuse color of the renderable. Z is in low bits.
+	U32 m_diffuseColor : 24; // The average diffuse color of the renderable. Blue is in low bits.
 	U32 m_padding : 8;
 };
 
-/// Almost similar to GpuSceneRenderable but with only what the material shaders need. Needs to fit in a UVec4 vertex attribute.
+// Almost similar to GpuSceneRenderable but with only what the material shaders need. Needs to fit in a UVec4 vertex attribute.
 struct GpuSceneRenderableInstance
 {
 	U32 m_worldTransformsIndex;
 	U32 m_constantsOffset;
-	U32 m_meshLodIndex; ///< Points to a single GpuSceneMeshLod in the mesh lods.
+	U32 m_meshLodIndex; // Points to a single GpuSceneMeshLod in the mesh lods.
 	U32 m_boneTransformsOffsetOrParticleEmitterIndex;
 };
 static_assert(sizeof(GpuSceneRenderableInstance) == sizeof(UVec4));
 
-/// Minimal data passed to the vertex shaders in the case of meshlet rendering.
+// Minimal data passed to the vertex shaders in the case of meshlet rendering.
 struct GpuSceneMeshletInstance
 {
 	U32 m_worldTransformsIndex_25bit_meshletPrimitiveCount_7bit;
 	U32 m_constantsOffset;
-	U32 m_meshletGeometryDescriptorIndex; ///< Index in the UGB.
+	U32 m_meshletGeometryDescriptorIndex; // Index in the UGB.
 	U32 m_boneTransformsOffsetOrParticleEmitterIndex;
 };
 static_assert(kMaxPrimitivesPerMeshlet < ((1u << 7u) - 1));
 
-/// Used in visibility testing.
+// Used in visibility testing.
 struct GpuSceneRenderableBoundingVolume
 {
 	Vec3 m_aabbMin ANKI_CPP_CODE(= Vec3(kSomeFarDistance));
@@ -64,24 +64,24 @@ struct GpuSceneRenderableBoundingVolume
 };
 static_assert(sizeof(GpuSceneRenderableBoundingVolume) == sizeof(Vec4) * 2);
 
-/// Represents the geometry data of a single LOD of an indexed mesh.
+// Represents the geometry data of a single LOD of an indexed mesh.
 struct GpuSceneMeshLod
 {
 	U32 m_vertexOffsets[(U32)VertexStreamId::kMeshRelatedCount];
 	U32 m_indexCount;
-	U32 m_firstIndex; ///< In sizeof(indexType)
+	U32 m_firstIndex; // In sizeof(indexType)
 	U32 m_lod;
 
-	U32 m_firstMeshletBoundingVolume; ///< In sizeof(MeshletBoundingVolume)
-	U32 m_firstMeshletGeometryDescriptor; ///< In sizeof(MeshletGeometryDescriptor)
-	U32 m_meshletCount; ///< Can be zero if the mesh doesn't support mesh shading (or mesh shading is off)
+	U32 m_firstMeshletBoundingVolume; // In sizeof(MeshletBoundingVolume)
+	U32 m_firstMeshletGeometryDescriptor; // In sizeof(MeshletGeometryDescriptor)
+	U32 m_meshletCount; // Can be zero if the mesh doesn't support mesh shading (or mesh shading is off)
 	U32 m_padding1;
 
 	Vec3 m_positionTranslation;
 	F32 m_positionScale;
 
 	UVec2 m_blasAddress;
-	U32 m_tlasInstanceMask; ///< Mask that goes to AccelerationStructureInstance::m_mask
+	U32 m_tlasInstanceMask; // Mask that goes to AccelerationStructureInstance::m_mask
 	U32 m_padding3;
 };
 static_assert(sizeof(GpuSceneMeshLod) == sizeof(Vec4) * 5);
@@ -93,7 +93,7 @@ struct GpuSceneParticleEmitter
 };
 static_assert(sizeof(GpuSceneParticleEmitter) == sizeof(Vec4) * 2);
 
-/// Contains common properties for all particle emitters.
+// Contains common properties for all particle emitters
 struct GpuSceneParticleEmitter2
 {
 	U32 m_particleStateSteamOffsets[(U32)ParticleProperty::kCount]; // Points to arrays of data
@@ -111,9 +111,6 @@ struct GpuSceneParticleEmitter2
 
 	Vec3 m_particleAabbMax;
 	U32 m_worldTransformsIndex;
-
-	U32 m_boundingVolumeOffset; // Points to its GpuSceneRenderableBoundingVolume. It's an offset because there are many arrays with bvolumes.
-	U32 m_padding[3];
 };
 static_assert(sizeof(GpuSceneParticleEmitter2) % sizeof(Vec4) == 0);
 
@@ -126,68 +123,69 @@ enum class GpuSceneLightFlag : U32
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(GpuSceneLightFlag)
 
-/// A hash of all visible renderables. If it matches between vis tests then skip the drawcalls. Touched only by the GPU.
+// A hash of all visible renderables. If it matches between vis tests then skip the drawcalls. Touched only by the GPU.
 struct GpuSceneLightVisibleRenderablesHash
 {
 	U32 m_hash;
 };
 
-/// Point or spot light.
+// Point or spot light.
 struct GpuSceneLight
 {
-	Vec3 m_position ANKI_CPP_CODE(= Vec3(kSomeFarDistance)); ///< Position in world space.
-	F32 m_radius ANKI_CPP_CODE(= 0.0f); ///< Radius.
+	Vec3 m_position ANKI_CPP_CODE(= Vec3(kSomeFarDistance)); // Position in world space.
+	F32 m_radius ANKI_CPP_CODE(= 0.0f); // Radius.
 
 	Vec3 m_diffuseColor;
-	U32 m_visibleRenderablesHashIndex; ///< Points to a GpuSceneLightVisibleRenderablesHash
+	U32 m_visibleRenderablesHashIndex; // Points to a GpuSceneLightVisibleRenderablesHash
 
 	GpuSceneLightFlag m_flags;
-	U32 m_componentArrayIndex; ///< Array index of the LightComponent in the CPU scene.
-	U32 m_uuid; ///< The UUID of that light. If it's zero the GPU will not inform the CPU about it.
-	F32 m_innerCos; ///< Only for spot light.
+	U32 m_componentArrayIndex; // Array index of the LightComponent in the CPU scene.
+	U32 m_uuid; // The UUID of that light. If it's zero the GPU will not inform the CPU about it.
+	F32 m_innerCos; // Only for spot light.
 
-	Vec3 m_direction; ///< Only for spot light. Light direction.
-	F32 m_outerCos; ///< Only for spot light.
+	Vec3 m_direction; // Only for spot light. Light direction.
+	F32 m_outerCos; // Only for spot light.
 
-	Vec4 m_edgePoints[4u]; ///< Edge points in world space. Only for spot light.
+	Vec4 m_edgePoints[4u]; // Edge points in world space. Only for spot light.
 
-	/// If it's a spot light the 4 first rows are the texture matrix. If it's point light it's the UV viewports in the shadow atlas.
+	// If it's a spot light the 4 first rows are the texture matrix. If it's point light it's the UV viewports in the
+	// shadow atlas.
 	Vec4 m_spotLightMatrixOrPointLightUvViewports[6u];
 };
 
-/// Representation of a reflection probe.
+// Representation of a reflection probe.
 struct GpuSceneReflectionProbe
 {
-	Vec3 m_position; ///< Position of the probe in world space.
-	U32 m_cubeTexture; ///< Bindless index of the reflection texture.
+	Vec3 m_position; // Position of the probe in world space.
+	U32 m_cubeTexture; // Bindless index of the reflection texture.
 
 	Vec3 m_aabbMin ANKI_CPP_CODE(= Vec3(kSomeFarDistance));
-	U32 m_uuid; ///< The UUID of that probe. If it's zero the GPU will not inform the CPU about it.
+	U32 m_uuid; // The UUID of that probe. If it's zero the GPU will not inform the CPU about it.
 
 	Vec3 m_aabbMax ANKI_CPP_CODE(= Vec3(kSomeFarDistance));
-	U32 m_componentArrayIndex; ///< Array in the CPU scene.
+	U32 m_componentArrayIndex; // Array in the CPU scene.
 };
 constexpr U32 kSizeof_GpuSceneReflectionProbe = 3u * sizeof(Vec4);
 static_assert(sizeof(GpuSceneReflectionProbe) == kSizeof_GpuSceneReflectionProbe);
 
-/// Global illumination probe
+// Global illumination probe
 struct GpuSceneGlobalIlluminationProbe
 {
 	Vec3 m_aabbMin ANKI_CPP_CODE(= Vec3(kSomeFarDistance));
-	U32 m_uuid; ///< The UUID of that probe. If it's zero the GPU will not inform the CPU about it.
+	U32 m_uuid; // The UUID of that probe. If it's zero the GPU will not inform the CPU about it.
 
 	Vec3 m_aabbMax ANKI_CPP_CODE(= Vec3(kSomeFarDistance));
-	U32 m_componentArrayIndex; ///< Array in the CPU scene.
+	U32 m_componentArrayIndex; // Array in the CPU scene.
 
-	U32 m_volumeTexture; ///< Bindless index of the irradiance volume texture.
-	F32 m_halfTexelSizeU; ///< (1.0 / textureSize(texArr[textureIndex]).x) / 2.0
-	F32 m_fadeDistance; ///< Used to calculate a factor that is zero when fragPos is close to AABB bounds and 1.0 at fadeDistance and less.
+	U32 m_volumeTexture; // Bindless index of the irradiance volume texture.
+	F32 m_halfTexelSizeU; // (1.0 / textureSize(texArr[textureIndex]).x) / 2.0
+	F32 m_fadeDistance; // Used to calculate a factor that is zero when fragPos is close to AABB bounds and 1.0 at fadeDistance and less
 	F32 m_padding2;
 };
 constexpr U32 kSizeof_GpuSceneGlobalIlluminationProbe = 3u * sizeof(Vec4);
 static_assert(sizeof(GpuSceneGlobalIlluminationProbe) == kSizeof_GpuSceneGlobalIlluminationProbe);
 
-/// Decal.
+// Decal
 struct GpuSceneDecal
 {
 	U32 m_diffuseTexture;
@@ -203,7 +201,7 @@ struct GpuSceneDecal
 constexpr U32 kSizeof_GpuSceneDecal = 2u * sizeof(Vec4) + 1u * sizeof(Mat4);
 static_assert(sizeof(GpuSceneDecal) == kSizeof_GpuSceneDecal);
 
-/// Fog density volume.
+// Fog density volume
 struct GpuSceneFogDensityVolume
 {
 	Vec3 m_aabbMinOrSphereCenter ANKI_CPP_CODE(= Vec3(kSomeFarDistance));
@@ -247,7 +245,7 @@ enum class GpuSceneNonRenderableObjectTypeBit : U32
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(GpuSceneNonRenderableObjectTypeBit)
 
-/// Non-renderable types that require GPU to CPU feedback.
+// Non-renderable types that require GPU to CPU feedback
 enum class GpuSceneNonRenderableObjectTypeWithFeedback : U32
 {
 	kLight,

+ 1 - 0
AnKi/Shaders/Include/ParticleTypes.h

@@ -65,6 +65,7 @@ inline constexpr Array<U32, U32(ParticleProperty::kCount)> kParticlePropertySize
 #define ANKI_PARTICLE_SIM_DEPTH_BUFFER 0
 #define ANKI_PARTICLE_SIM_NORMAL_BUFFER 1
 #define ANKI_PARTICLE_SIM_GPU_SCENE_TRANSFORMS 2
+#define ANKI_PARTICLE_SIM_GPU_SCENE_BOUNDING_VOLUME_LIST 3
 
 // UAV
 #define ANKI_PARTICLE_SIM_SCRATCH 0