Browse Source

Move the clusterer objects to the GPU scene

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
f226c1e2f2
53 changed files with 834 additions and 584 deletions
  1. 5 0
      AnKi/Core/GpuMemoryPools.h
  2. 2 0
      AnKi/Gr/RenderGraph.inl.h
  3. 5 0
      AnKi/Gr/Utils/SegregatedListsGpuMemoryPool.h
  4. 61 268
      AnKi/Renderer/ClusterBinning.cpp
  5. 18 0
      AnKi/Renderer/ClusterBinning.h
  6. 0 26
      AnKi/Renderer/Common.h
  7. 10 5
      AnKi/Renderer/ForwardShading.cpp
  8. 12 8
      AnKi/Renderer/GBufferPost.cpp
  9. 1 1
      AnKi/Renderer/GBufferPost.h
  10. 6 4
      AnKi/Renderer/IndirectDiffuse.cpp
  11. 5 4
      AnKi/Renderer/IndirectSpecular.cpp
  12. 9 8
      AnKi/Renderer/LightShading.cpp
  13. 173 0
      AnKi/Renderer/PackVisibleClusteredObjects.cpp
  14. 55 0
      AnKi/Renderer/PackVisibleClusteredObjects.h
  15. 1 0
      AnKi/Renderer/ProbeReflections.cpp
  16. 0 1
      AnKi/Renderer/ProbeReflections.h
  17. 14 0
      AnKi/Renderer/RenderQueue.h
  18. 5 0
      AnKi/Renderer/Renderer.cpp
  19. 1 0
      AnKi/Renderer/RendererObject.defs.h
  20. 11 9
      AnKi/Renderer/RtShadows.cpp
  21. 1 1
      AnKi/Renderer/RtShadows.h
  22. 15 12
      AnKi/Renderer/ShadowmapsResolve.cpp
  23. 1 1
      AnKi/Renderer/ShadowmapsResolve.h
  24. 12 8
      AnKi/Renderer/VolumetricLightingAccumulation.cpp
  25. 7 4
      AnKi/Scene/Components/DecalComponent.cpp
  26. 2 1
      AnKi/Scene/Components/DecalComponent.h
  27. 7 4
      AnKi/Scene/Components/FogDensityComponent.cpp
  28. 3 1
      AnKi/Scene/Components/FogDensityComponent.h
  29. 8 5
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp
  30. 2 1
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.h
  31. 56 24
      AnKi/Scene/Components/LightComponent.cpp
  32. 13 8
      AnKi/Scene/Components/LightComponent.h
  33. 29 14
      AnKi/Scene/Components/ModelComponent.cpp
  34. 2 2
      AnKi/Scene/Components/ModelComponent.h
  35. 13 9
      AnKi/Scene/Components/ParticleEmitterComponent.cpp
  36. 1 1
      AnKi/Scene/Components/ParticleEmitterComponent.h
  37. 8 4
      AnKi/Scene/Components/ReflectionProbeComponent.cpp
  38. 3 1
      AnKi/Scene/Components/ReflectionProbeComponent.h
  39. 14 13
      AnKi/Scene/ContiguousArrayAllocator.cpp
  40. 41 8
      AnKi/Scene/ContiguousArrayAllocator.h
  41. 14 0
      AnKi/Scene/Visibility.cpp
  42. 16 15
      AnKi/Shaders/ClusterBinning.ankiprog
  43. 16 33
      AnKi/Shaders/ClusteredShadingCommon.hlsl
  44. 22 61
      AnKi/Shaders/Include/ClusteredShadingTypes.h
  45. 4 2
      AnKi/Shaders/Include/Common.h
  46. 5 10
      AnKi/Shaders/Include/GpuSceneTypes.h
  47. 1 1
      AnKi/Shaders/Include/MaterialTypes.h
  48. 19 0
      AnKi/Shaders/Include/MiscRendererTypes.h
  49. 4 4
      AnKi/Shaders/Include/ModelTypes.h
  50. 1 1
      AnKi/Shaders/LightShading.ankiprog
  51. 98 0
      AnKi/Shaders/PackVisibleClusteredObjects.ankiprog
  52. 1 1
      AnKi/Util/Assert.cpp
  53. 1 0
      AnKi/Util/Win32Minimal.h

+ 5 - 0
AnKi/Core/GpuMemoryPools.h

@@ -162,6 +162,11 @@ public:
 		return m_buffer;
 	}
 
+	U8* getBufferMappedAddress()
+	{
+		return m_mappedMem;
+	}
+
 private:
 	BufferPtr m_buffer;
 	U8* m_mappedMem = nullptr; ///< Cache it.

+ 2 - 0
AnKi/Gr/RenderGraph.inl.h

@@ -331,6 +331,8 @@ inline BufferHandle RenderGraphDescription::importBuffer(BufferPtr buff, BufferU
 		ANKI_ASSERT((offset + range) <= buff->getSize());
 	}
 
+	ANKI_ASSERT(range > 0);
+
 	for([[maybe_unused]] const Buffer& bb : m_buffers)
 	{
 		ANKI_ASSERT((bb.m_importedBuff != buff || !bufferRangeOverlaps(bb.m_offset, bb.m_range, offset, range))

+ 5 - 0
AnKi/Gr/Utils/SegregatedListsGpuMemoryPool.h

@@ -28,6 +28,11 @@ public:
 		return m_offset == b.m_offset && m_chunk == b.m_chunk && m_chunkOffset == b.m_chunkOffset && m_size == b.m_size;
 	}
 
+	Bool isValid() const
+	{
+		return m_chunk != nullptr;
+	}
+
 private:
 	void* m_chunk = nullptr;
 	PtrSize m_chunkOffset = kMaxPtrSize;

+ 61 - 268
AnKi/Renderer/ClusterBinning.cpp

@@ -5,6 +5,7 @@
 
 #include <AnKi/Renderer/ClusterBinning.h>
 #include <AnKi/Renderer/Renderer.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/VolumetricLightingAccumulation.h>
 #include <AnKi/Renderer/ProbeReflections.h>
@@ -56,47 +57,50 @@ void ClusterBinning::populateRenderGraph(RenderingContext& ctx)
 	m_runCtx.m_ctx = &ctx;
 	writeClustererBuffers(ctx);
 
-	ctx.m_clusteredShading.m_clustersBufferHandle = ctx.m_renderGraphDescr.importBuffer(
+	m_runCtx.m_rebarHandle = ctx.m_renderGraphDescr.importBuffer(
 		m_r->getExternalSubsystems().m_rebarStagingPool->getBuffer(), BufferUsageBit::kNone,
-		ctx.m_clusteredShading.m_clustersToken.m_offset, ctx.m_clusteredShading.m_clustersToken.m_range);
+		m_runCtx.m_clustersToken.m_offset, m_runCtx.m_clustersToken.m_range);
 
 	const RenderQueue& rqueue = *ctx.m_renderQueue;
-	if(rqueue.m_pointLights.getSize() || rqueue.m_spotLights.getSize() || rqueue.m_decals.getSize()
-	   || rqueue.m_reflectionProbes.getSize() || rqueue.m_fogDensityVolumes.getSize() || rqueue.m_giProbes.getSize())
-		[[likely]]
+	if(rqueue.m_pointLights.getSize() == 0 && rqueue.m_spotLights.getSize() == 0 && rqueue.m_decals.getSize() == 0
+	   && rqueue.m_reflectionProbes.getSize() == 0 && rqueue.m_fogDensityVolumes.getSize() == 0
+	   && rqueue.m_giProbes.getSize() == 0) [[unlikely]]
 	{
-		RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
-		ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("Cluster Binning");
-
-		pass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageComputeWrite);
-
-		pass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
-			CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-
-			const ClusteredShadingContext& tokens = ctx.m_clusteredShading;
-
-			cmdb->bindShaderProgram(m_grProg);
-			bindUniforms(cmdb, 0, 0, tokens.m_clusteredShadingUniformsToken);
-			bindStorage(cmdb, 0, 1, tokens.m_clustersToken);
-			bindStorage(cmdb, 0, 2, tokens.m_pointLightsToken);
-			bindStorage(cmdb, 0, 3, tokens.m_spotLightsToken);
-			bindStorage(cmdb, 0, 4, tokens.m_reflectionProbesToken);
-			bindStorage(cmdb, 0, 5, tokens.m_globalIlluminationProbesToken);
-			bindStorage(cmdb, 0, 6, tokens.m_fogDensityVolumesToken);
-			bindStorage(cmdb, 0, 7, tokens.m_decalsToken);
-
-			const U32 sampleCount = 4;
-			const U32 sizex = m_tileCount * sampleCount;
-			const RenderQueue& rqueue = *ctx.m_renderQueue;
-			U32 clusterObjectCounts = rqueue.m_pointLights.getSize();
-			clusterObjectCounts += rqueue.m_spotLights.getSize();
-			clusterObjectCounts += rqueue.m_reflectionProbes.getSize();
-			clusterObjectCounts += rqueue.m_giProbes.getSize();
-			clusterObjectCounts += rqueue.m_fogDensityVolumes.getSize();
-			clusterObjectCounts += rqueue.m_decals.getSize();
-			cmdb->dispatchCompute((sizex + 64 - 1) / 64, clusterObjectCounts, 1);
-		});
+		return;
 	}
+
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+	ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("Cluster Binning");
+
+	pass.newBufferDependency(m_r->getPackVisibleClusteredObjects().getClusteredObjectsRenderGraphHandle(),
+							 BufferUsageBit::kStorageComputeRead);
+
+	pass.newBufferDependency(m_runCtx.m_rebarHandle, BufferUsageBit::kStorageComputeWrite);
+
+	pass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
+		CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+		cmdb->bindShaderProgram(m_grProg);
+
+		bindUniforms(cmdb, 0, 0, m_runCtx.m_clusteredShadingUniformsToken);
+		bindStorage(cmdb, 0, 1, m_runCtx.m_clustersToken);
+
+		for(ClusteredObjectType type : EnumIterable<ClusteredObjectType>())
+		{
+			m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, U32(type) + 2, type);
+		}
+
+		const U32 sampleCount = 4;
+		const U32 sizex = m_tileCount * sampleCount;
+		const RenderQueue& rqueue = *ctx.m_renderQueue;
+		U32 clusterObjectCounts = rqueue.m_pointLights.getSize();
+		clusterObjectCounts += rqueue.m_spotLights.getSize();
+		clusterObjectCounts += rqueue.m_reflectionProbes.getSize();
+		clusterObjectCounts += rqueue.m_giProbes.getSize();
+		clusterObjectCounts += rqueue.m_fogDensityVolumes.getSize();
+		clusterObjectCounts += rqueue.m_decals.getSize();
+		cmdb->dispatchCompute((sizex + 64 - 1) / 64, clusterObjectCounts, 1);
+	});
 }
 
 void ClusterBinning::writeClustererBuffers(RenderingContext& ctx)
@@ -147,72 +151,9 @@ void ClusterBinning::writeClustererBuffers(RenderingContext& ctx)
 	}
 
 	// Allocate buffers
-	ClusteredShadingContext& cs = ctx.m_clusteredShading;
 	RebarStagingGpuMemoryPool& stagingMem = *m_r->getExternalSubsystems().m_rebarStagingPool;
-
-	cs.m_clusteredShadingUniformsAddress =
-		stagingMem.allocateFrame(sizeof(ClusteredShadingUniforms), cs.m_clusteredShadingUniformsToken);
-
-	if(rqueue.m_pointLights.getSize())
-	{
-		cs.m_pointLightsAddress =
-			stagingMem.allocateFrame(rqueue.m_pointLights.getSize() * sizeof(PointLight), cs.m_pointLightsToken);
-	}
-	else
-	{
-		cs.m_pointLightsToken.markUnused();
-	}
-
-	if(rqueue.m_spotLights.getSize())
-	{
-		cs.m_spotLightsAddress =
-			stagingMem.allocateFrame(rqueue.m_spotLights.getSize() * sizeof(SpotLight), cs.m_spotLightsToken);
-	}
-	else
-	{
-		cs.m_spotLightsToken.markUnused();
-	}
-
-	if(rqueue.m_reflectionProbes.getSize())
-	{
-		cs.m_reflectionProbesAddress = stagingMem.allocateFrame(
-			rqueue.m_reflectionProbes.getSize() * sizeof(ReflectionProbe), cs.m_reflectionProbesToken);
-	}
-	else
-	{
-		cs.m_reflectionProbesToken.markUnused();
-	}
-
-	if(rqueue.m_decals.getSize())
-	{
-		cs.m_decalsAddress = stagingMem.allocateFrame(rqueue.m_decals.getSize() * sizeof(Decal), cs.m_decalsToken);
-	}
-	else
-	{
-		cs.m_decalsToken.markUnused();
-	}
-
-	if(rqueue.m_fogDensityVolumes.getSize())
-	{
-		cs.m_fogDensityVolumesAddress = stagingMem.allocateFrame(
-			rqueue.m_fogDensityVolumes.getSize() * sizeof(FogDensityVolume), cs.m_fogDensityVolumesToken);
-	}
-	else
-	{
-		cs.m_fogDensityVolumesToken.markUnused();
-	}
-
-	if(rqueue.m_giProbes.getSize())
-	{
-		cs.m_globalIlluminationProbesAddress = stagingMem.allocateFrame(
-			rqueue.m_giProbes.getSize() * sizeof(GlobalIlluminationProbe), cs.m_globalIlluminationProbesToken);
-	}
-	else
-	{
-		cs.m_globalIlluminationProbesToken.markUnused();
-	}
-
-	cs.m_clustersAddress = stagingMem.allocateFrame(sizeof(Cluster) * m_clusterCount, cs.m_clustersToken);
+	stagingMem.allocateFrame(sizeof(ClusteredShadingUniforms), m_runCtx.m_clusteredShadingUniformsToken);
+	stagingMem.allocateFrame(sizeof(Cluster) * m_clusterCount, m_runCtx.m_clustersToken);
 }
 
 void ClusterBinning::writeClusterBuffersAsync()
@@ -230,165 +171,13 @@ void ClusterBinning::writeClustererBuffersTask()
 	ANKI_TRACE_SCOPED_EVENT(RWriteClusterShadingObjects);
 
 	RenderingContext& ctx = *m_runCtx.m_ctx;
-	ClusteredShadingContext& cs = ctx.m_clusteredShading;
 	const RenderQueue& rqueue = *ctx.m_renderQueue;
 
-	// Point lights
-	if(rqueue.m_pointLights.getSize())
-	{
-		PointLight* lights = static_cast<PointLight*>(cs.m_pointLightsAddress);
-
-		for(U32 i = 0; i < rqueue.m_pointLights.getSize(); ++i)
-		{
-			PointLight& out = lights[i];
-			const PointLightQueueElement& in = rqueue.m_pointLights[i];
-
-			out.m_position = in.m_worldPosition;
-			out.m_diffuseColor = in.m_diffuseColor;
-			out.m_radius = in.m_radius;
-			out.m_squareRadiusOverOne = 1.0f / (in.m_radius * in.m_radius);
-
-			if(in.m_shadowRenderQueues[0] == nullptr)
-			{
-				out.m_shadowAtlasTileScale = -1.0f;
-				out.m_shadowLayer = kMaxU32;
-			}
-			else
-			{
-				out.m_shadowLayer = in.m_shadowLayer;
-				out.m_shadowAtlasTileScale = in.m_shadowAtlasTileSize;
-				for(U32 f = 0; f < 6; ++f)
-				{
-					out.m_shadowAtlasTileOffsets[f] = Vec4(in.m_shadowAtlasTileOffsets[f], 0.0f, 0.0f);
-				}
-			}
-		}
-	}
-
-	// Spot lights
-	if(rqueue.m_spotLights.getSize())
-	{
-		SpotLight* lights = static_cast<SpotLight*>(cs.m_spotLightsAddress);
-
-		for(U32 i = 0; i < rqueue.m_spotLights.getSize(); ++i)
-		{
-			const SpotLightQueueElement& in = rqueue.m_spotLights[i];
-			SpotLight& out = lights[i];
-
-			out.m_position = in.m_worldTransform.getTranslationPart().xyz();
-			for(U32 j = 0; j < 4; ++j)
-			{
-				out.m_edgePoints[j] = in.m_edgePoints[j].xyz0();
-			}
-			out.m_diffuseColor = in.m_diffuseColor;
-			out.m_radius = in.m_distance;
-			out.m_squareRadiusOverOne = 1.0f / (in.m_distance * in.m_distance);
-			out.m_shadowLayer = (in.hasShadow()) ? in.m_shadowLayer : kMaxU32;
-			out.m_direction = -in.m_worldTransform.getRotationPart().getZAxis();
-			out.m_outerCos = cos(in.m_outerAngle / 2.0f);
-			out.m_innerCos = cos(in.m_innerAngle / 2.0f);
-
-			if(in.hasShadow())
-			{
-				// bias * proj_l * view_l
-				out.m_textureMatrix = in.m_textureMatrix;
-			}
-			else
-			{
-				out.m_textureMatrix = Mat4::getIdentity();
-			}
-		}
-	}
-
-	// Reflection probes
-	if(rqueue.m_reflectionProbes.getSize())
-	{
-		ReflectionProbe* probes = static_cast<ReflectionProbe*>(cs.m_reflectionProbesAddress);
-
-		for(U32 i = 0; i < rqueue.m_reflectionProbes.getSize(); ++i)
-		{
-			const ReflectionProbeQueueElement& in = rqueue.m_reflectionProbes[i];
-			ReflectionProbe& out = probes[i];
-
-			out.m_position = in.m_worldPosition;
-			out.m_cubeTexture = in.m_textureBindlessIndex;
-			out.m_aabbMin = in.m_aabbMin;
-			out.m_aabbMax = in.m_aabbMax;
-		}
-	}
-
-	// Decals
-	if(rqueue.m_decals.getSize())
-	{
-		Decal* decals = static_cast<Decal*>(cs.m_decalsAddress);
-
-		for(U32 i = 0; i < rqueue.m_decals.getSize(); ++i)
-		{
-			const DecalQueueElement& in = rqueue.m_decals[i];
-			Decal& out = decals[i];
-
-			out.m_diffuseTexture = in.m_diffuseBindlessTextureIndex;
-			out.m_roughnessMetalnessTexture = in.m_roughnessMetalnessBindlessTextureIndex;
-			out.m_diffuseBlendFactor = in.m_diffuseBlendFactor;
-			out.m_roughnessMetalnessFactor = in.m_roughnessMetalnessBlendFactor;
-
-			// bias * proj_l * view
-			out.m_textureMatrix = in.m_textureMatrix;
-
-			// Transform
-			const Mat4 trf(in.m_obbCenter.xyz1(), in.m_obbRotation, 1.0f);
-			out.m_invertedTransform = trf.getInverse();
-			out.m_obbExtend = in.m_obbExtend;
-		}
-	}
-
-	// Fog volumes
-	if(rqueue.m_fogDensityVolumes.getSize())
-	{
-		FogDensityVolume* volumes = static_cast<FogDensityVolume*>(cs.m_fogDensityVolumesAddress);
-
-		for(U32 i = 0; i < rqueue.m_fogDensityVolumes.getSize(); ++i)
-		{
-			const FogDensityQueueElement& in = rqueue.m_fogDensityVolumes[i];
-			FogDensityVolume& out = volumes[i];
-
-			out.m_density = in.m_density;
-			if(in.m_isBox)
-			{
-				out.m_isBox = 1;
-				out.m_aabbMinOrSphereCenter = in.m_aabbMin;
-				out.m_aabbMaxOrSphereRadiusSquared = in.m_aabbMax;
-			}
-			else
-			{
-				out.m_isBox = 0;
-				out.m_aabbMinOrSphereCenter = in.m_sphereCenter;
-				out.m_aabbMaxOrSphereRadiusSquared = Vec3(in.m_sphereRadius * in.m_sphereRadius);
-			}
-		}
-	}
-
-	// GI
-	if(rqueue.m_giProbes.getSize())
-	{
-		GlobalIlluminationProbe* probes = static_cast<GlobalIlluminationProbe*>(cs.m_globalIlluminationProbesAddress);
-
-		for(U32 i = 0; i < rqueue.m_giProbes.getSize(); ++i)
-		{
-			const GlobalIlluminationProbeQueueElement& in = rqueue.m_giProbes[i];
-			GlobalIlluminationProbe& out = probes[i];
-
-			out.m_aabbMin = in.m_aabbMin;
-			out.m_aabbMax = in.m_aabbMax;
-			out.m_volumeTexture = in.m_volumeTextureBindlessIndex;
-			out.m_halfTexelSizeU = 1.0f / F32(F32(in.m_cellCounts.x()) * 6.0f) / 2.0f;
-			out.m_fadeDistance = in.m_fadeDistance;
-		}
-	}
-
 	// General uniforms
 	{
-		ClusteredShadingUniforms& unis = *static_cast<ClusteredShadingUniforms*>(cs.m_clusteredShadingUniformsAddress);
+		ClusteredShadingUniforms& unis = *reinterpret_cast<ClusteredShadingUniforms*>(
+			m_r->getExternalSubsystems().m_rebarStagingPool->getBufferMappedAddress()
+			+ m_runCtx.m_clusteredShadingUniformsToken.m_offset);
 
 		unis.m_renderingSize = Vec2(F32(m_r->getInternalResolution().x()), F32(m_r->getInternalResolution().y()));
 
@@ -411,18 +200,20 @@ void ClusterBinning::writeClustererBuffersTask()
 		unis.m_tileSize = m_r->getTileSize();
 		unis.m_lightVolumeLastZSplit = m_r->getVolumetricLightingAccumulation().getFinalZSplit();
 
-		unis.m_objectCountsUpTo[ClusterObjectType::kPointLight].x() = rqueue.m_pointLights.getSize();
-		unis.m_objectCountsUpTo[ClusterObjectType::kSpotLight].x() =
-			unis.m_objectCountsUpTo[ClusterObjectType::kSpotLight - 1].x() + rqueue.m_spotLights.getSize();
-		unis.m_objectCountsUpTo[ClusterObjectType::kDecal].x() =
-			unis.m_objectCountsUpTo[ClusterObjectType::kDecal - 1].x() + rqueue.m_decals.getSize();
-		unis.m_objectCountsUpTo[ClusterObjectType::kFogDensityVolume].x() =
-			unis.m_objectCountsUpTo[ClusterObjectType::kFogDensityVolume - 1].x()
+		unis.m_objectCountsUpTo[ClusteredObjectType::kPointLight].x() = rqueue.m_pointLights.getSize();
+		unis.m_objectCountsUpTo[ClusteredObjectType::kSpotLight].x() =
+			unis.m_objectCountsUpTo[ClusteredObjectType::kSpotLight - 1].x() + rqueue.m_spotLights.getSize();
+		unis.m_objectCountsUpTo[ClusteredObjectType::kDecal].x() =
+			unis.m_objectCountsUpTo[ClusteredObjectType::kDecal - 1].x() + rqueue.m_decals.getSize();
+		unis.m_objectCountsUpTo[ClusteredObjectType::kFogDensityVolume].x() =
+			unis.m_objectCountsUpTo[ClusteredObjectType::kFogDensityVolume - 1].x()
 			+ rqueue.m_fogDensityVolumes.getSize();
-		unis.m_objectCountsUpTo[ClusterObjectType::kReflectionProbe].x() =
-			unis.m_objectCountsUpTo[ClusterObjectType::kReflectionProbe - 1].x() + rqueue.m_reflectionProbes.getSize();
-		unis.m_objectCountsUpTo[ClusterObjectType::kGlobalIlluminationProbe].x() =
-			unis.m_objectCountsUpTo[ClusterObjectType::kGlobalIlluminationProbe - 1].x() + rqueue.m_giProbes.getSize();
+		unis.m_objectCountsUpTo[ClusteredObjectType::kReflectionProbe].x() =
+			unis.m_objectCountsUpTo[ClusteredObjectType::kReflectionProbe - 1].x()
+			+ rqueue.m_reflectionProbes.getSize();
+		unis.m_objectCountsUpTo[ClusteredObjectType::kGlobalIlluminationProbe].x() =
+			unis.m_objectCountsUpTo[ClusteredObjectType::kGlobalIlluminationProbe - 1].x()
+			+ rqueue.m_giProbes.getSize();
 
 		unis.m_reflectionProbesMipCount = F32(m_r->getProbeReflections().getReflectionTextureMipmapCount());
 
@@ -457,7 +248,9 @@ void ClusterBinning::writeClustererBuffersTask()
 	}
 
 	// Zero the memory because atomics will happen
-	memset(cs.m_clustersAddress, 0, sizeof(Cluster) * m_clusterCount);
+	U8* clustersAddress =
+		m_r->getExternalSubsystems().m_rebarStagingPool->getBufferMappedAddress() + m_runCtx.m_clustersToken.m_offset;
+	memset(clustersAddress, 0, sizeof(Cluster) * m_clusterCount);
 }
 
 } // end namespace anki

+ 18 - 0
AnKi/Renderer/ClusterBinning.h

@@ -29,6 +29,21 @@ public:
 	/// called after the render queue is finalized.
 	void writeClusterBuffersAsync();
 
+	const RebarGpuMemoryToken& getClusteredUniformsRebarToken() const
+	{
+		return m_runCtx.m_clusteredShadingUniformsToken;
+	}
+
+	const RebarGpuMemoryToken& getClustersRebarToken() const
+	{
+		return m_runCtx.m_clustersToken;
+	}
+
+	const BufferHandle& getClustersRenderGraphHandle() const
+	{
+		return m_runCtx.m_rebarHandle;
+	}
+
 private:
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramPtr m_grProg;
@@ -40,6 +55,9 @@ private:
 	{
 	public:
 		RenderingContext* m_ctx = nullptr;
+		RebarGpuMemoryToken m_clusteredShadingUniformsToken;
+		RebarGpuMemoryToken m_clustersToken;
+		BufferHandle m_rebarHandle; ///< For dependency tracking.
 	} m_runCtx;
 
 	void writeClustererBuffers(RenderingContext& ctx);

+ 0 - 26
AnKi/Renderer/Common.h

@@ -88,30 +88,6 @@ public:
 	PhysicsWorld* m_physicsWorld = nullptr; ///< For debug drawing.
 };
 
-/// GPU buffers and textures that the clusterer refers to.
-class ClusteredShadingContext
-{
-public:
-	RebarGpuMemoryToken m_pointLightsToken;
-	void* m_pointLightsAddress = nullptr;
-	RebarGpuMemoryToken m_spotLightsToken;
-	void* m_spotLightsAddress = nullptr;
-	RebarGpuMemoryToken m_reflectionProbesToken;
-	void* m_reflectionProbesAddress = nullptr;
-	RebarGpuMemoryToken m_decalsToken;
-	void* m_decalsAddress = nullptr;
-	RebarGpuMemoryToken m_fogDensityVolumesToken;
-	void* m_fogDensityVolumesAddress = nullptr;
-	RebarGpuMemoryToken m_globalIlluminationProbesToken;
-	void* m_globalIlluminationProbesAddress = nullptr;
-	RebarGpuMemoryToken m_clusteredShadingUniformsToken;
-	void* m_clusteredShadingUniformsAddress = nullptr;
-	RebarGpuMemoryToken m_clustersToken;
-	void* m_clustersAddress = nullptr;
-
-	BufferHandle m_clustersBufferHandle; ///< To track dependencies. Don't track all tokens, not worth it.
-};
-
 /// Rendering context.
 class RenderingContext
 {
@@ -127,8 +103,6 @@ public:
 	/// The render target that the Renderer will populate.
 	RenderTargetHandle m_outRenderTarget;
 
-	ClusteredShadingContext m_clusteredShading;
-
 	RenderingContext(StackMemoryPool* pool)
 		: m_tempPool(pool)
 		, m_renderGraphDescr(pool)

+ 10 - 5
AnKi/Renderer/ForwardShading.cpp

@@ -11,6 +11,9 @@
 #include <AnKi/Renderer/ShadowMapping.h>
 #include <AnKi/Renderer/DepthDownscale.h>
 #include <AnKi/Renderer/LensFlare.h>
+#include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
+#include <AnKi/Renderer/LensFlare.h>
 #include <AnKi/Renderer/VolumetricLightingAccumulation.h>
 #include <AnKi/Shaders/Include/MaterialTypes.h>
 
@@ -34,7 +37,6 @@ void ForwardShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgr
 		cmdb->setDepthWrite(false);
 		cmdb->setBlendFactors(0, BlendFactor::kSrcAlpha, BlendFactor::kOneMinusSrcAlpha);
 
-		const ClusteredShadingContext& rsrc = ctx.m_clusteredShading;
 		const U32 set = U32(MaterialSet::kGlobal);
 		cmdb->bindSampler(set, U32(MaterialBinding::kLinearClampSampler), m_r->getSamplers().m_trilinearClamp);
 		cmdb->bindSampler(set, U32(MaterialBinding::kShadowSampler), m_r->getSamplers().m_trilinearClampShadow);
@@ -44,12 +46,15 @@ void ForwardShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgr
 		rgraphCtx.bindColorTexture(set, U32(MaterialBinding::kLightVolume),
 								   m_r->getVolumetricLightingAccumulation().getRt());
 
-		bindUniforms(cmdb, set, U32(MaterialBinding::kClusterShadingUniforms), rsrc.m_clusteredShadingUniformsToken);
-		bindUniforms(cmdb, set, U32(MaterialBinding::kClusterShadingLights), rsrc.m_pointLightsToken);
-		bindUniforms(cmdb, set, U32(MaterialBinding::kClusterShadingLights) + 1, rsrc.m_spotLightsToken);
+		bindUniforms(cmdb, set, U32(MaterialBinding::kClusterShadingUniforms),
+					 m_r->getClusterBinning().getClusteredUniformsRebarToken());
+		m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(
+			cmdb, set, U32(MaterialBinding::kClusterShadingLights), ClusteredObjectType::kPointLight);
+		m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(
+			cmdb, set, U32(MaterialBinding::kClusterShadingLights) + 1, ClusteredObjectType::kSpotLight);
 		rgraphCtx.bindColorTexture(set, U32(MaterialBinding::kClusterShadingLights) + 2,
 								   m_r->getShadowMapping().getShadowmapRt());
-		bindStorage(cmdb, set, U32(MaterialBinding::kClusters), rsrc.m_clustersToken);
+		bindStorage(cmdb, set, U32(MaterialBinding::kClusters), m_r->getClusterBinning().getClustersRebarToken());
 
 		RenderableDrawerArguments args;
 		args.m_viewMatrix = ctx.m_matrices.m_view;

+ 12 - 8
AnKi/Renderer/GBufferPost.cpp

@@ -7,6 +7,8 @@
 #include <AnKi/Renderer/Renderer.h>
 #include <AnKi/Renderer/GBuffer.h>
 #include <AnKi/Renderer/RenderQueue.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
+#include <AnKi/Renderer/ClusterBinning.h>
 
 namespace anki {
 
@@ -63,8 +65,8 @@ void GBufferPost::populateRenderGraph(RenderingContext& ctx)
 	// Create pass
 	GraphicsRenderPassDescription& rpass = rgraph.newGraphicsRenderPass("GBuffPost");
 
-	rpass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
-		run(ctx, rgraphCtx);
+	rpass.setWork([this](RenderPassWorkContext& rgraphCtx) {
+		run(rgraphCtx);
 	});
 
 	rpass.setFramebufferInfo(m_fbDescr, {m_r->getGBuffer().getColorRt(0), m_r->getGBuffer().getColorRt(1)});
@@ -73,12 +75,12 @@ void GBufferPost::populateRenderGraph(RenderingContext& ctx)
 	rpass.newTextureDependency(m_r->getGBuffer().getColorRt(1), TextureUsageBit::kAllFramebuffer);
 	rpass.newTextureDependency(m_r->getGBuffer().getDepthRt(), TextureUsageBit::kSampledFragment,
 							   TextureSubresourceInfo(DepthStencilAspectBit::kDepth));
-	rpass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageFragmentRead);
+	rpass.newBufferDependency(m_r->getClusterBinning().getClustersRenderGraphHandle(),
+							  BufferUsageBit::kStorageFragmentRead);
 }
 
-void GBufferPost::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
+void GBufferPost::run(RenderPassWorkContext& rgraphCtx)
 {
-	const ClusteredShadingContext& rsrc = ctx.m_clusteredShading;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 	cmdb->setViewport(0, 0, m_r->getInternalResolution().x(), m_r->getInternalResolution().y());
@@ -94,9 +96,11 @@ void GBufferPost::run(const RenderingContext& ctx, RenderPassWorkContext& rgraph
 
 	cmdb->bindSampler(0, 2, m_r->getSamplers().m_trilinearRepeat);
 
-	bindUniforms(cmdb, 0, 3, rsrc.m_clusteredShadingUniformsToken);
-	bindUniforms(cmdb, 0, 4, rsrc.m_decalsToken);
-	bindStorage(cmdb, 0, 5, rsrc.m_clustersToken);
+	bindUniforms(cmdb, 0, 3, m_r->getClusterBinning().getClusteredUniformsRebarToken());
+
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 4, ClusteredObjectType::kDecal);
+
+	bindStorage(cmdb, 0, 5, m_r->getClusterBinning().getClustersRebarToken());
 
 	cmdb->bindAllBindless(1);
 

+ 1 - 1
AnKi/Renderer/GBufferPost.h

@@ -36,7 +36,7 @@ private:
 
 	Error initInternal();
 
-	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+	void run(RenderPassWorkContext& rgraphCtx);
 };
 /// @}
 

+ 6 - 4
AnKi/Renderer/IndirectDiffuse.cpp

@@ -10,6 +10,8 @@
 #include <AnKi/Renderer/DownscaleBlur.h>
 #include <AnKi/Renderer/MotionVectors.h>
 #include <AnKi/Renderer/IndirectDiffuseProbes.h>
+#include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
 #include <AnKi/Core/ConfigSet.h>
 
 namespace anki {
@@ -268,10 +270,10 @@ void IndirectDiffuse::populateRenderGraph(RenderingContext& ctx)
 			CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 			cmdb->bindShaderProgram(m_main.m_grProg);
 
-			const ClusteredShadingContext& binning = ctx.m_clusteredShading;
-			bindUniforms(cmdb, 0, 0, binning.m_clusteredShadingUniformsToken);
-			bindUniforms(cmdb, 0, 1, binning.m_globalIlluminationProbesToken);
-			bindStorage(cmdb, 0, 2, binning.m_clustersToken);
+			bindUniforms(cmdb, 0, 0, m_r->getClusterBinning().getClusteredUniformsRebarToken());
+			m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(
+				cmdb, 0, 1, ClusteredObjectType::kGlobalIlluminationProbe);
+			bindStorage(cmdb, 0, 2, m_r->getClusterBinning().getClustersRebarToken());
 
 			cmdb->bindSampler(0, 3, m_r->getSamplers().m_trilinearClamp);
 			rgraphCtx.bindColorTexture(0, 4, m_r->getGBuffer().getColorRt(2));

+ 5 - 4
AnKi/Renderer/IndirectSpecular.cpp

@@ -12,6 +12,8 @@
 #include <AnKi/Renderer/ProbeReflections.h>
 #include <AnKi/Renderer/MotionVectors.h>
 #include <AnKi/Renderer/VrsSriGeneration.h>
+#include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
 #include <AnKi/Core/ConfigSet.h>
 
 namespace anki {
@@ -210,10 +212,9 @@ void IndirectSpecular::run(const RenderingContext& ctx, RenderPassWorkContext& r
 	cmdb->bindSampler(0, 9, m_r->getSamplers().m_trilinearRepeat);
 	cmdb->bindTexture(0, 10, m_noiseImage->getTextureView());
 
-	const ClusteredShadingContext& binning = ctx.m_clusteredShading;
-	bindUniforms(cmdb, 0, 11, binning.m_clusteredShadingUniformsToken);
-	bindUniforms(cmdb, 0, 12, binning.m_reflectionProbesToken);
-	bindStorage(cmdb, 0, 13, binning.m_clustersToken);
+	bindUniforms(cmdb, 0, 11, m_r->getClusterBinning().getClusteredUniformsRebarToken());
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 12, ClusteredObjectType::kReflectionProbe);
+	bindStorage(cmdb, 0, 13, m_r->getClusterBinning().getClustersRebarToken());
 
 	cmdb->bindAllBindless(1);
 

+ 9 - 8
AnKi/Renderer/LightShading.cpp

@@ -17,6 +17,8 @@
 #include <AnKi/Renderer/RtShadows.h>
 #include <AnKi/Renderer/IndirectDiffuse.h>
 #include <AnKi/Renderer/VrsSriGeneration.h>
+#include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
 #include <AnKi/Core/ConfigSet.h>
 
 namespace anki {
@@ -175,14 +177,13 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 		cmdb->setDepthWrite(false);
 
 		// Bind all
-		const ClusteredShadingContext& binning = ctx.m_clusteredShading;
-		bindUniforms(cmdb, 0, 0, binning.m_clusteredShadingUniformsToken);
+		bindUniforms(cmdb, 0, 0, m_r->getClusterBinning().getClusteredUniformsRebarToken());
 
-		bindUniforms(cmdb, 0, 1, binning.m_pointLightsToken);
-		bindUniforms(cmdb, 0, 2, binning.m_spotLightsToken);
+		m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 1, ClusteredObjectType::kPointLight);
+		m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 2, ClusteredObjectType::kSpotLight);
 		rgraphCtx.bindColorTexture(0, 3, m_r->getShadowMapping().getShadowmapRt());
 
-		bindStorage(cmdb, 0, 4, binning.m_clustersToken);
+		bindStorage(cmdb, 0, 4, m_r->getClusterBinning().getClustersRebarToken());
 
 		cmdb->bindSampler(0, 5, m_r->getSamplers().m_nearestNearestClamp);
 		cmdb->bindSampler(0, 6, m_r->getSamplers().m_trilinearClamp);
@@ -223,8 +224,7 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 		rgraphCtx.bindColorTexture(0, 8, m_r->getGBuffer().getColorRt(2));
 		cmdb->bindTexture(0, 9, m_r->getProbeReflections().getIntegrationLut());
 
-		const ClusteredShadingContext& binning = ctx.m_clusteredShading;
-		bindUniforms(cmdb, 0, 10, binning.m_clusteredShadingUniformsToken);
+		bindUniforms(cmdb, 0, 10, m_r->getClusterBinning().getClusteredUniformsRebarToken());
 
 		const Vec4 pc(ctx.m_renderQueue->m_cameraNear, ctx.m_renderQueue->m_cameraFar, 0.0f, 0.0f);
 		cmdb->setPushConstants(&pc, sizeof(pc));
@@ -399,7 +399,8 @@ void LightShading::populateRenderGraph(RenderingContext& ctx)
 	{
 		pass.newTextureDependency(m_r->getShadowmapsResolve().getRt(), readUsage);
 	}
-	pass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageFragmentRead);
+	pass.newBufferDependency(m_r->getClusterBinning().getClustersRenderGraphHandle(),
+							 BufferUsageBit::kStorageFragmentRead);
 
 	// Apply indirect
 	pass.newTextureDependency(m_r->getIndirectDiffuse().getRt(), readUsage);

+ 173 - 0
AnKi/Renderer/PackVisibleClusteredObjects.cpp

@@ -0,0 +1,173 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
+#include <AnKi/Renderer/Renderer.h>
+#include <AnKi/Renderer/RenderQueue.h>
+#include <AnKi/Resource/ResourceManager.h>
+#include <AnKi/Shaders/Include/GpuSceneTypes.h>
+#include <AnKi/Shaders/Include/MiscRendererTypes.h>
+
+namespace anki {
+
+PackVisibleClusteredObjects::PackVisibleClusteredObjects(Renderer* r)
+	: RendererObject(r)
+{
+}
+
+PackVisibleClusteredObjects::~PackVisibleClusteredObjects()
+{
+}
+
+Error PackVisibleClusteredObjects::init()
+{
+	ANKI_CHECK(m_r->getExternalSubsystems().m_resourceManager->loadResource(
+		"ShaderBinaries/PackVisibleClusteredObjects.ankiprogbin", m_packProg));
+
+	U32 maxWaveSize = m_r->getExternalSubsystems().m_grManager->getDeviceCapabilities().m_maxSubgroupSize;
+	if(maxWaveSize == 16 || maxWaveSize == 32)
+	{
+		m_threadGroupSize = maxWaveSize;
+	}
+	else
+	{
+		m_threadGroupSize = 64;
+	}
+
+	for(ClusteredObjectType type = ClusteredObjectType::kFirst; type < ClusteredObjectType::kCount; ++type)
+	{
+		const ShaderProgramResourceVariant* variant;
+		ShaderProgramResourceVariantInitInfo variantInit(m_packProg);
+		variantInit.addMutation("OBJECT_TYPE", U32(type));
+		variantInit.addMutation("THREAD_GROUP_SIZE", m_threadGroupSize);
+		m_packProg->getOrCreateVariant(variantInit, variant);
+		m_grProgs[type] = variant->getProgram();
+	}
+
+	U32 offset = 0;
+	for(ClusteredObjectType type = ClusteredObjectType::kFirst; type < ClusteredObjectType::kCount; ++type)
+	{
+		alignRoundUp(getExternalSubsystems().m_grManager->getDeviceCapabilities().m_storageBufferBindOffsetAlignment,
+					 offset);
+		m_structureBufferOffsets[type] = offset;
+		offset += kMaxVisibleClusteredObjects[type] * kClusteredObjectSizes[type];
+	}
+
+	BufferInitInfo buffInit("ClusterObjects");
+	buffInit.m_size = offset;
+	buffInit.m_usage = BufferUsageBit::kAllCompute | BufferUsageBit::kAllGraphics;
+	m_allClustererObjects = m_r->getExternalSubsystems().m_grManager->newBuffer(buffInit);
+
+	return Error::kNone;
+}
+
+template<typename TClustererType, ClusteredObjectType kType, typename TRenderQueueElement>
+void PackVisibleClusteredObjects::dispatchType(WeakArray<TRenderQueueElement> array, const RenderQueue& rqueue,
+											   CommandBufferPtr& cmdb)
+{
+	if(array.getSize() == 0)
+	{
+		return;
+	}
+
+	RebarGpuMemoryToken token;
+	U32* indices = allocateStorage<U32*>(array.getSize() * sizeof(U32), token);
+
+	RebarGpuMemoryToken extrasToken;
+	PointLightExtra* plightExtras = nullptr;
+	SpotLightExtra* slightExtras = nullptr;
+	if constexpr(std::is_same_v<TClustererType, PointLight>)
+	{
+		plightExtras = allocateStorage<PointLightExtra*>(array.getSize() * sizeof(PointLightExtra), extrasToken);
+	}
+	else if constexpr(std::is_same_v<TClustererType, SpotLight>)
+	{
+		slightExtras = allocateStorage<SpotLightExtra*>(array.getSize() * sizeof(SpotLightExtra), extrasToken);
+	}
+
+	// Write ReBAR
+	for(const auto& el : array)
+	{
+		*indices = el.m_index;
+		++indices;
+
+		if constexpr(std::is_same_v<TClustererType, PointLight>)
+		{
+			if(el.m_shadowRenderQueues[0] == nullptr)
+			{
+				plightExtras->m_shadowAtlasTileScale = -1.0f;
+				plightExtras->m_shadowLayer = kMaxU32;
+			}
+			else
+			{
+				plightExtras->m_shadowLayer = el.m_shadowLayer;
+				plightExtras->m_shadowAtlasTileScale = el.m_shadowAtlasTileSize;
+				for(U32 f = 0; f < 6; ++f)
+				{
+					plightExtras->m_shadowAtlasTileOffsets[f] = Vec4(el.m_shadowAtlasTileOffsets[f], 0.0f, 0.0f);
+				}
+			}
+
+			++plightExtras;
+		}
+		else if constexpr(std::is_same_v<TClustererType, SpotLight>)
+		{
+			slightExtras->m_shadowLayer = el.m_shadowLayer;
+			slightExtras->m_textureMatrix = el.m_textureMatrix;
+
+			++slightExtras;
+		}
+	}
+
+	cmdb->bindStorageBuffer(0, 0, getExternalSubsystems().m_gpuScenePool->getBuffer(),
+							rqueue.m_clustererObjectsArrayOffsets[kType], rqueue.m_clustererObjectsArrayRanges[kType]);
+
+	cmdb->bindStorageBuffer(0, 1, m_allClustererObjects, m_structureBufferOffsets[kType],
+							array.getSize() * sizeof(TClustererType));
+
+	cmdb->bindStorageBuffer(0, 2, getExternalSubsystems().m_rebarStagingPool->getBuffer(), token.m_offset,
+							token.m_range);
+
+	if constexpr(std::is_same_v<TClustererType, PointLight> || std::is_same_v<TClustererType, SpotLight>)
+	{
+		cmdb->bindStorageBuffer(0, 3, getExternalSubsystems().m_rebarStagingPool->getBuffer(), extrasToken.m_offset,
+								extrasToken.m_range);
+	}
+
+	cmdb->bindShaderProgram(m_grProgs[kType]);
+
+	const UVec4 pc(array.getSize());
+	cmdb->setPushConstants(&pc, sizeof(pc));
+
+	dispatchPPCompute(cmdb, m_threadGroupSize, 1, 1, array.getSize(), 1, 1);
+}
+
+void PackVisibleClusteredObjects::populateRenderGraph(RenderingContext& ctx)
+{
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("PackClusterObjects");
+
+	pass.newBufferDependency(m_r->getGpuSceneBufferHandle(), BufferUsageBit::kStorageComputeRead);
+
+	m_allClustererObjectsHandle = rgraph.importBuffer(m_allClustererObjects, BufferUsageBit::kNone);
+	pass.newBufferDependency(m_allClustererObjectsHandle, BufferUsageBit::kStorageComputeWrite);
+
+	pass.setWork([&ctx, this](RenderPassWorkContext& rgraphCtx) {
+		const RenderQueue& rqueue = *ctx.m_renderQueue;
+		CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+		dispatchType<PointLight, ClusteredObjectType::kPointLight>(rqueue.m_pointLights, rqueue, cmdb);
+		dispatchType<SpotLight, ClusteredObjectType::kSpotLight>(rqueue.m_spotLights, rqueue, cmdb);
+		dispatchType<Decal, ClusteredObjectType::kDecal>(rqueue.m_decals, rqueue, cmdb);
+		dispatchType<FogDensityVolume, ClusteredObjectType::kFogDensityVolume>(rqueue.m_fogDensityVolumes, rqueue,
+																			   cmdb);
+		dispatchType<ReflectionProbe, ClusteredObjectType::kReflectionProbe>(rqueue.m_reflectionProbes, rqueue, cmdb);
+		dispatchType<GlobalIlluminationProbe, ClusteredObjectType::kGlobalIlluminationProbe>(rqueue.m_giProbes, rqueue,
+																							 cmdb);
+	});
+}
+
+} // end namespace anki

+ 55 - 0
AnKi/Renderer/PackVisibleClusteredObjects.h

@@ -0,0 +1,55 @@
+// Copyright (C) 2009-2023, 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/Shaders/Include/ClusteredShadingTypes.h>
+
+namespace anki {
+
+/// @addtogroup renderer
+/// @{
+
+/// Pack the visible clusterer objects.
+class PackVisibleClusteredObjects : public RendererObject
+{
+public:
+	PackVisibleClusteredObjects(Renderer* r);
+
+	~PackVisibleClusteredObjects();
+
+	Error init();
+
+	void populateRenderGraph(RenderingContext& ctx);
+
+	BufferHandle getClusteredObjectsRenderGraphHandle() const
+	{
+		return m_allClustererObjectsHandle;
+	}
+
+	void bindClusteredObjectBuffer(CommandBufferPtr& cmdb, U32 set, U32 binding, ClusteredObjectType type) const
+	{
+		cmdb->bindStorageBuffer(set, binding, m_allClustererObjects, m_structureBufferOffsets[type],
+								kClusteredObjectSizes[type] * kMaxVisibleClusteredObjects[type]);
+	}
+
+private:
+	BufferPtr m_allClustererObjects;
+	BufferHandle m_allClustererObjectsHandle;
+
+	ShaderProgramResourcePtr m_packProg;
+	Array<ShaderProgramPtr, U32(ClusteredObjectType::kCount)> m_grProgs;
+
+	Array<U32, U32(ClusteredObjectType::kCount)> m_structureBufferOffsets = {};
+
+	U32 m_threadGroupSize = 0;
+
+	template<typename TClustererType, ClusteredObjectType kType, typename TRenderQueueElement>
+	void dispatchType(WeakArray<TRenderQueueElement> array, const RenderQueue& rqueue, CommandBufferPtr& cmdb);
+};
+/// @}
+
+} // end namespace anki

+ 1 - 0
AnKi/Renderer/ProbeReflections.cpp

@@ -4,6 +4,7 @@
 // http://www.anki3d.org/LICENSE
 
 #include <AnKi/Renderer/ProbeReflections.h>
+#include <AnKi/Renderer/Renderer.h>
 #include <AnKi/Renderer/LightShading.h>
 #include <AnKi/Renderer/FinalComposite.h>
 #include <AnKi/Renderer/GBuffer.h>

+ 0 - 1
AnKi/Renderer/ProbeReflections.h

@@ -5,7 +5,6 @@
 
 #pragma once
 
-#include <AnKi/Renderer/Renderer.h>
 #include <AnKi/Renderer/RendererObject.h>
 #include <AnKi/Renderer/TraditionalDeferredShading.h>
 #include <AnKi/Resource/ImageResource.h>

+ 14 - 0
AnKi/Renderer/RenderQueue.h

@@ -120,6 +120,8 @@ public:
 	F32 m_shadowAtlasTileSize; ///< Renderer internal.
 	U8 m_shadowLayer; ///< Renderer internal.
 
+	U32 m_index;
+
 	PointLightQueueElement()
 	{
 	}
@@ -147,6 +149,8 @@ public:
 
 	U8 m_shadowLayer; ///< Renderer internal.
 
+	U32 m_index;
+
 	SpotLightQueueElement()
 	{
 	}
@@ -196,6 +200,8 @@ public:
 	Vec3 m_aabbMax;
 	U32 m_textureBindlessIndex;
 
+	U32 m_index;
+
 	ReflectionProbeQueueElement()
 	{
 	}
@@ -223,6 +229,8 @@ public:
 	F32 m_fadeDistance;
 	U32 m_volumeTextureBindlessIndex;
 
+	U32 m_index;
+
 	GlobalIlluminationProbeQueueElement()
 	{
 	}
@@ -284,6 +292,8 @@ public:
 	Vec3 m_obbExtend;
 	Mat3 m_obbRotation;
 
+	U32 m_index;
+
 	DecalQueueElement()
 	{
 	}
@@ -323,6 +333,7 @@ public:
 	};
 
 	F32 m_density;
+	U32 m_index;
 	Bool m_isBox;
 
 	FogDensityQueueElement()
@@ -411,6 +422,9 @@ public:
 	ReflectionProbeQueueElementForRefresh* m_reflectionProbeForRefresh = nullptr;
 	GlobalIlluminationProbeQueueElementForRefresh* m_giProbeForRefresh = nullptr;
 
+	Array<PtrSize, U32(ClusteredObjectType::kCount)> m_clustererObjectsArrayOffsets = {};
+	Array<PtrSize, U32(ClusteredObjectType::kCount)> m_clustererObjectsArrayRanges = {};
+
 	RenderQueue()
 	{
 		zeroMemory(m_directionalLight);

+ 5 - 0
AnKi/Renderer/Renderer.cpp

@@ -42,6 +42,7 @@
 #include <AnKi/Renderer/Scale.h>
 #include <AnKi/Renderer/IndirectDiffuse.h>
 #include <AnKi/Renderer/VrsSriGeneration.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
 
 namespace anki {
 
@@ -253,6 +254,9 @@ Error Renderer::initInternal(UVec2 swapchainResolution)
 	m_clusterBinning.reset(newInstance<ClusterBinning>(*m_pool, this));
 	ANKI_CHECK(m_clusterBinning->init());
 
+	m_packVisibleClustererObjects.reset(newInstance<PackVisibleClusteredObjects>(*m_pool, this));
+	ANKI_CHECK(m_packVisibleClustererObjects->init());
+
 	// Init samplers
 	{
 		SamplerInitInfo sinit("NearestNearestClamp");
@@ -349,6 +353,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 
 	// Populate render graph. WARNING Watch the order
 	gpuSceneCopy(ctx);
+	m_packVisibleClustererObjects->populateRenderGraph(ctx);
 	m_genericCompute->populateRenderGraph(ctx);
 	m_clusterBinning->populateRenderGraph(ctx);
 	if(m_accelerationStructureBuilder)

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

@@ -31,3 +31,4 @@ ANKI_RENDERER_OBJECT_DEF(ClusterBinning, clusterBinning)
 ANKI_RENDERER_OBJECT_DEF(Scale, scale)
 ANKI_RENDERER_OBJECT_DEF(IndirectDiffuse, indirectDiffuse)
 ANKI_RENDERER_OBJECT_DEF(VrsSriGeneration, vrsSriGeneration)
+ANKI_RENDERER_OBJECT_DEF(PackVisibleClusteredObjects, packVisibleClustererObjects)

+ 11 - 9
AnKi/Renderer/RtShadows.cpp

@@ -11,6 +11,8 @@
 #include <AnKi/Renderer/MotionVectors.h>
 #include <AnKi/Renderer/DepthDownscale.h>
 #include <AnKi/Renderer/RenderQueue.h>
+#include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
 #include <AnKi/Util/Tracer.h>
 #include <AnKi/Core/ConfigSet.h>
 #include <AnKi/Shaders/Include/MaterialTypes.h>
@@ -249,8 +251,8 @@ void RtShadows::populateRenderGraph(RenderingContext& ctx)
 	// RT shadows pass
 	{
 		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("RtShadows");
-		rpass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
-			run(ctx, rgraphCtx);
+		rpass.setWork([this](RenderPassWorkContext& rgraphCtx) {
+			run(rgraphCtx);
 		});
 
 		rpass.newTextureDependency(m_runCtx.m_historyRt, TextureUsageBit::kSampledTraceRays);
@@ -266,7 +268,8 @@ void RtShadows::populateRenderGraph(RenderingContext& ctx)
 		rpass.newTextureDependency(m_runCtx.m_prevMomentsRt, TextureUsageBit::kSampledTraceRays);
 		rpass.newTextureDependency(m_runCtx.m_currentMomentsRt, TextureUsageBit::kImageTraceRaysWrite);
 
-		rpass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageTraceRaysRead);
+		rpass.newBufferDependency(m_r->getClusterBinning().getClustersRenderGraphHandle(),
+								  BufferUsageBit::kStorageTraceRaysRead);
 	}
 
 	// Denoise pass horizontal
@@ -436,10 +439,9 @@ void RtShadows::populateRenderGraph(RenderingContext& ctx)
 	}
 }
 
-void RtShadows::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
+void RtShadows::run(RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	const ClusteredShadingContext& rsrc = ctx.m_clusteredShading;
 
 	cmdb->bindShaderProgram(m_rtLibraryGrProg);
 
@@ -472,13 +474,13 @@ void RtShadows::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCt
 
 	constexpr U32 kSet = 2;
 
-	bindUniforms(cmdb, kSet, 0, rsrc.m_clusteredShadingUniformsToken);
+	bindUniforms(cmdb, kSet, 0, m_r->getClusterBinning().getClusteredUniformsRebarToken());
 
-	bindUniforms(cmdb, kSet, 1, rsrc.m_pointLightsToken);
-	bindUniforms(cmdb, kSet, 2, rsrc.m_spotLightsToken);
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, kSet, 1, ClusteredObjectType::kPointLight);
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, kSet, 2, ClusteredObjectType::kSpotLight);
 	rgraphCtx.bindColorTexture(kSet, 3, m_r->getShadowMapping().getShadowmapRt());
 
-	bindStorage(cmdb, kSet, 4, rsrc.m_clustersToken);
+	bindStorage(cmdb, kSet, 4, m_r->getClusterBinning().getClustersRebarToken());
 
 	cmdb->bindSampler(kSet, 5, m_r->getSamplers().m_trilinearRepeat);
 

+ 1 - 1
AnKi/Renderer/RtShadows.h

@@ -120,7 +120,7 @@ public:
 
 	Error initInternal();
 
-	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+	void run(RenderPassWorkContext& rgraphCtx);
 	void runDenoise(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
 	void runSvgfVariance(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
 	void runSvgfAtrous(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);

+ 15 - 12
AnKi/Renderer/ShadowmapsResolve.cpp

@@ -8,6 +8,8 @@
 #include <AnKi/Renderer/GBuffer.h>
 #include <AnKi/Renderer/ShadowMapping.h>
 #include <AnKi/Renderer/DepthDownscale.h>
+#include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
 #include <AnKi/Core/ConfigSet.h>
 
 namespace anki {
@@ -72,8 +74,8 @@ void ShadowmapsResolve::populateRenderGraph(RenderingContext& ctx)
 	{
 		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("ResolveShadows");
 
-		rpass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
-			run(ctx, rgraphCtx);
+		rpass.setWork([this](RenderPassWorkContext& rgraphCtx) {
+			run(rgraphCtx);
 		});
 
 		rpass.newTextureDependency(m_runCtx.m_rt, TextureUsageBit::kImageComputeWrite);
@@ -82,15 +84,16 @@ void ShadowmapsResolve::populateRenderGraph(RenderingContext& ctx)
 								   TextureUsageBit::kSampledCompute, TextureSurfaceInfo(0, 0, 0, 0));
 		rpass.newTextureDependency(m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::kSampledCompute);
 
-		rpass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageComputeRead);
+		rpass.newBufferDependency(m_r->getClusterBinning().getClustersRenderGraphHandle(),
+								  BufferUsageBit::kStorageComputeRead);
 	}
 	else
 	{
 		GraphicsRenderPassDescription& rpass = rgraph.newGraphicsRenderPass("ResolveShadows");
 		rpass.setFramebufferInfo(m_fbDescr, {m_runCtx.m_rt});
 
-		rpass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
-			run(ctx, rgraphCtx);
+		rpass.setWork([this](RenderPassWorkContext& rgraphCtx) {
+			run(rgraphCtx);
 		});
 
 		rpass.newTextureDependency(m_runCtx.m_rt, TextureUsageBit::kFramebufferWrite);
@@ -99,22 +102,22 @@ void ShadowmapsResolve::populateRenderGraph(RenderingContext& ctx)
 								   TextureUsageBit::kSampledFragment, TextureSurfaceInfo(0, 0, 0, 0));
 		rpass.newTextureDependency(m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::kSampledFragment);
 
-		rpass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageFragmentRead);
+		rpass.newBufferDependency(m_r->getClusterBinning().getClustersRenderGraphHandle(),
+								  BufferUsageBit::kStorageFragmentRead);
 	}
 }
 
-void ShadowmapsResolve::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
+void ShadowmapsResolve::run(RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	const ClusteredShadingContext& rsrc = ctx.m_clusteredShading;
 
 	cmdb->bindShaderProgram(m_grProg);
 
-	bindUniforms(cmdb, 0, 0, rsrc.m_clusteredShadingUniformsToken);
-	bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
-	bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
+	bindUniforms(cmdb, 0, 0, m_r->getClusterBinning().getClusteredUniformsRebarToken());
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 1, ClusteredObjectType::kPointLight);
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 2, ClusteredObjectType::kSpotLight);
 	rgraphCtx.bindColorTexture(0, 3, m_r->getShadowMapping().getShadowmapRt());
-	bindStorage(cmdb, 0, 4, rsrc.m_clustersToken);
+	bindStorage(cmdb, 0, 4, m_r->getClusterBinning().getClustersRebarToken());
 
 	cmdb->bindSampler(0, 5, m_r->getSamplers().m_trilinearClamp);
 	cmdb->bindSampler(0, 6, m_r->getSamplers().m_trilinearClampShadow);

+ 1 - 1
AnKi/Renderer/ShadowmapsResolve.h

@@ -59,7 +59,7 @@ public:
 
 	Error initInternal();
 
-	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+	void run(RenderPassWorkContext& rgraphCtx);
 };
 /// @}
 

+ 12 - 8
AnKi/Renderer/VolumetricLightingAccumulation.cpp

@@ -7,6 +7,8 @@
 #include <AnKi/Renderer/ShadowMapping.h>
 #include <AnKi/Renderer/IndirectDiffuseProbes.h>
 #include <AnKi/Renderer/Renderer.h>
+#include <AnKi/Renderer/ClusterBinning.h>
+#include <AnKi/Renderer/PackVisibleClusteredObjects.h>
 #include <AnKi/Resource/ImageResource.h>
 #include <AnKi/Core/ConfigSet.h>
 
@@ -90,7 +92,8 @@ void VolumetricLightingAccumulation::populateRenderGraph(RenderingContext& ctx)
 	pass.newTextureDependency(m_runCtx.m_rts[1], TextureUsageBit::kImageComputeWrite);
 	pass.newTextureDependency(m_r->getShadowMapping().getShadowmapRt(), TextureUsageBit::kSampledCompute);
 
-	pass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageComputeRead);
+	pass.newBufferDependency(m_r->getClusterBinning().getClustersRenderGraphHandle(),
+							 BufferUsageBit::kStorageComputeRead);
 
 	if(m_r->getIndirectDiffuseProbes().hasCurrentlyRefreshedVolumeRt())
 	{
@@ -102,7 +105,6 @@ void VolumetricLightingAccumulation::populateRenderGraph(RenderingContext& ctx)
 void VolumetricLightingAccumulation::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	const ClusteredShadingContext& rsrc = ctx.m_clusteredShading;
 
 	cmdb->bindShaderProgram(m_grProg);
 
@@ -117,15 +119,17 @@ void VolumetricLightingAccumulation::run(const RenderingContext& ctx, RenderPass
 
 	rgraphCtx.bindColorTexture(0, 5, m_runCtx.m_rts[0]);
 
-	bindUniforms(cmdb, 0, 6, rsrc.m_clusteredShadingUniformsToken);
-	bindUniforms(cmdb, 0, 7, rsrc.m_pointLightsToken);
-	bindUniforms(cmdb, 0, 8, rsrc.m_spotLightsToken);
+	bindUniforms(cmdb, 0, 6, m_r->getClusterBinning().getClusteredUniformsRebarToken());
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 7, ClusteredObjectType::kPointLight);
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 8, ClusteredObjectType::kSpotLight);
 	rgraphCtx.bindColorTexture(0, 9, m_r->getShadowMapping().getShadowmapRt());
 
-	bindUniforms(cmdb, 0, 10, rsrc.m_globalIlluminationProbesToken);
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 10,
+																	ClusteredObjectType::kGlobalIlluminationProbe);
 
-	bindUniforms(cmdb, 0, 11, rsrc.m_fogDensityVolumesToken);
-	bindStorage(cmdb, 0, 12, rsrc.m_clustersToken);
+	m_r->getPackVisibleClusteredObjects().bindClusteredObjectBuffer(cmdb, 0, 11,
+																	ClusteredObjectType::kFogDensityVolume);
+	bindStorage(cmdb, 0, 12, m_r->getClusterBinning().getClustersRebarToken());
 
 	cmdb->bindAllBindless(1);
 

+ 7 - 4
AnKi/Scene/Components/DecalComponent.cpp

@@ -15,8 +15,8 @@ DecalComponent::DecalComponent(SceneNode* node)
 	, m_node(node)
 	, m_spatial(this)
 {
-	m_gpuSceneDecalOffset =
-		U32(node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(GpuSceneContiguousArrayType::kDecals));
+	m_gpuSceneIndex =
+		node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(GpuSceneContiguousArrayType::kDecals);
 }
 
 DecalComponent::~DecalComponent()
@@ -27,7 +27,7 @@ DecalComponent::~DecalComponent()
 void DecalComponent::onDestroy(SceneNode& node)
 {
 	node.getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(GpuSceneContiguousArrayType::kDecals,
-																	   m_gpuSceneDecalOffset);
+																	   m_gpuSceneIndex);
 }
 
 void DecalComponent::setLayer(CString fname, F32 blendFactor, LayerType type)
@@ -94,8 +94,11 @@ Error DecalComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 		gpuDecal.m_obbExtend = m_obb.getExtend().xyz();
 
+		const PtrSize offset = m_gpuSceneIndex * sizeof(GpuSceneDecal)
+							   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kDecals);
 		getExternalSubsystems(*info.m_node)
-			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneDecalOffset, sizeof(gpuDecal), &gpuDecal);
+			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, offset, sizeof(gpuDecal), &gpuDecal);
 	}
 
 	const Bool spatialUpdated = m_spatial.update(info.m_node->getSceneGraph().getOctree());

+ 2 - 1
AnKi/Scene/Components/DecalComponent.h

@@ -67,6 +67,7 @@ public:
 		el.m_obbCenter = m_obb.getCenter().xyz();
 		el.m_obbExtend = m_obb.getExtend().xyz();
 		el.m_obbRotation = m_obb.getRotation().getRotationPart();
+		el.m_index = m_gpuSceneIndex;
 	}
 
 private:
@@ -93,7 +94,7 @@ private:
 	Vec3 m_boxSize = Vec3(1.0f);
 	Obb m_obb = Obb(Vec4(0.0f), Mat3x4::getIdentity(), Vec4(0.5f, 0.5f, 0.5f, 0.0f));
 
-	U32 m_gpuSceneDecalOffset = kMaxU32;
+	U32 m_gpuSceneIndex = kMaxU32;
 
 	Bool m_dirty = true;
 

+ 7 - 4
AnKi/Scene/Components/FogDensityComponent.cpp

@@ -13,8 +13,8 @@ FogDensityComponent::FogDensityComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	, m_spatial(this)
 {
-	m_gpuSceneOffset = U32(node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
-		GpuSceneContiguousArrayType::kFogDensityVolumes));
+	m_gpuSceneIndex = node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
+		GpuSceneContiguousArrayType::kFogDensityVolumes);
 }
 
 void FogDensityComponent::onDestroy(SceneNode& node)
@@ -22,7 +22,7 @@ void FogDensityComponent::onDestroy(SceneNode& node)
 	m_spatial.removeFromOctree(node.getSceneGraph().getOctree());
 
 	node.getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(GpuSceneContiguousArrayType::kFogDensityVolumes,
-																	   m_gpuSceneOffset);
+																	   m_gpuSceneIndex);
 }
 
 Error FogDensityComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
@@ -61,8 +61,11 @@ Error FogDensityComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		gpuVolume.m_isBox = m_isBox;
 		gpuVolume.m_density = m_density;
 
+		const PtrSize offset = m_gpuSceneIndex * sizeof(GpuSceneFogDensityVolume)
+							   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kFogDensityVolumes);
 		getExternalSubsystems(*info.m_node)
-			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneOffset, sizeof(gpuVolume), &gpuVolume);
+			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, offset, sizeof(gpuVolume), &gpuVolume);
 	}
 
 	const Bool spatialUpdated = m_spatial.update(info.m_node->getSceneGraph().getOctree());

+ 3 - 1
AnKi/Scene/Components/FogDensityComponent.h

@@ -90,6 +90,8 @@ public:
 			el.m_sphereCenter = m_worldPos.xyz();
 			el.m_sphereRadius = m_sphereRadius;
 		}
+		ANKI_ASSERT(m_gpuSceneIndex != kMaxU32);
+		el.m_index = m_gpuSceneIndex;
 	}
 
 private:
@@ -106,7 +108,7 @@ private:
 	Vec3 m_worldPos = Vec3(0.0f);
 	F32 m_density = 1.0f;
 
-	U32 m_gpuSceneOffset = kMaxU32;
+	U32 m_gpuSceneIndex = kMaxU32;
 
 	Bool m_isBox = true;
 	Bool m_dirty = true;

+ 8 - 5
AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp

@@ -26,8 +26,8 @@ GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* no
 		m_frustums[i].update();
 	}
 
-	m_gpuSceneOffset = U32(node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
-		GpuSceneContiguousArrayType::kGlobalIlluminationProbes));
+	m_gpuSceneIndex = node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
+		GpuSceneContiguousArrayType::kGlobalIlluminationProbes);
 
 	const Error err = getExternalSubsystems(*node).m_resourceManager->loadResource(
 		"ShaderBinaries/ClearTextureCompute.ankiprogbin", m_clearTextureProg);
@@ -46,7 +46,7 @@ void GlobalIlluminationProbeComponent::onDestroy(SceneNode& node)
 	m_spatial.removeFromOctree(node.getSceneGraph().getOctree());
 
 	node.getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(
-		GpuSceneContiguousArrayType::kGlobalIlluminationProbes, m_gpuSceneOffset);
+		GpuSceneContiguousArrayType::kGlobalIlluminationProbes, m_gpuSceneIndex);
 }
 
 Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
@@ -125,11 +125,14 @@ Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, B
 		gpuProbe.m_aabbMin = aabb.getMin().xyz();
 		gpuProbe.m_aabbMax = aabb.getMax().xyz();
 		gpuProbe.m_volumeTexture = m_volTexBindlessIdx;
-		gpuProbe.m_halfTexelSizeU = 1.0f / F32(m_cellCounts.y()) / 2.0f;
+		gpuProbe.m_halfTexelSizeU = 1.0f / (F32(m_cellCounts.y()) * 6.0f) / 2.0f;
 		gpuProbe.m_fadeDistance = m_fadeDistance;
 
+		const PtrSize offset = m_gpuSceneIndex * sizeof(GpuSceneGlobalIlluminationProbe)
+							   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kGlobalIlluminationProbes);
 		getExternalSubsystems(*info.m_node)
-			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneOffset, sizeof(gpuProbe), &gpuProbe);
+			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, offset, sizeof(gpuProbe), &gpuProbe);
 	}
 
 	if(needsRefresh()) [[unlikely]]

+ 2 - 1
AnKi/Scene/Components/GlobalIlluminationProbeComponent.h

@@ -77,6 +77,7 @@ public:
 		el.m_cellSizes = (m_halfSize * 2.0f) / Vec3(m_cellCounts);
 		el.m_fadeDistance = m_fadeDistance;
 		el.m_volumeTextureBindlessIndex = m_volTexBindlessIdx;
+		el.m_index = m_gpuSceneIndex;
 	}
 
 	void setupGlobalIlluminationProbeQueueElementForRefresh(GlobalIlluminationProbeQueueElementForRefresh& el)
@@ -110,7 +111,7 @@ private:
 	TextureViewPtr m_volView;
 	U32 m_volTexBindlessIdx = 0;
 
-	U32 m_gpuSceneOffset = kMaxU32;
+	U32 m_gpuSceneIndex = kMaxU32;
 
 	Array<Frustum, 6> m_frustums;
 

+ 56 - 24
AnKi/Scene/Components/LightComponent.cpp

@@ -18,6 +18,7 @@ namespace anki {
 
 LightComponent::LightComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
+	, m_node(node)
 	, m_uuid(node->getSceneGraph().getNewUuid())
 	, m_spatial(this)
 	, m_type(LightComponentType::kPoint)
@@ -27,9 +28,6 @@ LightComponent::LightComponent(SceneNode* node)
 
 	setLightComponentType(LightComponentType::kPoint);
 	m_worldTransform = node->getWorldTransform();
-
-	m_gpuSceneLightOffset =
-		U32(node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(GpuSceneContiguousArrayType::kLights));
 }
 
 LightComponent::~LightComponent()
@@ -39,9 +37,8 @@ LightComponent::~LightComponent()
 void LightComponent::setLightComponentType(LightComponentType type)
 {
 	ANKI_ASSERT(type >= LightComponentType::kFirst && type < LightComponentType::kCount);
-	m_type = type;
-	m_markedForUpdate = true;
-	m_forceFullUpdate = true;
+	m_shapeUpdated = true;
+	m_typeChanged = type != m_type;
 
 	if(type == LightComponentType::kDirectional)
 	{
@@ -53,15 +50,38 @@ void LightComponent::setLightComponentType(LightComponentType type)
 		m_spatial.setAlwaysVisible(false);
 		m_spatial.setUpdatesOctreeBounds(true);
 	}
+
+	if(m_typeChanged && m_gpuSceneLightIndex != kMaxU32)
+	{
+		m_node->getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(
+			(m_type == LightComponentType::kPoint) ? GpuSceneContiguousArrayType::kPointLights
+												   : GpuSceneContiguousArrayType::kSpotLights,
+			m_gpuSceneLightIndex);
+		m_gpuSceneLightIndex = kMaxU32;
+	}
+
+	if(m_gpuSceneLightIndex == kMaxU32 && type == LightComponentType::kPoint)
+	{
+		m_gpuSceneLightIndex = m_node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
+			GpuSceneContiguousArrayType::kPointLights);
+	}
+	else if(m_gpuSceneLightIndex == kMaxU32 && type == LightComponentType::kSpot)
+	{
+		m_gpuSceneLightIndex =
+			m_node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(GpuSceneContiguousArrayType::kSpotLights);
+	}
+
+	m_type = type;
 }
 
 Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
-	const Bool moveUpdated = info.m_node->movedThisFrame() || m_forceFullUpdate;
-	const Bool shapeUpdated = m_markedForUpdate || m_forceFullUpdate;
-	updated = moveUpdated || shapeUpdated;
-	m_markedForUpdate = false;
-	m_forceFullUpdate = false;
+	const Bool typeChanged = m_typeChanged;
+	const Bool moveUpdated = info.m_node->movedThisFrame() || typeChanged;
+	const Bool shapeUpdated = m_shapeUpdated || typeChanged;
+	updated = moveUpdated || shapeUpdated || typeChanged;
+	m_shapeUpdated = false;
+	m_typeChanged = false;
 
 	if(moveUpdated)
 	{
@@ -114,14 +134,12 @@ Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		gpuLight.m_radius = m_point.m_radius;
 		gpuLight.m_diffuseColor = m_diffColor.xyz();
 		gpuLight.m_squareRadiusOverOne = 1.0f / (m_point.m_radius * m_point.m_radius);
-		gpuLight.m_shadowLayer = 0; // Don't know this at this point.
-		gpuLight.m_shadowAtlasTileScale = 0.0f; // Don't know this at this point.
-		for(U32 i = 0; i < 6; ++i)
-		{
-			gpuLight.m_shadowAtlasTileOffsets[i] = Vec4(0.0f); // Don't know this at this point.
-		}
+		gpuLight.m_shadow = m_shadow;
 		GpuSceneMicroPatcher& gpuScenePatcher = *getExternalSubsystems(*info.m_node).m_gpuSceneMicroPatcher;
-		gpuScenePatcher.newCopy(*info.m_framePool, m_gpuSceneLightOffset, sizeof(gpuLight), &gpuLight);
+		const PtrSize offset = m_gpuSceneLightIndex * sizeof(GpuScenePointLight)
+							   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kPointLights);
+		gpuScenePatcher.newCopy(*info.m_framePool, offset, sizeof(gpuLight), &gpuLight);
 	}
 	else if(updated && m_type == LightComponentType::kSpot)
 	{
@@ -184,12 +202,14 @@ Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		gpuLight.m_radius = m_spot.m_distance;
 		gpuLight.m_direction = -m_worldTransform.getRotation().getZAxis();
 		gpuLight.m_squareRadiusOverOne = 1.0f / (m_spot.m_distance * m_spot.m_distance);
-		gpuLight.m_shadowLayer = 1; // Don't know this at this point.
+		gpuLight.m_shadow = m_shadow;
 		gpuLight.m_outerCos = cos(m_spot.m_outerAngle / 2.0f);
 		gpuLight.m_innerCos = cos(m_spot.m_innerAngle / 2.0f);
-		gpuLight.m_textureMatrix = Mat4::getIdentity(); // Don't know this at this point.
 		GpuSceneMicroPatcher& gpuScenePatcher = *getExternalSubsystems(*info.m_node).m_gpuSceneMicroPatcher;
-		gpuScenePatcher.newCopy(*info.m_framePool, m_gpuSceneLightOffset, sizeof(gpuLight), &gpuLight);
+		const PtrSize offset = m_gpuSceneLightIndex * sizeof(GpuSceneSpotLight)
+							   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kSpotLights);
+		gpuScenePatcher.newCopy(*info.m_framePool, offset, sizeof(gpuLight), &gpuLight);
 	}
 	else if(m_type == LightComponentType::kDirectional)
 	{
@@ -373,10 +393,22 @@ void LightComponent::onDestroy(SceneNode& node)
 	deleteArray(node.getMemoryPool(), m_frustums, m_frustumCount);
 	m_spatial.removeFromOctree(node.getSceneGraph().getOctree());
 
-	if(m_gpuSceneLightOffset != kMaxU32)
+	if(m_gpuSceneLightIndex != kMaxU32)
 	{
-		node.getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(GpuSceneContiguousArrayType::kLights,
-																		   m_gpuSceneLightOffset);
+		if(m_type == LightComponentType::kPoint)
+		{
+			node.getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(
+				GpuSceneContiguousArrayType::kPointLights, m_gpuSceneLightIndex);
+		}
+		else if(m_type == LightComponentType::kSpot)
+		{
+			node.getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(GpuSceneContiguousArrayType::kSpotLights,
+																			   m_gpuSceneLightIndex);
+		}
+		else
+		{
+			ANKI_ASSERT(0);
+		}
 	}
 }
 

+ 13 - 8
AnKi/Scene/Components/LightComponent.h

@@ -58,7 +58,7 @@ public:
 	void setRadius(F32 x)
 	{
 		m_point.m_radius = x;
-		m_markedForUpdate = true;
+		m_shapeUpdated = true;
 	}
 
 	F32 getRadius() const
@@ -69,7 +69,7 @@ public:
 	void setDistance(F32 x)
 	{
 		m_spot.m_distance = x;
-		m_markedForUpdate = true;
+		m_shapeUpdated = true;
 	}
 
 	F32 getDistance() const
@@ -80,7 +80,7 @@ public:
 	void setInnerAngle(F32 ang)
 	{
 		m_spot.m_innerAngle = ang;
-		m_markedForUpdate = true;
+		m_shapeUpdated = true;
 	}
 
 	F32 getInnerAngle() const
@@ -91,7 +91,7 @@ public:
 	void setOuterAngle(F32 ang)
 	{
 		m_spot.m_outerAngle = ang;
-		m_markedForUpdate = true;
+		m_shapeUpdated = true;
 	}
 
 	F32 getOuterAngle() const
@@ -107,7 +107,7 @@ public:
 	void setShadowEnabled(const Bool x)
 	{
 		m_shadow = x;
-		m_markedForUpdate = true;
+		m_shapeUpdated = true;
 	}
 
 	ANKI_INTERNAL WeakArray<Frustum> getFrustums() const
@@ -124,6 +124,8 @@ public:
 		el.m_radius = m_point.m_radius;
 		el.m_diffuseColor = m_diffColor.xyz();
 		el.m_shadowLayer = kMaxU8;
+		ANKI_ASSERT(m_gpuSceneLightIndex != kMaxU32);
+		el.m_index = m_gpuSceneLightIndex;
 	}
 
 	void setupSpotLightQueueElement(SpotLightQueueElement& el) const
@@ -138,6 +140,8 @@ public:
 		el.m_diffuseColor = m_diffColor.xyz();
 		el.m_edgePoints = m_spot.m_edgePointsWspace;
 		el.m_shadowLayer = kMaxU8;
+		ANKI_ASSERT(m_gpuSceneLightIndex != kMaxU32);
+		el.m_index = m_gpuSceneLightIndex;
 	}
 
 	/// Setup a directional queue element.
@@ -148,6 +152,7 @@ public:
 										   WeakArray<Frustum> cascadeFrustums) const;
 
 private:
+	SceneNode* m_node;
 	U64 m_uuid;
 	Vec4 m_diffColor = Vec4(0.5f);
 	Transform m_worldTransform = Transform::getIdentity();
@@ -183,13 +188,13 @@ private:
 
 	Frustum* m_frustums = nullptr;
 
-	U32 m_gpuSceneLightOffset = kMaxU32;
+	U32 m_gpuSceneLightIndex = kMaxU32;
 
 	LightComponentType m_type;
 
 	U8 m_shadow : 1 = false;
-	U8 m_markedForUpdate : 1 = true;
-	U8 m_forceFullUpdate : 1 = true;
+	U8 m_shapeUpdated : 1 = true;
+	U8 m_typeChanged : 1 = true;
 	U8 m_frustumCount : 4 = 0; ///< The size of m_frustums array.
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);

+ 29 - 14
AnKi/Scene/Components/ModelComponent.cpp

@@ -18,7 +18,7 @@ ModelComponent::ModelComponent(SceneNode* node)
 	, m_node(node)
 	, m_spatial(this)
 {
-	m_gpuSceneTransformsOffset = U32(
+	m_gpuSceneTransformsIndex = U32(
 		node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(GpuSceneContiguousArrayType::kTransformPairs));
 }
 
@@ -29,15 +29,15 @@ ModelComponent::~ModelComponent()
 
 	for(const PatchInfo& patch : m_patchInfos)
 	{
-		if(patch.m_gpuSceneMeshLodsOffset != kMaxU32)
+		if(patch.m_gpuSceneMeshLodsIndex != kMaxU32)
 		{
 			m_node->getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(
-				GpuSceneContiguousArrayType::kMeshLods, patch.m_gpuSceneMeshLodsOffset);
+				GpuSceneContiguousArrayType::kMeshLods, patch.m_gpuSceneMeshLodsIndex);
 		}
 	}
 
 	m_node->getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(GpuSceneContiguousArrayType::kTransformPairs,
-																		  m_gpuSceneTransformsOffset);
+																		  m_gpuSceneTransformsIndex);
 
 	m_patchInfos.destroy(m_node->getMemoryPool());
 
@@ -64,17 +64,17 @@ void ModelComponent::loadModelResource(CString filename)
 
 	for(const PatchInfo& patch : m_patchInfos)
 	{
-		if(patch.m_gpuSceneMeshLodsOffset != kMaxU32)
+		if(patch.m_gpuSceneMeshLodsIndex != kMaxU32)
 		{
 			m_node->getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(
-				GpuSceneContiguousArrayType::kMeshLods, patch.m_gpuSceneMeshLodsOffset);
+				GpuSceneContiguousArrayType::kMeshLods, patch.m_gpuSceneMeshLodsIndex);
 		}
 	}
 
 	m_patchInfos.resize(m_node->getMemoryPool(), modelPatchCount);
 	for(U32 i = 0; i < modelPatchCount; ++i)
 	{
-		m_patchInfos[i].m_gpuSceneMeshLodsOffset = U32(
+		m_patchInfos[i].m_gpuSceneMeshLodsIndex = U32(
 			m_node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(GpuSceneContiguousArrayType::kMeshLods));
 	}
 
@@ -181,8 +181,10 @@ Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 				meshLods[l] = meshLods[l - 1];
 			}
 
-			gpuScenePatcher.newCopy(*info.m_framePool, m_patchInfos[i].m_gpuSceneMeshLodsOffset,
-									meshLods.getSizeInBytes(), &meshLods[0]);
+			const PtrSize offset = m_patchInfos[i].m_gpuSceneMeshLodsIndex * sizeof(meshLods)
+								   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+									   GpuSceneContiguousArrayType::kMeshLods);
+			gpuScenePatcher.newCopy(*info.m_framePool, offset, meshLods.getSizeInBytes(), &meshLods[0]);
 		}
 
 		// Upload the uniforms
@@ -210,8 +212,11 @@ Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		trfs[0] = Mat3x4(info.m_node->getWorldTransform());
 		trfs[1] = Mat3x4(info.m_node->getPreviousWorldTransform());
 
+		const PtrSize offset = m_gpuSceneTransformsIndex * sizeof(trfs)
+							   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kTransformPairs);
 		getExternalSubsystems(*info.m_node)
-			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneTransformsOffset, sizeof(trfs), &trfs[0]);
+			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, offset, sizeof(trfs), &trfs[0]);
 	}
 
 	// Spatial update
@@ -294,10 +299,15 @@ void ModelComponent::setupRenderableQueueElements(U32 lod, RenderingTechnique te
 		ModelRenderingInfo modelInf;
 		patch.getRenderingInfo(key, modelInf);
 
+		AllGpuSceneContiguousArrays& gpuArrays = m_node->getSceneGraph().getAllGpuSceneContiguousArrays();
+
 		queueElem.m_program = modelInf.m_program.get();
-		queueElem.m_worldTransformsOffset = m_gpuSceneTransformsOffset;
+		queueElem.m_worldTransformsOffset = U32(m_gpuSceneTransformsIndex * sizeof(Mat3x4) * 2
+												+ gpuArrays.getArrayBase(GpuSceneContiguousArrayType::kTransformPairs));
 		queueElem.m_uniformsOffset = m_patchInfos[i].m_gpuSceneUniformsOffset;
-		queueElem.m_geometryOffset = m_patchInfos[i].m_gpuSceneMeshLodsOffset + lod * sizeof(GpuSceneMeshLod);
+		queueElem.m_geometryOffset = m_patchInfos[i].m_gpuSceneMeshLodsIndex * sizeof(GpuSceneMeshLod) * kMaxLodCount
+									 + lod * sizeof(GpuSceneMeshLod);
+		queueElem.m_geometryOffset += U32(gpuArrays.getArrayBase(GpuSceneContiguousArrayType::kMeshLods));
 		queueElem.m_boneTransformsOffset = (hasSkin) ? m_skinComponent->getBoneTransformsGpuSceneOffset() : 0;
 		queueElem.m_indexCount = modelInf.m_indexCount;
 		queueElem.m_firstIndex = U32(modelInf.m_indexBufferOffset / 2 + modelInf.m_firstIndex);
@@ -360,14 +370,19 @@ void ModelComponent::setupRayTracingInstanceQueueElements(U32 lod, RenderingTech
 
 		const ModelPatch& patch = m_model->getModelPatches()[i];
 
+		AllGpuSceneContiguousArrays& gpuArrays = m_node->getSceneGraph().getAllGpuSceneContiguousArrays();
+
 		ModelRayTracingInfo modelInf;
 		patch.getRayTracingInfo(key, modelInf);
 
 		queueElem.m_bottomLevelAccelerationStructure = modelInf.m_bottomLevelAccelerationStructure.get();
 		queueElem.m_shaderGroupHandleIndex = modelInf.m_shaderGroupHandleIndex;
-		queueElem.m_worldTransformsOffset = m_gpuSceneTransformsOffset;
+		queueElem.m_worldTransformsOffset = U32(m_gpuSceneTransformsIndex * sizeof(Mat3x4) * 2
+												+ gpuArrays.getArrayBase(GpuSceneContiguousArrayType::kTransformPairs));
 		queueElem.m_uniformsOffset = m_patchInfos[i].m_gpuSceneUniformsOffset;
-		queueElem.m_geometryOffset = m_patchInfos[i].m_gpuSceneMeshLodsOffset + lod * sizeof(GpuSceneMeshLod);
+		queueElem.m_geometryOffset = m_patchInfos[i].m_gpuSceneMeshLodsIndex * sizeof(GpuSceneMeshLod) * kMaxLodCount
+									 + lod * sizeof(GpuSceneMeshLod);
+		queueElem.m_geometryOffset += U32(gpuArrays.getArrayBase(GpuSceneContiguousArrayType::kMeshLods));
 		queueElem.m_indexBufferOffset = U32(modelInf.m_indexBufferOffset);
 
 		const Transform positionTransform(patch.getMesh()->getPositionsTranslation().xyz0(), Mat3x4::getIdentity(),

+ 2 - 2
AnKi/Scene/Components/ModelComponent.h

@@ -54,7 +54,7 @@ private:
 	{
 	public:
 		U32 m_gpuSceneUniformsOffset = kMaxU32;
-		U32 m_gpuSceneMeshLodsOffset = kMaxU32;
+		U32 m_gpuSceneMeshLodsIndex = kMaxU32;
 		RenderingTechniqueBit m_techniques;
 	};
 
@@ -65,7 +65,7 @@ private:
 	ModelResourcePtr m_model;
 
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneUniforms;
-	U32 m_gpuSceneTransformsOffset = kMaxU32;
+	U32 m_gpuSceneTransformsIndex = kMaxU32;
 	DynamicArray<PatchInfo> m_patchInfos;
 
 	Bool m_dirty : 1 = true;

+ 13 - 9
AnKi/Scene/Components/ParticleEmitterComponent.cpp

@@ -211,10 +211,10 @@ ParticleEmitterComponent::~ParticleEmitterComponent()
 	gpuScenePool.deferredFree(m_gpuSceneAlphas);
 	gpuScenePool.deferredFree(m_gpuSceneUniforms);
 
-	if(m_gpuSceneParticleEmitterOffset != kMaxU32)
+	if(m_gpuSceneIndex != kMaxU32)
 	{
 		m_node->getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(
-			GpuSceneContiguousArrayType::kParticleEmitters, m_gpuSceneParticleEmitterOffset);
+			GpuSceneContiguousArrayType::kParticleEmitters, m_gpuSceneIndex);
 	}
 
 	m_spatial.removeFromOctree(m_node->getSceneGraph().getOctree());
@@ -245,10 +245,10 @@ void ParticleEmitterComponent::loadParticleEmitterResource(CString filename)
 	gpuScenePool.deferredFree(m_gpuSceneAlphas);
 	gpuScenePool.deferredFree(m_gpuSceneUniforms);
 
-	if(m_gpuSceneParticleEmitterOffset != kMaxU32)
+	if(m_gpuSceneIndex != kMaxU32)
 	{
 		m_node->getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(
-			GpuSceneContiguousArrayType::kParticleEmitters, m_gpuSceneParticleEmitterOffset);
+			GpuSceneContiguousArrayType::kParticleEmitters, m_gpuSceneIndex);
 	}
 
 	// Init particles
@@ -281,8 +281,8 @@ void ParticleEmitterComponent::loadParticleEmitterResource(CString filename)
 	gpuScenePool.allocate(m_particleEmitterResource->getMaterial()->getPrefilledLocalUniforms().getSizeInBytes(),
 						  alignof(U32), m_gpuSceneUniforms);
 
-	m_gpuSceneParticleEmitterOffset = U32(m_node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
-		GpuSceneContiguousArrayType::kParticleEmitters));
+	m_gpuSceneIndex = m_node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
+		GpuSceneContiguousArrayType::kParticleEmitters);
 }
 
 Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
@@ -331,8 +331,10 @@ Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 		particles.m_vertexOffsets[U32(VertexStreamId::kParticleColor)] = U32(m_gpuSceneAlphas.m_offset);
 		particles.m_vertexOffsets[U32(VertexStreamId::kParticleScale)] = U32(m_gpuSceneScales.m_offset);
 
-		patcher.newCopy(*info.m_framePool, m_gpuSceneParticleEmitterOffset, sizeof(GpuSceneParticleEmitter),
-						&particles);
+		const PtrSize offset = m_gpuSceneIndex * sizeof(GpuSceneParticleEmitter)
+							   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kParticleEmitters);
+		patcher.newCopy(*info.m_framePool, offset, sizeof(GpuSceneParticleEmitter), &particles);
 
 		patcher.newCopy(*info.m_framePool, m_gpuSceneUniforms.m_offset,
 						m_particleEmitterResource->getMaterial()->getPrefilledLocalUniforms().getSizeInBytes(),
@@ -469,7 +471,9 @@ void ParticleEmitterComponent::setupRenderableQueueElements(RenderingTechnique t
 	el->m_program = prog.get();
 	el->m_worldTransformsOffset = 0;
 	el->m_uniformsOffset = U32(m_gpuSceneUniforms.m_offset);
-	el->m_geometryOffset = m_gpuSceneParticleEmitterOffset;
+	el->m_geometryOffset = U32(m_gpuSceneIndex * sizeof(GpuSceneParticleEmitter)
+							   + m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kParticleEmitters));
 	el->m_boneTransformsOffset = 0;
 	el->m_vertexCount = 6 * m_aliveParticleCount;
 	el->m_firstVertex = 0;

+ 1 - 1
AnKi/Scene/Components/ParticleEmitterComponent.h

@@ -67,7 +67,7 @@ private:
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneAlphas;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneScales;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneUniforms;
-	U32 m_gpuSceneParticleEmitterOffset = kMaxU32;
+	U32 m_gpuSceneIndex = kMaxU32;
 
 	Bool m_resourceUpdated = true;
 	SimulationType m_simulationType = SimulationType::kUndefined;

+ 8 - 4
AnKi/Scene/Components/ReflectionProbeComponent.cpp

@@ -27,8 +27,8 @@ ReflectionProbeComponent::ReflectionProbeComponent(SceneNode* node)
 		m_frustums[i].update();
 	}
 
-	m_gpuSceneOffset = U32(node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
-		GpuSceneContiguousArrayType::kReflectionProbes));
+	m_gpuSceneIndex =
+		node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(GpuSceneContiguousArrayType::kReflectionProbes);
 }
 
 ReflectionProbeComponent::~ReflectionProbeComponent()
@@ -102,8 +102,12 @@ Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 		gpuProbe.m_cubeTexture = m_reflectionTexBindlessIndex;
 		gpuProbe.m_aabbMin = aabbWorld.getMin().xyz();
 		gpuProbe.m_aabbMax = aabbWorld.getMax().xyz();
+
+		const PtrSize offset = m_gpuSceneIndex * sizeof(GpuSceneReflectionProbe)
+							   + info.m_node->getSceneGraph().getAllGpuSceneContiguousArrays().getArrayBase(
+								   GpuSceneContiguousArrayType::kReflectionProbes);
 		getExternalSubsystems(*info.m_node)
-			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneOffset, sizeof(gpuProbe), &gpuProbe);
+			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, offset, sizeof(gpuProbe), &gpuProbe);
 	}
 
 	// Update spatial and frustums
@@ -124,7 +128,7 @@ void ReflectionProbeComponent::onDestroy(SceneNode& node)
 	m_spatial.removeFromOctree(node.getSceneGraph().getOctree());
 
 	node.getSceneGraph().getAllGpuSceneContiguousArrays().deferredFree(GpuSceneContiguousArrayType::kReflectionProbes,
-																	   m_gpuSceneOffset);
+																	   m_gpuSceneIndex);
 }
 
 } // end namespace anki

+ 3 - 1
AnKi/Scene/Components/ReflectionProbeComponent.h

@@ -52,6 +52,8 @@ public:
 		el.m_aabbMax = m_halfSize + m_worldPos;
 		ANKI_ASSERT(el.m_textureBindlessIndex != kMaxU32);
 		el.m_textureBindlessIndex = m_reflectionTexBindlessIndex;
+		ANKI_ASSERT(m_gpuSceneIndex != kMaxU32);
+		el.m_index = m_gpuSceneIndex;
 	}
 
 	ANKI_INTERNAL void setupReflectionProbeQueueElementForRefresh(ReflectionProbeQueueElementForRefresh& el) const
@@ -75,7 +77,7 @@ private:
 	Vec3 m_worldPos = Vec3(kMaxF32);
 	Vec3 m_halfSize = Vec3(1.0f);
 
-	U32 m_gpuSceneOffset = kMaxU32;
+	U32 m_gpuSceneIndex = kMaxU32;
 
 	Spatial m_spatial;
 

+ 14 - 13
AnKi/Scene/ContiguousArrayAllocator.cpp

@@ -19,7 +19,7 @@ void AllGpuSceneContiguousArrays::ContiguousArrayAllocator::destroy(GpuSceneMemo
 	}
 }
 
-AllGpuSceneContiguousArrays::ContiguousArrayAllocator::Index
+AllGpuSceneContiguousArrays::Index
 AllGpuSceneContiguousArrays::ContiguousArrayAllocator::allocateObject(GpuSceneMemoryPool* gpuScene,
 																	  HeapMemoryPool* cpuPool)
 {
@@ -30,7 +30,7 @@ AllGpuSceneContiguousArrays::ContiguousArrayAllocator::allocateObject(GpuSceneMe
 	if(m_poolToken.m_offset == kMaxPtrSize)
 	{
 		// Initialize
-		const U32 alignment = sizeof(U32);
+		const U32 alignment = gpuScene->getGrManager().getDeviceCapabilities().m_storageBufferBindOffsetAlignment;
 		gpuScene->allocate(m_objectSize * m_initialArraySize, alignment, m_poolToken);
 		m_nextSlotIndex = 0;
 
@@ -114,10 +114,15 @@ void AllGpuSceneContiguousArrays::init(SceneGraph* scene)
 	constexpr F32 kGrowRate = 2.0;
 
 	const Array<U32, U32(GpuSceneContiguousArrayType::kCount)> minElementCount = {
-		cfg.getSceneMinGpuSceneTransforms(),       cfg.getSceneMinGpuSceneMeshes(),
-		cfg.getSceneMinGpuSceneParticleEmitters(), cfg.getSceneMinGpuSceneLights(),
-		cfg.getSceneMinGpuSceneReflectionProbes(), cfg.getSceneMinGpuSceneGlobalIlluminationProbes(),
-		cfg.getSceneMinGpuSceneDecals(),           cfg.getSceneMinGpuSceneFogDensityVolumes()};
+		cfg.getSceneMinGpuSceneTransforms(),
+		cfg.getSceneMinGpuSceneMeshes(),
+		cfg.getSceneMinGpuSceneParticleEmitters(),
+		cfg.getSceneMinGpuSceneLights(),
+		cfg.getSceneMinGpuSceneLights(),
+		cfg.getSceneMinGpuSceneReflectionProbes(),
+		cfg.getSceneMinGpuSceneGlobalIlluminationProbes(),
+		cfg.getSceneMinGpuSceneDecals(),
+		cfg.getSceneMinGpuSceneFogDensityVolumes()};
 
 	for(GpuSceneContiguousArrayType type : EnumIterable<GpuSceneContiguousArrayType>())
 	{
@@ -136,18 +141,14 @@ void AllGpuSceneContiguousArrays::destroy()
 	}
 }
 
-PtrSize AllGpuSceneContiguousArrays::allocate(GpuSceneContiguousArrayType type)
+AllGpuSceneContiguousArrays::Index AllGpuSceneContiguousArrays::allocate(GpuSceneContiguousArrayType type)
 {
 	const U32 idx = m_allocs[type].allocateObject(m_scene->m_subsystems.m_gpuSceneMemoryPool, &m_scene->m_pool);
-	return PtrSize(idx) * m_componentCount[type] * m_componentSize[type] + m_allocs[type].m_poolToken.m_offset;
+	return idx;
 }
 
-void AllGpuSceneContiguousArrays::deferredFree(GpuSceneContiguousArrayType type, PtrSize offset)
+void AllGpuSceneContiguousArrays::deferredFree(GpuSceneContiguousArrayType type, Index idx)
 {
-	ANKI_ASSERT(offset >= m_allocs[type].m_poolToken.m_offset);
-	offset -= m_allocs[type].m_poolToken.m_offset;
-	ANKI_ASSERT((offset % (m_componentCount[type] * m_componentSize[type])) == 0);
-	const U32 idx = U32(offset / (m_componentCount[type] * m_componentSize[type]));
 	m_allocs[type].deferredFree(m_frame, &m_scene->m_pool, idx);
 }
 

+ 41 - 8
AnKi/Scene/ContiguousArrayAllocator.h

@@ -18,7 +18,8 @@ enum class GpuSceneContiguousArrayType : U8
 	kTransformPairs,
 	kMeshLods,
 	kParticleEmitters,
-	kLights,
+	kPointLights,
+	kSpotLights,
 	kReflectionProbes,
 	kGlobalIlluminationProbes,
 	kDecals,
@@ -33,14 +34,36 @@ ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(GpuSceneContiguousArrayType)
 class AllGpuSceneContiguousArrays
 {
 public:
+	using Index = U32;
+
 	void init(SceneGraph* scene);
 
 	void destroy();
 
-	PtrSize allocate(GpuSceneContiguousArrayType type);
+	/// @note Thread-safe against allocate(), deferredFree() and endFrame()
+	Index allocate(GpuSceneContiguousArrayType type);
+
+	/// @note It's not thread-safe
+	PtrSize getArrayBase(GpuSceneContiguousArrayType type) const
+	{
+		return m_allocs[type].getArrayBase();
+	}
+
+	/// @note It's not thread-safe
+	U32 getElementCount(GpuSceneContiguousArrayType type) const
+	{
+		return m_allocs[type].getElementCount();
+	}
+
+	constexpr static U32 getElementSize(GpuSceneContiguousArrayType type)
+	{
+		return m_componentSize[type] * m_componentCount[type];
+	}
 
-	void deferredFree(GpuSceneContiguousArrayType type, PtrSize offset);
+	/// @note Thread-safe against allocate(), deferredFree() and endFrame()
+	void deferredFree(GpuSceneContiguousArrayType type, Index idx);
 
+	/// @note Thread-safe against allocate(), deferredFree() and endFrame()
 	void endFrame();
 
 private:
@@ -51,8 +74,6 @@ private:
 		friend class AllGpuSceneContiguousArrays;
 
 	public:
-		using Index = U32;
-
 		~ContiguousArrayAllocator()
 		{
 			ANKI_ASSERT(m_nextSlotIndex == 0 && "Forgot to deallocate");
@@ -87,6 +108,17 @@ private:
 		/// @note It's thread-safe against itself, deferredFree and allocateObject.
 		void collectGarbage(U32 newFrameIdx, GpuSceneMemoryPool* gpuScene, HeapMemoryPool* cpuPool);
 
+		PtrSize getArrayBase() const
+		{
+			ANKI_ASSERT(m_poolToken.isValid());
+			return m_poolToken.m_offset;
+		}
+
+		U32 getElementCount() const
+		{
+			return m_nextSlotIndex;
+		}
+
 	private:
 		SegregatedListsGpuMemoryPoolToken m_poolToken;
 
@@ -94,7 +126,7 @@ private:
 
 		Array<DynamicArray<Index>, kMaxFramesInFlight> m_garbage;
 
-		SpinLock m_mtx;
+		mutable SpinLock m_mtx;
 
 		F32 m_growRate = 2.0;
 		U32 m_initialArraySize = 0;
@@ -110,12 +142,13 @@ private:
 	U8 m_frame = 0;
 
 	static constexpr Array<U8, U32(GpuSceneContiguousArrayType::kCount)> m_componentCount = {
-		2, kMaxLodCount, 1, 1, 1, 1, 1, 1};
+		2, kMaxLodCount, 1, 1, 1, 1, 1, 1, 1};
 	static constexpr Array<U8, U32(GpuSceneContiguousArrayType::kCount)> m_componentSize = {
 		sizeof(Mat3x4),
 		sizeof(GpuSceneMeshLod),
 		sizeof(GpuSceneParticleEmitter),
-		max<U8>(sizeof(GpuScenePointLight), sizeof(GpuSceneSpotLight)),
+		sizeof(GpuScenePointLight),
+		sizeof(GpuSceneSpotLight),
 		sizeof(GpuSceneReflectionProbe),
 		sizeof(GpuSceneGlobalIlluminationProbe),
 		sizeof(GpuSceneDecal),

+ 14 - 0
AnKi/Scene/Visibility.cpp

@@ -752,6 +752,20 @@ void CombineResultsTask::combine()
 				  }
 			  });
 
+	const AllGpuSceneContiguousArrays& arrays = m_frcCtx->m_visCtx->m_scene->getAllGpuSceneContiguousArrays();
+
+	auto setOffset = [&](ClusteredObjectType type, GpuSceneContiguousArrayType type2) {
+		results.m_clustererObjectsArrayOffsets[type] = arrays.getElementCount(type2) ? arrays.getArrayBase(type2) : 0;
+		results.m_clustererObjectsArrayRanges[type] = arrays.getElementCount(type2) * arrays.getElementSize(type2);
+	};
+
+	setOffset(ClusteredObjectType::kPointLight, GpuSceneContiguousArrayType::kPointLights);
+	setOffset(ClusteredObjectType::kSpotLight, GpuSceneContiguousArrayType::kSpotLights);
+	setOffset(ClusteredObjectType::kDecal, GpuSceneContiguousArrayType::kDecals);
+	setOffset(ClusteredObjectType::kFogDensityVolume, GpuSceneContiguousArrayType::kFogDensityVolumes);
+	setOffset(ClusteredObjectType::kGlobalIlluminationProbe, GpuSceneContiguousArrayType::kGlobalIlluminationProbes);
+	setOffset(ClusteredObjectType::kReflectionProbe, GpuSceneContiguousArrayType::kReflectionProbes);
+
 	// Cleanup
 	if(m_frcCtx->m_r)
 	{

+ 16 - 15
AnKi/Shaders/ClusterBinning.ankiprog

@@ -16,12 +16,13 @@ ANKI_SPECIALIZATION_CONSTANT_UVEC2(kRenderingSize, 4u);
 
 [[vk::binding(0)]] ConstantBuffer<ClusteredShadingUniforms> g_unis;
 [[vk::binding(1)]] RWStructuredBuffer<Cluster> g_clusters;
+
 [[vk::binding(2)]] StructuredBuffer<PointLight> g_pointLights;
 [[vk::binding(3)]] StructuredBuffer<SpotLightBinning> g_spotLights;
-[[vk::binding(4)]] StructuredBuffer<ReflectionProbe> g_reflectionProbes;
-[[vk::binding(5)]] StructuredBuffer<GlobalIlluminationProbe> g_giProbes;
-[[vk::binding(6)]] StructuredBuffer<FogDensityVolume> g_fogVolumes;
-[[vk::binding(7)]] StructuredBuffer<Decal> g_decals;
+[[vk::binding(4)]] StructuredBuffer<Decal> g_decals;
+[[vk::binding(5)]] StructuredBuffer<FogDensityVolume> g_fogVolumes;
+[[vk::binding(6)]] StructuredBuffer<ReflectionProbe> g_reflectionProbes;
+[[vk::binding(7)]] StructuredBuffer<GlobalIlluminationProbe> g_giProbes;
 
 #define THREADGROUP_SIZE 64
 constexpr U32 kTileCount = kTileCountX * kTileCountY;
@@ -41,32 +42,32 @@ groupshared ExtendedClusterObjectMask s_zSplitMasks[kMaxZsplitCount];
 
 Bool isPointLight(U32 objectIdx)
 {
-	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kPointLight].x;
+	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kPointLight].x;
 }
 
 Bool isSpotLight(U32 objectIdx)
 {
-	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kSpotLight].x;
+	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kSpotLight].x;
 }
 
 Bool isDecal(U32 objectIdx)
 {
-	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kDecal].x;
+	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kDecal].x;
 }
 
 Bool isFogVolume(U32 objectIdx)
 {
-	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kFogDensityVolume].x;
+	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kFogDensityVolume].x;
 }
 
 Bool isReflectionProbe(U32 objectIdx)
 {
-	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kReflectionProbe].x;
+	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kReflectionProbe].x;
 }
 
 Bool isGiProbe(U32 objectIdx)
 {
-	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kGlobalIlluminationProbe].x;
+	return objectIdx < g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kGlobalIlluminationProbe].x;
 }
 
 [numthreads(THREADGROUP_SIZE, 1, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID,
@@ -118,7 +119,7 @@ Bool isGiProbe(U32 objectIdx)
 	// Spot light
 	else if(isSpotLight(clustererObjectIdx))
 	{
-		objectArrayIdx = clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kSpotLight - 1u].x;
+		objectArrayIdx = clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kSpotLight - 1u].x;
 		const SpotLightBinning light = g_spotLights[objectArrayIdx];
 
 		t0 = 10000.0;
@@ -156,7 +157,7 @@ Bool isGiProbe(U32 objectIdx)
 	// Decal
 	else if(isDecal(clustererObjectIdx))
 	{
-		objectArrayIdx = clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kDecal - 1u].x;
+		objectArrayIdx = clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kDecal - 1u].x;
 		const Decal decal = g_decals[objectArrayIdx];
 
 		collides = testRayObb(rayOrigin, rayDir, decal.m_obbExtend, decal.m_invertedTransform, t0, t1);
@@ -165,7 +166,7 @@ Bool isGiProbe(U32 objectIdx)
 	else if(isFogVolume(clustererObjectIdx))
 	{
 		objectArrayIdx =
-			clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kFogDensityVolume - 1u].x;
+			clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kFogDensityVolume - 1u].x;
 		const FogDensityVolume vol = g_fogVolumes[objectArrayIdx];
 
 		if(vol.m_isBox != 0u)
@@ -183,7 +184,7 @@ Bool isGiProbe(U32 objectIdx)
 	else if(isReflectionProbe(clustererObjectIdx))
 	{
 		objectArrayIdx =
-			clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kReflectionProbe - 1u].x;
+			clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kReflectionProbe - 1u].x;
 		const ReflectionProbe probe = g_reflectionProbes[objectArrayIdx];
 
 		collides = testRayAabb(rayOrigin, rayDir, probe.m_aabbMin, probe.m_aabbMax, t0, t1);
@@ -192,7 +193,7 @@ Bool isGiProbe(U32 objectIdx)
 	else
 	{
 		objectArrayIdx =
-			clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusterObjectType::kGlobalIlluminationProbe - 1u].x;
+			clustererObjectIdx - g_unis.m_objectCountsUpTo[(U32)ClusteredObjectType::kGlobalIlluminationProbe - 1u].x;
 		const GlobalIlluminationProbe probe = g_giProbes[objectArrayIdx];
 
 		collides = testRayAabb(rayOrigin, rayDir, probe.m_aabbMin, probe.m_aabbMax, t0, t1);

+ 16 - 33
AnKi/Shaders/ClusteredShadingCommon.hlsl

@@ -19,16 +19,8 @@
 // Light uniforms (3)
 //
 #if defined(CLUSTERED_SHADING_LIGHTS_BINDING)
-[[vk::binding(CLUSTERED_SHADING_LIGHTS_BINDING, CLUSTERED_SHADING_SET)]] cbuffer b_pointLights
-{
-	PointLight g_pointLights[kMaxVisiblePointLights];
-};
-
-[[vk::binding(CLUSTERED_SHADING_LIGHTS_BINDING + 1u, CLUSTERED_SHADING_SET)]] cbuffer b_spotLights
-{
-	SpotLight g_spotLights[kMaxVisibleSpotLights];
-};
-
+[[vk::binding(CLUSTERED_SHADING_LIGHTS_BINDING, CLUSTERED_SHADING_SET)]] StructuredBuffer<PointLight> g_pointLights;
+[[vk::binding(CLUSTERED_SHADING_LIGHTS_BINDING + 1u, CLUSTERED_SHADING_SET)]] StructuredBuffer<SpotLight> g_spotLights;
 [[vk::binding(CLUSTERED_SHADING_LIGHTS_BINDING + 2u, CLUSTERED_SHADING_SET)]] Texture2D g_shadowAtlasTex;
 #endif
 
@@ -36,40 +28,31 @@
 // Reflection probes (1)
 //
 #if defined(CLUSTERED_SHADING_REFLECTIONS_BINDING)
-[[vk::binding(CLUSTERED_SHADING_REFLECTIONS_BINDING, CLUSTERED_SHADING_SET)]] cbuffer b_reflectionProbes
-{
-	ReflectionProbe g_reflectionProbes[kMaxVisibleReflectionProbes];
-};
+[[vk::binding(CLUSTERED_SHADING_REFLECTIONS_BINDING, CLUSTERED_SHADING_SET)]] StructuredBuffer<ReflectionProbe>
+	g_reflectionProbes;
 #endif
 
 //
 // Decal uniforms (1)
 //
 #if defined(CLUSTERED_SHADING_DECALS_BINDING)
-[[vk::binding(CLUSTERED_SHADING_DECALS_BINDING, CLUSTERED_SHADING_SET)]] cbuffer b_decals
-{
-	Decal g_decals[kMaxVisibleDecals];
-};
+[[vk::binding(CLUSTERED_SHADING_DECALS_BINDING, CLUSTERED_SHADING_SET)]] StructuredBuffer<Decal> g_decals;
 #endif
 
 //
 // Fog density uniforms (1)
 //
 #if defined(CLUSTERED_SHADING_FOG_BINDING)
-[[vk::binding(CLUSTERED_SHADING_FOG_BINDING, CLUSTERED_SHADING_SET)]] cbuffer b_fogDensityVolumes
-{
-	FogDensityVolume g_fogDensityVolumes[kMaxVisibleFogDensityVolumes];
-};
+[[vk::binding(CLUSTERED_SHADING_FOG_BINDING, CLUSTERED_SHADING_SET)]] StructuredBuffer<FogDensityVolume>
+	g_fogDensityVolumes;
 #endif
 
 //
 // GI (1)
 //
 #if defined(CLUSTERED_SHADING_GI_BINDING)
-[[vk::binding(CLUSTERED_SHADING_GI_BINDING, CLUSTERED_SHADING_SET)]] cbuffer b_giProbes
-{
-	GlobalIlluminationProbe g_giProbes[kMaxVisibleGlobalIlluminationProbes];
-};
+[[vk::binding(CLUSTERED_SHADING_GI_BINDING, CLUSTERED_SHADING_SET)]] StructuredBuffer<GlobalIlluminationProbe>
+	g_giProbes;
 #endif
 
 //
@@ -85,37 +68,37 @@ Vec3 clusterHeatmap(Cluster cluster, U32 objectTypeMask)
 	U32 maxObjects = 0u;
 	I32 count = 0;
 
-	if((objectTypeMask & (1u << (U32)ClusterObjectType::kPointLight)) != 0u)
+	if((objectTypeMask & (1u << (U32)ClusteredObjectType::kPointLight)) != 0u)
 	{
 		maxObjects += kMaxVisiblePointLights;
 		count += I32(countbits(cluster.m_pointLightsMask));
 	}
 
-	if((objectTypeMask & (1u << (U32)ClusterObjectType::kSpotLight)) != 0u)
+	if((objectTypeMask & (1u << (U32)ClusteredObjectType::kSpotLight)) != 0u)
 	{
 		maxObjects += kMaxVisibleSpotLights;
 		count += I32(countbits(cluster.m_spotLightsMask));
 	}
 
-	if((objectTypeMask & (1u << (U32)ClusterObjectType::kDecal)) != 0u)
+	if((objectTypeMask & (1u << (U32)ClusteredObjectType::kDecal)) != 0u)
 	{
 		maxObjects += kMaxVisibleDecals;
 		count += I32(countbits(cluster.m_decalsMask));
 	}
 
-	if((objectTypeMask & (1u << (U32)ClusterObjectType::kFogDensityVolume)) != 0u)
+	if((objectTypeMask & (1u << (U32)ClusteredObjectType::kFogDensityVolume)) != 0u)
 	{
 		maxObjects += kMaxVisibleFogDensityVolumes;
 		count += countbits(cluster.m_fogDensityVolumesMask);
 	}
 
-	if((objectTypeMask & (1u << (U32)ClusterObjectType::kReflectionProbe)) != 0u)
+	if((objectTypeMask & (1u << (U32)ClusteredObjectType::kReflectionProbe)) != 0u)
 	{
 		maxObjects += kMaxVisibleReflectionProbes;
 		count += countbits(cluster.m_reflectionProbesMask);
 	}
 
-	if((objectTypeMask & (1u << (U32)ClusterObjectType::kGlobalIlluminationProbe)) != 0u)
+	if((objectTypeMask & (1u << (U32)ClusteredObjectType::kGlobalIlluminationProbe)) != 0u)
 	{
 		maxObjects += kMaxVisibleGlobalIlluminationProbes;
 		count += countbits(cluster.m_giProbesMask);
@@ -143,7 +126,7 @@ U32 computeTileClusterIndexFragCoord(Vec2 fragCoord, U32 tileSize, U32 tileCount
 /// Merge the tiles with z splits into a single cluster.
 Cluster mergeClusters(Cluster tileCluster, Cluster zCluster)
 {
-//#define ANKI_OR_MASKS(x) subgroupOr(x)
+// #define ANKI_OR_MASKS(x) subgroupOr(x)
 #define ANKI_OR_MASKS(x) (x)
 
 	Cluster outCluster;

+ 22 - 61
AnKi/Shaders/Include/ClusteredShadingTypes.h

@@ -5,14 +5,14 @@
 
 #pragma once
 
-#include <AnKi/Shaders/Include/Common.h>
+#include <AnKi/Shaders/Include/GpuSceneTypes.h>
 
 #define ANKI_CLUSTERED_SHADING_USE_64BIT ANKI_SUPPORTS_64BIT_TYPES
 
 ANKI_BEGIN_NAMESPACE
 
 // Enum of clusterer object types
-enum class ClusterObjectType : U32
+enum class ClusteredObjectType : U32
 {
 	kPointLight,
 	kSpotLight,
@@ -24,7 +24,7 @@ enum class ClusterObjectType : U32
 	kCount,
 	kFirst = 0
 };
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ClusterObjectType)
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ClusteredObjectType)
 
 // Limits
 #if ANKI_CLUSTERED_SHADING_USE_64BIT
@@ -130,67 +130,16 @@ static_assert(sizeof(DirectionalLight) == kSizeof_DirectionalLight);
 static_assert(kMaxShadowCascades == 4u); // Because m_shadowCascadeDistances is a Vec4
 
 /// Representation of a reflection probe.
-struct ReflectionProbe
-{
-	Vec3 m_position; ///< Position of the probe in world space.
-	U32 m_cubeTexture; ///< Bindless index of the reflection texture.
-
-	Vec3 m_aabbMin;
-	F32 m_padding0;
-
-	Vec3 m_aabbMax;
-	F32 m_padding1;
-};
-constexpr U32 kSizeof_ReflectionProbe = 3u * sizeof(Vec4);
-static_assert(sizeof(ReflectionProbe) == kSizeof_ReflectionProbe);
+typedef GpuSceneReflectionProbe ReflectionProbe;
 
 /// Decal.
-struct Decal
-{
-	U32 m_diffuseTexture;
-	U32 m_roughnessMetalnessTexture;
-	RF32 m_diffuseBlendFactor;
-	RF32 m_roughnessMetalnessFactor;
-
-	Mat4 m_textureMatrix;
-
-	Mat4 m_invertedTransform;
-
-	Vec3 m_obbExtend;
-	F32 m_padding0;
-};
-constexpr U32 kSizeof_Decal = 2u * sizeof(Vec4) + 2u * sizeof(Mat4);
-static_assert(sizeof(Decal) == kSizeof_Decal);
+typedef GpuSceneDecal Decal;
 
 /// Fog density volume.
-struct FogDensityVolume
-{
-	Vec3 m_aabbMinOrSphereCenter;
-	U32 m_isBox;
-
-	Vec3 m_aabbMaxOrSphereRadiusSquared;
-	RF32 m_density;
-};
-constexpr U32 kSizeof_FogDensityVolume = 2u * sizeof(Vec4);
-static_assert(sizeof(FogDensityVolume) == kSizeof_FogDensityVolume);
+typedef GpuSceneFogDensityVolume FogDensityVolume;
 
 /// Global illumination probe
-struct GlobalIlluminationProbe
-{
-	Vec3 m_aabbMin;
-	F32 m_padding0;
-
-	Vec3 m_aabbMax;
-	F32 m_padding1;
-
-	U32 m_volumeTexture; ///< Bindless index of the irradiance volume texture.
-	F32 m_halfTexelSizeU; ///< (1.0 / textureSize(texArr[textureIndex]).x) / 2.0
-	/// Used to calculate a factor that is zero when fragPos is close to AABB bounds and 1.0 at fadeDistance and less.
-	RF32 m_fadeDistance;
-	F32 m_padding2;
-};
-constexpr U32 kSizeof_GlobalIlluminationProbe = 3u * sizeof(Vec4);
-static_assert(sizeof(GlobalIlluminationProbe) == kSizeof_GlobalIlluminationProbe);
+typedef GpuSceneGlobalIlluminationProbe GlobalIlluminationProbe;
 
 /// Common matrices.
 struct CommonMatrices
@@ -256,13 +205,13 @@ struct ClusteredShadingUniforms
 
 	/// This are some additive counts used to map a flat index to the index of the specific object
 #if defined(__cplusplus)
-	Array<UVec4, U32(ClusterObjectType::kCount)> m_objectCountsUpTo;
+	Array<UVec4, U32(ClusteredObjectType::kCount)> m_objectCountsUpTo;
 #else
-	UVec4 m_objectCountsUpTo[(U32)ClusterObjectType::kCount];
+	UVec4 m_objectCountsUpTo[(U32)ClusteredObjectType::kCount];
 #endif
 };
 constexpr U32 kSizeof_ClusteredShadingUniforms =
-	(6u + (U32)ClusterObjectType::kCount) * sizeof(Vec4) + 2u * sizeof(CommonMatrices) + sizeof(DirectionalLight);
+	(6u + (U32)ClusteredObjectType::kCount) * sizeof(Vec4) + 2u * sizeof(CommonMatrices) + sizeof(DirectionalLight);
 static_assert(sizeof(ClusteredShadingUniforms) == kSizeof_ClusteredShadingUniforms);
 
 // Define the type of some cluster object masks
@@ -309,4 +258,16 @@ constexpr U32 kSizeof_Cluster = 2u * sizeof(Vec4);
 static_assert(sizeof(Cluster) == kSizeof_Cluster);
 #endif
 
+constexpr ANKI_ARRAY(U32, ClusteredObjectType::kCount, kClusteredObjectSizes) = {
+	sizeof(PointLight),       sizeof(SpotLight),       sizeof(Decal),
+	sizeof(FogDensityVolume), sizeof(ReflectionProbe), sizeof(GlobalIlluminationProbe)};
+
+constexpr ANKI_ARRAY(U32, ClusteredObjectType::kCount, kMaxVisibleClusteredObjects) = {
+#if ANKI_CLUSTERED_SHADING_USE_64BIT
+	64, 64, 64,
+#else
+	32, 32, 32,
+#endif
+	16, 16, 16};
+
 ANKI_END_NAMESPACE

+ 4 - 2
AnKi/Shaders/Include/Common.h

@@ -11,6 +11,7 @@
 #if defined(__cplusplus)
 
 #	include <AnKi/Math.h>
+#	include <AnKi/Gr/Common.h>
 
 #	define ANKI_HLSL 0
 #	define ANKI_GLSL 0
@@ -20,7 +21,7 @@
 #	define ANKI_END_NAMESPACE }
 #	define ANKI_SHADER_FUNC_INLINE inline
 
-#	define ANKI_SHADER_STATIC_ASSERT(cond_) static_assert(cond_)
+#	define ANKI_ARRAY(type, size, name) Array<type, U32(size)> name
 
 ANKI_BEGIN_NAMESPACE
 using Address = U64;
@@ -47,7 +48,8 @@ ANKI_END_NAMESPACE
 #	define ANKI_END_NAMESPACE
 #	define ANKI_SHADER_FUNC_INLINE
 
-#	define ANKI_SHADER_STATIC_ASSERT(cond_)
+#	define ANKI_ARRAY(type, size, name) type name[(U32)size]
+
 #	define ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(enum_)
 
 #	define constexpr static const

+ 5 - 10
AnKi/Shaders/Include/GpuSceneTypes.h

@@ -48,13 +48,10 @@ struct GpuScenePointLight
 	RVec3 m_diffuseColor;
 	RF32 m_squareRadiusOverOne; ///< 1/(radius^2).
 
-	Vec2 m_padding0;
-	U32 m_shadowLayer; ///< Shadow layer used in RT shadows. Also used to show that it doesn't cast shadow.
-	F32 m_shadowAtlasTileScale; ///< UV scale for all tiles.
-
-	Vec4 m_shadowAtlasTileOffsets[6u]; ///< It's a array of Vec2 but because of padding round it up.
+	Vec3 m_padding;
+	U32 m_shadow;
 };
-constexpr U32 kSizeof_GpuScenePointLight = 9u * sizeof(Vec4);
+constexpr U32 kSizeof_GpuScenePointLight = 3u * sizeof(Vec4);
 static_assert(sizeof(GpuScenePointLight) == kSizeof_GpuScenePointLight);
 
 /// Spot light.
@@ -71,14 +68,12 @@ struct GpuSceneSpotLight
 	RVec3 m_direction; ///< Light direction.
 	RF32 m_squareRadiusOverOne; ///< 1/(radius^2).
 
-	U32 m_shadowLayer; ///< Shadow layer used in RT shadows. Also used to show that it doesn't cast shadow.
+	U32 m_shadow;
 	RF32 m_outerCos;
 	RF32 m_innerCos;
 	U32 m_padding1;
-
-	Mat4 m_textureMatrix;
 };
-constexpr U32 kSizeof_GpuSceneSpotLight = 12u * sizeof(Vec4);
+constexpr U32 kSizeof_GpuSceneSpotLight = 8u * sizeof(Vec4);
 static_assert(sizeof(GpuSceneSpotLight) == kSizeof_GpuSceneSpotLight);
 
 /// Representation of a reflection probe.

+ 1 - 1
AnKi/Shaders/Include/MaterialTypes.h

@@ -17,7 +17,7 @@ struct MaterialGlobalUniforms
 	Mat3x4 m_viewTransform;
 	Mat3x4 m_cameraTransform;
 };
-ANKI_SHADER_STATIC_ASSERT(sizeof(MaterialGlobalUniforms) == 14 * sizeof(Vec4));
+static_assert(sizeof(MaterialGlobalUniforms) == 14 * sizeof(Vec4));
 
 constexpr U32 kMaterialSetBindless = 0u;
 constexpr U32 kMaterialSetGlobal = 1u;

+ 19 - 0
AnKi/Shaders/Include/MiscRendererTypes.h

@@ -128,4 +128,23 @@ struct VolumetricLightingUniforms
 	F32 m_maxZSplitsToProcessf;
 };
 
+// Pack visible clusterer objects
+struct PointLightExtra
+{
+	Vec2 m_padding0;
+	U32 m_shadowLayer;
+	F32 m_shadowAtlasTileScale;
+
+	Vec4 m_shadowAtlasTileOffsets[6u];
+};
+
+// Pack visible clusterer objects
+struct SpotLightExtra
+{
+	Vec3 m_padding;
+	U32 m_shadowLayer;
+
+	Mat4 m_textureMatrix;
+};
+
 ANKI_END_NAMESPACE

+ 4 - 4
AnKi/Shaders/Include/ModelTypes.h

@@ -72,7 +72,7 @@ struct MainVertex
 
 constexpr U32 kSizeof_MainVertex = 4u * 4u;
 constexpr U32 kAlignof_MainVertex = 4u;
-ANKI_SHADER_STATIC_ASSERT(kSizeof_MainVertex == sizeof(MainVertex));
+static_assert(kSizeof_MainVertex == sizeof(MainVertex));
 
 /// The vertex that contains the bone influences.
 struct BoneInfoVertex
@@ -83,7 +83,7 @@ struct BoneInfoVertex
 
 constexpr U32 kSizeof_BoneInfoVertex = 8u;
 constexpr U32 kAlignof_BoneInfoVertex = 1u;
-ANKI_SHADER_STATIC_ASSERT(kSizeof_BoneInfoVertex == sizeof(BoneInfoVertex));
+static_assert(kSizeof_BoneInfoVertex == sizeof(BoneInfoVertex));
 
 /// A structure that contains all the info of a geometry.
 struct MeshGpuDescriptor
@@ -102,7 +102,7 @@ struct MeshGpuDescriptor
 
 constexpr U32 kSizeof_MeshGpuDescriptor = 4u * sizeof(UVec2) + 8u * sizeof(F32);
 constexpr U32 kAlignof_MeshGpuDescriptor = 8u;
-ANKI_SHADER_STATIC_ASSERT(kSizeof_MeshGpuDescriptor == sizeof(MeshGpuDescriptor));
+static_assert(kSizeof_MeshGpuDescriptor == sizeof(MeshGpuDescriptor));
 
 #if defined(__cplusplus)
 enum class TextureChannelId : U8
@@ -147,7 +147,7 @@ struct MaterialGpuDescriptor
 
 constexpr U32 kSizeof_MaterialGpuDescriptor = 8u * sizeof(U16) + 3u * sizeof(Vec3) + 2u * sizeof(F32);
 constexpr U32 kAlignof_MaterialGpuDescriptor = 4u;
-ANKI_SHADER_STATIC_ASSERT(kSizeof_MaterialGpuDescriptor == sizeof(MaterialGpuDescriptor));
+static_assert(kSizeof_MaterialGpuDescriptor == sizeof(MaterialGpuDescriptor));
 
 struct ModelGpuDescriptor
 {

+ 1 - 1
AnKi/Shaders/LightShading.ankiprog

@@ -64,7 +64,7 @@ RVec3 main(Vec4 svPosition : SV_POSITION, Vec2 uv : TEXCOORD) : SV_TARGET0
 	Cluster cluster = getClusterFragCoord(Vec3(svPosition.xy, depth), kTileSize, kTileCount, kZSplitCount,
 										  g_clusteredShading.m_zSplitMagic.x, g_clusteredShading.m_zSplitMagic.y);
 
-	// return clusterHeatmap(cluster, 1u << (U32)ClusterObjectType::kPointLight);
+	// return clusterHeatmap(cluster, 1u << (U32)ClusteredObjectType::kPointLight);
 
 	// Decode GBuffer
 	GbufferInfo gbuffer = (GbufferInfo)0;

+ 98 - 0
AnKi/Shaders/PackVisibleClusteredObjects.ankiprog

@@ -0,0 +1,98 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma anki mutator THREAD_GROUP_SIZE 16 32 64
+#pragma anki mutator OBJECT_TYPE 0 1 2 3 4 5
+
+#pragma anki start comp
+
+#include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
+#include <AnKi/Shaders/Include/MiscRendererTypes.h>
+
+#if OBJECT_TYPE == 0
+typedef PointLight ClusteredType;
+typedef GpuScenePointLight GpuSceneType;
+
+[[vk::binding(3)]] StructuredBuffer<PointLightExtra> g_extras;
+#elif OBJECT_TYPE == 1
+typedef SpotLight ClusteredType;
+typedef GpuSceneSpotLight GpuSceneType;
+
+[[vk::binding(3)]] StructuredBuffer<SpotLightExtra> g_extras;
+#elif OBJECT_TYPE == 2
+typedef Decal ClusteredType;
+typedef GpuSceneDecal GpuSceneType;
+#elif OBJECT_TYPE == 3
+typedef FogDensityVolume ClusteredType;
+typedef GpuSceneFogDensityVolume GpuSceneType;
+#elif OBJECT_TYPE == 4
+typedef ReflectionProbe ClusteredType;
+typedef GpuSceneReflectionProbe GpuSceneType;
+#elif OBJECT_TYPE == 5
+typedef GlobalIlluminationProbe ClusteredType;
+typedef GpuSceneGlobalIlluminationProbe GpuSceneType;
+#else
+#	error See file
+#endif
+
+[[vk::binding(0)]] StructuredBuffer<GpuSceneType> g_inBuffer;
+
+[[vk::binding(1)]] RWStructuredBuffer<ClusteredType> g_outBuffer;
+
+[[vk::binding(2)]] StructuredBuffer<U32> g_indices;
+
+struct Uniforms
+{
+	UVec3 m_padding;
+	U32 m_objectCount;
+};
+
+[[vk::push_constant]] ConstantBuffer<Uniforms> g_unis;
+
+[numthreads(THREAD_GROUP_SIZE, 1, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID)
+{
+	const U32 idxOut = svDispatchThreadId.x;
+	if(idxOut >= g_unis.m_objectCount)
+	{
+		return;
+	}
+
+#if OBJECT_TYPE == 0
+	const GpuScenePointLight input = g_inBuffer[g_indices[idxOut]];
+	const PointLightExtra extra = g_extras[idxOut];
+
+	PointLight output;
+	output.m_position = input.m_position;
+	output.m_radius = input.m_radius;
+	output.m_diffuseColor = input.m_diffuseColor;
+	output.m_squareRadiusOverOne = input.m_squareRadiusOverOne;
+	output.m_shadowLayer = (input.m_shadow) ? extra.m_shadowLayer : kMaxU32;
+	output.m_shadowAtlasTileScale = extra.m_shadowAtlasTileScale;
+	output.m_shadowAtlasTileOffsets = extra.m_shadowAtlasTileOffsets;
+
+	g_outBuffer[idxOut] = output;
+#elif OBJECT_TYPE == 1
+	const GpuSceneSpotLight input = g_inBuffer[g_indices[idxOut]];
+	const SpotLightExtra extra = g_extras[idxOut];
+
+	SpotLight output;
+	output.m_position = input.m_position;
+	output.m_edgePoints = input.m_edgePoints;
+	output.m_diffuseColor = input.m_diffuseColor;
+	output.m_radius = input.m_radius;
+	output.m_direction = input.m_direction;
+	output.m_squareRadiusOverOne = input.m_squareRadiusOverOne;
+	output.m_shadowLayer = (input.m_shadow) ? extra.m_shadowLayer : kMaxU32;
+	output.m_outerCos = input.m_outerCos;
+	output.m_innerCos = input.m_innerCos;
+	output.m_textureMatrix = extra.m_textureMatrix;
+
+	g_outBuffer[idxOut] = output;
+#else
+	g_outBuffer[idxOut] = g_inBuffer[g_indices[idxOut]];
+#endif
+}
+
+#pragma anki end

+ 1 - 1
AnKi/Util/Assert.cpp

@@ -43,7 +43,7 @@ void akassert(const char* exprTxt, const char* file, int line, const char* func)
 #	if ANKI_OS_WINDOWS
 	Array<Char, 512> msg;
 	snprintf(msg.getBegin(), msg.getSize(), "%s\n\n%s:%d %s", exprTxt, file, line, func);
-	MessageBoxA(nullptr, msg.getBegin(), "Assertion", MB_OK | MB_ICONWARNING);
+	MessageBoxA(nullptr, msg.getBegin(), "Assert failed", MB_OK | MB_ICONERROR);
 #	endif
 	ANKI_DEBUG_BREAK();
 }

+ 1 - 0
AnKi/Util/Win32Minimal.h

@@ -158,6 +158,7 @@ constexpr WORD BACKGROUND_RED = 0x0040;
 
 constexpr WORD MB_OK = 0x00000000L;
 constexpr WORD MB_ICONWARNING = 0x00000030L;
+constexpr WORD MB_ICONERROR = 0x00000010L;
 
 // Types
 typedef union _LARGE_INTEGER