Browse Source

Up the limits of visible objects to something way higher

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
c0405c8d5b

+ 17 - 13
AnKi/Shaders/ClusterBinning2.ankiprog

@@ -64,6 +64,7 @@ constexpr UVec2 kSampleLocations[kSampleCount] = {LOCATION(1, -3), LOCATION(-1,
 	const U32 tileIdx = dispatchThreadIdX / kSampleCount;
 	const U32 sampleIdx = dispatchThreadIdX % kSampleCount;
 	const U32 visibleObjectIdx = svDispatchThreadId.y;
+	ANKI_ASSERT(visibleObjectIdx < kMaxVisibleClusteredObjects2[OBJECT_TYPE]);
 
 	const UVec2 tileXY = UVec2(tileIdx % g_unis.m_tileCountX, tileIdx / g_unis.m_tileCountX);
 
@@ -149,25 +150,28 @@ constexpr UVec2 kSampleLocations[kSampleCount] = {LOCATION(1, -3), LOCATION(-1,
 	// Update the masks
 	if(collides)
 	{
+		const U32 mask = 1u << (visibleObjectIdx % 32);
+		const U32 maskArrayIdx = visibleObjectIdx / 32;
+		ANKI_MAYBE_UNUSED(maskArrayIdx);
+
 		// Set the tile
-		const ExtendedClusterObjectMask mask = ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(visibleObjectIdx);
 #if OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_LIGHT
 		if((U32)obj.m_flags & (U32)GpuSceneLightFlag::kPointLight)
 		{
-			InterlockedOr(g_clusters[tileIdx].m_pointLightsMask, mask);
+			InterlockedOr(g_clusters[tileIdx].m_pointLightsMask[maskArrayIdx], mask);
 		}
 		else
 		{
-			InterlockedOr(g_clusters[tileIdx].m_spotLightsMask, mask);
+			InterlockedOr(g_clusters[tileIdx].m_spotLightsMask[maskArrayIdx], mask);
 		}
 #elif OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_DECAL
-		InterlockedOr(g_clusters[tileIdx].m_decalsMask, mask);
+		InterlockedOr(g_clusters[tileIdx].m_decalsMask[maskArrayIdx], mask);
 #elif OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_FOG_DENSITY_VOLUME
-		InterlockedOr(g_clusters[tileIdx].m_fogDensityVolumesMask, U32(mask));
+		InterlockedOr(g_clusters[tileIdx].m_fogDensityVolumesMask, mask);
 #elif OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_REFLECTION_PROBE
-		InterlockedOr(g_clusters[tileIdx].m_reflectionProbesMask, U32(mask));
+		InterlockedOr(g_clusters[tileIdx].m_reflectionProbesMask, mask);
 #elif OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_GLOBAL_ILLUMINATION_PROBE
-		InterlockedOr(g_clusters[tileIdx].m_giProbesMask, U32(mask));
+		InterlockedOr(g_clusters[tileIdx].m_giProbesMask, mask);
 #else
 #	error See file
 #endif
@@ -198,20 +202,20 @@ constexpr UVec2 kSampleLocations[kSampleCount] = {LOCATION(1, -3), LOCATION(-1,
 #if OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_LIGHT
 			if((U32)obj.m_flags & (U32)GpuSceneLightFlag::kPointLight)
 			{
-				InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_pointLightsMask, mask);
+				InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_pointLightsMask[maskArrayIdx], mask);
 			}
 			else
 			{
-				InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_spotLightsMask, mask);
+				InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_spotLightsMask[maskArrayIdx], mask);
 			}
 #elif OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_DECAL
-			InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_decalsMask, mask);
+			InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_decalsMask[maskArrayIdx], mask);
 #elif OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_FOG_DENSITY_VOLUME
-			InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_fogDensityVolumesMask, U32(mask));
+			InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_fogDensityVolumesMask, mask);
 #elif OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_REFLECTION_PROBE
-			InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_reflectionProbesMask, U32(mask));
+			InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_reflectionProbesMask, mask);
 #elif OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_GLOBAL_ILLUMINATION_PROBE
-			InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_giProbesMask, U32(mask));
+			InterlockedOr(g_clusters[g_unis.m_tileCount + i].m_giProbesMask, mask);
 #else
 #	error See file
 #endif

+ 68 - 8
AnKi/Shaders/ClusteredShadingFunctions.hlsl

@@ -8,7 +8,7 @@
 #include <AnKi/Shaders/LightFunctions.hlsl>
 
 // Debugging function
-Vec3 clusterHeatmap(Cluster cluster, U32 objectTypeMask)
+Vec3 clusterHeatmap(Cluster cluster, U32 objectTypeMask, U32 maxObjectOverride = 0)
 {
 	U32 maxObjects = 0u;
 	I32 count = 0;
@@ -16,13 +16,19 @@ Vec3 clusterHeatmap(Cluster cluster, U32 objectTypeMask)
 	if((objectTypeMask & (1u << (U32)GpuSceneNonRenderableObjectType::kLight)) != 0u)
 	{
 		maxObjects += kMaxVisibleLights;
-		count += I32(countbits(cluster.m_pointLightsMask | cluster.m_spotLightsMask));
+		for(U32 i = 0; i < kMaxVisibleLights / 32; ++i)
+		{
+			count += I32(countbits(cluster.m_pointLightsMask[i] | cluster.m_spotLightsMask[i]));
+		}
 	}
 
 	if((objectTypeMask & (1u << (U32)GpuSceneNonRenderableObjectType::kDecal)) != 0u)
 	{
 		maxObjects += kMaxVisibleDecals;
-		count += I32(countbits(cluster.m_decalsMask));
+		for(U32 i = 0; i < kMaxVisibleDecals / 32; ++i)
+		{
+			count += I32(countbits(cluster.m_decalsMask[i]));
+		}
 	}
 
 	if((objectTypeMask & (1u << (U32)GpuSceneNonRenderableObjectType::kFogDensityVolume)) != 0u)
@@ -43,7 +49,7 @@ Vec3 clusterHeatmap(Cluster cluster, U32 objectTypeMask)
 		count += countbits(cluster.m_giProbesMask);
 	}
 
-	const F32 factor = min(1.0, F32(count) / F32(maxObjects));
+	const F32 factor = min(1.0, F32(count) / F32((maxObjectOverride > 0) ? maxObjectOverride : maxObjects));
 	return heatmap(factor);
 }
 
@@ -65,13 +71,22 @@ U32 computeTileClusterIndexFragCoord(Vec2 fragCoord, U32 tileCountX)
 /// 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) WaveActiveBitOr(x)
 #define ANKI_OR_MASKS(x) (x)
 
 	Cluster outCluster;
-	outCluster.m_pointLightsMask = ANKI_OR_MASKS(tileCluster.m_pointLightsMask & zCluster.m_pointLightsMask);
-	outCluster.m_spotLightsMask = ANKI_OR_MASKS(tileCluster.m_spotLightsMask & zCluster.m_spotLightsMask);
-	outCluster.m_decalsMask = ANKI_OR_MASKS(tileCluster.m_decalsMask & zCluster.m_decalsMask);
+
+	[unroll] for(U32 i = 0; i < kMaxVisibleLights / 32; ++i)
+	{
+		outCluster.m_pointLightsMask[i] = ANKI_OR_MASKS(tileCluster.m_pointLightsMask[i] & zCluster.m_pointLightsMask[i]);
+		outCluster.m_spotLightsMask[i] = ANKI_OR_MASKS(tileCluster.m_spotLightsMask[i] & zCluster.m_spotLightsMask[i]);
+	}
+
+	[unroll] for(U32 i = 0; i < kMaxVisibleDecals / 32; ++i)
+	{
+		outCluster.m_decalsMask[i] = ANKI_OR_MASKS(tileCluster.m_decalsMask[i] & zCluster.m_decalsMask[i]);
+	}
+
 	outCluster.m_fogDensityVolumesMask = ANKI_OR_MASKS(tileCluster.m_fogDensityVolumesMask & zCluster.m_fogDensityVolumesMask);
 	outCluster.m_reflectionProbesMask = ANKI_OR_MASKS(tileCluster.m_reflectionProbesMask & zCluster.m_reflectionProbesMask);
 	outCluster.m_giProbesMask = ANKI_OR_MASKS(tileCluster.m_giProbesMask & zCluster.m_giProbesMask);
@@ -93,3 +108,48 @@ Cluster getClusterFragCoord(StructuredBuffer<Cluster> clusters, ClusteredShading
 {
 	return getClusterFragCoord(clusters, fragCoord, unis.m_tileCounts, unis.m_zSplitCount, unis.m_zSplitMagic.x, unis.m_zSplitMagic.y);
 }
+
+U32 iteratePointLights(inout Cluster cluster)
+{
+	for(U32 block = 0; block < kMaxVisibleLights / 32; ++block)
+	{
+		if(cluster.m_pointLightsMask[block] != 0)
+		{
+			const U32 idx = (U32)firstbitlow2(cluster.m_pointLightsMask[block]);
+			cluster.m_pointLightsMask[block] ^= 1u << idx;
+			return idx + block * 32;
+		}
+	}
+
+	return kMaxU32;
+}
+
+U32 iterateSpotLights(inout Cluster cluster)
+{
+	for(U32 block = 0; block < kMaxVisibleLights / 32; ++block)
+	{
+		if(cluster.m_spotLightsMask[block] != 0)
+		{
+			const U32 idx = (U32)firstbitlow2(cluster.m_spotLightsMask[block]);
+			cluster.m_spotLightsMask[block] ^= 1u << idx;
+			return idx + block * 32;
+		}
+	}
+
+	return kMaxU32;
+}
+
+U32 iterateDecals(inout Cluster cluster)
+{
+	for(U32 block = 0; block < kMaxVisibleDecals / 32; ++block)
+	{
+		if(cluster.m_decalsMask[block] != 0)
+		{
+			const U32 idx = (U32)firstbitlow2(cluster.m_decalsMask[block]);
+			cluster.m_decalsMask[block] ^= 1u << idx;
+			return idx + block * 32;
+		}
+	}
+
+	return kMaxU32;
+}

+ 3 - 6
AnKi/Shaders/ForwardShadingCommon.hlsl

@@ -48,10 +48,9 @@ Vec3 computeLightColorHigh(Vec3 diffCol, Vec3 worldPos, Vec4 svPosition)
 	Cluster cluster = getClusterFragCoord(g_clusters, g_clusteredShading, svPosition.xyz);
 
 	// Point lights
-	[loop] while(cluster.m_pointLightsMask != 0)
+	U32 idx = 0;
+	[loop] while((idx = iteratePointLights(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(cluster.m_pointLightsMask);
-		cluster.m_pointLightsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const PointLight light = g_pointLights[idx];
 
 		const Vec3 diffC = diffCol * light.m_diffuseColor;
@@ -73,10 +72,8 @@ Vec3 computeLightColorHigh(Vec3 diffCol, Vec3 worldPos, Vec4 svPosition)
 	}
 
 	// Spot lights
-	[loop] while(cluster.m_spotLightsMask != 0)
+	[loop] while((idx = iterateSpotLights(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(cluster.m_spotLightsMask);
-		cluster.m_spotLightsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const SpotLight light = g_spotLights[idx];
 
 		const Vec3 diffC = diffCol * light.m_diffuseColor;

+ 11 - 7
AnKi/Shaders/GBufferPost.ankiprog

@@ -50,20 +50,24 @@ FragOut main([[vk::location(0)]] Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITI
 	const Vec3 worldPos = worldPos4.xyz / worldPos4.w;
 
 	// Get the cluster
-	const Cluster cluster = getClusterFragCoord(g_clusters, Vec3(svPosition.xy, depth), kTileCount, kZSplitCount, g_clusteredShading.m_zSplitMagic.x,
-												g_clusteredShading.m_zSplitMagic.y);
+	Cluster cluster = getClusterFragCoord(g_clusters, Vec3(svPosition.xy, depth), kTileCount, kZSplitCount, g_clusteredShading.m_zSplitMagic.x,
+										  g_clusteredShading.m_zSplitMagic.y);
 
 	// Make the decalsMask uniform across the wave because we are accessing bindless textures later on
-	ExtendedClusterObjectMask decalsMask = WaveActiveBitAnd(cluster.m_decalsMask);
-	if(decalsMask == 0)
+	U32 decalsMask = cluster.m_decalsMask[0];
+	for(U32 i = 1; i < kMaxVisibleDecals / 32; ++i)
+	{
+		decalsMask |= cluster.m_decalsMask[i];
+	}
+
+	if(WaveActiveAllTrue(decalsMask == 0))
 	{
 		discard;
 	}
 
-	[loop] while(decalsMask != 0)
+	U32 idx;
+	[loop] while((idx = iterateDecals(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(decalsMask);
-		decalsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const Decal decal = g_decals[idx];
 
 		// Project pos to decal space

+ 5 - 45
AnKi/Shaders/Include/ClusteredShadingTypes.h

@@ -7,18 +7,11 @@
 
 #include <AnKi/Shaders/Include/GpuSceneTypes.h>
 
-#define ANKI_CLUSTERED_SHADING_USE_64BIT ANKI_SUPPORTS_64BIT_TYPES
-
 ANKI_BEGIN_NAMESPACE
 
 // Limits
-#if ANKI_CLUSTERED_SHADING_USE_64BIT
-constexpr U32 kMaxVisibleLights = 64u;
-constexpr U32 kMaxVisibleDecals = 64u;
-#else
-constexpr U32 kMaxVisibleLights = 32u;
-constexpr U32 kMaxVisibleDecals = 32u;
-#endif
+constexpr U32 kMaxVisibleLights = 32u * 4u;
+constexpr U32 kMaxVisibleDecals = 32u * 4u;
 constexpr U32 kMaxVisibleFogDensityVolumes = 16u;
 constexpr U32 kMaxVisibleReflectionProbes = 16u;
 constexpr U32 kMaxVisibleGlobalIlluminationProbes = 8u;
@@ -189,50 +182,17 @@ struct ClusteredShadingUniforms
 	CommonMatrices m_previousMatrices;
 };
 
-// Define the type of some cluster object masks
-#if ANKI_GLSL
-#	if ANKI_CLUSTERED_SHADING_USE_64BIT
-#		define ExtendedClusterObjectMask U64
-#	else
-#		define ExtendedClusterObjectMask U32
-#	endif
-#else
-#	if ANKI_CLUSTERED_SHADING_USE_64BIT
-typedef U64 ExtendedClusterObjectMask;
-#	else
-typedef U32 ExtendedClusterObjectMask;
-#	endif
-#endif
-
 /// Information that a tile or a Z-split will contain.
 struct Cluster
 {
-	ExtendedClusterObjectMask m_pointLightsMask;
-	ExtendedClusterObjectMask m_spotLightsMask;
-	ExtendedClusterObjectMask m_decalsMask;
+	U32 m_pointLightsMask[kMaxVisibleLights / 32];
+	U32 m_spotLightsMask[kMaxVisibleLights / 32];
+	U32 m_decalsMask[kMaxVisibleDecals / 32];
 	U32 m_fogDensityVolumesMask;
 	U32 m_reflectionProbesMask;
 	U32 m_giProbesMask;
-
-	// Pad to 16byte
-#if ANKI_CLUSTERED_SHADING_USE_64BIT
-	U32 m_padding0;
-	U32 m_padding1;
-	U32 m_padding2;
-#else
-	U32 m_padding0;
-	U32 m_padding1;
-#endif
 };
 
-#if ANKI_CLUSTERED_SHADING_USE_64BIT
-constexpr U32 kSizeof_Cluster = 3u * sizeof(Vec4);
-static_assert(sizeof(Cluster) == kSizeof_Cluster);
-#else
-constexpr U32 kSizeof_Cluster = 2u * sizeof(Vec4);
-static_assert(sizeof(Cluster) == kSizeof_Cluster);
-#endif
-
 constexpr ANKI_ARRAY(U32, GpuSceneNonRenderableObjectType::kCount, kClusteredObjectSizes2) = {
 	sizeof(LightUnion), sizeof(Decal), sizeof(FogDensityVolume), sizeof(ReflectionProbe), sizeof(GlobalIlluminationProbe)};
 

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

@@ -58,6 +58,8 @@ ANKI_END_NAMESPACE
 
 #	define constexpr static const
 
+#	define ANKI_ASSERT(x)
+
 template<typename T>
 void maybeUnused(T a)
 {

+ 4 - 7
AnKi/Shaders/LightShading.ankiprog

@@ -58,7 +58,7 @@ RVec3 main(Vec4 svPosition : SV_POSITION, Vec2 uv : TEXCOORD) : SV_TARGET0
 	Cluster cluster = getClusterFragCoord(g_clusters, Vec3(svPosition.xy, depth), kTileCount, kZSplitCount, g_clusteredShading.m_zSplitMagic.x,
 										  g_clusteredShading.m_zSplitMagic.y);
 
-	// return clusterHeatmap(cluster, 1u << (U32)ClusteredObjectType::kPointLight);
+	// return clusterHeatmap(cluster, 1u << (U32)GpuSceneNonRenderableObjectType::kLight, 3);
 
 	// Decode GBuffer
 	GbufferInfo gbuffer = (GbufferInfo)0;
@@ -101,10 +101,9 @@ RVec3 main(Vec4 svPosition : SV_POSITION, Vec2 uv : TEXCOORD) : SV_TARGET0
 	}
 
 	// Point lights
-	[loop] while(cluster.m_pointLightsMask != 0)
+	U32 idx;
+	[loop] while((idx = iteratePointLights(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(cluster.m_pointLightsMask);
-		cluster.m_pointLightsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const PointLight light = g_pointLights[idx];
 
 		LIGHTING_COMMON_BRDF();
@@ -119,10 +118,8 @@ RVec3 main(Vec4 svPosition : SV_POSITION, Vec2 uv : TEXCOORD) : SV_TARGET0
 	}
 
 	// Spot lights
-	[loop] while(cluster.m_spotLightsMask != 0)
+	[loop] while((idx = iterateSpotLights(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(cluster.m_spotLightsMask);
-		cluster.m_spotLightsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const SpotLight light = g_spotLights[idx];
 
 		LIGHTING_COMMON_BRDF();

+ 1 - 1
AnKi/Shaders/RtShadowsRayGen.ankiprog

@@ -18,7 +18,7 @@
 #define SET 2u
 
 [[vk::binding(0, SET)]] ConstantBuffer<ClusteredShadingUniforms> g_clusteredShading;
-[[vk::binding(1, SET)]] StructuredBuffer<Cluster> g_clusters;
+[[vk::binding(1, SET)]] StructuredBuffer<Cluster> g_clusters; // TODO: rm
 
 [[vk::binding(2, SET)]] SamplerState g_trilinearRepeatSampler;
 

+ 3 - 6
AnKi/Shaders/ShadowmapsResolve.hlsl

@@ -167,10 +167,9 @@ RVec4 main(Vec2 uv : TEXCOORD) : SV_TARGET0
 #endif // DIRECTIONAL_LIGHT_SHADOW_RESOLVED
 
 	// Point lights
-	[loop] while(cluster.m_pointLightsMask != ExtendedClusterObjectMask(0))
+	U32 idx = 0;
+	[loop] while((idx = iteratePointLights(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(cluster.m_pointLightsMask);
-		cluster.m_pointLightsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const PointLight light = g_pointLights[idx];
 
 		[branch] if(light.m_shadowAtlasTileScale >= 0.0)
@@ -188,10 +187,8 @@ RVec4 main(Vec2 uv : TEXCOORD) : SV_TARGET0
 	}
 
 	// Spot lights
-	[loop] while(cluster.m_spotLightsMask != ExtendedClusterObjectMask(0))
+	[loop] while((idx = iterateSpotLights(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(cluster.m_spotLightsMask);
-		cluster.m_spotLightsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const SpotLight light = g_spotLights[idx];
 
 		[branch] if(light.m_shadow)

+ 4 - 10
AnKi/Shaders/VolumetricLightingAccumulation.ankiprog

@@ -115,10 +115,9 @@ Vec4 accumulateLightsAndFog(Cluster cluster, Vec3 worldPos, F32 negativeZViewSpa
 	}
 
 	// Point lights
-	[loop] while(cluster.m_pointLightsMask != ExtendedClusterObjectMask(0))
+	U32 idx = 0;
+	[loop] while((idx = iteratePointLights(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(cluster.m_pointLightsMask);
-		cluster.m_pointLightsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const PointLight light = g_pointLights[idx];
 
 		const Vec3 frag2Light = light.m_position - worldPos;
@@ -137,10 +136,8 @@ Vec4 accumulateLightsAndFog(Cluster cluster, Vec3 worldPos, F32 negativeZViewSpa
 	}
 
 	// Spot lights
-	[loop] while(cluster.m_spotLightsMask != ExtendedClusterObjectMask(0))
+	[loop] while((idx = iterateSpotLights(cluster)) != kMaxU32)
 	{
-		const I32 idx = firstbitlow2(cluster.m_spotLightsMask);
-		cluster.m_spotLightsMask &= ~(ExtendedClusterObjectMask(1) << ExtendedClusterObjectMask(idx));
 		const SpotLight light = g_spotLights[idx];
 
 		const Vec3 frag2Light = light.m_position - worldPos;
@@ -265,10 +262,7 @@ Vec4 accumulateLightsAndFog(Cluster cluster, Vec3 worldPos, F32 negativeZViewSpa
 	const U32 zSplitIdx = U32(uvw.z * F32(g_clusteredShading.m_zSplitCount));
 	const Cluster split = g_clusters[g_clusteredShading.m_tileCounts.x * g_clusteredShading.m_tileCounts.y + zSplitIdx];
 
-	cluster.m_pointLightsMask |= split.m_pointLightsMask;
-	cluster.m_spotLightsMask |= split.m_spotLightsMask;
-	cluster.m_giProbesMask |= split.m_giProbesMask;
-	cluster.m_fogDensityVolumesMask |= split.m_fogDensityVolumesMask;
+	cluster = mergeClusters(cluster, split);
 
 	// Get lighting
 	Vec4 lightAndFog = accumulateLightsAndFog(cluster, worldPos, negativeZViewSpace);