Browse Source

Add the ability to generate a hash for all visible objects

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
3ecdd26b63

+ 1 - 1
AnKi/Renderer/ForwardShading.cpp

@@ -97,7 +97,7 @@ void ForwardShading::setDependencies(GraphicsRenderPassDescription& pass)
 		pass.newBufferDependency(getRenderer().getLensFlare().getIndirectDrawBuffer(), BufferUsageBit::kIndirectDraw);
 	}
 
-	pass.newBufferDependency(m_runCtx.m_visOut.m_mdiDrawCountsHandle, BufferUsageBit::kIndirectDraw);
+	pass.newBufferDependency(m_runCtx.m_visOut.m_someBufferHandle, BufferUsageBit::kIndirectDraw);
 }
 
 } // end namespace anki

+ 1 - 1
AnKi/Renderer/GBuffer.cpp

@@ -240,7 +240,7 @@ void GBuffer::populateRenderGraph(RenderingContext& ctx)
 	pass.newBufferDependency(getRenderer().getGpuSceneBufferHandle(), BufferUsageBit::kStorageGeometryRead | BufferUsageBit::kStorageFragmentRead);
 
 	// Only add one depedency to the GPU visibility. No need to track all buffers
-	pass.newBufferDependency(visOut.m_mdiDrawCountsHandle, BufferUsageBit::kIndirectDraw);
+	pass.newBufferDependency(visOut.m_someBufferHandle, BufferUsageBit::kIndirectDraw);
 
 	// HZB generation for the next frame
 	getRenderer().getHzbGenerator().populateRenderGraph(m_runCtx.m_crntFrameDepthRt, getRenderer().getInternalResolution(), m_runCtx.m_hzbRt,

+ 2 - 2
AnKi/Renderer/IndirectDiffuseProbes.cpp

@@ -271,7 +271,7 @@ void IndirectDiffuseProbes::populateRenderGraph(RenderingContext& rctx)
 
 			for(U32 i = 0; i < 6; ++i)
 			{
-				pass.newBufferDependency(visOuts[i].m_mdiDrawCountsHandle, BufferUsageBit::kIndirectDraw);
+				pass.newBufferDependency(visOuts[i].m_someBufferHandle, BufferUsageBit::kIndirectDraw);
 			}
 
 			pass.setWork(6, [this, visOuts, viewProjMats, viewMats](RenderPassWorkContext& rgraphCtx) {
@@ -334,7 +334,7 @@ void IndirectDiffuseProbes::populateRenderGraph(RenderingContext& rctx)
 			pass.newTextureDependency(shadowsRt, TextureUsageBit::kAllFramebuffer, TextureSubresourceInfo(DepthStencilAspectBit::kDepth));
 			for(U32 i = 0; i < 6; ++i)
 			{
-				pass.newBufferDependency(shadowVisOuts[i].m_mdiDrawCountsHandle, BufferUsageBit::kIndirectDraw);
+				pass.newBufferDependency(shadowVisOuts[i].m_someBufferHandle, BufferUsageBit::kIndirectDraw);
 			}
 
 			pass.setWork(6, [this, shadowVisOuts, cascadeViewProjMats, cascadeViewMats](RenderPassWorkContext& rgraphCtx) {

+ 2 - 2
AnKi/Renderer/ProbeReflections.cpp

@@ -407,7 +407,7 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 
 		for(U32 i = 0; i < 6; ++i)
 		{
-			pass.newBufferDependency(visOuts[i].m_mdiDrawCountsHandle, BufferUsageBit::kIndirectDraw);
+			pass.newBufferDependency(visOuts[i].m_someBufferHandle, BufferUsageBit::kIndirectDraw);
 		}
 	}
 
@@ -458,7 +458,7 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 
 		for(U32 i = 0; i < 6; ++i)
 		{
-			pass.newBufferDependency(shadowVisOuts[i].m_mdiDrawCountsHandle, BufferUsageBit::kIndirectDraw);
+			pass.newBufferDependency(shadowVisOuts[i].m_someBufferHandle, BufferUsageBit::kIndirectDraw);
 		}
 	}
 	else

+ 14 - 0
AnKi/Renderer/RendererObject.cpp

@@ -69,4 +69,18 @@ Error RendererObject::loadShaderProgram(CString filename, ConstWeakArray<SubMuta
 	return Error::kNone;
 }
 
+void RendererObject::zeroBuffer(Buffer* buff)
+{
+	CommandBufferInitInfo cmdbInit("Zero buffer");
+	cmdbInit.m_flags |= CommandBufferFlag::kSmallBatch;
+	CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(cmdbInit);
+
+	cmdb->fillBuffer(buff, 0, kMaxPtrSize, 0);
+
+	FencePtr fence;
+	cmdb->flush({}, &fence);
+
+	fence->clientWait(16.0_sec);
+}
+
 } // end namespace anki

+ 2 - 0
AnKi/Renderer/RendererObject.h

@@ -95,6 +95,8 @@ protected:
 	};
 
 	static Error loadShaderProgram(CString filename, ConstWeakArray<SubMutation> mutators, ShaderProgramResourcePtr& rsrc, ShaderProgramPtr& grProg);
+
+	static void zeroBuffer(Buffer* buff);
 };
 /// @}
 

+ 1 - 1
AnKi/Renderer/ShadowMapping.cpp

@@ -188,7 +188,7 @@ void ShadowMapping::populateRenderGraph(RenderingContext& ctx)
 
 		for(const ViewportWorkItem& work : m_runCtx.m_workItems)
 		{
-			pass.newBufferDependency(work.m_visOut.m_mdiDrawCountsHandle, BufferUsageBit::kIndirectDraw);
+			pass.newBufferDependency(work.m_visOut.m_someBufferHandle, BufferUsageBit::kIndirectDraw);
 		}
 
 		TextureSubresourceInfo subresource = TextureSubresourceInfo(DepthStencilAspectBit::kDepth);

+ 71 - 31
AnKi/Renderer/Utils/GpuVisibility.cpp

@@ -22,17 +22,25 @@ Error GpuVisibility::init()
 	{
 		for(MutatorValue gatherAabbs = 0; gatherAabbs < 2; ++gatherAabbs)
 		{
-			ANKI_CHECK(loadShaderProgram("ShaderBinaries/GpuVisibility.ankiprogbin",
-										 Array<SubMutation, 3>{{{"HZB_TEST", hzb}, {"DISTANCE_TEST", 0}, {"GATHER_AABBS", gatherAabbs}}}, m_prog,
-										 m_frustumGrProgs[hzb][gatherAabbs]));
+			for(MutatorValue genHash = 0; genHash < 2; ++genHash)
+			{
+				ANKI_CHECK(loadShaderProgram(
+					"ShaderBinaries/GpuVisibility.ankiprogbin",
+					Array<SubMutation, 4>{{{"HZB_TEST", hzb}, {"DISTANCE_TEST", 0}, {"GATHER_AABBS", gatherAabbs}, {"HASH_VISIBLES", genHash}}},
+					m_prog, m_frustumGrProgs[hzb][gatherAabbs][genHash]));
+			}
 		}
 	}
 
 	for(MutatorValue gatherAabbs = 0; gatherAabbs < 2; ++gatherAabbs)
 	{
-		ANKI_CHECK(loadShaderProgram("ShaderBinaries/GpuVisibility.ankiprogbin",
-									 Array<SubMutation, 3>{{{"HZB_TEST", 0}, {"DISTANCE_TEST", 1}, {"GATHER_AABBS", gatherAabbs}}}, m_prog,
-									 m_distGrProgs[gatherAabbs]));
+		for(MutatorValue genHash = 0; genHash < 2; ++genHash)
+		{
+			ANKI_CHECK(loadShaderProgram(
+				"ShaderBinaries/GpuVisibility.ankiprogbin",
+				Array<SubMutation, 4>{{{"HZB_TEST", 0}, {"DISTANCE_TEST", 1}, {"GATHER_AABBS", gatherAabbs}, {"HASH_VISIBLES", genHash}}}, m_prog,
+				m_distGrProgs[gatherAabbs][genHash]));
+		}
 	}
 
 	return Error::kNone;
@@ -99,7 +107,7 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 		U32* atomics;
 		out.m_mdiDrawCountsBuffer = RebarTransientMemoryPool::getSingleton().allocateFrame<U32>(1, atomics);
 		atomics[0] = 0;
-		out.m_mdiDrawCountsHandle = in.m_rgraph->importBuffer(BufferUsageBit::kNone, out.m_mdiDrawCountsBuffer);
+		out.m_someBufferHandle = in.m_rgraph->importBuffer(BufferUsageBit::kNone, out.m_mdiDrawCountsBuffer);
 
 		if(in.m_gatherAabbIndices)
 		{
@@ -114,36 +122,61 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 	const U32 bucketCount = RenderStateBucketContainer::getSingleton().getBucketCount(in.m_technique);
 
 	// Allocate memory for the indirect commands
-	const GpuVisibleTransientMemoryAllocation indirectArgs =
-		GpuVisibleTransientMemoryPool::getSingleton().allocate(aabbCount * sizeof(DrawIndexedIndirectArgs));
-	out.m_drawIndexedIndirectArgsBuffer = indirectArgs;
+	out.m_drawIndexedIndirectArgsBuffer = GpuVisibleTransientMemoryPool::getSingleton().allocate(aabbCount * sizeof(DrawIndexedIndirectArgs));
+	out.m_instanceRateRenderablesBuffer = GpuVisibleTransientMemoryPool::getSingleton().allocate(aabbCount * sizeof(GpuSceneRenderableVertex));
 
-	const GpuVisibleTransientMemoryAllocation instanceRateRenderables =
-		GpuVisibleTransientMemoryPool::getSingleton().allocate(aabbCount * sizeof(GpuSceneRenderable));
-	out.m_instanceRateRenderablesBuffer = instanceRateRenderables;
+	// Allocate memory for AABB indices
+	if(in.m_gatherAabbIndices)
+	{
+		out.m_visibleAaabbIndicesBuffer = GpuVisibleTransientMemoryPool::getSingleton().allocate((aabbCount + 1) * sizeof(U32));
+	}
+
+	// Allocate memory for counters
+	PtrSize counterMemory = 0;
+	if(in.m_hashVisibles)
+	{
+		counterMemory += sizeof(GpuVisibilityHash);
+		alignRoundUp(GrManager::getSingleton().getDeviceCapabilities().m_storageBufferBindOffsetAlignment, counterMemory);
+	}
 
-	// Allocate and zero the MDI counts
-	U32* atomics;
-	RebarAllocation mdiDrawCounts = RebarTransientMemoryPool::getSingleton().allocateFrame(bucketCount, atomics);
-	memset(atomics, 0, mdiDrawCounts.getRange());
-	out.m_mdiDrawCountsBuffer = mdiDrawCounts;
+	const PtrSize mdiBufferOffset = counterMemory;
+	const PtrSize mdiBufferSize = sizeof(U32) * bucketCount;
+	counterMemory += mdiBufferSize;
+	alignRoundUp(GrManager::getSingleton().getDeviceCapabilities().m_storageBufferBindOffsetAlignment, counterMemory);
 
-	// Import buffers
-	out.m_mdiDrawCountsHandle = in.m_rgraph->importBuffer(BufferUsageBit::kNone, mdiDrawCounts);
+	const BufferOffsetRange counterBuffer = GpuVisibleTransientMemoryPool::getSingleton().allocate(counterMemory);
+	const BufferHandle counterBufferHandle = in.m_rgraph->importBuffer(BufferUsageBit::kNone, counterBuffer);
+	out.m_someBufferHandle = counterBufferHandle;
 
-	// Allocate memory for AABB indices
-	if(in.m_gatherAabbIndices)
+	BufferOffsetRange hashBuffer;
+	if(in.m_hashVisibles)
 	{
-		U32* atomic;
-		out.m_visibleAaabbIndicesBuffer = RebarTransientMemoryPool::getSingleton().allocateFrame(aabbCount + 1, atomic);
-		atomic[0] = 0;
+		hashBuffer = {counterBuffer.m_buffer, 0, sizeof(GpuVisibilityHash)};
 	}
 
+	// Zero some stuff
+	{
+		ComputeRenderPassDescription& pass = in.m_rgraph->newComputeRenderPass("GPU visibility: Zero stuff");
+		pass.newBufferDependency(counterBufferHandle, BufferUsageBit::kTransferDestination);
+
+		pass.setWork([counterBuffer, visibleAaabbIndicesBuffer = out.m_visibleAaabbIndicesBuffer](RenderPassWorkContext& rpass) {
+			rpass.m_commandBuffer->fillBuffer(counterBuffer.m_buffer, counterBuffer.m_offset, counterBuffer.m_range, 0);
+
+			if(visibleAaabbIndicesBuffer.m_buffer)
+			{
+				rpass.m_commandBuffer->fillBuffer(visibleAaabbIndicesBuffer.m_buffer, visibleAaabbIndicesBuffer.m_offset, sizeof(U32), 0);
+			}
+		});
+	}
+
+	// Set the MDI count buffer
+	out.m_mdiDrawCountsBuffer = {counterBuffer.m_buffer, counterBuffer.m_offset + mdiBufferOffset, mdiBufferSize};
+
 	// Create the renderpass
 	ComputeRenderPassDescription& pass = in.m_rgraph->newComputeRenderPass(in.m_passesName);
 
 	pass.newBufferDependency(getRenderer().getGpuSceneBufferHandle(), BufferUsageBit::kStorageComputeRead);
-	pass.newBufferDependency(out.m_mdiDrawCountsHandle, BufferUsageBit::kStorageComputeWrite);
+	pass.newBufferDependency(counterBufferHandle, BufferUsageBit::kStorageComputeWrite);
 
 	if(!distanceBased && static_cast<FrustumGpuVisibilityInput&>(in).m_hzbRt)
 	{
@@ -152,19 +185,21 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 	}
 
 	pass.setWork([this, frustumTestData, distTestData, lodReferencePoint = in.m_lodReferencePoint, lodDistances = in.m_lodDistances,
-				  technique = in.m_technique, mdiDrawCountsHandle = out.m_mdiDrawCountsHandle, instanceRateRenderables, indirectArgs, aabbCount,
-				  visibleAabbsBuffer = out.m_visibleAaabbIndicesBuffer](RenderPassWorkContext& rpass) {
+				  technique = in.m_technique, mdiDrawCountsBuffer = out.m_mdiDrawCountsBuffer,
+				  instanceRateRenderables = out.m_instanceRateRenderablesBuffer, indirectArgs = out.m_drawIndexedIndirectArgsBuffer, aabbCount,
+				  visibleAabbsBuffer = out.m_visibleAaabbIndicesBuffer, hashBuffer](RenderPassWorkContext& rpass) {
 		CommandBuffer& cmdb = *rpass.m_commandBuffer;
 
 		const Bool gatherAabbIndices = visibleAabbsBuffer.m_buffer != nullptr;
+		const Bool genHash = hashBuffer.m_buffer != nullptr;
 
 		if(frustumTestData)
 		{
-			cmdb.bindShaderProgram(m_frustumGrProgs[frustumTestData->m_hzbRt.isValid()][gatherAabbIndices].get());
+			cmdb.bindShaderProgram(m_frustumGrProgs[frustumTestData->m_hzbRt.isValid()][gatherAabbIndices][genHash].get());
 		}
 		else
 		{
-			cmdb.bindShaderProgram(m_distGrProgs[gatherAabbIndices].get());
+			cmdb.bindShaderProgram(m_distGrProgs[gatherAabbIndices][genHash].get());
 		}
 
 		BufferOffsetRange aabbsBuffer;
@@ -199,7 +234,7 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 		});
 		ANKI_ASSERT(userCount == RenderStateBucketContainer::getSingleton().getBucketsItemCount(technique));
 
-		rpass.bindStorageBuffer(0, 6, mdiDrawCountsHandle);
+		cmdb.bindStorageBuffer(0, 6, mdiDrawCountsBuffer);
 
 		if(frustumTestData)
 		{
@@ -248,6 +283,11 @@ void GpuVisibility::populateRenderGraphInternal(Bool distanceBased, BaseGpuVisib
 			cmdb.bindStorageBuffer(0, 12, visibleAabbsBuffer);
 		}
 
+		if(genHash)
+		{
+			cmdb.bindStorageBuffer(0, 13, hashBuffer);
+		}
+
 		dispatchPPCompute(cmdb, 64, 1, aabbCount, 1);
 	});
 }

+ 11 - 6
AnKi/Renderer/Utils/GpuVisibility.h

@@ -26,6 +26,7 @@ public:
 	RenderGraphDescription* m_rgraph = nullptr;
 
 	Bool m_gatherAabbIndices = false; ///< For debug draw.
+	Bool m_hashVisibles = false; ///< Create a hash for the visible renderables.
 };
 
 /// @memberof GpuVisibility
@@ -48,13 +49,15 @@ public:
 class GpuVisibilityOutput
 {
 public:
-	BufferHandle m_mdiDrawCountsHandle; ///< Just expose one handle for depedencies. No need to track all other buffers.
+	BufferHandle m_someBufferHandle; ///< Just expose one handle for depedencies. No need to track all buffers.
 
-	BufferOffsetRange m_instanceRateRenderablesBuffer;
-	BufferOffsetRange m_drawIndexedIndirectArgsBuffer;
-	BufferOffsetRange m_mdiDrawCountsBuffer;
+	BufferOffsetRange m_instanceRateRenderablesBuffer; ///< An array of GpuSceneRenderableVertex.
+	BufferOffsetRange m_drawIndexedIndirectArgsBuffer; ///< An array of DrawIndexedIndirectArgs.
+	BufferOffsetRange m_mdiDrawCountsBuffer; ///< An array of U32, one for each render state bucket.
 
 	BufferOffsetRange m_visibleAaabbIndicesBuffer; ///< Optional.
+
+	BufferOffsetRange m_visiblesHashBuffer; ///< Optional.
 };
 
 /// Performs GPU visibility for some pass.
@@ -64,6 +67,7 @@ public:
 	Error init();
 
 	/// Perform frustum visibility testing.
+	/// @note Not thread-safe.
 	void populateRenderGraph(FrustumGpuVisibilityInput& in, GpuVisibilityOutput& out)
 	{
 		ANKI_ASSERT(in.m_viewProjectionMatrix != Mat4::getZero());
@@ -71,6 +75,7 @@ public:
 	}
 
 	/// Perform simple distance-based visibility testing.
+	/// @note Not thread-safe.
 	void populateRenderGraph(DistanceGpuVisibilityInput& in, GpuVisibilityOutput& out)
 	{
 		populateRenderGraphInternal(true, in, out);
@@ -78,8 +83,8 @@ public:
 
 private:
 	ShaderProgramResourcePtr m_prog;
-	Array2d<ShaderProgramPtr, 2, 2> m_frustumGrProgs;
-	Array<ShaderProgramPtr, 2> m_distGrProgs;
+	Array3d<ShaderProgramPtr, 2, 2, 2> m_frustumGrProgs;
+	Array2d<ShaderProgramPtr, 2, 2> m_distGrProgs;
 
 	void populateRenderGraphInternal(Bool distanceBased, BaseGpuVisibilityInput& in, GpuVisibilityOutput& out);
 };

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

@@ -201,6 +201,7 @@ Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 				const MaterialVariant& variant = mtl.getOrCreateVariant(key);
 				gpuRenderable.m_rtShadowsShaderHandleIndex = variant.getRtShaderGroupHandleIndex();
 			}
+			gpuRenderable.m_uuid = SceneGraph::getSingleton().getNewUuid();
 			m_patchInfos[i].m_gpuSceneRenderable.uploadToGpuScene(gpuRenderable);
 		}
 

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

@@ -403,6 +403,7 @@ Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 		renderable.m_meshLodsOffset = m_gpuSceneMeshLods.getGpuSceneOffset();
 		renderable.m_particleEmitterOffset = m_gpuSceneParticleEmitter.getGpuSceneOffset();
 		renderable.m_worldTransformsOffset = 0;
+		renderable.m_uuid = SceneGraph::getSingleton().getNewUuid();
 		if(!m_gpuSceneRenderable.isValid())
 		{
 			m_gpuSceneRenderable.allocate();

+ 37 - 10
AnKi/Shaders/GpuVisibility.ankiprog

@@ -6,6 +6,7 @@
 #pragma anki mutator HZB_TEST 0 1
 #pragma anki mutator DISTANCE_TEST 0 1
 #pragma anki mutator GATHER_AABBS 0 1
+#pragma anki mutator HASH_VISIBLES 0 1
 
 #pragma anki skip_mutation DISTANCE_TEST 1 HZB_TEST 1
 
@@ -55,6 +56,10 @@ struct DrawIndirectArgsWithPadding
 [[vk::binding(12)]] RWStructuredBuffer<U32> g_visibleAabbIndices; ///< Indices of the visible AABBs. The 1st element is the count.
 #endif
 
+#if HASH_VISIBLES
+[[vk::binding(13)]] RWStructuredBuffer<GpuVisibilityHash> g_hash;
+#endif
+
 [numthreads(64, 1, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID)
 {
 	const U32 aabbIdx = svDispatchThreadId.x;
@@ -178,11 +183,11 @@ struct DrawIndirectArgsWithPadding
 	InterlockedAdd(g_mdiDrawCounts[renderStateBucket], 1, indirectIdx);
 	indirectIdx += g_drawIndirectArgsOffsets[renderStateBucket];
 
-	const GpuSceneRenderable renderableIn = g_renderables[renderableIdx];
-	const U32 meshLodOffset = renderableIn.m_meshLodsOffset + sizeof(GpuSceneMeshLod) * lod;
+	const GpuSceneRenderable renderable = g_renderables[renderableIdx];
+	const U32 meshLodOffset = renderable.m_meshLodsOffset + sizeof(GpuSceneMeshLod) * lod;
 	const GpuSceneMeshLod meshLod = g_gpuScene.Load<GpuSceneMeshLod>(meshLodOffset);
 
-	const Bool isParticleEmitter = renderableIn.m_particleEmitterOffset != 0;
+	const Bool isParticleEmitter = renderable.m_particleEmitterOffset != 0;
 
 	if(!isParticleEmitter)
 	{
@@ -197,15 +202,15 @@ struct DrawIndirectArgsWithPadding
 		g_drawIndexedIndirectArgs[indirectIdx] = indirect;
 
 		UVec4 instanceVertex;
-		instanceVertex.x = renderableIn.m_worldTransformsOffset;
-		instanceVertex.y = renderableIn.m_uniformsOffset;
+		instanceVertex.x = renderable.m_worldTransformsOffset;
+		instanceVertex.y = renderable.m_uniformsOffset;
 		instanceVertex.z = meshLodOffset;
-		instanceVertex.w = renderableIn.m_boneTransformsOffset;
+		instanceVertex.w = renderable.m_boneTransformsOffset;
 		g_instanceRateRenderables[indirectIdx] = instanceVertex;
 	}
 	else
 	{
-		const GpuSceneParticleEmitter emitter = g_gpuScene.Load<GpuSceneParticleEmitter>(renderableIn.m_particleEmitterOffset);
+		const GpuSceneParticleEmitter emitter = g_gpuScene.Load<GpuSceneParticleEmitter>(renderable.m_particleEmitterOffset);
 
 		DrawIndirectArgsWithPadding indirect;
 		indirect.m_vertexCount = emitter.m_aliveParticleCount * meshLod.m_indexCount;
@@ -215,13 +220,35 @@ struct DrawIndirectArgsWithPadding
 		g_drawIndirectArgs[indirectIdx] = indirect;
 
 		UVec4 instanceVertex;
-		instanceVertex.x = renderableIn.m_worldTransformsOffset;
-		instanceVertex.y = renderableIn.m_uniformsOffset;
+		instanceVertex.x = renderable.m_worldTransformsOffset;
+		instanceVertex.y = renderable.m_uniformsOffset;
 		instanceVertex.z = meshLodOffset;
-		instanceVertex.w = renderableIn.m_particleEmitterOffset;
+		instanceVertex.w = renderable.m_particleEmitterOffset;
 		g_instanceRateRenderables[indirectIdx] = instanceVertex;
 	}
 
+#if HASH_VISIBLES
+	// Update the renderables hash
+	{
+		const Mat3x4 trf = g_gpuScene.Load<Mat3x4>(renderable.m_worldTransformsOffset);
+		const Vec3 pos = trf.getTranslationPart();
+		const UVec3 posu = UVec3(asuint(pos.x), asuint(pos.y), asuint(pos.z));
+
+		U32 hash = posu.x;
+		hash ^= posu.y;
+		hash ^= posu.z;
+		hash ^= renderable.m_uuid;
+
+		InterlockedXor(g_hash[0].m_renderablesHash, hash);
+
+		const Bool deformable = isParticleEmitter || renderable.m_boneTransformsOffset != 0;
+		if(deformable)
+		{
+			g_hash[0].m_containsDeformable = 1;
+		}
+	}
+#endif
+
 #if GATHER_AABBS
 	U32 index;
 	InterlockedAdd(g_visibleAabbIndices[0], 1, index);

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

@@ -280,6 +280,11 @@ struct Mat3x4
 	Vec4 m_row2;
 
 	_ANKI_DEFINE_ALL_OPERATORS_ROWS3(Mat3x4, F32)
+
+	Vec3 getTranslationPart()
+	{
+		return Vec3(m_row0.w, m_row1.w, m_row2.w);
+	}
 };
 
 #	if ANKI_FORCE_FULL_FP_PRECISION

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

@@ -25,6 +25,7 @@ struct GpuSceneRenderable
 	U32 m_boneTransformsOffset; ///< Array of Mat3x4 or 0 if its not a skin.
 	U32 m_particleEmitterOffset; ///< Offset to GpuSceneParticleEmitter or 0 if it's not an emitter.
 	U32 m_rtShadowsShaderHandleIndex; ///< The index of the shader handle in the array of library's handles.
+	U32 m_uuid;
 };
 
 /// Almost similar to GpuSceneRenderable but with only what the material shaders need. Needs to fit in a UVec4 vertex attribute.

+ 6 - 0
AnKi/Shaders/Include/GpuVisibilityTypes.h

@@ -47,4 +47,10 @@ struct GpuVisibilityAccelerationStructuresUniforms
 	Vec4 m_maxLodDistances;
 };
 
+struct GpuVisibilityHash
+{
+	U32 m_renderablesHash;
+	U32 m_containsDeformable;
+};
+
 ANKI_END_NAMESPACE