Browse Source

Some particle fixes

Panagiotis Christopoulos Charitos 1 month ago
parent
commit
3127b5fb4d

+ 3 - 0
AnKi/GpuMemory/GpuReadbackMemoryPool.h

@@ -83,8 +83,10 @@ class GpuReadbackMemoryPool : public MakeSingleton<GpuReadbackMemoryPool>
 	friend class MakeSingleton;
 
 public:
+	// Thread-safe
 	GpuReadbackMemoryAllocation allocate(PtrSize size, U32 alignment);
 
+	// Thread-safe
 	template<typename T>
 	GpuReadbackMemoryAllocation allocateStructuredBuffer(U32 count)
 	{
@@ -92,6 +94,7 @@ public:
 		return allocate(sizeof(T) * count, alignment);
 	}
 
+	// Thread-safe
 	void deferredFree(GpuReadbackMemoryAllocation& allocation);
 
 	void endFrame();

+ 59 - 46
AnKi/Renderer/GpuParticles.cpp

@@ -52,52 +52,6 @@ void GpuParticles::populateRenderGraph(RenderingContext& ctx)
 		return;
 	}
 
-	// Handle the readbacks
-	for(ParticleEmitter2Component& emitter : emitters)
-	{
-		auto it = m_readbacks.find(emitter.getUuid());
-		if(it != m_readbacks.getEnd())
-		{
-			// Emitter found, access the readback
-			it->m_lastFrameSeen = getRenderer().getFrameCount();
-
-			ParticleSimulationCpuFeedback feedback;
-			PtrSize dataOut = 0;
-			getRenderer().getReadbackManager().readMostRecentData(it->m_readback, &feedback, sizeof(feedback), dataOut);
-			if(dataOut)
-			{
-				ANKI_ASSERT(feedback.m_uuid == emitter.getUuid());
-				emitter.updateBoundingVolume(feedback.m_aabbMin, feedback.m_aabbMax);
-			}
-		}
-		else
-		{
-			// Emitter not found, create new entry
-			ReadbackData& data = *m_readbacks.emplace(emitter.getUuid());
-			data.m_lastFrameSeen = getRenderer().getFrameCount();
-		}
-	}
-
-	// Remove old emitters
-	while(true)
-	{
-		Bool foundAnOldOne = false;
-		for(auto it = m_readbacks.getBegin(); it != m_readbacks.getEnd(); ++it)
-		{
-			const U32 deleteAfterNFrames = 10;
-			if(it->m_lastFrameSeen + deleteAfterNFrames < getRenderer().getFrameCount())
-			{
-				m_readbacks.erase(it);
-				foundAnOldOne = true;
-			}
-		}
-
-		if(!foundAnOldOne)
-		{
-			break;
-		}
-	}
-
 	// Create the renderpass
 	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
 	NonGraphicsRenderPass& pass = rgraph.newNonGraphicsRenderPass("GPU particle sim");
@@ -110,9 +64,68 @@ void GpuParticles::populateRenderGraph(RenderingContext& ctx)
 		CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
 
 		SceneBlockArray<ParticleEmitter2Component>& emitters = SceneGraph::getSingleton().getComponentArrays().getParticleEmitter2s();
+
+		// Handle the readbacks
+		for(ParticleEmitter2Component& emitter : emitters)
+		{
+			if(!emitter.isValid())
+			{
+				continue;
+			}
+
+			auto it = m_readbacks.find(emitter.getUuid());
+			if(it != m_readbacks.getEnd())
+			{
+				// Emitter found, access the readback
+				it->m_lastFrameSeen = getRenderer().getFrameCount();
+
+				ParticleSimulationCpuFeedback feedback;
+				PtrSize dataOut = 0;
+				getRenderer().getReadbackManager().readMostRecentData(it->m_readback, &feedback, sizeof(feedback), dataOut);
+
+				if(dataOut && feedback.m_aabbMin < feedback.m_aabbMax)
+				{
+					ANKI_ASSERT(feedback.m_uuid == emitter.getUuid());
+					emitter.updateBoundingVolume(feedback.m_aabbMin, feedback.m_aabbMax);
+				}
+			}
+			else
+			{
+				// Emitter not found, create new entry
+				ReadbackData& data = *m_readbacks.emplace(emitter.getUuid());
+				data.m_lastFrameSeen = getRenderer().getFrameCount();
+			}
+		}
+
+		// Remove old emitters
+		while(true)
+		{
+			Bool foundAnOldOne = false;
+			for(auto it = m_readbacks.getBegin(); it != m_readbacks.getEnd(); ++it)
+			{
+				const U32 deleteAfterNFrames = 10;
+				if(it->m_lastFrameSeen + deleteAfterNFrames < getRenderer().getFrameCount())
+				{
+					m_readbacks.erase(it);
+					foundAnOldOne = true;
+				}
+			}
+
+			if(!foundAnOldOne)
+			{
+				break;
+			}
+		}
+
+		// Compute work
 		U32 count = 0;
 		for(ParticleEmitter2Component& emitter : emitters)
 		{
+			if(!emitter.isValid())
+			{
+				continue;
+			}
+
 			cmdb.pushDebugMarker(generateTempPassName("Emitter %u", emitter.getUuid()), Vec3(1.0f, 1.0f, 0.0f));
 
 			const ParticleEmitterResource2& rsrc = emitter.getParticleEmitterResource();

+ 1 - 1
AnKi/Renderer/Renderer.cpp

@@ -330,13 +330,13 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 
 	// Populate render graph. WARNING Watch the order
 	gpuSceneCopy(ctx);
-	m_gpuParticles->populateRenderGraph(ctx);
 	m_primaryNonRenderableVisibility->populateRenderGraph(ctx);
 	if(m_accelerationStructureBuilder)
 	{
 		m_accelerationStructureBuilder->populateRenderGraph(ctx);
 	}
 	m_gbuffer->populateRenderGraph(ctx);
+	m_gpuParticles->populateRenderGraph(ctx);
 	m_motionVectors->populateRenderGraph(ctx);
 	m_historyLength->populateRenderGraph(ctx);
 	m_depthDownscale->populateRenderGraph(ctx);

+ 1 - 0
AnKi/Resource.h

@@ -7,6 +7,7 @@
 
 #include <AnKi/Resource/ResourceManager.h>
 #include <AnKi/Resource/ParticleEmitterResource.h>
+#include <AnKi/Resource/ParticleEmitterResource2.h>
 #include <AnKi/Resource/AnimationResource.h>
 #include <AnKi/Resource/ScriptResource.h>
 #include <AnKi/Resource/MeshResource.h>

+ 1 - 1
AnKi/Resource/ParticleEmitterResource2.cpp

@@ -251,7 +251,7 @@ Error ParticleEmitterResource2::parseInput(XmlElement inputEl)
 	case ShaderVariableDataType::k##type: \
 	{ \
 		Array<baseType, rowCount * columnCount> arr; \
-		ANKI_CHECK(inputEl.getNumbers(arr)); \
+		ANKI_CHECK(inputEl.getAttributeNumbers("value", arr)); \
 		ANKI_ASSERT(memberOffset + memberSize <= m_prefilledAnKiParticleEmitterProperties.getSize()); \
 		ANKI_ASSERT(memberSize == sizeof(arr)); \
 		memcpy(m_prefilledAnKiParticleEmitterProperties.getBegin() + memberOffset, arr.getBegin(), memberSize); \

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

@@ -184,14 +184,14 @@ void MaterialComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	if(!dirty) [[likely]]
 	{
 		// Update Scene bounds
-		if(info.m_forceUpdateSceneBounds) [[unlikely]]
+		if(info.m_forceUpdateSceneBounds || m_skinComponent) [[unlikely]]
 		{
 			const Aabb aabbWorld = computeAabb(*info.m_node);
 			info.updateSceneBounds(aabbWorld.getMin().xyz(), aabbWorld.getMax().xyz());
 		}
 
 		// Update the GPU scene AABBs
-		if(prioritizeEmitter)
+		if(prioritizeEmitter || m_skinComponent)
 		{
 			const Aabb aabbWorld = computeAabb(*info.m_node);
 			for(RenderingTechnique t : EnumBitsIterable<RenderingTechnique, RenderingTechniqueBit>(mtl.getRenderingTechniques()))

+ 14 - 22
AnKi/Scene/Components/ParticleEmitter2Component.cpp

@@ -14,7 +14,7 @@
 namespace anki {
 
 // Contains some shared geometry data and it's a singleton because we don't want to be wasting time and memory for every emitter
-class ParticleEmitter2Component::ParticleEmitterQuadGeometry : public MakeSingleton<ParticleEmitterQuadGeometry>
+class ParticleEmitter2Component::ParticleEmitterQuadGeometry : public MakeSingletonLazyInit<ParticleEmitterQuadGeometry>
 {
 public:
 	UnifiedGeometryBufferAllocation m_quadPositions;
@@ -160,7 +160,7 @@ ParticleEmitter2Component& ParticleEmitter2Component::setParticleEmitterFilename
 	else
 	{
 		m_particleEmitterResource = std::move(newRsrc);
-		m_resourceDirty = true;
+		m_anyDirty = true;
 	}
 
 	return *this;
@@ -185,7 +185,7 @@ void ParticleEmitter2Component::onOtherComponentRemovedOrAdded(SceneComponent* o
 	if(other->getType() == SceneComponentType::kMesh)
 	{
 		bookkeepComponent(m_meshComponent, other, added);
-		m_meshComponentDirty = true;
+		m_anyDirty = true;
 	}
 }
 
@@ -203,6 +203,9 @@ Bool ParticleEmitter2Component::isValid() const
 
 void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
+	m_gpuSceneReallocationsThisFrame = false;
+	m_dt = F32(info.m_dt);
+
 	if(!isValid()) [[unlikely]]
 	{
 		for(auto& s : m_gpuScene.m_particleStreams)
@@ -216,41 +219,33 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 		return;
 	}
 
-	m_gpuSceneReallocationsThisFrame = false;
-	m_dt = F32(info.m_dt);
-
-	if(!m_resourceDirty && !m_geomTypeDirty && !m_meshComponentDirty) [[likely]]
+	if(!m_anyDirty) [[likely]]
 	{
 		return;
 	}
 
-	// From now on it's dirty and needs some kind of update
+	// From now on it's dirty, do all updates
 
-	const Bool gpuSceneMeshLodsChanged = m_geomTypeDirty;
-	m_gpuSceneReallocationsThisFrame = gpuSceneMeshLodsChanged;
+	m_gpuSceneReallocationsThisFrame = true; // Difficult to properly track so set it to true and don't bother
+	m_anyDirty = false;
 	updated = true;
 	const ParticleEmitterResourceCommonProperties& commonProps = m_particleEmitterResource->getCommonProperties();
 
 	// Streams
-	if(m_resourceDirty)
+	for(ParticleProperty prop : EnumIterable<ParticleProperty>())
 	{
-		for(ParticleProperty prop : EnumIterable<ParticleProperty>())
-		{
-			GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_particleStreams[prop]);
-			m_gpuScene.m_particleStreams[prop] =
-				GpuSceneBuffer::getSingleton().allocate(commonProps.m_particleCount * kParticlePropertySize[prop], alignof(U32));
-		}
+		GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_particleStreams[prop]);
+		m_gpuScene.m_particleStreams[prop] =
+			GpuSceneBuffer::getSingleton().allocate(commonProps.m_particleCount * kParticlePropertySize[prop], alignof(U32));
 	}
 
 	// Alive particles index buffer
-	if(m_resourceDirty)
 	{
 		GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_aliveParticleIndices);
 		m_gpuScene.m_aliveParticleIndices = GpuSceneBuffer::getSingleton().allocate(commonProps.m_particleCount * sizeof(U32), alignof(U32));
 	}
 
 	// AnKiParticleEmitterProperties
-	if(m_resourceDirty)
 	{
 		GpuSceneBuffer::getSingleton().deferredFree(m_gpuScene.m_anKiParticleEmitterProperties);
 
@@ -261,12 +256,9 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 	}
 
 	// GpuSceneParticleEmitter2
-	const Bool updateGpuSceneEmitter = m_resourceDirty || m_meshComponentDirty || m_geomTypeDirty;
-	if(updateGpuSceneEmitter)
 	{
 		if(!m_gpuScene.m_gpuSceneParticleEmitter)
 		{
-			m_gpuSceneReallocationsThisFrame = true;
 			m_gpuScene.m_gpuSceneParticleEmitter.allocate();
 		}
 

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

@@ -41,7 +41,7 @@ public:
 		if(type != m_geomType && ANKI_EXPECT(type < ParticleGeometryType::kCount))
 		{
 			m_geomType = type;
-			m_geomTypeDirty = true;
+			m_anyDirty = true;
 		}
 
 		return *this;
@@ -115,10 +115,8 @@ private:
 	F32 m_dt = 0.0f;
 
 	ParticleGeometryType m_geomType = ParticleGeometryType::kQuad;
-	Bool m_resourceDirty : 1 = true;
-	Bool m_meshComponentDirty : 1 = true;
-	Bool m_geomTypeDirty : 1 = true;
-	Bool m_gpuSceneReallocationsThisFrame : 1 = true; // Only tracks the memory shared externally
+	Bool m_anyDirty = true;
+	Bool m_gpuSceneReallocationsThisFrame = true; // Only tracks the memory shared externally
 
 	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 

+ 12 - 9
AnKi/Shaders/GpuParticlesGass.ankiprog

@@ -50,8 +50,8 @@ void appendAlive(GpuSceneParticleEmitter2 emitter, Vec3 particlePos, F32 particl
 {
 	// Add the alive particle index to the array
 	U32 count;
-	InterlockedAdd(g_gpuSceneParticleEmitters[g_consts.m_gpuSceneParticleEmitterIndex].m_aliveParticleCount, 1, count);
-	g_gpuScene.Store(emitter.m_aliveParticleIndicesOffset + count * sizeof(U32), g_particleIdx);
+	InterlockedAdd(g_scratch[0].m_aliveParticleCount, 1, count);
+	BAB_STORE(g_gpuScene, U32, emitter.m_aliveParticleIndicesOffset + count * sizeof(U32), g_particleIdx);
 
 	// Update the AABB
 	const F32 toCentimeters = 100.0;
@@ -140,7 +140,7 @@ void initializeParticle(AnKiParticleEmitterProperties props, GpuSceneParticleEmi
 	const Bool reinit = emitter.m_reinitializeOnNextUpdate;
 	const Bool canEmitThisFrame = emitter.m_timeLeftForNextEmission - g_consts.m_dt <= 0.0;
 
-	if(emitter.m_particleCount < g_particleIdx)
+	if(g_particleIdx < emitter.m_particleCount)
 	{
 		// Decide what to do
 		Bool init = false;
@@ -207,12 +207,6 @@ void initializeParticle(AnKiParticleEmitterProperties props, GpuSceneParticleEmi
 			feedback.m_uuid = emitter.m_uuid;
 			g_cpuFeedback[0] = feedback;
 
-			// Reset the scratch stract
-			g_scratch[0].m_aabbMin = kMaxI32;
-			g_scratch[0].m_aabbMax = kMinI32;
-			g_scratch[0].m_threadgroupCount = 0;
-			g_scratch[0].m_emittedParticleCount = 0;
-
 			// Update the GPU scene emitter
 			if(canEmitThisFrame)
 			{
@@ -227,6 +221,15 @@ void initializeParticle(AnKiParticleEmitterProperties props, GpuSceneParticleEmi
 			{
 				SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_reinitializeOnNextUpdate = 0;
 			}
+
+			SBUFF(g_gpuSceneParticleEmitters, g_consts.m_gpuSceneParticleEmitterIndex).m_aliveParticleCount = g_scratch[0].m_aliveParticleCount;
+
+			// Reset the scratch struct for next frame
+			g_scratch[0].m_aabbMin = kMaxI32;
+			g_scratch[0].m_aabbMax = kMinI32;
+			g_scratch[0].m_threadgroupCount = 0;
+			g_scratch[0].m_emittedParticleCount = 0;
+			g_scratch[0].m_aliveParticleCount = 0;
 		}
 	}
 }

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

@@ -17,6 +17,9 @@ struct ParticleSimulationScratch
 
 	IVec3 m_aabbMax; // U32 because of atomics. In cm
 	U32 m_emittedParticleCount;
+
+	UVec3 m_padding;
+	U32 m_aliveParticleCount;
 };
 
 // Constants used in the simulation.