浏览代码

Editor: Add particle emitter vis and picking

Panagiotis Christopoulos Charitos 6 天之前
父节点
当前提交
c513ddb4ba
共有 7 个文件被更改,包括 370 次插入115 次删除
  1. 7 11
      AnKi/Core/CoreTracer.h
  2. 164 47
      AnKi/Renderer/Dbg.cpp
  3. 8 3
      AnKi/Renderer/Dbg.h
  4. 1 1
      AnKi/Renderer/Utils/GpuVisibility.cpp
  5. 183 52
      AnKi/Shaders/Dbg.ankiprog
  6. 3 1
      Samples/Common/SampleApp.cpp
  7. 4 0
      Sandbox/Main.cpp

+ 7 - 11
AnKi/Core/CoreTracer.h

@@ -13,27 +13,24 @@
 
 
 namespace anki {
 namespace anki {
 
 
-/// @addtogroup core
-/// @{
+#if ANKI_TRACING_ENABLED
 
 
 ANKI_CVAR(BoolCVar, Core, TracingEnabled, false, "Enable or disable tracing")
 ANKI_CVAR(BoolCVar, Core, TracingEnabled, false, "Enable or disable tracing")
-#if ANKI_OS_ANDROID
+#	if ANKI_OS_ANDROID
 ANKI_CVAR(BoolCVar, Core, StreamlineAnnotations, false, "Enable or disable Streamline annotations")
 ANKI_CVAR(BoolCVar, Core, StreamlineAnnotations, false, "Enable or disable Streamline annotations")
-#endif
-
-#if ANKI_TRACING_ENABLED
+#	endif
 
 
-/// A system that sits on top of the tracer and processes the counters and events.
+// A system that sits on top of the tracer and processes the counters and events.
 class CoreTracer : public MakeSingleton<CoreTracer>
 class CoreTracer : public MakeSingleton<CoreTracer>
 {
 {
 	template<typename>
 	template<typename>
 	friend class MakeSingleton;
 	friend class MakeSingleton;
 
 
 public:
 public:
-	/// @param directory The directory to store the trace and counters.
+	// directory: The directory to store the trace and counters.
 	Error init(CString directory);
 	Error init(CString directory);
 
 
-	/// It will flush everything.
+	// It will flush everything.
 	void flushFrame(U64 frame);
 	void flushFrame(U64 frame);
 
 
 private:
 private:
@@ -47,7 +44,7 @@ private:
 	CoreDynamicArray<CoreString> m_counterNames;
 	CoreDynamicArray<CoreString> m_counterNames;
 	IntrusiveList<PerFrameCounters> m_frameCounters;
 	IntrusiveList<PerFrameCounters> m_frameCounters;
 
 
-	IntrusiveList<ThreadWorkItem> m_workItems; ///< Items for the thread to process.
+	IntrusiveList<ThreadWorkItem> m_workItems; // Items for the thread to process.
 	CoreString m_traceJsonFilename;
 	CoreString m_traceJsonFilename;
 	CoreString m_countersCsvFilename;
 	CoreString m_countersCsvFilename;
 	File m_traceJsonFile;
 	File m_traceJsonFile;
@@ -65,6 +62,5 @@ private:
 };
 };
 
 
 #endif
 #endif
-/// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 164 - 47
AnKi/Renderer/Dbg.cpp

@@ -392,6 +392,18 @@ static constexpr F32 kCubePositions[] = {
 	// Bottom face
 	// Bottom face
 	-0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f};
 	-0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f};
 
 
+class Dbg::InternalCtx
+{
+public:
+	class
+	{
+	public:
+		BufferView m_renderableIndices;
+		BufferView m_drawIndirectArgs;
+		BufferHandle m_handle;
+	} m_particleEmitter;
+};
+
 Dbg::Dbg()
 Dbg::Dbg()
 {
 {
 	registerDebugRenderTarget("ObjectPicking");
 	registerDebugRenderTarget("ObjectPicking");
@@ -422,6 +434,7 @@ Error Dbg::init()
 	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/Editor/SpotLight.png", m_spotLightImage));
 	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/Editor/SpotLight.png", m_spotLightImage));
 	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/Editor/Decal.png", m_decalImage));
 	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/Editor/Decal.png", m_decalImage));
 	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/Editor/ReflectionProbe.png", m_reflectionImage));
 	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/Editor/ReflectionProbe.png", m_reflectionImage));
+	ANKI_CHECK(rsrcManager.loadResource("EngineAssets/Editor/Particles.png", m_particlesImage));
 
 
 	ANKI_CHECK(rsrcManager.loadResource("ShaderBinaries/Dbg.ankiprogbin", m_dbgProg));
 	ANKI_CHECK(rsrcManager.loadResource("ShaderBinaries/Dbg.ankiprogbin", m_dbgProg));
 
 
@@ -545,7 +558,8 @@ void Dbg::drawNonRenderable(GpuSceneNonRenderableObjectType type, U32 objCount,
 
 
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
 	variantInitInfo.addMutation("OBJECT_TYPE", U32(type));
 	variantInitInfo.addMutation("OBJECT_TYPE", U32(type));
-	variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, (objectPicking) ? "BilboardsPicking" : "Bilboards");
+	variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel,
+											 (objectPicking) ? "BilboardsRenderPicking" : "BilboardsRenderMain");
 	const ShaderProgramResourceVariant* variant;
 	const ShaderProgramResourceVariant* variant;
 	m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 	m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 	cmdb.bindShaderProgram(&variant->getProgram());
 	cmdb.bindShaderProgram(&variant->getProgram());
@@ -578,6 +592,46 @@ void Dbg::drawNonRenderable(GpuSceneNonRenderableObjectType type, U32 objCount,
 	cmdb.draw(PrimitiveTopology::kTriangles, 6, objCount);
 	cmdb.draw(PrimitiveTopology::kTriangles, 6, objCount);
 }
 }
 
 
+void Dbg::drawParticleEmitters(const RenderingContext& ctx, const InternalCtx& ictx, Bool objectPicking, RenderPassWorkContext& rgraphCtx)
+{
+	CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+
+	ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
+	variantInitInfo.addMutation("OBJECT_TYPE", 0);
+	variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel,
+											 (objectPicking) ? "ParticleEmittersRenderPicking" : "ParticleEmittersRenderMain");
+	const ShaderProgramResourceVariant* variant;
+	m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
+	cmdb.bindShaderProgram(&variant->getProgram());
+
+	struct Constants
+	{
+		Mat4 m_viewProjMat;
+		Mat3x4 m_camTrf;
+
+		UVec3 m_padding;
+		U32 m_depthFailureVisualization;
+	} consts;
+	consts.m_viewProjMat = ctx.m_matrices.m_viewProjection;
+	consts.m_camTrf = ctx.m_matrices.m_cameraTransform;
+	consts.m_depthFailureVisualization = !(m_options & DbgOption::kDepthTest);
+	cmdb.setFastConstants(&consts, sizeof(consts));
+
+	if(!objectPicking)
+	{
+		rgraphCtx.bindSrv(0, 0, getGBuffer().getDepthRt());
+	}
+
+	cmdb.bindSrv(1, 0, GpuSceneArrays::Renderable::getSingleton().getBufferView());
+	cmdb.bindSrv(2, 0, ictx.m_particleEmitter.m_renderableIndices);
+	cmdb.bindSrv(3, 0, GpuSceneArrays::Transform::getSingleton().getBufferView());
+	cmdb.bindSrv(4, 0, TextureView(&m_particlesImage->getTexture(), TextureSubresourceDesc::all()));
+
+	cmdb.bindSampler(1, 1, getRenderer().getSamplers().m_trilinearRepeatAniso.get());
+
+	cmdb.drawIndirect(PrimitiveTopology::kTriangles, ictx.m_particleEmitter.m_drawIndirectArgs);
+}
+
 void Dbg::populateRenderGraph(RenderingContext& ctx)
 void Dbg::populateRenderGraph(RenderingContext& ctx)
 {
 {
 	ANKI_TRACE_SCOPED_EVENT(Dbg);
 	ANKI_TRACE_SCOPED_EVENT(Dbg);
@@ -589,20 +643,106 @@ void Dbg::populateRenderGraph(RenderingContext& ctx)
 		return;
 		return;
 	}
 	}
 
 
+	InternalCtx ictx;
+
+	// Common stuff for the particle emitters
+	populateRenderGraphParticleEmitters(ctx, ictx);
+
 	// Debug visualization
 	// Debug visualization
 	if(!!(m_options & (DbgOption::kDbgScene)))
 	if(!!(m_options & (DbgOption::kDbgScene)))
 	{
 	{
-		populateRenderGraphMain(ctx);
+		populateRenderGraphMain(ctx, ictx);
 	}
 	}
 
 
 	// Object picking
 	// Object picking
 	if(!!(m_options & DbgOption::kObjectPicking))
 	if(!!(m_options & DbgOption::kObjectPicking))
 	{
 	{
-		populateRenderGraphObjectPicking(ctx);
+		populateRenderGraphObjectPicking(ctx, ictx);
 	}
 	}
 }
 }
 
 
-void Dbg::populateRenderGraphMain(RenderingContext& ctx)
+void Dbg::populateRenderGraphParticleEmitters(RenderingContext& ctx, InternalCtx& ictx)
+{
+	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
+
+	const U32 particleEmitterCount = GpuSceneArrays::ParticleEmitter2::getSingleton().getElementCount();
+	const BufferView renderableIndices = GpuVisibleTransientMemoryPool::getSingleton().allocateStructuredBuffer<U32>(particleEmitterCount + 1);
+
+	const BufferView drawIndirectArgs = GpuVisibleTransientMemoryPool::getSingleton().allocateStructuredBuffer<DrawIndirectArgs>(1);
+	const BufferHandle handle = rgraph.importBuffer(drawIndirectArgs, BufferUsageBit::kNone);
+
+	ictx.m_particleEmitter.m_drawIndirectArgs = drawIndirectArgs;
+	ictx.m_particleEmitter.m_renderableIndices = renderableIndices;
+	ictx.m_particleEmitter.m_handle = handle;
+
+	// Prepare the prepare
+	{
+		NonGraphicsRenderPass& pass = rgraph.newNonGraphicsRenderPass("Dbg: Zero particle emitter stuff");
+
+		pass.newBufferDependency(handle, BufferUsageBit::kCopyDestination);
+
+		pass.setWork([drawIndirectArgs](RenderPassWorkContext& rgraphCtx) {
+			rgraphCtx.m_commandBuffer->zeroBuffer(drawIndirectArgs);
+		});
+	}
+
+	// Prepare
+	{
+		NonGraphicsRenderPass& pass = rgraph.newNonGraphicsRenderPass("Dbg: Prepare particle emitters");
+
+		pass.newBufferDependency(handle, BufferUsageBit::kUavCompute);
+
+		const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
+		if(visOut.m_dependency.isValid())
+		{
+			pass.newBufferDependency(visOut.m_dependency, BufferUsageBit::kSrvCompute);
+		}
+
+		const GpuVisibilityOutput& fvisOut = getForwardShading().getGpuVisibilityOutput();
+		if(fvisOut.m_dependency.isValid())
+		{
+			pass.newBufferDependency(fvisOut.m_dependency, BufferUsageBit::kSrvCompute);
+		}
+
+		pass.setWork([drawIndirectArgs, renderableIndices, this](RenderPassWorkContext& rgraphCtx) {
+			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
+
+			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
+			variantInitInfo.addMutation("OBJECT_TYPE", 0);
+			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kCompute, "ParticleEmittersPrepare");
+			const ShaderProgramResourceVariant* variant;
+			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
+			cmdb.bindShaderProgram(&variant->getProgram());
+
+			cmdb.bindSrv(0, 0, GpuSceneArrays::Renderable::getSingleton().getBufferView());
+
+			cmdb.bindUav(0, 0, drawIndirectArgs);
+			cmdb.bindUav(1, 0, renderableIndices);
+
+			const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
+			if(visOut.m_dependency.isValid())
+			{
+				cmdb.bindSrv(1, 0, GpuSceneArrays::RenderableBoundingVolumeGBuffer::getSingleton().getBufferView());
+				cmdb.bindSrv(2, 0, visOut.m_visibleAaabbIndicesBuffer);
+
+				const U32 allAabbCount = GpuSceneArrays::RenderableBoundingVolumeGBuffer::getSingleton().getElementCount();
+				cmdb.dispatchCompute((allAabbCount + 63) / 64, 1, 1);
+			}
+
+			const GpuVisibilityOutput& fvisOut = getForwardShading().getGpuVisibilityOutput();
+			if(fvisOut.m_dependency.isValid())
+			{
+				cmdb.bindSrv(1, 0, GpuSceneArrays::RenderableBoundingVolumeForward::getSingleton().getBufferView());
+				cmdb.bindSrv(2, 0, fvisOut.m_visibleAaabbIndicesBuffer);
+
+				const U32 allAabbCount = GpuSceneArrays::RenderableBoundingVolumeForward::getSingleton().getElementCount();
+				cmdb.dispatchCompute((allAabbCount + 63) / 64, 1, 1);
+			}
+		});
+	}
+}
+
+void Dbg::populateRenderGraphMain(RenderingContext& ctx, InternalCtx& ictx)
 {
 {
 	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
 	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
 
 
@@ -620,6 +760,8 @@ void Dbg::populateRenderGraphMain(RenderingContext& ctx)
 	pass.newTextureDependency(m_runCtx.m_rt, TextureUsageBit::kRtvDsvWrite);
 	pass.newTextureDependency(m_runCtx.m_rt, TextureUsageBit::kRtvDsvWrite);
 	pass.newTextureDependency(getGBuffer().getDepthRt(), TextureUsageBit::kSrvPixel | TextureUsageBit::kRtvDsvRead);
 	pass.newTextureDependency(getGBuffer().getDepthRt(), TextureUsageBit::kSrvPixel | TextureUsageBit::kRtvDsvRead);
 
 
+	pass.newBufferDependency(ictx.m_particleEmitter.m_handle, BufferUsageBit::kIndirectDraw);
+
 	const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
 	const GpuVisibilityOutput& visOut = getGBuffer().getVisibilityOutput();
 	if(visOut.m_dependency.isValid())
 	if(visOut.m_dependency.isValid())
 	{
 	{
@@ -632,7 +774,7 @@ void Dbg::populateRenderGraphMain(RenderingContext& ctx)
 		pass.newBufferDependency(fvisOut.m_dependency, BufferUsageBit::kSrvGeometry);
 		pass.newBufferDependency(fvisOut.m_dependency, BufferUsageBit::kSrvGeometry);
 	}
 	}
 
 
-	pass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
+	pass.setWork([this, &ctx, ictx](RenderPassWorkContext& rgraphCtx) {
 		ANKI_TRACE_SCOPED_EVENT(Dbg);
 		ANKI_TRACE_SCOPED_EVENT(Dbg);
 		ANKI_ASSERT(!!(m_options & DbgOption::kDbgScene));
 		ANKI_ASSERT(!!(m_options & DbgOption::kDbgScene));
 
 
@@ -713,6 +855,12 @@ void Dbg::populateRenderGraphMain(RenderingContext& ctx)
 							  ctx, *m_reflectionImage, false, rgraphCtx);
 							  ctx, *m_reflectionImage, false, rgraphCtx);
 		}
 		}
 
 
+		// Particle emitter icons
+		if(!!(m_options & DbgOption::kIcons))
+		{
+			drawParticleEmitters(ctx, ictx, false, rgraphCtx);
+		}
+
 		// Physics
 		// Physics
 		if(!!(m_options & DbgOption::kPhysics))
 		if(!!(m_options & DbgOption::kPhysics))
 		{
 		{
@@ -772,42 +920,6 @@ void Dbg::populateRenderGraphMain(RenderingContext& ctx)
 			}
 			}
 		}
 		}
 
 
-		// Debug point
-		if(m_debugPoint.m_position != kMaxF32)
-		{
-			struct Consts
-			{
-				Mat4 m_mvp;
-				Vec4 m_color;
-			} consts;
-			const Mat4 trf = Mat4(m_debugPoint.m_position, Mat3::getIdentity(), Vec3(m_debugPoint.m_size));
-			consts.m_mvp = ctx.m_matrices.m_viewProjection * trf;
-			consts.m_color = m_debugPoint.m_color;
-			cmdb.setFastConstants(&consts, sizeof(consts));
-
-			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
-			variantInitInfo.addMutation("OBJECT_TYPE", 0);
-			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "Gizmos");
-			const ShaderProgramResourceVariant* variant;
-			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
-			cmdb.bindShaderProgram(&variant->getProgram());
-
-			cmdb.setVertexAttribute(VertexAttributeSemantic::kPosition, 0, Format::kR32G32B32_Sfloat, 0);
-			cmdb.bindVertexBuffer(0, BufferView(m_debugPoint.m_positionsBuff.get()), sizeof(Vec3));
-
-			if(!m_debugPoint.m_enableDepthTest)
-			{
-				cmdb.setDepthCompareOperation(CompareOperation::kAlways);
-			}
-
-			cmdb.draw(PrimitiveTopology::kTriangles, sizeof(kCubePositions) / sizeof(F32));
-
-			if(!m_debugPoint.m_enableDepthTest)
-			{
-				cmdb.setDepthCompareOperation(CompareOperation::kLess);
-			}
-		}
-
 		if(m_gizmos.m_enabled)
 		if(m_gizmos.m_enabled)
 		{
 		{
 			cmdb.setDepthCompareOperation(CompareOperation::kAlways);
 			cmdb.setDepthCompareOperation(CompareOperation::kAlways);
@@ -821,7 +933,7 @@ void Dbg::populateRenderGraphMain(RenderingContext& ctx)
 	});
 	});
 }
 }
 
 
-void Dbg::populateRenderGraphObjectPicking(RenderingContext& ctx)
+void Dbg::populateRenderGraphObjectPicking(RenderingContext& ctx, InternalCtx& ictx)
 {
 {
 	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
 	RenderGraphBuilder& rgraph = ctx.m_renderGraphDescr;
 
 
@@ -880,7 +992,7 @@ void Dbg::populateRenderGraphObjectPicking(RenderingContext& ctx)
 
 
 			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
 			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
 			variantInitInfo.addMutation("OBJECT_TYPE", 0);
 			variantInitInfo.addMutation("OBJECT_TYPE", 0);
-			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kCompute, "PrepareRenderablesPicking");
+			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kCompute, "RenderablesPreparePicking");
 			const ShaderProgramResourceVariant* variant;
 			const ShaderProgramResourceVariant* variant;
 			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 			cmdb.bindShaderProgram(&variant->getProgram());
 			cmdb.bindShaderProgram(&variant->getProgram());
@@ -926,6 +1038,7 @@ void Dbg::populateRenderGraphObjectPicking(RenderingContext& ctx)
 		pass.newBufferDependency(bufferHandle, BufferUsageBit::kIndirectDraw);
 		pass.newBufferDependency(bufferHandle, BufferUsageBit::kIndirectDraw);
 		pass.newTextureDependency(objectPickingRt, TextureUsageBit::kRtvDsvWrite);
 		pass.newTextureDependency(objectPickingRt, TextureUsageBit::kRtvDsvWrite);
 		pass.newTextureDependency(objectPickingDepthRt, TextureUsageBit::kRtvDsvWrite);
 		pass.newTextureDependency(objectPickingDepthRt, TextureUsageBit::kRtvDsvWrite);
+		pass.newBufferDependency(ictx.m_particleEmitter.m_handle, BufferUsageBit::kIndirectDraw);
 
 
 		GraphicsRenderPassTargetDesc colorRti(objectPickingRt);
 		GraphicsRenderPassTargetDesc colorRti(objectPickingRt);
 		colorRti.m_loadOperation = RenderTargetLoadOperation::kClear;
 		colorRti.m_loadOperation = RenderTargetLoadOperation::kClear;
@@ -935,8 +1048,8 @@ void Dbg::populateRenderGraphObjectPicking(RenderingContext& ctx)
 		depthRti.m_subresource.m_depthStencilAspect = DepthStencilAspectBit::kDepth;
 		depthRti.m_subresource.m_depthStencilAspect = DepthStencilAspectBit::kDepth;
 		pass.setRenderpassInfo({colorRti}, &depthRti);
 		pass.setRenderpassInfo({colorRti}, &depthRti);
 
 
-		pass.setWork([this, lodAndRenderableIndicesBuff, &ctx, drawIndirectArgsBuff, drawCountBuff,
-					  maxVisibleCount](RenderPassWorkContext& rgraphCtx) {
+		pass.setWork([this, lodAndRenderableIndicesBuff, &ctx, drawIndirectArgsBuff, drawCountBuff, maxVisibleCount,
+					  ictx](RenderPassWorkContext& rgraphCtx) {
 			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
 			CommandBuffer& cmdb = *rgraphCtx.m_commandBuffer;
 
 
 			// Set common state
 			// Set common state
@@ -945,7 +1058,7 @@ void Dbg::populateRenderGraphObjectPicking(RenderingContext& ctx)
 
 
 			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
 			ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
 			variantInitInfo.addMutation("OBJECT_TYPE", 0);
 			variantInitInfo.addMutation("OBJECT_TYPE", 0);
-			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "RenderablesPicking");
+			variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "RenderablesRenderPicking");
 			const ShaderProgramResourceVariant* variant;
 			const ShaderProgramResourceVariant* variant;
 			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 			m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 			cmdb.bindShaderProgram(&variant->getProgram());
 			cmdb.bindShaderProgram(&variant->getProgram());
@@ -978,6 +1091,9 @@ void Dbg::populateRenderGraphObjectPicking(RenderingContext& ctx)
 								  GpuSceneArrays::ReflectionProbe::getSingleton().getElementCount(), ctx, *m_reflectionImage, true, rgraphCtx);
 								  GpuSceneArrays::ReflectionProbe::getSingleton().getElementCount(), ctx, *m_reflectionImage, true, rgraphCtx);
 			}
 			}
 
 
+			// Draw particle emitters
+			drawParticleEmitters(ctx, ictx, true, rgraphCtx);
+
 			// Draw gizmos
 			// Draw gizmos
 			if(m_gizmos.m_enabled)
 			if(m_gizmos.m_enabled)
 			{
 			{
@@ -1101,7 +1217,8 @@ void Dbg::drawGizmos(const Mat3x4& worldTransform, const RenderingContext& ctx,
 
 
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_dbgProg);
 	variantInitInfo.addMutation("OBJECT_TYPE", 0);
 	variantInitInfo.addMutation("OBJECT_TYPE", 0);
-	variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, (objectPicking) ? "GizmosPicking" : "Gizmos");
+	variantInitInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel,
+											 (objectPicking) ? "GizmosRenderPicking" : "GizmosRenderMain");
 	const ShaderProgramResourceVariant* variant;
 	const ShaderProgramResourceVariant* variant;
 	m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 	m_dbgProg->getOrCreateVariant(variantInitInfo, variant);
 	cmdb.bindShaderProgram(&variant->getProgram());
 	cmdb.bindShaderProgram(&variant->getProgram());

+ 8 - 3
AnKi/Renderer/Dbg.h

@@ -118,6 +118,7 @@ private:
 	ImageResourcePtr m_spotLightImage;
 	ImageResourcePtr m_spotLightImage;
 	ImageResourcePtr m_decalImage;
 	ImageResourcePtr m_decalImage;
 	ImageResourcePtr m_reflectionImage;
 	ImageResourcePtr m_reflectionImage;
+	ImageResourcePtr m_particlesImage;
 
 
 	ShaderProgramResourcePtr m_dbgProg;
 	ShaderProgramResourcePtr m_dbgProg;
 
 
@@ -165,15 +166,19 @@ private:
 		DbgObjectPickingResult m_objPickingRes;
 		DbgObjectPickingResult m_objPickingRes;
 	} m_runCtx;
 	} m_runCtx;
 
 
-	void initGizmos();
+	class InternalCtx;
 
 
-	void populateRenderGraphMain(RenderingContext& ctx);
+	void initGizmos();
 
 
-	void populateRenderGraphObjectPicking(RenderingContext& ctx);
+	void populateRenderGraphParticleEmitters(RenderingContext& ctx, InternalCtx& ictx);
+	void populateRenderGraphMain(RenderingContext& ctx, InternalCtx& ictx);
+	void populateRenderGraphObjectPicking(RenderingContext& ctx, InternalCtx& ictx);
 
 
 	void drawNonRenderable(GpuSceneNonRenderableObjectType type, U32 objCount, const RenderingContext& ctx, const ImageResource& image,
 	void drawNonRenderable(GpuSceneNonRenderableObjectType type, U32 objCount, const RenderingContext& ctx, const ImageResource& image,
 						   Bool objectPicking, RenderPassWorkContext& rgraphCtx);
 						   Bool objectPicking, RenderPassWorkContext& rgraphCtx);
 
 
+	void drawParticleEmitters(const RenderingContext& ctx, const InternalCtx& ictx, Bool objectPicking, RenderPassWorkContext& rgraphCtx);
+
 	void drawGizmos(const Mat3x4& worldTransform, const RenderingContext& ctx, Bool objectPicking, CommandBuffer& cmdb) const;
 	void drawGizmos(const Mat3x4& worldTransform, const RenderingContext& ctx, Bool objectPicking, CommandBuffer& cmdb) const;
 
 
 	void getDebugRenderTarget([[maybe_unused]] CString rtName, Array<RenderTargetHandle, U32(DebugRenderTargetRegister::kCount)>& handles,
 	void getDebugRenderTarget([[maybe_unused]] CString rtName, Array<RenderTargetHandle, U32(DebugRenderTargetRegister::kCount)>& handles,

+ 1 - 1
AnKi/Renderer/Utils/GpuVisibility.cpp

@@ -386,7 +386,7 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 
 
 	if(in.m_gatherAabbIndices)
 	if(in.m_gatherAabbIndices)
 	{
 	{
-		stage1Mem.m_visibleAabbIndices = allocateStructuredBuffer<U32>(buckets.getBucketsActiveUserCount(in.m_technique));
+		stage1Mem.m_visibleAabbIndices = allocateStructuredBuffer<U32>(buckets.getBucketsActiveUserCount(in.m_technique) + 1);
 	}
 	}
 
 
 	if(in.m_hashVisibles)
 	if(in.m_hashVisibles)

+ 183 - 52
AnKi/Shaders/Dbg.ankiprog

@@ -3,22 +3,25 @@
 // Code licensed under the BSD License.
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
+// Contains many shaders used by the editor for scene visualization
+
 #pragma anki mutator OBJECT_TYPE 0 1 2 3 4 // Same as GpuSceneNonRenderableObjectType
 #pragma anki mutator OBJECT_TYPE 0 1 2 3 4 // Same as GpuSceneNonRenderableObjectType
 
 
 #pragma anki technique RenderableBoxes vert pixel mutators
 #pragma anki technique RenderableBoxes vert pixel mutators
 #pragma anki technique Lines vert pixel mutators
 #pragma anki technique Lines vert pixel mutators
 
 
-#pragma anki technique Bilboards vert pixel
-#pragma anki technique BilboardsPicking vert pixel
+#pragma anki technique BilboardsRenderMain vert pixel
+#pragma anki technique BilboardsRenderPicking vert pixel
 
 
-// Prepare the indirect calls for RenderablesPicking
-#pragma anki technique PrepareRenderablesPicking comp mutators
+#pragma anki technique RenderablesPreparePicking comp mutators
+#pragma anki technique RenderablesRenderPicking vert pixel mutators
 
 
-// Draw the renderables to output the UUID
-#pragma anki technique RenderablesPicking vert pixel mutators
+#pragma anki technique GizmosRenderMain vert pixel mutators
+#pragma anki technique GizmosRenderPicking vert pixel mutators
 
 
-#pragma anki technique Gizmos vert pixel mutators
-#pragma anki technique GizmosPicking vert pixel mutators
+#pragma anki technique ParticleEmittersPrepare comp mutators
+#pragma anki technique ParticleEmittersRenderMain vert pixel mutators
+#pragma anki technique ParticleEmittersRenderPicking vert pixel mutators
 
 
 // Pick one UUID at mouse position
 // Pick one UUID at mouse position
 #pragma anki technique ReadPickingBuffer comp mutators
 #pragma anki technique ReadPickingBuffer comp mutators
@@ -27,6 +30,8 @@
 #include <AnKi/Shaders/Include/GpuVisibilityTypes.h>
 #include <AnKi/Shaders/Include/GpuVisibilityTypes.h>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
 
 
+constexpr F32 kBillboardScale = 0.25f;
+
 // ===========================================================================
 // ===========================================================================
 // RenderableBoxes                                                           =
 // RenderableBoxes                                                           =
 // ===========================================================================
 // ===========================================================================
@@ -107,14 +112,13 @@ Vec4 main(VertOut input) : SV_TARGET0
 #endif // ANKI_TECHNIQUE_RenderableBoxes
 #endif // ANKI_TECHNIQUE_RenderableBoxes
 
 
 // ===========================================================================
 // ===========================================================================
-// Bilboards & BilboardsPicking                                              =
+// BilboardsRenderMain & BilboardsRenderPicking                              =
 // ===========================================================================
 // ===========================================================================
-#if ANKI_TECHNIQUE_Bilboards || ANKI_TECHNIQUE_BilboardsPicking
+#if ANKI_TECHNIQUE_BilboardsRenderMain || ANKI_TECHNIQUE_BilboardsRenderPicking
 #	include <AnKi/Shaders/ClusteredShadingFunctions.hlsl>
 #	include <AnKi/Shaders/ClusteredShadingFunctions.hlsl>
 #	include <AnKi/Shaders/TonemappingFunctions.hlsl>
 #	include <AnKi/Shaders/TonemappingFunctions.hlsl>
 
 
 constexpr F32 kAlpha = 1.0f;
 constexpr F32 kAlpha = 1.0f;
-constexpr F32 kBillboardScale = 0.25f;
 
 
 struct Constants
 struct Constants
 {
 {
@@ -141,7 +145,7 @@ typedef GpuSceneGlobalIlluminationProbe GpuSceneType;
 #		error See file
 #		error See file
 #	endif
 #	endif
 
 
-#	if ANKI_TECHNIQUE_Bilboards
+#	if ANKI_TECHNIQUE_BilboardsRenderMain
 Texture2D g_depthRt : register(t0);
 Texture2D g_depthRt : register(t0);
 #	endif
 #	endif
 StructuredBuffer<GpuSceneType> g_visibleObjects : register(t1);
 StructuredBuffer<GpuSceneType> g_visibleObjects : register(t1);
@@ -216,7 +220,7 @@ VertOut main(VertIn input)
 #	endif // ANKI_VERTEX_SHADER
 #	endif // ANKI_VERTEX_SHADER
 
 
 #	if ANKI_PIXEL_SHADER
 #	if ANKI_PIXEL_SHADER
-#		if ANKI_TECHNIQUE_Bilboards
+#		if ANKI_TECHNIQUE_BilboardsRenderMain
 Vec4
 Vec4
 #		else
 #		else
 U32
 U32
@@ -238,7 +242,7 @@ main(VertOut input) : SV_TARGET0
 
 
 	color.xyz *= input.m_colorScale;
 	color.xyz *= input.m_colorScale;
 
 
-#		if ANKI_TECHNIQUE_Bilboards
+#		if ANKI_TECHNIQUE_BilboardsRenderMain
 	if(g_consts.m_depthFailureVisualization)
 	if(g_consts.m_depthFailureVisualization)
 	{
 	{
 		Vec2 texSize;
 		Vec2 texSize;
@@ -263,7 +267,7 @@ main(VertOut input) : SV_TARGET0
 #		endif
 #		endif
 }
 }
 #	endif // ANKI_PIXEL_SHADER
 #	endif // ANKI_PIXEL_SHADER
-#endif // ANKI_TECHNIQUE_Bilboards
+#endif
 
 
 // ===========================================================================
 // ===========================================================================
 // Lines                                                                     =
 // Lines                                                                     =
@@ -307,45 +311,19 @@ Vec4 main(VertOut input) : SV_TARGET0
 #endif // ANKI_TECHNIQUE_Lines
 #endif // ANKI_TECHNIQUE_Lines
 
 
 // ===========================================================================
 // ===========================================================================
-// Gizmos                                                                    =
+// GizmosRenderMain & GizmosRenderPicking                                    =
 // ===========================================================================
 // ===========================================================================
-#if ANKI_TECHNIQUE_Gizmos
+#if ANKI_TECHNIQUE_GizmosRenderMain || ANKI_TECHNIQUE_GizmosRenderPicking
 
 
 struct Consts
 struct Consts
 {
 {
 	Mat4 m_mvp;
 	Mat4 m_mvp;
+#	if ANKI_TECHNIQUE_GizmosRenderMain
 	Vec4 m_color;
 	Vec4 m_color;
-};
-
-ANKI_FAST_CONSTANTS(Consts, g_consts)
-
-#	if ANKI_VERTEX_SHADER
-Vec4 main(Vec3 positions : POSITION) : SV_POSITION
-{
-	return mul(g_consts.m_mvp, Vec4(positions, 1.0));
-}
-#	endif
-
-#	if ANKI_PIXEL_SHADER
-Vec4 main() : SV_TARGET0
-{
-	return g_consts.m_color;
-}
-#	endif
-
-#endif
-
-// ===========================================================================
-// GizmosPicking                                                             =
-// ===========================================================================
-#if ANKI_TECHNIQUE_GizmosPicking
-
-struct Consts
-{
-	Mat4 m_mvp;
-
+#	else
 	UVec3 m_padding;
 	UVec3 m_padding;
 	U32 m_id;
 	U32 m_id;
+#	endif
 };
 };
 
 
 ANKI_FAST_CONSTANTS(Consts, g_consts)
 ANKI_FAST_CONSTANTS(Consts, g_consts)
@@ -358,18 +336,26 @@ Vec4 main(Vec3 positions : POSITION) : SV_POSITION
 #	endif
 #	endif
 
 
 #	if ANKI_PIXEL_SHADER
 #	if ANKI_PIXEL_SHADER
-U32 main() : SV_TARGET0
+#		if ANKI_TECHNIQUE_GizmosRenderMain
+Vec4
+#		else
+U32
+#		endif
+main() : SV_TARGET0
 {
 {
+#		if ANKI_TECHNIQUE_GizmosRenderMain
+	return g_consts.m_color;
+#		else
 	return g_consts.m_id;
 	return g_consts.m_id;
+#		endif
 }
 }
 #	endif
 #	endif
-
 #endif
 #endif
 
 
 // ===========================================================================
 // ===========================================================================
-// PrepareRenderablesPicking                                                  =
+// RenderablesPreparePicking                                                 =
 // ===========================================================================
 // ===========================================================================
-#if ANKI_TECHNIQUE_PrepareRenderablesPicking
+#if ANKI_TECHNIQUE_RenderablesPreparePicking
 
 
 StructuredBuffer<GpuSceneRenderableBoundingVolume> g_renderableBoundingVolumes : register(t0);
 StructuredBuffer<GpuSceneRenderableBoundingVolume> g_renderableBoundingVolumes : register(t0);
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
@@ -423,9 +409,9 @@ RWStructuredBuffer<U32> g_lodAndRenderableIndices : register(u2);
 #endif
 #endif
 
 
 // ===========================================================================
 // ===========================================================================
-// RenderablesPicking                                                         =
+// RenderablesRenderPicking                                                         =
 // ===========================================================================
 // ===========================================================================
-#if ANKI_TECHNIQUE_RenderablesPicking
+#if ANKI_TECHNIQUE_RenderablesRenderPicking
 
 
 StructuredBuffer<U32> g_lodAndRenderableIndices : register(t0);
 StructuredBuffer<U32> g_lodAndRenderableIndices : register(t0);
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
@@ -503,6 +489,151 @@ U32 main(VertOut input) : SV_TARGET0
 
 
 #endif
 #endif
 
 
+// ===========================================================================
+// ParticleEmittersPrepare                                                   =
+// ===========================================================================
+// Iterate the visible renderables and for each particle emitter: update an indirect call and stash the emitter
+#if ANKI_TECHNIQUE_ParticleEmittersPrepare
+
+StructuredBuffer<GpuSceneRenderable> g_renderables : register(t0);
+StructuredBuffer<GpuSceneRenderableBoundingVolume> g_renderableBoundingVolumes : register(t1);
+StructuredBuffer<LodAndGpuSceneRenderableBoundingVolumeIndex> g_visibleRenderableBoundingVolumeIndices : register(t2);
+
+RWStructuredBuffer<DrawIndirectArgs> g_drawArgs : register(u0);
+RWStructuredBuffer<U32> g_renderableIndices : register(u1);
+
+[numthreads(64, 1, 1)] void main(COMPUTE_ARGS)
+{
+	if(svDispatchThreadId.x >= SBUFF(g_visibleRenderableBoundingVolumeIndices, 0))
+	{
+		return;
+	}
+
+	const LodAndGpuSceneRenderableBoundingVolumeIndex packed = SBUFF(g_visibleRenderableBoundingVolumeIndices, svDispatchThreadId.x + 1);
+	const U32 lod = packed >> 29u;
+	const U32 bvolIdx = packed & ((1u << 29u) - 1u);
+
+	const GpuSceneRenderableBoundingVolume bvol = SBUFF(g_renderableBoundingVolumes, bvolIdx);
+	const GpuSceneRenderable renderable = SBUFF(g_renderables, bvol.m_renderableIndex);
+
+	const Bool isParticleEmitter = renderable.m_particleEmitterIndex2 < kMaxU32;
+	if(!isParticleEmitter)
+	{
+		return;
+	}
+
+	U32 instanceId;
+	InterlockedAdd(SBUFF(g_drawArgs, 0).m_instanceCount, 1, instanceId);
+	SBUFF(g_drawArgs, 0).m_vertexCount = 6;
+
+	SBUFF(g_renderableIndices, instanceId) = bvol.m_renderableIndex;
+}
+#endif
+
+// ===========================================================================
+// ParticleEmittersRenderMain & ParticleEmittersRenderPicking                =
+// ===========================================================================
+#if ANKI_TECHNIQUE_ParticleEmittersRenderMain || ANKI_TECHNIQUE_ParticleEmittersRenderPicking
+
+struct Constants
+{
+	Mat4 m_viewProjMat;
+	Mat3x4 m_camTrf;
+
+	UVec3 m_padding;
+	U32 m_depthFailureVisualization;
+};
+
+ANKI_FAST_CONSTANTS(Constants, g_consts)
+
+#	if ANKI_TECHNIQUE_ParticleEmittersRenderMain
+Texture2D g_depthRt : register(t0);
+#	endif
+StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
+StructuredBuffer<U32> g_renderableIndices : register(t2);
+StructuredBuffer<Mat3x4> g_transforms : register(t3);
+Texture2D<Vec4> g_tex : register(t4);
+
+SamplerState g_trilinearRepeatSampler : register(s1);
+
+struct VertIn
+{
+	U32 m_svInstanceId : SV_INSTANCEID;
+	U32 m_svVertexId : SV_VERTEXID;
+};
+
+struct VertOut
+{
+	Vec4 m_svPosition : SV_POSITION;
+	Vec2 m_uv : TEXCOORD;
+	U32 m_sceneNodeUuid : UUID;
+};
+
+#	if ANKI_VERTEX_SHADER
+VertOut main(VertIn input)
+{
+	VertOut output;
+
+	output.m_uv = Vec2(input.m_svVertexId & 1u, ((input.m_svVertexId + 1u) / 3u) & 1u);
+
+	const U32 renderableIdx = SBUFF(g_renderableIndices, input.m_svInstanceId);
+	const GpuSceneRenderable renderable = SBUFF(g_renderables, renderableIdx);
+	const Mat3x4 trf = SBUFF(g_transforms, renderable.m_worldTransformsIndex);
+
+	const Vec3 worldPos = mul(g_consts.m_camTrf, Vec4((output.m_uv * 2.0 - 1.0) * kBillboardScale, 0.0, 0.0)) + trf.getTranslationPart();
+
+	output.m_svPosition = mul(g_consts.m_viewProjMat, Vec4(worldPos, 1.0));
+
+#		if ANKI_TECHNIQUE_ParticleEmittersRenderPicking
+	output.m_sceneNodeUuid = renderable.m_sceneNodeUuid;
+#		else
+	output.m_sceneNodeUuid = 0;
+#		endif
+
+	return output;
+}
+#	endif // ANKI_VERTEX_SHADER
+
+#	if ANKI_PIXEL_SHADER
+#		if ANKI_TECHNIQUE_ParticleEmittersRenderPicking
+U32
+#		else
+Vec4
+#		endif
+main(VertOut input) : SV_TARGET0
+{
+	ANKI_MAYBE_UNUSED(input);
+
+	// Write the color
+	Vec4 color = g_tex.Sample(g_trilinearRepeatSampler, input.m_uv);
+
+#		if ANKI_TECHNIQUE_ParticleEmittersRenderPicking
+	if(color.a <= 0.01)
+	{
+		discard;
+	}
+
+	return input.m_sceneNodeUuid;
+#		else
+	if(g_consts.m_depthFailureVisualization)
+	{
+		Vec2 texSize;
+		g_depthRt.GetDimensions(texSize.x, texSize.y);
+
+		const F32 depthRef = g_depthRt[clamp(input.m_svPosition.xy, 0.0, texSize - 1.0)].r;
+		const Bool depthTestFailed = input.m_svPosition.z >= depthRef;
+		if(depthTestFailed)
+		{
+			color.xyz *= 0.5;
+		}
+	}
+
+	return color;
+#		endif
+}
+#	endif // ANKI_PIXEL_SHADER
+#endif
+
 // ===========================================================================
 // ===========================================================================
 // ReadPickingBuffer                                                         =
 // ReadPickingBuffer                                                         =
 // ===========================================================================
 // ===========================================================================

+ 3 - 1
Samples/Common/SampleApp.cpp

@@ -155,10 +155,12 @@ Error SampleApp::userMainLoop(Bool& quit, Second elapsedTime)
 		renderer.getDbg().setOptions(options);
 		renderer.getDbg().setOptions(options);
 	}
 	}
 
 
-	if(in.getKey(KeyCode::kF11) == 1 && ANKI_TRACING_ENABLED)
+#if ANKI_TRACING_ENABLED
+	if(in.getKey(KeyCode::kF11) == 1)
 	{
 	{
 		g_cvarCoreTracingEnabled = !g_cvarCoreTracingEnabled;
 		g_cvarCoreTracingEnabled = !g_cvarCoreTracingEnabled;
 	}
 	}
+#endif
 
 
 	if(in.getMouseButton(MouseButton::kRight) > 0 || in.hasTouchDevice())
 	if(in.getMouseButton(MouseButton::kRight) > 0 || in.hasTouchDevice())
 	{
 	{

+ 4 - 0
Sandbox/Main.cpp

@@ -59,7 +59,9 @@ Error MyApp::userPostInit()
 	{
 	{
 		m_profile = true;
 		m_profile = true;
 		g_cvarCoreTargetFps = 240;
 		g_cvarCoreTargetFps = 240;
+#if ANKI_TRACING_ENABLED
 		g_cvarCoreTracingEnabled = true;
 		g_cvarCoreTracingEnabled = true;
+#endif
 	}
 	}
 
 
 	// Load scene
 	// Load scene
@@ -146,10 +148,12 @@ Error MyApp::userMainLoop(Bool& quit, Second elapsedTime)
 		renderer.getDbg().setOptions(options);
 		renderer.getDbg().setOptions(options);
 	}
 	}
 
 
+#if ANKI_TRACING_ENABLED
 	if(in.getKey(KeyCode::kF11) == 1)
 	if(in.getKey(KeyCode::kF11) == 1)
 	{
 	{
 		g_cvarCoreTracingEnabled = !g_cvarCoreTracingEnabled;
 		g_cvarCoreTracingEnabled = !g_cvarCoreTracingEnabled;
 	}
 	}
+#endif
 
 
 #if !PLAYER
 #if !PLAYER
 	static Vec2 mousePosOn1stClick = in.getMousePositionNdc();
 	static Vec2 mousePosOn1stClick = in.getMousePositionNdc();