Browse Source

Gpu particles: Add the renderer part

Panagiotis Christopoulos Charitos 1 month ago
parent
commit
188ca55a75

+ 0 - 1
AnKi/Renderer/GBufferPost.h

@@ -18,7 +18,6 @@ class GBufferPost : public RendererObject
 public:
 	Error init();
 
-	/// Populate the rendergraph.
 	void populateRenderGraph(RenderingContext& ctx);
 
 private:

+ 154 - 0
AnKi/Renderer/GpuParticles.cpp

@@ -0,0 +1,154 @@
+// 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/Renderer/GpuParticles.h>
+#include <AnKi/Renderer/GBuffer.h>
+#include <AnKi/Resource/ParticleEmitterResource2.h>
+#include <AnKi/Scene/Components/ParticleEmitter2Component.h>
+
+namespace anki {
+
+Error GpuParticles::init()
+{
+	ParticleSimulationScratch scratchTemplate = {};
+	scratchTemplate.m_aabbMin = IVec3(kMaxI32);
+	scratchTemplate.m_aabbMax = IVec3(kMinI32);
+
+	m_scratchBuffers.resize(kMaxEmittersToSimulate);
+
+	CommandBufferInitInfo cmdbInit("Init particles");
+	CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(cmdbInit);
+
+	U32 count = 0;
+	for(auto& buff : m_scratchBuffers)
+	{
+		BufferInitInfo buffInit(generateTempPassName("GPU particles scratch #%u", count++));
+		buffInit.m_size = sizeof(ParticleSimulationScratch);
+		buffInit.m_usage = BufferUsageBit::kSrvCompute | BufferUsageBit::kCopyDestination;
+		buff = GrManager::getSingleton().newBuffer(buffInit);
+
+		WeakArray<ParticleSimulationScratch> out;
+		const BufferView src = RebarTransientMemoryPool::getSingleton().allocateCopyBuffer<ParticleSimulationScratch>(1, out);
+		out[0] = scratchTemplate;
+
+		cmdb->copyBufferToBuffer(src, BufferView(buff.get()));
+	}
+
+	cmdb->endRecording();
+	FencePtr fence;
+	GrManager::getSingleton().submit(cmdb.get(), {}, &fence);
+	fence->clientWait(kMaxSecond);
+
+	return Error::kNone;
+}
+
+void GpuParticles::populateRenderGraph(RenderingContext& ctx)
+{
+	SceneBlockArray<ParticleEmitter2Component>& emitters = SceneGraph::getSingleton().getComponentArrays().getParticleEmitter2s();
+	if(emitters.getSize() == 0) [[unlikely]]
+	{
+		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");
+
+	pass.newTextureDependency(getGBuffer().getDepthRt(), TextureUsageBit::kSrvCompute);
+	pass.newTextureDependency(getGBuffer().getColorRt(2), TextureUsageBit::kSrvCompute);
+	pass.newBufferDependency(getRenderer().getGpuSceneBufferHandle(), BufferUsageBit::kUavCompute);
+
+	pass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
+		CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+
+		SceneBlockArray<ParticleEmitter2Component>& emitters = SceneGraph::getSingleton().getComponentArrays().getParticleEmitter2s();
+		U32 count = 0;
+		for(ParticleEmitter2Component& emitter : emitters)
+		{
+			cmdb.pushDebugMarker(generateTempPassName("Emitter %u", emitter.getUuid()), Vec3(1.0f, 1.0f, 0.0f));
+
+			const ParticleEmitterResource2& rsrc = emitter.getParticleEmitterResource();
+			const ParticleEmitterResourceCommonProperties& commonProps = rsrc.getCommonProperties();
+
+			auto it = m_readbacks.find(emitter.getUuid());
+			ANKI_ASSERT(it != m_readbacks.getEnd());
+			const BufferView readbackBuffer =
+				getRenderer().getReadbackManager().allocateStructuredBuffer<ParticleSimulationCpuFeedback>(it->m_readback, 1);
+
+			cmdb.bindShaderProgram(&rsrc.getShaderProgram());
+
+			ParticleSimulationConstants consts;
+			consts.m_viewProjMat = ctx.m_matrices.m_viewProjection;
+			consts.m_invertedViewProjMat = ctx.m_matrices.m_invertedViewProjection;
+			consts.m_unprojectionParams = ctx.m_matrices.m_unprojectionParameters;
+			consts.m_randomNumber = getRandom() % kMaxU32;
+			consts.m_dt = emitter.getDt();
+			consts.m_gpuSceneParticleEmitterIndex = emitter.getGpuSceneParticleEmitter2Index();
+			*allocateAndBindConstants<ParticleSimulationConstants>(cmdb, ANKI_PARTICLE_SIM_CONSTANTS, 0) = consts;
+
+			rgraphCtx.bindSrv(ANKI_PARTICLE_SIM_DEPTH_BUFFER, 0, getGBuffer().getDepthRt());
+			rgraphCtx.bindSrv(ANKI_PARTICLE_SIM_NORMAL_BUFFER, 0, getGBuffer().getColorRt(2));
+			cmdb.bindSrv(ANKI_PARTICLE_SIM_GPU_SCENE_TRANSFORMS, 0, GpuSceneArrays::Transform::getSingleton().getBufferView());
+
+			cmdb.bindUav(ANKI_PARTICLE_SIM_GPU_SCENE, 0, GpuSceneBuffer::getSingleton().getBufferView());
+			cmdb.bindUav(ANKI_PARTICLE_SIM_GPU_SCENE_PARTICLE_EMITTERS, 0, GpuSceneArrays::ParticleEmitter2::getSingleton().getBufferView());
+			cmdb.bindUav(ANKI_PARTICLE_SIM_SCRATCH, 0, BufferView(m_scratchBuffers[count++].get()));
+			cmdb.bindUav(ANKI_PARTICLE_SIM_CPU_FEEDBACK, 0, readbackBuffer);
+
+			const U32 numthreads = GrManager::getSingleton().getDeviceCapabilities().m_maxWaveSize;
+			cmdb.dispatchCompute((commonProps.m_particleCount + numthreads - 1) / numthreads, 1, 1);
+
+			cmdb.popDebugMarker();
+		}
+	});
+}
+
+} // end namespace anki

+ 36 - 0
AnKi/Renderer/GpuParticles.h

@@ -0,0 +1,36 @@
+// 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/Renderer/RendererObject.h>
+#include <AnKi/Renderer/Utils/Readback.h>
+
+namespace anki {
+
+// Iterates the scene particle components and initiates GPU simulations
+class GpuParticles : public RendererObject
+{
+public:
+	static constexpr U32 kMaxEmittersToSimulate = 16;
+
+	Error init();
+
+	void populateRenderGraph(RenderingContext& ctx);
+
+private:
+	RendererDynamicArray<BufferPtr> m_scratchBuffers;
+
+	class ReadbackData
+	{
+	public:
+		MultiframeReadbackToken m_readback;
+		U64 m_lastFrameSeen = 0;
+	};
+
+	RendererHashMap<U32, ReadbackData> m_readbacks; // One readback per emitter
+};
+
+} // end namespace anki

+ 2 - 0
AnKi/Renderer/Renderer.cpp

@@ -51,6 +51,7 @@
 #include <AnKi/Renderer/IndirectDiffuse.h>
 #include <AnKi/Renderer/IndirectDiffuseClipmaps.h>
 #include <AnKi/Renderer/HistoryLength.h>
+#include <AnKi/Renderer/GpuParticles.h>
 #include <AnKi/Renderer/Utils/Drawer.h>
 #include <AnKi/Renderer/Utils/GpuVisibility.h>
 #include <AnKi/Renderer/Utils/MipmapGenerator.h>
@@ -329,6 +330,7 @@ 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)
 	{

+ 1 - 0
AnKi/Renderer/RendererObject.def.h

@@ -45,6 +45,7 @@ ANKI_RENDERER_OBJECT_DEF(RtMaterialFetchDbg, rtMaterialFetchDbg,
 ANKI_RENDERER_OBJECT_DEF(Reflections, reflections, 1)
 ANKI_RENDERER_OBJECT_DEF(IndirectDiffuse, indirectDiffuse,
 						 GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled&& g_cvarRenderRtIndirectDiffuse)
+ANKI_RENDERER_OBJECT_DEF(GpuParticles, gpuParticles, 1)
 
 // Util objects
 ANKI_RENDERER_OBJECT_DEF(RenderableDrawer, drawer, 1)

+ 5 - 0
AnKi/Resource/ParticleEmitterResource2.h

@@ -102,6 +102,11 @@ public:
 		return m_prefilledAnKiParticleEmitterProperties;
 	}
 
+	ShaderProgram& getShaderProgram() const
+	{
+		return *m_grProg;
+	}
+
 private:
 	ResourceDynamicArray<U8> m_prefilledAnKiParticleEmitterProperties;
 

+ 1 - 0
AnKi/Scene/Components/ParticleEmitter2Component.cpp

@@ -217,6 +217,7 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 	}
 
 	m_gpuSceneReallocationsThisFrame = false;
+	m_dt = F32(info.m_dt);
 
 	if(!m_resourceDirty && !m_geomTypeDirty && !m_meshComponentDirty) [[likely]]
 	{

+ 14 - 0
AnKi/Scene/Components/ParticleEmitter2Component.h

@@ -82,6 +82,18 @@ public:
 		return m_boundingVolume;
 	}
 
+	ANKI_INTERNAL ParticleEmitterResource2& getParticleEmitterResource() const
+	{
+		ANKI_ASSERT(isValid());
+		return *m_particleEmitterResource;
+	}
+
+	ANKI_INTERNAL F32 getDt() const
+	{
+		ANKI_ASSERT(isValid());
+		return m_dt;
+	}
+
 private:
 	class ParticleEmitterQuadGeometry;
 
@@ -100,6 +112,8 @@ private:
 
 	Array<Vec3, 2> m_boundingVolume;
 
+	F32 m_dt = 0.0f;
+
 	ParticleGeometryType m_geomType = ParticleGeometryType::kQuad;
 	Bool m_resourceDirty : 1 = true;
 	Bool m_meshComponentDirty : 1 = true;

+ 2 - 2
AnKi/Shaders/GpuParticlesCommon.hlsl

@@ -12,12 +12,12 @@
 Texture2D<Vec4> g_depthTex : register(ANKI_CONCATENATE(t, ANKI_PARTICLE_SIM_DEPTH_BUFFER));
 Texture2D<Vec4> g_gbufferRt2Tex : register(ANKI_CONCATENATE(t, ANKI_PARTICLE_SIM_NORMAL_BUFFER));
 
+StructuredBuffer<Mat3x4> g_gpuSceneTransforms : register(ANKI_CONCATENATE(t, ANKI_PARTICLE_SIM_GPU_SCENE_TRANSFORMS));
+
 ConstantBuffer<ParticleSimulationConstants> g_consts : register(ANKI_CONCATENATE(b, ANKI_PARTICLE_SIM_CONSTANTS));
 
 RWByteAddressBuffer g_gpuScene : register(ANKI_CONCATENATE(u, ANKI_PARTICLE_SIM_GPU_SCENE));
 
-StructuredBuffer<Mat3x4> g_gpuSceneTransforms : register(ANKI_CONCATENATE(t, ANKI_PARTICLE_SIM_GPU_SCENE_TRANSFORMS));
-
 RWStructuredBuffer<GpuSceneParticleEmitter2> g_gpuSceneParticleEmitters :
 	register(ANKI_CONCATENATE(u, ANKI_PARTICLE_SIM_GPU_SCENE_PARTICLE_EMITTERS));
 

+ 2 - 2
AnKi/Shaders/Include/ParticleTypes.h

@@ -9,7 +9,7 @@
 
 ANKI_BEGIN_NAMESPACE
 
-/// Some data that are used temporarely by a simulation.
+// Some data that are used temporarely by a simulation.
 struct ParticleSimulationScratch
 {
 	IVec3 m_aabbMin; // U32 because of atomics. In cm
@@ -19,7 +19,7 @@ struct ParticleSimulationScratch
 	U32 m_emittedParticleCount;
 };
 
-/// Constants used in the simulation.
+// Constants used in the simulation.
 struct ParticleSimulationConstants
 {
 	Mat4 m_viewProjMat;