Browse Source

GPU particles: Hook up the new emitter to rendering

Panagiotis Christopoulos Charitos 1 month ago
parent
commit
cbacbd3ba9

+ 2 - 0
AnKi/Core/App.cpp

@@ -419,8 +419,10 @@ Error App::mainLoop()
 			ANKI_CHECK(Input::getSingleton().handleEvents());
 			ANKI_CHECK(Input::getSingleton().handleEvents());
 			GrManager::getSingleton().beginFrame();
 			GrManager::getSingleton().beginFrame();
 
 
+			GpuSceneMicroPatcher::getSingleton().beginPatching();
 			ANKI_CHECK(userMainLoop(quit, crntTime - prevUpdateTime));
 			ANKI_CHECK(userMainLoop(quit, crntTime - prevUpdateTime));
 			SceneGraph::getSingleton().update(prevUpdateTime, crntTime);
 			SceneGraph::getSingleton().update(prevUpdateTime, crntTime);
+			GpuSceneMicroPatcher::getSingleton().endPatching();
 
 
 			ANKI_CHECK(Renderer::getSingleton().render());
 			ANKI_CHECK(Renderer::getSingleton().render());
 
 

+ 86 - 1
AnKi/Editor/EditorUi.cpp

@@ -305,7 +305,22 @@ void EditorUi::sceneNode(SceneNode& node)
 		treeFlags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
 		treeFlags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
 	}
 	}
 
 
-	const Bool nodeOpen = ImGui::TreeNodeEx("", treeFlags, "%s", node.getName().cstr());
+	String componentsString;
+	for(SceneComponentType sceneComponentType : EnumBitsIterable<SceneComponentType, SceneComponentTypeMask>(node.getSceneComponentMask()))
+	{
+		switch(sceneComponentType)
+		{
+#define ANKI_DEFINE_SCENE_COMPONENT(name, weight, sceneNodeCanHaveMany, icon) \
+	case SceneComponentType::k##name: \
+		componentsString += ICON_MDI_##icon; \
+		break;
+#include <AnKi/Scene/Components/SceneComponentClasses.def.h>
+		default:
+			ANKI_ASSERT(0);
+		}
+	}
+
+	const Bool nodeOpen = ImGui::TreeNodeEx("", treeFlags, "%s  %s", node.getName().cstr(), componentsString.cstr());
 
 
 	if(ImGui::IsItemFocused())
 	if(ImGui::IsItemFocused())
 	{
 	{
@@ -582,6 +597,9 @@ void EditorUi::sceneNodePropertiesWindow()
 					case SceneComponentType::kSkin:
 					case SceneComponentType::kSkin:
 						skinComponent(static_cast<SkinComponent&>(comp));
 						skinComponent(static_cast<SkinComponent&>(comp));
 						break;
 						break;
+					case SceneComponentType::kParticleEmitter2:
+						particleEmitterComponent(static_cast<ParticleEmitter2Component&>(comp));
+						break;
 					default:
 					default:
 						ImGui::Text("TODO");
 						ImGui::Text("TODO");
 					}
 					}
@@ -675,6 +693,13 @@ void EditorUi::scriptComponent(ScriptComponent& comp)
 
 
 void EditorUi::materialComponent(MaterialComponent& comp)
 void EditorUi::materialComponent(MaterialComponent& comp)
 {
 {
+	if(!comp.isValid())
+	{
+		ImGui::SameLine();
+		ImGui::TextUnformatted(ICON_MDI_ALERT);
+		ImGui::SetItemTooltip("Component not valid");
+	}
+
 	// Locate button
 	// Locate button
 	{
 	{
 		ImGui::BeginDisabled(!comp.hasMaterialResource());
 		ImGui::BeginDisabled(!comp.hasMaterialResource());
@@ -792,6 +817,66 @@ void EditorUi::skinComponent(SkinComponent& comp)
 	}
 	}
 }
 }
 
 
+void EditorUi::particleEmitterComponent(ParticleEmitter2Component& comp)
+{
+	// Locate button
+	{
+		ImGui::BeginDisabled(!comp.hasParticleEmitterResource());
+		if(ImGui::Button(comp.hasParticleEmitterResource() ? ICON_MDI_MAP_MARKER : ICON_MDI_ALERT_CIRCLE "##PemCompBtn"))
+		{
+			ANKI_LOGW("TODO");
+		}
+		ImGui::SetItemTooltip("Locate");
+		ImGui::EndDisabled();
+		ImGui::SameLine();
+	}
+
+	// Filename
+	{
+		ImGui::SetNextItemWidth(-1.0f);
+
+		Char buff[kMaxTextInputLen] = "";
+		if(comp.hasParticleEmitterResource())
+		{
+			std::strncpy(buff, comp.getParticleEmitterFilename().cstr(), sizeof(buff));
+		}
+
+		if(ImGui::InputTextWithHint("##PemCompFname", ".ankiparts Filename", buff, sizeof(buff)))
+		{
+			comp.setParticleEmitterFilename(buff);
+		}
+
+		if(comp.hasParticleEmitterResource())
+		{
+			ImGui::SetItemTooltip("%s", comp.getParticleEmitterFilename().cstr());
+		}
+	}
+
+	// Geometry type
+	{
+		dummyButton(0);
+
+		if(ImGui::BeginCombo("Geometry Type", kParticleEmitterGeometryTypeName[comp.getParticleGeometryType()]))
+		{
+			for(ParticleGeometryType n : EnumIterable<ParticleGeometryType>())
+			{
+				const Bool isSelected = (comp.getParticleGeometryType() == n);
+				if(ImGui::Selectable(kParticleEmitterGeometryTypeName[n], isSelected))
+				{
+					comp.setParticleGeometryType(n);
+				}
+
+				// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
+				if(isSelected)
+				{
+					ImGui::SetItemDefaultFocus();
+				}
+			}
+			ImGui::EndCombo();
+		}
+	}
+}
+
 void EditorUi::cVarsWindow()
 void EditorUi::cVarsWindow()
 {
 {
 	if(!m_showCVarEditorWindow)
 	if(!m_showCVarEditorWindow)

+ 1 - 0
AnKi/Editor/EditorUi.h

@@ -162,6 +162,7 @@ private:
 	void materialComponent(MaterialComponent& comp);
 	void materialComponent(MaterialComponent& comp);
 	void meshComponent(MeshComponent& comp);
 	void meshComponent(MeshComponent& comp);
 	void skinComponent(SkinComponent& comp);
 	void skinComponent(SkinComponent& comp);
+	void particleEmitterComponent(ParticleEmitter2Component& comp);
 	void dirTree(const AssetPath& path);
 	void dirTree(const AssetPath& path);
 
 
 	// Widget/UI utils
 	// Widget/UI utils

+ 24 - 14
AnKi/GpuMemory/GpuSceneBuffer.cpp

@@ -41,11 +41,12 @@ void GpuSceneBuffer::updateStats() const
 	g_svarGpuSceneBufferFragmentation.set(externalFragmentation);
 	g_svarGpuSceneBufferFragmentation.set(externalFragmentation);
 }
 }
 
 
-/// It packs the source and destination offsets as well as the size of the patch itself.
+// It packs the source and destination offsets as well as the size of the patch itself. Needs to match the HLSL structure
 class GpuSceneMicroPatcher::PatchHeader
 class GpuSceneMicroPatcher::PatchHeader
 {
 {
 public:
 public:
-	U32 m_dwordCountAndSrcDwordOffsetPack;
+	U32 m_dwordSizeMinusOne : kDwordsPerPatchBitCount;
+	U32 m_srcDwordOffset : 32 - kDwordsPerPatchBitCount;
 	U32 m_dstDwordOffset;
 	U32 m_dstDwordOffset;
 };
 };
 
 
@@ -66,14 +67,29 @@ Error GpuSceneMicroPatcher::init()
 	m_copyProgram->getOrCreateVariant(varInit, variant);
 	m_copyProgram->getOrCreateVariant(varInit, variant);
 	m_grProgram.reset(&variant->getProgram());
 	m_grProgram.reset(&variant->getProgram());
 
 
+	m_stackMemPool.init(CoreMemoryPool::getSingleton().getAllocationCallback(), CoreMemoryPool::getSingleton().getAllocationCallbackUserData(),
+						512_KB);
+
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
-void GpuSceneMicroPatcher::newCopy(StackMemoryPool& frameCpuPool, PtrSize gpuSceneDestOffset, PtrSize dataSize, const void* data)
+void GpuSceneMicroPatcher::beginPatching()
+{
+	ANKI_ASSERT(m_bPatchingMode.fetchAdd(1) == 0);
+
+	m_stackMemPool.reset();
+
+	m_crntFramePatchHeaders = DynamicArray<PatchHeader, MemoryPoolPtrWrapper<StackMemoryPool>>(&m_stackMemPool);
+	m_crntFramePatchData = DynamicArray<U32, MemoryPoolPtrWrapper<StackMemoryPool>>(&m_stackMemPool);
+}
+
+void GpuSceneMicroPatcher::newCopy(PtrSize gpuSceneDestOffset, PtrSize dataSize, const void* data)
 {
 {
+	ANKI_ASSERT(m_bPatchingMode.load() == 1);
 	ANKI_ASSERT(dataSize > 0 && (dataSize % 4) == 0);
 	ANKI_ASSERT(dataSize > 0 && (dataSize % 4) == 0);
 	ANKI_ASSERT((ptrToNumber(data) % 4) == 0);
 	ANKI_ASSERT((ptrToNumber(data) % 4) == 0);
 	ANKI_ASSERT((gpuSceneDestOffset % 4) == 0 && gpuSceneDestOffset / 4 < kMaxU32);
 	ANKI_ASSERT((gpuSceneDestOffset % 4) == 0 && gpuSceneDestOffset / 4 < kMaxU32);
+	ANKI_ASSERT(gpuSceneDestOffset + dataSize <= GpuSceneBuffer::getSingleton().getBufferView().getRange());
 
 
 	const U32 dataDwords = U32(dataSize / 4);
 	const U32 dataDwords = U32(dataSize / 4);
 	U32 gpuSceneDestDwordOffset = U32(gpuSceneDestOffset / 4);
 	U32 gpuSceneDestDwordOffset = U32(gpuSceneDestOffset / 4);
@@ -84,22 +100,15 @@ void GpuSceneMicroPatcher::newCopy(StackMemoryPool& frameCpuPool, PtrSize gpuSce
 	// Break the data into multiple copies
 	// Break the data into multiple copies
 	LockGuard lock(m_mtx);
 	LockGuard lock(m_mtx);
 
 
-	if(m_crntFramePatchHeaders.getSize() == 0)
-	{
-		m_crntFramePatchHeaders = DynamicArray<PatchHeader, MemoryPoolPtrWrapper<StackMemoryPool>>(&frameCpuPool);
-		m_crntFramePatchData = DynamicArray<U32, MemoryPoolPtrWrapper<StackMemoryPool>>(&frameCpuPool);
-	}
-
 	while(patchIt < patchEnd)
 	while(patchIt < patchEnd)
 	{
 	{
 		const U32 patchDwords = min(kDwordsPerPatch, U32(patchEnd - patchIt));
 		const U32 patchDwords = min(kDwordsPerPatch, U32(patchEnd - patchIt));
 
 
 		PatchHeader& header = *m_crntFramePatchHeaders.emplaceBack();
 		PatchHeader& header = *m_crntFramePatchHeaders.emplaceBack();
 		ANKI_ASSERT(((patchDwords - 1) & 0b111111) == (patchDwords - 1));
 		ANKI_ASSERT(((patchDwords - 1) & 0b111111) == (patchDwords - 1));
-		header.m_dwordCountAndSrcDwordOffsetPack = patchDwords - 1;
-		header.m_dwordCountAndSrcDwordOffsetPack <<= 26;
+		header.m_dwordSizeMinusOne = patchDwords - 1;
 		ANKI_ASSERT((m_crntFramePatchData.getSize() & 0x3FFFFFF) == m_crntFramePatchData.getSize());
 		ANKI_ASSERT((m_crntFramePatchData.getSize() & 0x3FFFFFF) == m_crntFramePatchData.getSize());
-		header.m_dwordCountAndSrcDwordOffsetPack |= m_crntFramePatchData.getSize();
+		header.m_srcDwordOffset = m_crntFramePatchData.getSize();
 		header.m_dstDwordOffset = gpuSceneDestDwordOffset;
 		header.m_dstDwordOffset = gpuSceneDestDwordOffset;
 
 
 		const U32 srcOffset = m_crntFramePatchData.getSize();
 		const U32 srcOffset = m_crntFramePatchData.getSize();
@@ -113,6 +122,7 @@ void GpuSceneMicroPatcher::newCopy(StackMemoryPool& frameCpuPool, PtrSize gpuSce
 
 
 void GpuSceneMicroPatcher::patchGpuScene(CommandBuffer& cmdb)
 void GpuSceneMicroPatcher::patchGpuScene(CommandBuffer& cmdb)
 {
 {
+	ANKI_ASSERT(m_bPatchingMode.load() == 0);
 	if(m_crntFramePatchHeaders.getSize() == 0)
 	if(m_crntFramePatchHeaders.getSize() == 0)
 	{
 	{
 		return;
 		return;
@@ -125,11 +135,11 @@ void GpuSceneMicroPatcher::patchGpuScene(CommandBuffer& cmdb)
 
 
 	WeakArray<PatchHeader> mapped;
 	WeakArray<PatchHeader> mapped;
 	const BufferView headersBuff = RebarTransientMemoryPool::getSingleton().allocateStructuredBuffer(m_crntFramePatchHeaders.getSize(), mapped);
 	const BufferView headersBuff = RebarTransientMemoryPool::getSingleton().allocateStructuredBuffer(m_crntFramePatchHeaders.getSize(), mapped);
-	memcpy(mapped.getBegin(), &m_crntFramePatchHeaders[0], m_crntFramePatchHeaders.getSizeInBytes());
+	memcpy(mapped.getBegin(), m_crntFramePatchHeaders.getBegin(), m_crntFramePatchHeaders.getSizeInBytes());
 
 
 	WeakArray<U32> mapped2;
 	WeakArray<U32> mapped2;
 	const BufferView dataBuff = RebarTransientMemoryPool::getSingleton().allocateStructuredBuffer(m_crntFramePatchData.getSize(), mapped2);
 	const BufferView dataBuff = RebarTransientMemoryPool::getSingleton().allocateStructuredBuffer(m_crntFramePatchData.getSize(), mapped2);
-	memcpy(mapped2.getBegin(), &m_crntFramePatchData[0], m_crntFramePatchData.getSizeInBytes());
+	memcpy(mapped2.getBegin(), m_crntFramePatchData.getBegin(), m_crntFramePatchData.getSizeInBytes());
 
 
 	cmdb.bindSrv(0, 0, headersBuff);
 	cmdb.bindSrv(0, 0, headersBuff);
 	cmdb.bindSrv(1, 0, dataBuff);
 	cmdb.bindSrv(1, 0, dataBuff);

+ 44 - 24
AnKi/GpuMemory/GpuSceneBuffer.h

@@ -10,15 +10,12 @@
 #include <AnKi/Gr/Utils/SegregatedListsGpuMemoryPool.h>
 #include <AnKi/Gr/Utils/SegregatedListsGpuMemoryPool.h>
 #include <AnKi/Resource/ShaderProgramResource.h>
 #include <AnKi/Resource/ShaderProgramResource.h>
 #include <AnKi/Gr/GrManager.h>
 #include <AnKi/Gr/GrManager.h>
+#include <AnKi/Util/Assert.h>
 
 
 namespace anki {
 namespace anki {
 
 
-/// @addtogroup gpu_memory
-/// @{
-
 ANKI_CVAR(NumericCVar<PtrSize>, Core, GpuSceneInitialSize, 64_MB, 16_MB, 2_GB, "Global memory for the GPU scene")
 ANKI_CVAR(NumericCVar<PtrSize>, Core, GpuSceneInitialSize, 64_MB, 16_MB, 2_GB, "Global memory for the GPU scene")
 
 
-/// @memberof GpuSceneBuffer
 class GpuSceneBufferAllocation
 class GpuSceneBufferAllocation
 {
 {
 	friend class GpuSceneBuffer;
 	friend class GpuSceneBuffer;
@@ -55,7 +52,7 @@ public:
 		return m_token.m_offset != kMaxPtrSize;
 		return m_token.m_offset != kMaxPtrSize;
 	}
 	}
 
 
-	/// Get offset in the Unified Geometry Buffer buffer.
+	// Get offset in the Unified Geometry Buffer buffer.
 	U32 getOffset() const
 	U32 getOffset() const
 	{
 	{
 		ANKI_ASSERT(isValid());
 		ANKI_ASSERT(isValid());
@@ -72,7 +69,7 @@ private:
 	SegregatedListsGpuMemoryPoolToken m_token;
 	SegregatedListsGpuMemoryPoolToken m_token;
 };
 };
 
 
-/// Memory pool for the GPU scene.
+// Memory pool for the GPU scene.
 class GpuSceneBuffer : public MakeSingleton<GpuSceneBuffer>
 class GpuSceneBuffer : public MakeSingleton<GpuSceneBuffer>
 {
 {
 	template<typename>
 	template<typename>
@@ -141,7 +138,7 @@ inline GpuSceneBufferAllocation::~GpuSceneBufferAllocation()
 	GpuSceneBuffer::getSingleton().deferredFree(*this);
 	GpuSceneBuffer::getSingleton().deferredFree(*this);
 }
 }
 
 
-/// Creates the copy jobs that will patch the GPU Scene.
+// Creates the copy jobs that will patch the GPU Scene.
 class GpuSceneMicroPatcher : public MakeSingleton<GpuSceneMicroPatcher>
 class GpuSceneMicroPatcher : public MakeSingleton<GpuSceneMicroPatcher>
 {
 {
 	template<typename>
 	template<typename>
@@ -154,44 +151,62 @@ public:
 
 
 	Error init();
 	Error init();
 
 
-	/// Copy data for the GPU scene to a staging buffer.
-	/// @note It's thread-safe.
-	void newCopy(StackMemoryPool& frameCpuPool, PtrSize gpuSceneDestOffset, PtrSize dataSize, const void* data);
+	// 1st thing to call before any calls to newCopy
+	// Note: Not thread-safe
+	void beginPatching();
+
+	// 2nd thing to call
+	// Copy data for the GPU scene to a staging buffer.
+	// Note: It's thread-safe against other newCopy()
+	void newCopy(PtrSize gpuSceneDestOffset, PtrSize dataSize, const void* data);
 
 
+	// See newCopy
 	template<typename T>
 	template<typename T>
-	void newCopy(StackMemoryPool& frameCpuPool, PtrSize gpuSceneDestOffset, const T& value)
+	void newCopy(PtrSize gpuSceneDestOffset, const T& value)
 	{
 	{
-		newCopy(frameCpuPool, gpuSceneDestOffset, sizeof(value), &value);
+		newCopy(gpuSceneDestOffset, sizeof(value), &value);
 	}
 	}
 
 
-	/// @see newCopy
-	void newCopy(StackMemoryPool& frameCpuPool, const GpuSceneBufferAllocation& dest, PtrSize dataSize, const void* data)
+	// See newCopy
+	void newCopy(const GpuSceneBufferAllocation& dest, PtrSize dataSize, const void* data)
 	{
 	{
 		ANKI_ASSERT(dataSize <= dest.getAllocatedSize());
 		ANKI_ASSERT(dataSize <= dest.getAllocatedSize());
-		newCopy(frameCpuPool, dest.getOffset(), dataSize, data);
+		newCopy(dest.getOffset(), dataSize, data);
 	}
 	}
 
 
-	/// @see newCopy
+	// See newCopy
 	template<typename T>
 	template<typename T>
-	void newCopy(StackMemoryPool& frameCpuPool, const GpuSceneBufferAllocation& dest, const T& value)
+	void newCopy(const GpuSceneBufferAllocation& dest, const T& value)
 	{
 	{
 		ANKI_ASSERT(sizeof(value) <= dest.getAllocatedSize());
 		ANKI_ASSERT(sizeof(value) <= dest.getAllocatedSize());
-		newCopy(frameCpuPool, dest.getOffset(), sizeof(value), &value);
+		newCopy(dest.getOffset(), sizeof(value), &value);
 	}
 	}
 
 
-	/// Check if there is a need to call patchGpuScene or if no copies are needed.
-	/// @note Not thread-safe. Nothing else should be happening before calling it.
+	// 3rd thing to call after all newCopy() calls have be done
+	// Note: Not thread-safe
+	void endPatching()
+	{
+		ANKI_ASSERT(m_bPatchingMode.fetchSub(1) == 1);
+#if ANKI_ASSERTIONS_ENABLED
+		m_patchingMode = false;
+#endif
+	}
+
+	// 4th optional thing to call. Check if there is a need to call patchGpuScene or if no copies are needed
+	// Note: Not thread-safe
 	Bool patchingIsNeeded() const
 	Bool patchingIsNeeded() const
 	{
 	{
+		ANKI_ASSERT(m_bPatchingMode.load() == 0);
 		return m_crntFramePatchHeaders.getSize() > 0;
 		return m_crntFramePatchHeaders.getSize() > 0;
 	}
 	}
 
 
-	/// Copy the data to the GPU scene buffer.
-	/// @note Not thread-safe. Nothing else should be happening before calling it.
+	// 5th thing to call to copy all scratch data to the GPU scene buffer
+	// Note: Not thread-safe
 	void patchGpuScene(CommandBuffer& cmdb);
 	void patchGpuScene(CommandBuffer& cmdb);
 
 
 private:
 private:
-	static constexpr U32 kDwordsPerPatch = 64;
+	static constexpr U32 kDwordsPerPatch = 64; // If you change this change the bellow as well
+	static constexpr U32 kDwordsPerPatchBitCount = 6;
 
 
 	class PatchHeader;
 	class PatchHeader;
 
 
@@ -202,10 +217,15 @@ private:
 	ShaderProgramResourcePtr m_copyProgram;
 	ShaderProgramResourcePtr m_copyProgram;
 	ShaderProgramPtr m_grProgram;
 	ShaderProgramPtr m_grProgram;
 
 
+	StackMemoryPool m_stackMemPool;
+
+#if ANKI_ASSERTIONS_ENABLED
+	Atomic<U32> m_bPatchingMode = {0};
+#endif
+
 	GpuSceneMicroPatcher();
 	GpuSceneMicroPatcher();
 
 
 	~GpuSceneMicroPatcher();
 	~GpuSceneMicroPatcher();
 };
 };
-/// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 5 - 0
AnKi/Gr/Vulkan/VkGrManager.cpp

@@ -1737,6 +1737,11 @@ VkBool32 GrManagerImpl::debugReportCallbackEXT(VkDebugUtilsMessageSeverityFlagBi
 		objectNames = "N/A";
 		objectNames = "N/A";
 	}
 	}
 
 
+	if(CString(pCallbackData->pMessage).find("Assertion failed") != CString::kNpos)
+	{
+		messageSeverity = VkDebugUtilsMessageSeverityFlagBitsEXT(messageSeverity | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT);
+	}
+
 	if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
 	if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
 	{
 	{
 		ANKI_VK_LOGE("VK debug report: %s. Affected objects: %s", pCallbackData->pMessage, objectNames.cstr());
 		ANKI_VK_LOGE("VK debug report: %s. Affected objects: %s", pCallbackData->pMessage, objectNames.cstr());

+ 1 - 0
AnKi/Renderer/Utils/Drawer.cpp

@@ -70,6 +70,7 @@ void RenderableDrawer::setState(const RenderableDrawerArguments& args, CommandBu
 	cmdb.bindSrv(ANKI_MATERIAL_REGISTER_MESH_LODS, 0, GpuSceneArrays::MeshLod::getSingleton().getBufferView());
 	cmdb.bindSrv(ANKI_MATERIAL_REGISTER_MESH_LODS, 0, GpuSceneArrays::MeshLod::getSingleton().getBufferView());
 	cmdb.bindSrv(ANKI_MATERIAL_REGISTER_TRANSFORMS, 0, GpuSceneArrays::Transform::getSingleton().getBufferView());
 	cmdb.bindSrv(ANKI_MATERIAL_REGISTER_TRANSFORMS, 0, GpuSceneArrays::Transform::getSingleton().getBufferView());
 	cmdb.bindSrv(ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS, 0, GpuSceneArrays::ParticleEmitter::getSingleton().getBufferViewSafe());
 	cmdb.bindSrv(ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS, 0, GpuSceneArrays::ParticleEmitter::getSingleton().getBufferViewSafe());
+	cmdb.bindSrv(ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS2, 0, GpuSceneArrays::ParticleEmitter2::getSingleton().getBufferViewSafe());
 	cmdb.bindSampler(ANKI_MATERIAL_REGISTER_NEAREST_CLAMP_SAMPLER, 0, getRenderer().getSamplers().m_nearestNearestClamp.get());
 	cmdb.bindSampler(ANKI_MATERIAL_REGISTER_NEAREST_CLAMP_SAMPLER, 0, getRenderer().getSamplers().m_nearestNearestClamp.get());
 	if(args.m_legacy.m_perDrawBuffer)
 	if(args.m_legacy.m_perDrawBuffer)
 	{
 	{

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

@@ -654,7 +654,6 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 			cmdb.bindSrv(1, 0, GpuSceneArrays::Renderable::getSingleton().getBufferView());
 			cmdb.bindSrv(1, 0, GpuSceneArrays::Renderable::getSingleton().getBufferView());
 			cmdb.bindSrv(2, 0, GpuSceneArrays::MeshLod::getSingleton().getBufferView());
 			cmdb.bindSrv(2, 0, GpuSceneArrays::MeshLod::getSingleton().getBufferView());
 			cmdb.bindSrv(3, 0, GpuSceneArrays::Transform::getSingleton().getBufferView());
 			cmdb.bindSrv(3, 0, GpuSceneArrays::Transform::getSingleton().getBufferView());
-			cmdb.bindSrv(4, 0, GpuSceneArrays::ParticleEmitter::getSingleton().getBufferViewSafe());
 
 
 			cmdb.bindUav(0, 0, stage1Mem.m_counters);
 			cmdb.bindUav(0, 0, stage1Mem.m_counters);
 
 
@@ -701,7 +700,7 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 
 
 				if(frustumTestData->m_hzbRt.isValid())
 				if(frustumTestData->m_hzbRt.isValid())
 				{
 				{
-					rpass.bindSrv(5, 0, frustumTestData->m_hzbRt);
+					rpass.bindSrv(4, 0, frustumTestData->m_hzbRt);
 					cmdb.bindSampler(0, 0, getRenderer().getSamplers().m_nearestNearestClamp.get());
 					cmdb.bindSampler(0, 0, getRenderer().getSamplers().m_nearestNearestClamp.get());
 				}
 				}
 			}
 			}
@@ -747,14 +746,15 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 
 
 				cmdb.bindSrv(0, 0, GpuSceneArrays::Renderable::getSingleton().getBufferView());
 				cmdb.bindSrv(0, 0, GpuSceneArrays::Renderable::getSingleton().getBufferView());
 				cmdb.bindSrv(1, 0, GpuSceneArrays::ParticleEmitter::getSingleton().getBufferViewSafe());
 				cmdb.bindSrv(1, 0, GpuSceneArrays::ParticleEmitter::getSingleton().getBufferViewSafe());
-				cmdb.bindSrv(2, 0, GpuSceneArrays::MeshLod::getSingleton().getBufferView());
+				cmdb.bindSrv(2, 0, GpuSceneArrays::ParticleEmitter2::getSingleton().getBufferViewSafe());
+				cmdb.bindSrv(3, 0, GpuSceneArrays::MeshLod::getSingleton().getBufferView());
 
 
-				cmdb.bindSrv(3, 0, stage1Mem.m_visibleRenderables);
-				cmdb.bindSrv(4, 0, stage1Mem.m_counters);
-				cmdb.bindSrv(5, 0, stage1Mem.m_renderablePrefixSums);
+				cmdb.bindSrv(4, 0, stage1Mem.m_visibleRenderables);
+				cmdb.bindSrv(5, 0, stage1Mem.m_counters);
+				cmdb.bindSrv(6, 0, stage1Mem.m_renderablePrefixSums);
 
 
 				WeakArray<UVec2> firstDrawIndirectArgAndCount =
 				WeakArray<UVec2> firstDrawIndirectArgAndCount =
-					allocateAndBindSrvStructuredBuffer<UVec2>(cmdb, 6, 0, out.m_legacy.m_bucketIndirectArgsRanges.getSize());
+					allocateAndBindSrvStructuredBuffer<UVec2>(cmdb, 7, 0, out.m_legacy.m_bucketIndirectArgsRanges.getSize());
 				for(U32 ibucket = 0; ibucket < out.m_legacy.m_bucketIndirectArgsRanges.getSize(); ++ibucket)
 				for(U32 ibucket = 0; ibucket < out.m_legacy.m_bucketIndirectArgsRanges.getSize(); ++ibucket)
 				{
 				{
 					firstDrawIndirectArgAndCount[ibucket].x() = out.m_legacy.m_bucketIndirectArgsRanges[ibucket].m_firstInstance;
 					firstDrawIndirectArgAndCount[ibucket].x() = out.m_legacy.m_bucketIndirectArgsRanges[ibucket].m_firstInstance;

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

@@ -289,7 +289,7 @@ void MaterialComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 			m_gpuSceneConstants = GpuSceneBuffer::getSingleton().allocate(preallocatedConsts.getSizeInBytes(), 4);
 			m_gpuSceneConstants = GpuSceneBuffer::getSingleton().allocate(preallocatedConsts.getSizeInBytes(), 4);
 		}
 		}
 
 
-		GpuSceneMicroPatcher::getSingleton().newCopy(*info.m_framePool, m_gpuSceneConstants.getOffset(), m_gpuSceneConstants.getAllocatedSize(),
+		GpuSceneMicroPatcher::getSingleton().newCopy(m_gpuSceneConstants.getOffset(), m_gpuSceneConstants.getAllocatedSize(),
 													 preallocatedConsts.getBegin());
 													 preallocatedConsts.getBegin());
 	}
 	}
 
 
@@ -303,7 +303,8 @@ void MaterialComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		gpuRenderable.m_meshLodsIndex =
 		gpuRenderable.m_meshLodsIndex =
 			(prioritizeEmitter) ? m_emitterComponent->getGpuSceneMeshLodIndex(m_submeshIdx) : m_meshComponent->getGpuSceneMeshLodsIndex(m_submeshIdx);
 			(prioritizeEmitter) ? m_emitterComponent->getGpuSceneMeshLodIndex(m_submeshIdx) : m_meshComponent->getGpuSceneMeshLodsIndex(m_submeshIdx);
 		gpuRenderable.m_boneTransformsOffset = (m_skinComponent) ? m_skinComponent->getBoneTransformsGpuSceneOffset() : 0;
 		gpuRenderable.m_boneTransformsOffset = (m_skinComponent) ? m_skinComponent->getBoneTransformsGpuSceneOffset() : 0;
-		gpuRenderable.m_particleEmitterIndex = (prioritizeEmitter) ? m_emitterComponent->getGpuSceneParticleEmitter2Index() : kMaxU32;
+		gpuRenderable.m_particleEmitterIndex = kMaxU32;
+		gpuRenderable.m_particleEmitterIndex2 = (prioritizeEmitter) ? m_emitterComponent->getGpuSceneParticleEmitter2Index() : kMaxU32;
 		if(!!(mtl.getRenderingTechniques() & RenderingTechniqueBit::kRtShadow))
 		if(!!(mtl.getRenderingTechniques() & RenderingTechniqueBit::kRtShadow))
 		{
 		{
 			const RenderingKey key(RenderingTechnique::kRtShadow, 0, false, false, false);
 			const RenderingKey key(RenderingTechnique::kRtShadow, 0, false, false, false);

+ 30 - 15
AnKi/Scene/Components/ParticleEmitter2Component.cpp

@@ -24,6 +24,7 @@ public:
 	GpuSceneArrays::MeshLod::Allocation m_gpuSceneMeshLods;
 	GpuSceneArrays::MeshLod::Allocation m_gpuSceneMeshLods;
 
 
 	U32 m_userCount = 0;
 	U32 m_userCount = 0;
+	Bool m_uploadedToGpuScene = false;
 	SpinLock m_mtx;
 	SpinLock m_mtx;
 
 
 	void init()
 	void init()
@@ -86,21 +87,7 @@ public:
 
 
 	void initGpuScene()
 	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.allocate();
-		m_gpuSceneMeshLods.uploadToGpuScene(meshLods);
 	}
 	}
 
 
 	void deinit()
 	void deinit()
@@ -136,6 +123,32 @@ public:
 
 
 		--m_userCount;
 		--m_userCount;
 	}
 	}
+
+	// Do that outside of init() because we can only upload to the GPU scene at specific places in the frame
+	void tryUploadToGpuScene()
+	{
+		LockGuard lock(m_mtx);
+
+		if(!m_uploadedToGpuScene)
+		{
+			m_uploadedToGpuScene = true;
+
+			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.uploadToGpuScene(meshLods);
+		}
+	}
 };
 };
 
 
 ParticleEmitter2Component::ParticleEmitter2Component(SceneNode* node)
 ParticleEmitter2Component::ParticleEmitter2Component(SceneNode* node)
@@ -219,6 +232,8 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 		return;
 		return;
 	}
 	}
 
 
+	ParticleEmitterQuadGeometry::getSingleton().tryUploadToGpuScene();
+
 	if(!m_anyDirty) [[likely]]
 	if(!m_anyDirty) [[likely]]
 	{
 	{
 		return;
 		return;
@@ -252,7 +267,7 @@ void ParticleEmitter2Component::update(SceneComponentUpdateInfo& info, Bool& upd
 		ConstWeakArray<U8> prefilled = m_particleEmitterResource->getPrefilledAnKiParticleEmitterProperties();
 		ConstWeakArray<U8> prefilled = m_particleEmitterResource->getPrefilledAnKiParticleEmitterProperties();
 		m_gpuScene.m_anKiParticleEmitterProperties = GpuSceneBuffer::getSingleton().allocate(prefilled.getSizeInBytes(), alignof(U32));
 		m_gpuScene.m_anKiParticleEmitterProperties = GpuSceneBuffer::getSingleton().allocate(prefilled.getSizeInBytes(), alignof(U32));
 
 
-		GpuSceneMicroPatcher::getSingleton().newCopy(*info.m_framePool, m_gpuScene.m_anKiParticleEmitterProperties, prefilled);
+		GpuSceneMicroPatcher::getSingleton().newCopy(m_gpuScene.m_anKiParticleEmitterProperties, prefilled);
 	}
 	}
 
 
 	// GpuSceneParticleEmitter2
 	// GpuSceneParticleEmitter2

+ 11 - 2
AnKi/Scene/Components/ParticleEmitter2Component.h

@@ -19,8 +19,12 @@ enum class ParticleGeometryType : U8
 {
 {
 	kQuad,
 	kQuad,
 	kMeshComponent,
 	kMeshComponent,
-	kCount
+	kCount,
+	kFirst = 0
 };
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ParticleGeometryType)
+
+inline constexpr Array<const Char*, U32(ParticleGeometryType::kCount)> kParticleEmitterGeometryTypeName = {"Quad", "MeshComponent"};
 
 
 // Contains a particle emitter resource and maybe connects to a mesh component
 // Contains a particle emitter resource and maybe connects to a mesh component
 class ParticleEmitter2Component : public SceneComponent
 class ParticleEmitter2Component : public SceneComponent
@@ -36,6 +40,11 @@ public:
 
 
 	CString getParticleEmitterFilename() const;
 	CString getParticleEmitterFilename() const;
 
 
+	Bool hasParticleEmitterResource() const
+	{
+		return !!m_particleEmitterResource;
+	}
+
 	ParticleEmitter2Component& setParticleGeometryType(ParticleGeometryType type)
 	ParticleEmitter2Component& setParticleGeometryType(ParticleGeometryType type)
 	{
 	{
 		if(type != m_geomType && ANKI_EXPECT(type < ParticleGeometryType::kCount))
 		if(type != m_geomType && ANKI_EXPECT(type < ParticleGeometryType::kCount))
@@ -110,7 +119,7 @@ private:
 		GpuSceneArrays::ParticleEmitter2::Allocation m_gpuSceneParticleEmitter;
 		GpuSceneArrays::ParticleEmitter2::Allocation m_gpuSceneParticleEmitter;
 	} m_gpuScene;
 	} m_gpuScene;
 
 
-	Array<Vec3, 2> m_boundingVolume;
+	Array<Vec3, 2> m_boundingVolume = {Vec3(-0.5f), Vec3(0.5f)};
 
 
 	F32 m_dt = 0.0f;
 	F32 m_dt = 0.0f;
 
 

+ 5 - 5
AnKi/Scene/Components/ParticleEmitterComponent.cpp

@@ -349,9 +349,9 @@ void ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& upda
 	GpuSceneMicroPatcher& patcher = GpuSceneMicroPatcher::getSingleton();
 	GpuSceneMicroPatcher& patcher = GpuSceneMicroPatcher::getSingleton();
 	if(m_aliveParticleCount > 0)
 	if(m_aliveParticleCount > 0)
 	{
 	{
-		patcher.newCopy(*info.m_framePool, m_gpuScenePositions, sizeof(Vec3) * m_aliveParticleCount, positions);
-		patcher.newCopy(*info.m_framePool, m_gpuSceneScales, sizeof(F32) * m_aliveParticleCount, scales);
-		patcher.newCopy(*info.m_framePool, m_gpuSceneAlphas, sizeof(F32) * m_aliveParticleCount, alphas);
+		patcher.newCopy(m_gpuScenePositions, sizeof(Vec3) * m_aliveParticleCount, positions);
+		patcher.newCopy(m_gpuSceneScales, sizeof(F32) * m_aliveParticleCount, scales);
+		patcher.newCopy(m_gpuSceneAlphas, sizeof(F32) * m_aliveParticleCount, alphas);
 	}
 	}
 
 
 	if(m_resourceUpdated)
 	if(m_resourceUpdated)
@@ -369,8 +369,7 @@ void ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& upda
 		m_gpuSceneParticleEmitter.uploadToGpuScene(particles);
 		m_gpuSceneParticleEmitter.uploadToGpuScene(particles);
 
 
 		// Upload uniforms
 		// Upload uniforms
-		patcher.newCopy(*info.m_framePool, m_gpuSceneConstants,
-						m_particleEmitterResource->getMaterial()->getPrefilledLocalConstants().getSizeInBytes(),
+		patcher.newCopy(m_gpuSceneConstants, m_particleEmitterResource->getMaterial()->getPrefilledLocalConstants().getSizeInBytes(),
 						m_particleEmitterResource->getMaterial()->getPrefilledLocalConstants().getBegin());
 						m_particleEmitterResource->getMaterial()->getPrefilledLocalConstants().getBegin());
 
 
 		// Upload mesh LODs
 		// Upload mesh LODs
@@ -397,6 +396,7 @@ void ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& upda
 		renderable.m_constantsOffset = m_gpuSceneConstants.getOffset();
 		renderable.m_constantsOffset = m_gpuSceneConstants.getOffset();
 		renderable.m_meshLodsIndex = m_gpuSceneMeshLods.getIndex() * kMaxLodCount;
 		renderable.m_meshLodsIndex = m_gpuSceneMeshLods.getIndex() * kMaxLodCount;
 		renderable.m_particleEmitterIndex = m_gpuSceneParticleEmitter.getIndex();
 		renderable.m_particleEmitterIndex = m_gpuSceneParticleEmitter.getIndex();
+		renderable.m_particleEmitterIndex2 = kMaxU32;
 		renderable.m_worldTransformsIndex = 0;
 		renderable.m_worldTransformsIndex = 0;
 		renderable.m_uuid = SceneGraph::getSingleton().getNewUuid();
 		renderable.m_uuid = SceneGraph::getSingleton().getNewUuid();
 		if(!m_gpuSceneRenderable.isValid())
 		if(!m_gpuSceneRenderable.isValid())

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

@@ -219,7 +219,7 @@ void SkinComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 			trfs[i * 2 + 0] = getBoneTransforms()[i];
 			trfs[i * 2 + 0] = getBoneTransforms()[i];
 			trfs[i * 2 + 1] = getPreviousFrameBoneTransforms()[i];
 			trfs[i * 2 + 1] = getPreviousFrameBoneTransforms()[i];
 		}
 		}
-		GpuSceneMicroPatcher::getSingleton().newCopy(*info.m_framePool, m_gpuSceneBoneTransforms, trfs.getSizeInBytes(), trfs.getBegin());
+		GpuSceneMicroPatcher::getSingleton().newCopy(m_gpuSceneBoneTransforms, trfs.getSizeInBytes(), trfs.getBegin());
 	}
 	}
 	else
 	else
 	{
 	{

+ 1 - 1
AnKi/Scene/GpuSceneArray.h

@@ -71,7 +71,7 @@ public:
 
 
 	void uploadToGpuScene(const TGpuSceneObject& data) const
 	void uploadToGpuScene(const TGpuSceneObject& data) const
 	{
 	{
-		GpuSceneMicroPatcher::getSingleton().newCopy(SceneGraph::getSingleton().getFrameMemoryPool(), getGpuSceneOffset(), data);
+		GpuSceneMicroPatcher::getSingleton().newCopy(getGpuSceneOffset(), data);
 	}
 	}
 
 
 	/// Allocate an element into the appropriate array. See GpuSceneArray::allocate()
 	/// Allocate an element into the appropriate array. See GpuSceneArray::allocate()

+ 1 - 1
AnKi/Scene/GpuSceneArray.inl.h

@@ -115,7 +115,7 @@ void GpuSceneArray<TGpuSceneObject, kId>::flushInternal(Bool nullifyElements)
 			for(U32 idx : m_freedAllocations)
 			for(U32 idx : m_freedAllocations)
 			{
 			{
 				const PtrSize offset = idx * sizeof(TGpuSceneObject) + m_gpuSceneAllocation.getOffset();
 				const PtrSize offset = idx * sizeof(TGpuSceneObject) + m_gpuSceneAllocation.getOffset();
-				GpuSceneMicroPatcher::getSingleton().newCopy(SceneGraph::getSingleton().getFrameMemoryPool(), offset, nullObj);
+				GpuSceneMicroPatcher::getSingleton().newCopy(offset, nullObj);
 			}
 			}
 		}
 		}
 
 

+ 5 - 0
AnKi/Scene/SceneNode.h

@@ -258,6 +258,11 @@ public:
 		return !!((1u << SceneComponentTypeMask(TComponent::kClassType)) & m_componentTypeMask);
 		return !!((1u << SceneComponentTypeMask(TComponent::kClassType)) & m_componentTypeMask);
 	}
 	}
 
 
+	SceneComponentTypeMask getSceneComponentMask() const
+	{
+		return m_componentTypeMask;
+	}
+
 	/// @name Movement
 	/// @name Movement
 	/// @{
 	/// @{
 
 

+ 14 - 6
AnKi/Shaders/GpuSceneMicroPatching.ankiprog

@@ -7,22 +7,30 @@
 
 
 #include <AnKi/Shaders/Common.hlsl>
 #include <AnKi/Shaders/Common.hlsl>
 
 
-StructuredBuffer<UVec2> g_patchHeaders : register(t0);
+// Needs to match the C++. See C++ for comments
+struct PatchHeader
+{
+	U32 m_dwordSizeMinusOne : 6;
+	U32 m_srcDwordOffset : 26;
+	U32 m_dstDwordOffset;
+};
+
+StructuredBuffer<PatchHeader> g_patchHeaders : register(t0);
 StructuredBuffer<U32> g_srcBuffer : register(t1);
 StructuredBuffer<U32> g_srcBuffer : register(t1);
 RWStructuredBuffer<U32> g_dstBuffer : register(u0);
 RWStructuredBuffer<U32> g_dstBuffer : register(u0);
 
 
 [numthreads(64, 1, 1)] void main(UVec3 svGroupId : SV_GROUPID, U32 svGroupIndex : SV_GROUPINDEX)
 [numthreads(64, 1, 1)] void main(UVec3 svGroupId : SV_GROUPID, U32 svGroupIndex : SV_GROUPINDEX)
 {
 {
-	const UVec2 header = g_patchHeaders[svGroupId.x];
-	const U32 dwordCount = (header.x >> 26u) + 1u;
+	const PatchHeader header = SBUFF(g_patchHeaders, svGroupId.x);
+	const U32 dwordCount = header.m_dwordSizeMinusOne + 1u;
 
 
 	if(svGroupIndex >= dwordCount)
 	if(svGroupIndex >= dwordCount)
 	{
 	{
 		return;
 		return;
 	}
 	}
 
 
-	const U32 srcDwordOffset = header.x & 0x3FFFFFFu;
-	const U32 dstDwordOffset = header.y;
+	const U32 srcDwordOffset = header.m_srcDwordOffset;
+	const U32 dstDwordOffset = header.m_dstDwordOffset;
 
 
-	g_dstBuffer[dstDwordOffset + svGroupIndex] = g_srcBuffer[srcDwordOffset + svGroupIndex];
+	SBUFF(g_dstBuffer, dstDwordOffset + svGroupIndex) = SBUFF(g_srcBuffer, srcDwordOffset + svGroupIndex);
 }
 }

+ 2 - 3
AnKi/Shaders/GpuVisibilityStage1.ankiprog

@@ -27,7 +27,6 @@ StructuredBuffer<GpuSceneRenderableBoundingVolume> g_renderableBoundingVolumes :
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(t1);
 StructuredBuffer<GpuSceneMeshLod> g_meshLods : register(t2);
 StructuredBuffer<GpuSceneMeshLod> g_meshLods : register(t2);
 StructuredBuffer<Mat3x4> g_transforms : register(t3);
 StructuredBuffer<Mat3x4> g_transforms : register(t3);
-StructuredBuffer<GpuSceneParticleEmitter> g_particleEmitters : register(t4);
 
 
 // 1st counter is the visible renderable count, 2nd the visible meshlet count and 3rd the number of threadgroups having been executed
 // 1st counter is the visible renderable count, 2nd the visible meshlet count and 3rd the number of threadgroups having been executed
 RWStructuredBuffer<U32> g_counters : register(u0);
 RWStructuredBuffer<U32> g_counters : register(u0);
@@ -66,7 +65,7 @@ ANKI_FAST_CONSTANTS(DistanceGpuVisibilityConstants, g_consts)
 #endif
 #endif
 
 
 #if HZB_TEST
 #if HZB_TEST
-Texture2D<Vec4> g_hzbTex : register(t5);
+Texture2D<Vec4> g_hzbTex : register(t4);
 SamplerState g_nearestAnyClampSampler : register(s0);
 SamplerState g_nearestAnyClampSampler : register(s0);
 #endif
 #endif
 
 
@@ -182,7 +181,7 @@ Bool isVisible(GpuSceneRenderableBoundingVolume bvolume)
 		const U32 meshLodIndex = renderable.m_meshLodsIndex + lod;
 		const U32 meshLodIndex = renderable.m_meshLodsIndex + lod;
 		const GpuSceneMeshLod meshLod = SBUFF(g_meshLods, meshLodIndex);
 		const GpuSceneMeshLod meshLod = SBUFF(g_meshLods, meshLodIndex);
 
 
-		const Bool isParticleEmitter = renderable.m_particleEmitterIndex < kMaxU32;
+		const Bool isParticleEmitter = renderable.m_particleEmitterIndex < kMaxU32 || renderable.m_particleEmitterIndex2 < kMaxU32;
 		ANKI_MAYBE_UNUSED(isParticleEmitter);
 		ANKI_MAYBE_UNUSED(isParticleEmitter);
 
 
 		const Bool hasMeshlets = meshLod.m_meshletCount != 0u;
 		const Bool hasMeshlets = meshLod.m_meshletCount != 0u;

+ 17 - 8
AnKi/Shaders/GpuVisibilityStage2And3.ankiprog

@@ -36,14 +36,15 @@ struct DrawIndirectArgsWithPadding
 // GPU scene
 // GPU scene
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(t0);
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(t0);
 StructuredBuffer<GpuSceneParticleEmitter> g_particleEmitters : register(t1);
 StructuredBuffer<GpuSceneParticleEmitter> g_particleEmitters : register(t1);
-StructuredBuffer<GpuSceneMeshLod> g_meshLods : register(t2);
+StructuredBuffer<GpuSceneParticleEmitter2> g_particleEmitters2 : register(t2);
+StructuredBuffer<GpuSceneMeshLod> g_meshLods : register(t3);
 
 
-StructuredBuffer<GpuVisibilityVisibleRenderableDesc> g_visibleRenderables : register(t3);
-StructuredBuffer<U32> g_counters : register(t4);
-StructuredBuffer<U32> g_renderablePrefixSums : register(t5);
+StructuredBuffer<GpuVisibilityVisibleRenderableDesc> g_visibleRenderables : register(t4);
+StructuredBuffer<U32> g_counters : register(t5);
+StructuredBuffer<U32> g_renderablePrefixSums : register(t6);
 
 
 // One for each bucket. Points to the 1st indirect args struct. 2nd element contains the max count
 // One for each bucket. Points to the 1st indirect args struct. 2nd element contains the max count
-StructuredBuffer<UVec2> g_firstDrawIndirectArgAndCount : register(t6);
+StructuredBuffer<UVec2> g_firstDrawIndirectArgAndCount : register(t7);
 
 
 // These 3 have the same size
 // These 3 have the same size
 RWStructuredBuffer<GpuScenePerDraw> g_perDraw : register(u0);
 RWStructuredBuffer<GpuScenePerDraw> g_perDraw : register(u0);
@@ -71,7 +72,7 @@ RWStructuredBuffer<U32> g_outOfMemoryBuffer : register(u3);
 	const U32 meshLodIndex = renderable.m_meshLodsIndex + lod;
 	const U32 meshLodIndex = renderable.m_meshLodsIndex + lod;
 	const GpuSceneMeshLod meshLod = SBUFF(g_meshLods, meshLodIndex);
 	const GpuSceneMeshLod meshLod = SBUFF(g_meshLods, meshLodIndex);
 
 
-	const Bool isParticleEmitter = renderable.m_particleEmitterIndex < kMaxU32;
+	const Bool isParticleEmitter = renderable.m_particleEmitterIndex < kMaxU32 || renderable.m_particleEmitterIndex2 < kMaxU32;
 
 
 	U32 bucketLocalIndex;
 	U32 bucketLocalIndex;
 	InterlockedAdd(SBUFF(g_mdiDrawCounts, renderStateBucket), 1u, bucketLocalIndex);
 	InterlockedAdd(SBUFF(g_mdiDrawCounts, renderStateBucket), 1u, bucketLocalIndex);
@@ -96,8 +97,16 @@ RWStructuredBuffer<U32> g_outOfMemoryBuffer : register(u3);
 
 
 		if(isParticleEmitter)
 		if(isParticleEmitter)
 		{
 		{
-			const GpuSceneParticleEmitter emitter = SBUFF(g_particleEmitters, renderable.m_particleEmitterIndex);
-			indirect.m_instanceCount = emitter.m_aliveParticleCount;
+			if(renderable.m_particleEmitterIndex < kMaxU32)
+			{
+				const GpuSceneParticleEmitter emitter = SBUFF(g_particleEmitters, renderable.m_particleEmitterIndex);
+				indirect.m_instanceCount = emitter.m_aliveParticleCount;
+			}
+			else
+			{
+				const GpuSceneParticleEmitter2 emitter = SBUFF(g_particleEmitters2, renderable.m_particleEmitterIndex2);
+				indirect.m_instanceCount = emitter.m_aliveParticleCount;
+			}
 		}
 		}
 		else
 		else
 		{
 		{

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

@@ -25,6 +25,7 @@ struct GpuSceneRenderable
 	U32 m_meshLodsIndex; // Points to the array of GpuSceneMeshLod. kMaxLodCount are reserved for each renderable.
 	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_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_particleEmitterIndex; // Index to the GpuSceneParticleEmitter array or kMaxU32 if it's not an emitter.
+	U32 m_particleEmitterIndex2; // Index to the GpuSceneParticleEmitter2 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_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_rtMaterialFetchShaderHandleIndex; // The index of the shader handle in the array of library's handles.
 	U32 m_uuid; // A UUID specific for this renderable. Don't come from some scene object
 	U32 m_uuid; // A UUID specific for this renderable. Don't come from some scene object

+ 14 - 13
AnKi/Shaders/Include/MaterialTypes.h

@@ -31,26 +31,27 @@ static_assert(sizeof(MaterialGlobalConstants) == 15 * sizeof(Vec4));
 #define ANKI_MATERIAL_REGISTER_RENDERABLES 4
 #define ANKI_MATERIAL_REGISTER_RENDERABLES 4
 #define ANKI_MATERIAL_REGISTER_MESH_LODS 5
 #define ANKI_MATERIAL_REGISTER_MESH_LODS 5
 #define ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS 6
 #define ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS 6
-#define ANKI_MATERIAL_REGISTER_TRANSFORMS 7
+#define ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS2 7
+#define ANKI_MATERIAL_REGISTER_TRANSFORMS 8
 #define ANKI_MATERIAL_REGISTER_NEAREST_CLAMP_SAMPLER 1
 #define ANKI_MATERIAL_REGISTER_NEAREST_CLAMP_SAMPLER 1
-#define ANKI_MATERIAL_REGISTER_FIRST_MESHLET 8
-#define ANKI_MATERIAL_REGISTER_PER_DRAW 9
-#define ANKI_MATERIAL_REGISTER_PER_DRAW_OFFSET 10
+#define ANKI_MATERIAL_REGISTER_FIRST_MESHLET 9
+#define ANKI_MATERIAL_REGISTER_PER_DRAW 10
+#define ANKI_MATERIAL_REGISTER_PER_DRAW_OFFSET 11
 
 
 // For FW shading:
 // For FW shading:
 #define ANKI_MATERIAL_REGISTER_LINEAR_CLAMP_SAMPLER 2
 #define ANKI_MATERIAL_REGISTER_LINEAR_CLAMP_SAMPLER 2
 #define ANKI_MATERIAL_REGISTER_SHADOW_SAMPLER 3
 #define ANKI_MATERIAL_REGISTER_SHADOW_SAMPLER 3
 #define ANKI_MATERIAL_REGISTER_CLUSTER_SHADING_CONSTANTS 1
 #define ANKI_MATERIAL_REGISTER_CLUSTER_SHADING_CONSTANTS 1
-#define ANKI_MATERIAL_REGISTER_SCENE_DEPTH 11
-#define ANKI_MATERIAL_REGISTER_LIGHT_VOLUME 12
-#define ANKI_MATERIAL_REGISTER_CLUSTER_SHADING_POINT_LIGHTS 13
-#define ANKI_MATERIAL_REGISTER_CLUSTER_SHADING_SPOT_LIGHTS 14
-#define ANKI_MATERIAL_REGISTER_SHADOW_ATLAS 15
-#define ANKI_MATERIAL_REGISTER_CLUSTERS 16
-
-#define ANKI_MATERIAL_REGISTER_UNIFIED_GEOMETRY 17
+#define ANKI_MATERIAL_REGISTER_SCENE_DEPTH 12
+#define ANKI_MATERIAL_REGISTER_LIGHT_VOLUME 13
+#define ANKI_MATERIAL_REGISTER_CLUSTER_SHADING_POINT_LIGHTS 14
+#define ANKI_MATERIAL_REGISTER_CLUSTER_SHADING_SPOT_LIGHTS 15
+#define ANKI_MATERIAL_REGISTER_SHADOW_ATLAS 16
+#define ANKI_MATERIAL_REGISTER_CLUSTERS 17
+
+#define ANKI_MATERIAL_REGISTER_UNIFIED_GEOMETRY 18
 // Always last because it's variable. Texture buffer bindings pointing to unified geom buffer:
 // Always last because it's variable. Texture buffer bindings pointing to unified geom buffer:
 // !!WARNING!! Remember to update the UnifiedGeometryTypes.def.h if you change that one
 // !!WARNING!! Remember to update the UnifiedGeometryTypes.def.h if you change that one
-#define ANKI_MATERIAL_REGISTER_UNIFIED_GEOMETRY_TYPED_BUFFER_START 18
+#define ANKI_MATERIAL_REGISTER_UNIFIED_GEOMETRY_TYPED_BUFFER_START 19
 
 
 ANKI_END_NAMESPACE
 ANKI_END_NAMESPACE

+ 6 - 6
AnKi/Shaders/Include/UnifiedGeometryTypes.def.h

@@ -11,17 +11,17 @@
 
 
 // !!!! ALL FORMATS NEED TO BE MORE THAN 4 BYTES, else we can't address large typed buffers !!!!
 // !!!! ALL FORMATS NEED TO BE MORE THAN 4 BYTES, else we can't address large typed buffers !!!!
 
 
-ANKI_UNIFIED_GEOM_FORMAT(R32G32_Sfloat, Vec2, 18)
+ANKI_UNIFIED_GEOM_FORMAT(R32G32_Sfloat, Vec2, 19)
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
-ANKI_UNIFIED_GEOM_FORMAT(R32G32B32_Sfloat, Vec3, 19)
+ANKI_UNIFIED_GEOM_FORMAT(R32G32B32_Sfloat, Vec3, 20)
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
-ANKI_UNIFIED_GEOM_FORMAT(R32G32B32A32_Sfloat, Vec4, 20)
+ANKI_UNIFIED_GEOM_FORMAT(R32G32B32A32_Sfloat, Vec4, 21)
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
-ANKI_UNIFIED_GEOM_FORMAT(R16G16B16A16_Unorm, Vec4, 21)
+ANKI_UNIFIED_GEOM_FORMAT(R16G16B16A16_Unorm, Vec4, 22)
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
-ANKI_UNIFIED_GEOM_FORMAT(R8G8B8A8_Snorm, Vec4, 22)
+ANKI_UNIFIED_GEOM_FORMAT(R8G8B8A8_Snorm, Vec4, 23)
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
 ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
-ANKI_UNIFIED_GEOM_FORMAT(R8G8B8A8_Uint, UVec4, 23)
+ANKI_UNIFIED_GEOM_FORMAT(R8G8B8A8_Uint, UVec4, 24)
 
 
 #undef ANKI_UNIFIED_GEOM_FORMAT
 #undef ANKI_UNIFIED_GEOM_FORMAT
 #undef ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR
 #undef ANKI_UNIFIED_GEOM_FORMAT_SEPERATOR

+ 1 - 0
AnKi/Shaders/MaterialShadersCommon.hlsl

@@ -31,6 +31,7 @@ StructuredBuffer<GpuSceneMeshletInstance> g_meshletInstances : register(ANKI_REG
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_RENDERABLES));
 StructuredBuffer<GpuSceneRenderable> g_renderables : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_RENDERABLES));
 StructuredBuffer<GpuSceneMeshLod> g_meshLods : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_MESH_LODS));
 StructuredBuffer<GpuSceneMeshLod> g_meshLods : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_MESH_LODS));
 StructuredBuffer<GpuSceneParticleEmitter> g_particleEmitters : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS));
 StructuredBuffer<GpuSceneParticleEmitter> g_particleEmitters : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS));
+StructuredBuffer<GpuSceneParticleEmitter2> g_particleEmitters2 : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_PARTICLE_EMITTERS2));
 StructuredBuffer<Mat3x4> g_transforms : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_TRANSFORMS));
 StructuredBuffer<Mat3x4> g_transforms : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_TRANSFORMS));
 SamplerState g_nearestClampSampler : register(ANKI_REG(s, ANKI_MATERIAL_REGISTER_NEAREST_CLAMP_SAMPLER));
 SamplerState g_nearestClampSampler : register(ANKI_REG(s, ANKI_MATERIAL_REGISTER_NEAREST_CLAMP_SAMPLER));
 StructuredBuffer<U32> g_firstMeshlet : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_FIRST_MESHLET));
 StructuredBuffer<U32> g_firstMeshlet : register(ANKI_REG(t, ANKI_MATERIAL_REGISTER_FIRST_MESHLET));