Browse Source

Move the reflection cube to the reflection component

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
e2460c123b

+ 1 - 1
AnKi/Gr/Texture.h

@@ -18,7 +18,7 @@ class alignas(4) TextureInitInfo : public GrBaseInitInfo
 public:
 	U32 m_width = 0;
 	U32 m_height = 0;
-	U32 m_depth = 1; //< Relevant only for 3D textures.
+	U32 m_depth = 1; ///< Relevant only for 3D textures.
 	U32 m_layerCount = 1; ///< Relevant only for texture arrays.
 
 	Format m_format = Format::kNone;

+ 1 - 1
AnKi/Renderer/ClusterBinning.cpp

@@ -311,7 +311,7 @@ void ClusterBinning::writeClustererBuffersTask()
 			ReflectionProbe& out = probes[i];
 
 			out.m_position = in.m_worldPosition;
-			out.m_cubemapIndex = F32(in.m_textureArrayIndex);
+			out.m_cubeTexture = in.m_textureBindlessIndex;
 			out.m_aabbMin = in.m_aabbMin;
 			out.m_aabbMax = in.m_aabbMax;
 		}

+ 0 - 2
AnKi/Renderer/ConfigVars.defs.h

@@ -67,9 +67,7 @@ ANKI_CONFIG_VAR_U32(RShadowMappingTileCountPerRowOrColumn, 32, 1, 256,
 ANKI_CONFIG_VAR_U32(RShadowMappingPcf, ((ANKI_PLATFORM_MOBILE) ? 0 : 1), 0, 1, "Shadow PCF (0: off, 1: on)")
 
 // Probe reflections
-ANKI_CONFIG_VAR_U32(RProbeReflectionResolution, 128, 4, 2048, "Reflection probe face resolution")
 ANKI_CONFIG_VAR_U32(RProbeReflectionIrradianceResolution, 16, 4, 2048, "Reflection probe irradiance resolution")
-ANKI_CONFIG_VAR_U32(RProbeRefectionMaxCachedProbes, 32, 4, 256, "Max cached number of reflection probes")
 ANKI_CONFIG_VAR_U32(RProbeReflectionShadowMapResolution, 64, 4, 2048, "Reflection probe shadow resolution")
 
 // Final composite

+ 8 - 4
AnKi/Renderer/IndirectSpecular.cpp

@@ -153,7 +153,10 @@ void IndirectSpecular::populateRenderGraph(RenderingContext& ctx)
 			min(getExternalSubsystems().m_config->getRSsrDepthLod() + 1, m_r->getDepthDownscale().getMipmapCount());
 		ppass->newTextureDependency(m_r->getDepthDownscale().getHiZRt(), readUsage, hizSubresource);
 
-		ppass->newTextureDependency(m_r->getProbeReflections().getReflectionRt(), readUsage);
+		if(m_r->getProbeReflections().getHasCurrentlyRefreshedReflectionRt())
+		{
+			ppass->newTextureDependency(m_r->getProbeReflections().getCurrentlyRefreshedReflectionRt(), readUsage);
+		}
 
 		ppass->newTextureDependency(m_r->getMotionVectors().getMotionVectorsRt(), readUsage);
 		ppass->newTextureDependency(m_r->getMotionVectors().getHistoryLengthRt(), readUsage);
@@ -210,12 +213,13 @@ void IndirectSpecular::run(const RenderingContext& ctx, RenderPassWorkContext& r
 	const ClusteredShadingContext& binning = ctx.m_clusteredShading;
 	bindUniforms(cmdb, 0, 11, binning.m_clusteredShadingUniformsToken);
 	bindUniforms(cmdb, 0, 12, binning.m_reflectionProbesToken);
-	rgraphCtx.bindColorTexture(0, 13, m_r->getProbeReflections().getReflectionRt());
-	bindStorage(cmdb, 0, 14, binning.m_clustersToken);
+	bindStorage(cmdb, 0, 13, binning.m_clustersToken);
+
+	cmdb->bindAllBindless(1);
 
 	if(getExternalSubsystems().m_config->getRPreferCompute())
 	{
-		rgraphCtx.bindImage(0, 15, m_runCtx.m_rts[kWrite], TextureSubresourceInfo());
+		rgraphCtx.bindImage(0, 14, m_runCtx.m_rts[kWrite], TextureSubresourceInfo());
 
 		dispatchPPCompute(cmdb, 8, 8, m_r->getInternalResolution().x() / 2, m_r->getInternalResolution().y() / 2);
 	}

+ 34 - 184
AnKi/Renderer/ProbeReflections.cpp

@@ -23,8 +23,6 @@ ProbeReflections::ProbeReflections(Renderer* r)
 
 ProbeReflections::~ProbeReflections()
 {
-	m_cacheEntries.destroy(getMemoryPool());
-	m_probeUuidToCacheEntryIdx.destroy(getMemoryPool());
 }
 
 Error ProbeReflections::init()
@@ -41,8 +39,6 @@ Error ProbeReflections::init()
 Error ProbeReflections::initInternal()
 {
 	// Init cache entries
-	m_cacheEntries.create(getMemoryPool(), getExternalSubsystems().m_config->getRProbeRefectionMaxCachedProbes());
-
 	ANKI_CHECK(initGBuffer());
 	ANKI_CHECK(initLightShading());
 	ANKI_CHECK(initIrradiance());
@@ -65,7 +61,7 @@ Error ProbeReflections::initInternal()
 
 Error ProbeReflections::initGBuffer()
 {
-	m_gbuffer.m_tileSize = getExternalSubsystems().m_config->getRProbeReflectionResolution();
+	m_gbuffer.m_tileSize = getExternalSubsystems().m_config->getSceneReflectionProbeResolution();
 
 	// Create RT descriptions
 	{
@@ -110,22 +106,18 @@ Error ProbeReflections::initGBuffer()
 
 Error ProbeReflections::initLightShading()
 {
-	m_lightShading.m_tileSize = getExternalSubsystems().m_config->getRProbeReflectionResolution();
+	m_lightShading.m_tileSize = getExternalSubsystems().m_config->getSceneReflectionProbeResolution();
 	m_lightShading.m_mipCount = computeMaxMipmapCount2d(m_lightShading.m_tileSize, m_lightShading.m_tileSize, 8);
 
-	// Init cube arr
+	for(U32 faceIdx = 0; faceIdx < 6; ++faceIdx)
 	{
-		TextureInitInfo texinit = m_r->create2DRenderTargetInitInfo(
-			m_lightShading.m_tileSize, m_lightShading.m_tileSize, m_r->getHdrFormat(),
-			TextureUsageBit::kSampledFragment | TextureUsageBit::kSampledCompute | TextureUsageBit::kImageComputeRead
-				| TextureUsageBit::kImageComputeWrite | TextureUsageBit::kAllFramebuffer
-				| TextureUsageBit::kGenerateMipmaps,
-			"CubeRefl refl");
-		texinit.m_mipmapCount = U8(m_lightShading.m_mipCount);
-		texinit.m_type = TextureType::kCubeArray;
-		texinit.m_layerCount = m_cacheEntries.getSize();
-
-		m_lightShading.m_cubeArr = m_r->createAndClearRenderTarget(texinit, TextureUsageBit::kSampledFragment);
+		// Light pass FB
+		FramebufferDescription& fbDescr = m_lightShading.m_fbDescr[faceIdx];
+		ANKI_ASSERT(!fbDescr.isBacked());
+		fbDescr.m_colorAttachmentCount = 1;
+		fbDescr.m_colorAttachments[0].m_surface.m_face = faceIdx;
+		fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::kClear;
+		fbDescr.bake();
 	}
 
 	// Init deferred
@@ -200,131 +192,11 @@ Error ProbeReflections::initShadowMapping()
 	return Error::kNone;
 }
 
-void ProbeReflections::initCacheEntry(U32 cacheEntryIdx)
-{
-	CacheEntry& cacheEntry = m_cacheEntries[cacheEntryIdx];
-
-	for(U32 faceIdx = 0; faceIdx < 6; ++faceIdx)
-	{
-		// Light pass FB
-		FramebufferDescription& fbDescr = cacheEntry.m_lightShadingFbDescrs[faceIdx];
-		ANKI_ASSERT(!fbDescr.isBacked());
-		fbDescr.m_colorAttachmentCount = 1;
-		fbDescr.m_colorAttachments[0].m_surface.m_layer = cacheEntryIdx;
-		fbDescr.m_colorAttachments[0].m_surface.m_face = faceIdx;
-		fbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::kClear;
-		fbDescr.bake();
-	}
-}
-
-void ProbeReflections::prepareProbes(RenderingContext& ctx, ReflectionProbeQueueElement*& probeToUpdateThisFrame,
-									 U32& probeToUpdateThisFrameCacheEntryIdx)
-{
-	probeToUpdateThisFrame = nullptr;
-	probeToUpdateThisFrameCacheEntryIdx = kMaxU32;
-
-	if(ctx.m_renderQueue->m_reflectionProbes.getSize() == 0) [[unlikely]]
-	{
-		return;
-	}
-
-	// Iterate the probes and:
-	// - Find a probe to update this frame
-	// - Find a probe to update next frame
-	// - Find the cache entries for each probe
-	DynamicArray<ReflectionProbeQueueElement> newListOfProbes;
-	newListOfProbes.create(*ctx.m_tempPool, ctx.m_renderQueue->m_reflectionProbes.getSize());
-	U32 newListOfProbeCount = 0;
-	Bool foundProbeToUpdateNextFrame = false;
-	for(U32 probeIdx = 0; probeIdx < ctx.m_renderQueue->m_reflectionProbes.getSize(); ++probeIdx)
-	{
-		ReflectionProbeQueueElement& probe = ctx.m_renderQueue->m_reflectionProbes[probeIdx];
-
-		// Find cache entry
-		const U32 cacheEntryIdx = findBestCacheEntry(probe.m_uuid, *getExternalSubsystems().m_globTimestamp,
-													 m_cacheEntries, m_probeUuidToCacheEntryIdx, getMemoryPool());
-		if(cacheEntryIdx == kMaxU32) [[unlikely]]
-		{
-			// Failed
-			ANKI_R_LOGW("There is not enough space in the indirect lighting atlas for more probes. "
-						"Increase the r_probeRefectionlMaxSimultaneousProbeCount or decrease the scene's probes");
-			continue;
-		}
-
-		const Bool probeFoundInCache = m_cacheEntries[cacheEntryIdx].m_uuid == probe.m_uuid;
-
-		// Check if we _should_ and _can_ update the probe
-		const Bool needsUpdate = !probeFoundInCache;
-		if(needsUpdate) [[unlikely]]
-		{
-			const Bool canUpdateThisFrame = probeToUpdateThisFrame == nullptr && probe.m_renderQueues[0] != nullptr;
-			const Bool canUpdateNextFrame = !foundProbeToUpdateNextFrame;
-
-			if(!canUpdateThisFrame && canUpdateNextFrame)
-			{
-				// Probe will be updated next frame
-				foundProbeToUpdateNextFrame = true;
-				probe.m_feedbackCallback(true, probe.m_feedbackCallbackUserData);
-				continue;
-			}
-			else if(!canUpdateThisFrame)
-			{
-				// Can't be updated this frame so remove it from the list
-				continue;
-			}
-			else
-			{
-				// Can be updated this frame so continue with it
-				probeToUpdateThisFrameCacheEntryIdx = cacheEntryIdx;
-				probeToUpdateThisFrame = &newListOfProbes[newListOfProbeCount];
-			}
-		}
-
-		// All good, can use this probe in this frame
-
-		// Update the cache entry
-		m_cacheEntries[cacheEntryIdx].m_uuid = probe.m_uuid;
-		m_cacheEntries[cacheEntryIdx].m_lastUsedTimestamp = *getExternalSubsystems().m_globTimestamp;
-
-		// Update the probe
-		probe.m_textureArrayIndex = cacheEntryIdx;
-
-		// Push the probe to the new list
-		newListOfProbes[newListOfProbeCount++] = probe;
-
-		// Update cache map
-		if(!probeFoundInCache)
-		{
-			m_probeUuidToCacheEntryIdx.emplace(getMemoryPool(), probe.m_uuid, cacheEntryIdx);
-		}
-
-		// Don't gather renderables next frame
-		if(probe.m_renderQueues[0] != nullptr)
-		{
-			probe.m_feedbackCallback(false, probe.m_feedbackCallbackUserData);
-		}
-	}
-
-	// Replace the probe list in the queue
-	if(newListOfProbeCount > 0)
-	{
-		ReflectionProbeQueueElement* firstProbe;
-		U32 probeCount, storage;
-		newListOfProbes.moveAndReset(firstProbe, probeCount, storage);
-		ctx.m_renderQueue->m_reflectionProbes = WeakArray<ReflectionProbeQueueElement>(firstProbe, newListOfProbeCount);
-	}
-	else
-	{
-		ctx.m_renderQueue->m_reflectionProbes = WeakArray<ReflectionProbeQueueElement>();
-		newListOfProbes.destroy(*ctx.m_tempPool);
-	}
-}
-
 void ProbeReflections::runGBuffer(RenderPassWorkContext& rgraphCtx)
 {
 	ANKI_ASSERT(m_ctx.m_probe);
 	ANKI_TRACE_SCOPED_EVENT(RCubeRefl);
-	const ReflectionProbeQueueElement& probe = *m_ctx.m_probe;
+	const ReflectionProbeQueueElementForRefresh& probe = *m_ctx.m_probe;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 	I32 start, end;
@@ -376,7 +248,7 @@ void ProbeReflections::runLightShading(U32 faceIdx, const RenderingContext& rctx
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 	ANKI_ASSERT(m_ctx.m_probe);
-	const ReflectionProbeQueueElement& probe = *m_ctx.m_probe;
+	const ReflectionProbeQueueElementForRefresh& probe = *m_ctx.m_probe;
 	const RenderQueue& rqueue = *probe.m_renderQueues[faceIdx];
 	const Bool hasDirLight = probe.m_renderQueues[0]->m_directionalLight.m_uuid;
 
@@ -414,11 +286,10 @@ void ProbeReflections::runLightShading(U32 faceIdx, const RenderingContext& rctx
 void ProbeReflections::runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 {
 	ANKI_ASSERT(faceIdx < 6);
-	ANKI_ASSERT(m_ctx.m_cacheEntryIdx < m_cacheEntries.getSize());
 
 	ANKI_TRACE_SCOPED_EVENT(RCubeRefl);
 
-	TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, m_ctx.m_cacheEntryIdx));
+	TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, 0));
 	subresource.m_mipmapCount = m_lightShading.m_mipCount;
 
 	TexturePtr texToBind;
@@ -431,8 +302,6 @@ void ProbeReflections::runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkCo
 void ProbeReflections::runIrradiance(RenderPassWorkContext& rgraphCtx)
 {
 	ANKI_TRACE_SCOPED_EVENT(RCubeRefl);
-	const U32 cacheEntryIdx = m_ctx.m_cacheEntryIdx;
-	ANKI_ASSERT(cacheEntryIdx < m_cacheEntries.getSize());
 
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
@@ -443,7 +312,6 @@ void ProbeReflections::runIrradiance(RenderPassWorkContext& rgraphCtx)
 
 	TextureSubresourceInfo subresource;
 	subresource.m_faceCount = 6;
-	subresource.m_firstLayer = cacheEntryIdx;
 	rgraphCtx.bindTexture(0, 1, m_ctx.m_lightShadingRt, subresource);
 
 	cmdb->bindStorageBuffer(0, 3, m_irradiance.m_diceValuesBuff, 0, m_irradiance.m_diceValuesBuff->getSize());
@@ -456,9 +324,6 @@ void ProbeReflections::runIrradianceToRefl(RenderPassWorkContext& rgraphCtx)
 {
 	ANKI_TRACE_SCOPED_EVENT(RCubeRefl);
 
-	const U32 cacheEntryIdx = m_ctx.m_cacheEntryIdx;
-	ANKI_ASSERT(cacheEntryIdx < m_cacheEntries.getSize());
-
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 	cmdb->bindShaderProgram(m_irradianceToRefl.m_grProg);
@@ -477,7 +342,6 @@ void ProbeReflections::runIrradianceToRefl(RenderPassWorkContext& rgraphCtx)
 		TextureSubresourceInfo subresource;
 		subresource.m_faceCount = 1;
 		subresource.m_firstFace = f;
-		subresource.m_firstLayer = cacheEntryIdx;
 		rgraphCtx.bindImage(0, 3, m_ctx.m_lightShadingRt, subresource, f);
 	}
 
@@ -488,32 +352,20 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 {
 	ANKI_TRACE_SCOPED_EVENT(RCubeRefl);
 
-#if ANKI_EXTRA_CHECKS
-	m_ctx = {};
-#endif
-	RenderGraphDescription& rgraph = rctx.m_renderGraphDescr;
-
-	// Prepare the probes and maybe get one to render this frame
-	ReflectionProbeQueueElement* probeToUpdate;
-	U32 probeToUpdateCacheEntryIdx;
-	prepareProbes(rctx, probeToUpdate, probeToUpdateCacheEntryIdx);
-
-	// Render a probe if needed
-	if(!probeToUpdate)
+	if(rctx.m_renderQueue->m_reflectionProbeForRefresh == nullptr) [[likely]]
 	{
-		// Just import and exit
-
-		m_ctx.m_lightShadingRt = rgraph.importRenderTarget(m_lightShading.m_cubeArr, TextureUsageBit::kSampledFragment);
+		// Early exit
+		m_ctx.m_lightShadingRt = {};
 		return;
 	}
 
-	m_ctx.m_cacheEntryIdx = probeToUpdateCacheEntryIdx;
-	m_ctx.m_probe = probeToUpdate;
+#if ANKI_EXTRA_CHECKS
+	m_ctx = {};
+#endif
 
-	if(!m_cacheEntries[probeToUpdateCacheEntryIdx].m_lightShadingFbDescrs[0].isBacked())
-	{
-		initCacheEntry(probeToUpdateCacheEntryIdx);
-	}
+	m_ctx.m_probe = rctx.m_renderQueue->m_reflectionProbeForRefresh;
+
+	RenderGraphDescription& rgraph = rctx.m_renderGraphDescr;
 
 	// G-buffer pass
 	{
@@ -530,7 +382,7 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 		m_ctx.m_gbufferRenderableCount = 0;
 		for(U32 i = 0; i < 6; ++i)
 		{
-			m_ctx.m_gbufferRenderableCount += probeToUpdate->m_renderQueues[i]->m_renderables.getSize();
+			m_ctx.m_gbufferRenderableCount += m_ctx.m_probe->m_renderQueues[i]->m_renderables.getSize();
 		}
 		const U32 taskCount = computeNumberOfSecondLevelCommandBuffers(m_ctx.m_gbufferRenderableCount);
 
@@ -554,14 +406,14 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 	}
 
 	// Shadow pass. Optional
-	if(probeToUpdate->m_renderQueues[0]->m_directionalLight.m_uuid
-	   && probeToUpdate->m_renderQueues[0]->m_directionalLight.m_shadowCascadeCount > 0)
+	if(m_ctx.m_probe->m_renderQueues[0]->m_directionalLight.m_uuid
+	   && m_ctx.m_probe->m_renderQueues[0]->m_directionalLight.m_shadowCascadeCount > 0)
 	{
 		// Update light matrices
 		for(U i = 0; i < 6; ++i)
 		{
-			ANKI_ASSERT(probeToUpdate->m_renderQueues[i]->m_directionalLight.m_uuid
-						&& probeToUpdate->m_renderQueues[i]->m_directionalLight.m_shadowCascadeCount == 1);
+			ANKI_ASSERT(m_ctx.m_probe->m_renderQueues[i]->m_directionalLight.m_uuid
+						&& m_ctx.m_probe->m_renderQueues[i]->m_directionalLight.m_shadowCascadeCount == 1);
 
 			const F32 xScale = 1.0f / 6.0f;
 			const F32 yScale = 1.0f;
@@ -570,7 +422,7 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 			const Mat4 atlasMtx(xScale, 0.0f, 0.0f, xOffset, 0.0f, yScale, 0.0f, yOffset, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
 								0.0f, 0.0f, 1.0f);
 
-			Mat4& lightMat = probeToUpdate->m_renderQueues[i]->m_directionalLight.m_textureMatrices[0];
+			Mat4& lightMat = m_ctx.m_probe->m_renderQueues[i]->m_directionalLight.m_textureMatrices[0];
 			lightMat = atlasMtx * lightMat;
 		}
 
@@ -579,7 +431,7 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 		for(U32 i = 0; i < 6; ++i)
 		{
 			m_ctx.m_shadowRenderableCount +=
-				probeToUpdate->m_renderQueues[i]->m_directionalLight.m_shadowRenderQueues[0]->m_renderables.getSize();
+				m_ctx.m_probe->m_renderQueues[i]->m_directionalLight.m_shadowRenderQueues[0]->m_renderables.getSize();
 		}
 		const U32 taskCount = computeNumberOfSecondLevelCommandBuffers(m_ctx.m_shadowRenderableCount);
 
@@ -607,7 +459,8 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 	// Light shading passes
 	{
 		// RT
-		m_ctx.m_lightShadingRt = rgraph.importRenderTarget(m_lightShading.m_cubeArr, TextureUsageBit::kSampledFragment);
+		m_ctx.m_lightShadingRt =
+			rgraph.importRenderTarget(TexturePtr(m_ctx.m_probe->m_reflectionTexture), TextureUsageBit::kNone);
 
 		// Passes
 		static constexpr Array<CString, 6> passNames = {"CubeRefl LightShad #0", "CubeRefl LightShad #1",
@@ -616,13 +469,12 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 		for(U32 faceIdx = 0; faceIdx < 6; ++faceIdx)
 		{
 			GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass(passNames[faceIdx]);
-			pass.setFramebufferInfo(m_cacheEntries[probeToUpdateCacheEntryIdx].m_lightShadingFbDescrs[faceIdx],
-									{m_ctx.m_lightShadingRt});
+			pass.setFramebufferInfo(m_lightShading.m_fbDescr[faceIdx], {m_ctx.m_lightShadingRt});
 			pass.setWork([this, faceIdx, &rctx](RenderPassWorkContext& rgraphCtx) {
 				runLightShading(faceIdx, rctx, rgraphCtx);
 			});
 
-			TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
+			TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, 0));
 			pass.newTextureDependency(m_ctx.m_lightShadingRt, TextureUsageBit::kFramebufferWrite, subresource);
 
 			for(U i = 0; i < kGBufferColorRenderTargetCount; ++i)
@@ -653,7 +505,6 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 		// Read a cube but only one layer and level
 		TextureSubresourceInfo readSubresource;
 		readSubresource.m_faceCount = 6;
-		readSubresource.m_firstLayer = probeToUpdateCacheEntryIdx;
 		pass.newTextureDependency(m_ctx.m_lightShadingRt, TextureUsageBit::kSampledCompute, readSubresource);
 
 		pass.newBufferDependency(m_ctx.m_irradianceDiceValuesBuffHandle, BufferUsageBit::kStorageComputeWrite);
@@ -674,7 +525,6 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 
 		TextureSubresourceInfo subresource;
 		subresource.m_faceCount = 6;
-		subresource.m_firstLayer = probeToUpdateCacheEntryIdx;
 		pass.newTextureDependency(m_ctx.m_lightShadingRt,
 								  TextureUsageBit::kImageComputeRead | TextureUsageBit::kImageComputeWrite,
 								  subresource);
@@ -693,7 +543,7 @@ void ProbeReflections::populateRenderGraph(RenderingContext& rctx)
 				runMipmappingOfLightShading(faceIdx, rgraphCtx);
 			});
 
-			TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, probeToUpdateCacheEntryIdx));
+			TextureSubresourceInfo subresource(TextureSurfaceInfo(0, 0, faceIdx, 0));
 			subresource.m_mipmapCount = m_lightShading.m_mipCount;
 
 			pass.newTextureDependency(m_ctx.m_lightShadingRt, TextureUsageBit::kGenerateMipmaps, subresource);

+ 9 - 23
AnKi/Renderer/ProbeReflections.h

@@ -45,11 +45,17 @@ public:
 		return m_integrationLutSampler;
 	}
 
-	RenderTargetHandle getReflectionRt() const
+	RenderTargetHandle getCurrentlyRefreshedReflectionRt() const
 	{
+		ANKI_ASSERT(m_ctx.m_lightShadingRt.isValid());
 		return m_ctx.m_lightShadingRt;
 	}
 
+	Bool getHasCurrentlyRefreshedReflectionRt() const
+	{
+		return m_ctx.m_lightShadingRt.isValid();
+	}
+
 private:
 	class
 	{
@@ -65,8 +71,7 @@ private:
 	public:
 		U32 m_tileSize = 0;
 		U32 m_mipCount = 0;
-		TexturePtr m_cubeArr;
-
+		Array<FramebufferDescription, 6> m_fbDescr;
 		TraditionalDeferredLightShading m_deferred;
 
 		LS(Renderer* r)
@@ -98,18 +103,6 @@ private:
 		FramebufferDescription m_fbDescr;
 	} m_shadowMapping;
 
-	class CacheEntry
-	{
-	public:
-		U64 m_uuid; ///< Probe UUID.
-		Timestamp m_lastUsedTimestamp = 0; ///< When it was last seen by the renderer.
-
-		Array<FramebufferDescription, 6> m_lightShadingFbDescrs;
-	};
-
-	DynamicArray<CacheEntry> m_cacheEntries;
-	HashMap<U64, U32> m_probeUuidToCacheEntryIdx;
-
 	// Other
 	ImageResourcePtr m_integrationLut;
 	SamplerPtr m_integrationLutSampler;
@@ -117,8 +110,7 @@ private:
 	class
 	{
 	public:
-		const ReflectionProbeQueueElement* m_probe = nullptr;
-		U32 m_cacheEntryIdx = kMaxU32;
+		const ReflectionProbeQueueElementForRefresh* m_probe = nullptr;
 
 		Array<RenderTargetHandle, kGBufferColorRenderTargetCount> m_gbufferColorRts;
 		RenderTargetHandle m_gbufferDepthRt;
@@ -137,12 +129,6 @@ private:
 	Error initIrradianceToRefl();
 	Error initShadowMapping();
 
-	/// Lazily init the cache entry
-	void initCacheEntry(U32 cacheEntryIdx);
-
-	void prepareProbes(RenderingContext& ctx, ReflectionProbeQueueElement*& probeToUpdate,
-					   U32& probeToUpdateCacheEntryIdx);
-
 	void runGBuffer(RenderPassWorkContext& rgraphCtx);
 	void runShadowMapping(RenderPassWorkContext& rgraphCtx);
 	void runLightShading(U32 faceIdx, const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);

+ 3 - 3
AnKi/Renderer/RenderQueue.cpp

@@ -33,13 +33,13 @@ U32 RenderQueue::countAllRenderables() const
 		}
 	}
 
-	for(const ReflectionProbeQueueElement& probe : m_reflectionProbes)
+	if(m_reflectionProbeForRefresh)
 	{
 		for(U i = 0; i < 6; ++i)
 		{
-			if(probe.m_renderQueues[i])
+			if(m_reflectionProbeForRefresh->m_renderQueues[i])
 			{
-				drawableCount += probe.m_renderQueues[i]->countAllRenderables();
+				drawableCount += m_reflectionProbeForRefresh->m_renderQueues[i]->countAllRenderables();
 			}
 		}
 	}

+ 12 - 10
AnKi/Renderer/RenderQueue.h

@@ -187,23 +187,14 @@ public:
 };
 static_assert(std::is_trivially_destructible<DirectionalLightQueueElement>::value == true);
 
-/// Normally the visibility tests don't perform tests on the reflection probes because probes dont change that often.
-/// This callback will be used by the renderer to inform a reflection probe that on the next frame it will be rendererd.
-/// In that case the visibility tests should fill the render queues of the probe.
-using ReflectionProbeQueueElementFeedbackCallback = void (*)(Bool fillRenderQueuesOnNextFrame, void* userData);
-
 /// Reflection probe render queue element.
 class ReflectionProbeQueueElement final
 {
 public:
-	U64 m_uuid;
-	ReflectionProbeQueueElementFeedbackCallback m_feedbackCallback;
-	void* m_feedbackCallbackUserData;
-	Array<RenderQueue*, 6> m_renderQueues;
 	Vec3 m_worldPosition;
 	Vec3 m_aabbMin;
 	Vec3 m_aabbMax;
-	U32 m_textureArrayIndex; ///< Renderer internal.
+	U32 m_textureBindlessIndex;
 
 	ReflectionProbeQueueElement()
 	{
@@ -211,6 +202,15 @@ public:
 };
 static_assert(std::is_trivially_destructible<ReflectionProbeQueueElement>::value == true);
 
+/// Contains info for a reflection probe that the renderer will have to refresh.
+class ReflectionProbeQueueElementForRefresh final
+{
+public:
+	Array<RenderQueue*, 6> m_renderQueues;
+	Vec3 m_worldPosition;
+	Texture* m_reflectionTexture;
+};
+
 /// See ReflectionProbeQueueElementFeedbackCallback for its purpose.
 using GlobalIlluminationProbeQueueElementFeedbackCallback = void (*)(Bool fillRenderQueuesOnNextFrame, void* userData,
 																	 const Vec4& eyeWorldPosition);
@@ -401,6 +401,8 @@ public:
 	FillCoverageBufferCallback m_fillCoverageBufferCallback = nullptr;
 	void* m_fillCoverageBufferCallbackUserData = nullptr;
 
+	ReflectionProbeQueueElementForRefresh* m_reflectionProbeForRefresh = nullptr;
+
 	RenderQueue()
 	{
 		zeroMemory(m_directionalLight);

+ 27 - 5
AnKi/Scene/Components/ReflectionProbeComponent.cpp

@@ -13,7 +13,6 @@ namespace anki {
 
 ReflectionProbeComponent::ReflectionProbeComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
-	, m_uuid(node->getSceneGraph().getNewUuid())
 	, m_spatial(this)
 {
 	m_worldPos = node->getWorldTransform().getOrigin().xyz();
@@ -43,8 +42,33 @@ Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 	m_dirty = false;
 	updated = moved || shapeUpdated;
 
+	if(shapeUpdated && !m_reflectionTex) [[unlikely]]
+	{
+		TextureInitInfo texInit("ReflectionProbe");
+		texInit.m_format =
+			(getExternalSubsystems(*info.m_node).m_grManager->getDeviceCapabilities().m_unalignedBbpTextureFormats)
+				? Format::kR16G16B16_Sfloat
+				: Format::kR16G16B16A16_Sfloat;
+		texInit.m_width = getExternalSubsystems(*info.m_node).m_config->getSceneReflectionProbeResolution();
+		texInit.m_height = texInit.m_width;
+		texInit.m_mipmapCount = U8(computeMaxMipmapCount2d(texInit.m_width, texInit.m_height, 8));
+		texInit.m_type = TextureType::kCube;
+		texInit.m_usage = TextureUsageBit::kAllSampled | TextureUsageBit::kImageComputeWrite
+						  | TextureUsageBit::kImageComputeRead | TextureUsageBit::kAllFramebuffer
+						  | TextureUsageBit::kGenerateMipmaps;
+
+		m_reflectionTex = getExternalSubsystems(*info.m_node).m_grManager->newTexture(texInit);
+
+		TextureViewInitInfo viewInit(m_reflectionTex, "ReflectionPRobe");
+		m_reflectionView = getExternalSubsystems(*info.m_node).m_grManager->newTextureView(viewInit);
+
+		m_reflectionTexBindlessIndex = m_reflectionView->getOrCreateBindlessTextureIndex();
+	}
+
 	if(updated) [[unlikely]]
 	{
+		m_reflectionNeedsRefresh = true;
+
 		m_worldPos = info.m_node->getWorldTransform().getOrigin().xyz();
 
 		F32 effectiveDistance = max(m_halfSize.x(), m_halfSize.y());
@@ -69,22 +93,20 @@ Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 										   effectiveDistance - 1.0f * kEpsilonf});
 		}
 
-		// Set a new UUID to force the renderer to update the probe
-		m_uuid = info.m_node->getSceneGraph().getNewUuid();
-
 		const Aabb aabbWorld(-m_halfSize + m_worldPos, m_halfSize + m_worldPos);
 		m_spatial.setBoundingShape(aabbWorld);
 
 		// Upload to the GPU scene
 		GpuSceneReflectionProbe gpuProbe;
 		gpuProbe.m_position = m_worldPos;
-		gpuProbe.m_cubemapIndex = 0; // Unknown at this point
+		gpuProbe.m_cubeTexture = m_reflectionTexBindlessIndex;
 		gpuProbe.m_aabbMin = aabbWorld.getMin().xyz();
 		gpuProbe.m_aabbMax = aabbWorld.getMax().xyz();
 		getExternalSubsystems(*info.m_node)
 			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneOffset, sizeof(gpuProbe), &gpuProbe);
 	}
 
+	// Update spatial and frustums
 	const Bool spatialUpdated = m_spatial.update(info.m_node->getSceneGraph().getOctree());
 	updated = updated || spatialUpdated;
 

+ 27 - 19
AnKi/Scene/Components/ReflectionProbeComponent.h

@@ -38,30 +38,40 @@ public:
 		return m_halfSize * 2.0f;
 	}
 
-	Bool getMarkedForRendering() const
-	{
-		return m_markedForRendering;
-	}
-
 	ANKI_INTERNAL WeakArray<Frustum> getFrustums()
 	{
 		return WeakArray<Frustum>(m_frustums);
 	}
 
-	void setupReflectionProbeQueueElement(ReflectionProbeQueueElement& el) const
+	ANKI_INTERNAL void setupReflectionProbeQueueElement(ReflectionProbeQueueElement& el) const
 	{
+		ANKI_ASSERT(!m_reflectionNeedsRefresh);
 		ANKI_ASSERT(m_worldPos.x() != kMaxF32);
-		el.m_feedbackCallback = reflectionProbeQueueElementFeedbackCallback;
-		el.m_feedbackCallbackUserData = const_cast<ReflectionProbeComponent*>(this);
-		el.m_uuid = m_uuid;
 		el.m_worldPosition = m_worldPos;
 		el.m_aabbMin = -m_halfSize + m_worldPos;
 		el.m_aabbMax = m_halfSize + m_worldPos;
-		el.m_textureArrayIndex = kMaxU32;
+		ANKI_ASSERT(el.m_textureBindlessIndex != kMaxU32);
+		el.m_textureBindlessIndex = m_reflectionTexBindlessIndex;
+	}
+
+	ANKI_INTERNAL void setupReflectionProbeQueueElementForRefresh(ReflectionProbeQueueElementForRefresh& el) const
+	{
+		ANKI_ASSERT(m_reflectionNeedsRefresh);
+		el.m_worldPosition = m_worldPos;
+		el.m_reflectionTexture = m_reflectionTex.get();
+	}
+
+	ANKI_INTERNAL Bool getReflectionNeedsRefresh() const
+	{
+		return m_reflectionNeedsRefresh;
+	}
+
+	ANKI_INTERNAL void setReflectionNeedsRefresh(Bool needsRefresh)
+	{
+		m_reflectionNeedsRefresh = needsRefresh;
 	}
 
 private:
-	U64 m_uuid = 0;
 	Vec3 m_worldPos = Vec3(kMaxF32);
 	Vec3 m_halfSize = Vec3(1.0f);
 
@@ -71,18 +81,16 @@ private:
 
 	Array<Frustum, 6> m_frustums;
 
-	Bool m_markedForRendering : 1 = false;
-	Bool m_dirty : 1 = true;
+	TexturePtr m_reflectionTex;
+	TextureViewPtr m_reflectionView;
+	U32 m_reflectionTexBindlessIndex = kMaxU32;
+
+	Bool m_dirty = true;
+	Bool m_reflectionNeedsRefresh = true;
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 
 	void onDestroy(SceneNode& node);
-
-	static void reflectionProbeQueueElementFeedbackCallback(Bool fillRenderQueuesOnNextFrame, void* userData)
-	{
-		ANKI_ASSERT(userData);
-		static_cast<ReflectionProbeComponent*>(userData)->m_markedForRendering = fillRenderQueuesOnNextFrame;
-	}
 };
 /// @}
 

+ 3 - 0
AnKi/Scene/ConfigVars.defs.h

@@ -27,6 +27,9 @@ ANKI_CONFIG_VAR_BOOL(SceneRayTracedShadows, true, "Enable or not ray traced shad
 ANKI_CONFIG_VAR_F32(SceneRayTracingExtendedFrustumDistance, 100.0f, 10.0f, 10000.0f,
 					"Every object that its distance from the camera is bellow that value will take part in ray tracing")
 
+ANKI_CONFIG_VAR_U32(SceneReflectionProbeResolution, 128, 8, 2048, "The resolution of the reflection probe's reflection")
+
+// GPU scene
 ANKI_CONFIG_VAR_U32(SceneMinGpuSceneTransforms, 8 * 1024, 8, 100 * 1024,
 					"The min number of transforms stored in the GPU scene")
 ANKI_CONFIG_VAR_U32(SceneMinGpuSceneMeshes, 8 * 1024, 8, 100 * 1024, "The min number of meshes stored in the GPU scene")

+ 15 - 8
AnKi/Scene/Visibility.cpp

@@ -516,24 +516,29 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 
 			ReflectionProbeComponent& reflc = static_cast<ReflectionProbeComponent&>(comp);
 
-			ReflectionProbeQueueElement* el = result.m_reflectionProbes.newElement(pool);
-			reflc.setupReflectionProbeQueueElement(*el);
-
-			if(reflc.getMarkedForRendering())
+			if(reflc.getReflectionNeedsRefresh() && m_frcCtx->m_reflectionProbesForRefreshCount.fetchAdd(1) == 0)
 			{
+				ReflectionProbeQueueElementForRefresh* el = newInstance<ReflectionProbeQueueElementForRefresh>(pool);
+				m_frcCtx->m_reflectionProbeForRefresh = el;
+
+				reflc.setupReflectionProbeQueueElementForRefresh(*el);
+				reflc.setReflectionNeedsRefresh(false);
+
 				nextQueues = WeakArray<RenderQueue>(newArray<RenderQueue>(pool, 6), 6);
 				nextFrustums = WeakArray<VisibilityFrustum>(newArray<VisibilityFrustum>(pool, 6), 6);
 
 				for(U32 i = 0; i < 6; ++i)
 				{
 					el->m_renderQueues[i] = &nextQueues[i];
+
 					nextFrustums[i].m_frustum = &reflc.getFrustums()[i];
 					static_cast<FrustumFlags&>(nextFrustums[i]) = getProbeFrustumFlags();
 				}
 			}
-			else
+			else if(!reflc.getReflectionNeedsRefresh())
 			{
-				memset(&el->m_renderQueues[0], 0, sizeof(el->m_renderQueues));
+				ReflectionProbeQueueElement* el = result.m_reflectionProbes.newElement(pool);
+				reflc.setupReflectionProbeQueueElement(*el);
 			}
 		}
 		else if(comp.getClassId() == DecalComponent::getStaticClassId())
@@ -684,6 +689,10 @@ void CombineResultsTask::combine()
 	ANKI_VIS_COMBINE(RayTracingInstanceQueueElement, m_rayTracingInstances);
 	ANKI_VIS_COMBINE(UiQueueElement, m_uis);
 
+#undef ANKI_VIS_COMBINE
+
+	results.m_reflectionProbeForRefresh = m_frcCtx->m_reflectionProbeForRefresh;
+
 	for(U32 i = 0; i < threadCount; ++i)
 	{
 		if(m_frcCtx->m_queueViews[i].m_directionalLight.m_uuid != 0)
@@ -697,8 +706,6 @@ void CombineResultsTask::combine()
 		}
 	}
 
-#undef ANKI_VIS_COMBINE
-
 	const Bool isShadowFrustum = m_frcCtx->m_frustum.m_gatherShadowCasterModelComponents;
 
 	// Sort some of the arrays

+ 3 - 0
AnKi/Scene/VisibilityInternal.h

@@ -173,6 +173,9 @@ public:
 
 	// Gather results members
 	RenderQueue* m_renderQueue = nullptr;
+
+	Atomic<U32> m_reflectionProbesForRefreshCount = {0};
+	ReflectionProbeQueueElementForRefresh* m_reflectionProbeForRefresh = nullptr;
 };
 
 /// ThreadHive task to set the depth map of the S/W rasterizer.

+ 0 - 3
AnKi/Shaders/ClusteredShadingCommon.hlsl

@@ -40,9 +40,6 @@
 {
 	ReflectionProbe g_reflectionProbes[kMaxVisibleReflectionProbes];
 };
-
-[[vk::binding(CLUSTERED_SHADING_REFLECTIONS_BINDING + 1u, CLUSTERED_SHADING_SET)]] TextureCubeArray<RVec4>
-	g_reflectionsTex;
 #endif
 
 //

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

@@ -127,7 +127,7 @@ ANKI_SHADER_STATIC_ASSERT(kMaxShadowCascades == 4u); // Because m_shadowCascadeD
 struct ReflectionProbe
 {
 	Vec3 m_position; ///< Position of the probe in world space.
-	F32 m_cubemapIndex; ///< Index in the cubemap array texture.
+	U32 m_cubeTexture; ///< Bindless index of the reflection texture.
 
 	Vec3 m_aabbMin;
 	F32 m_padding0;

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

@@ -69,6 +69,7 @@ void maybeUnused(T a)
 		[[vk::binding(0, s)]] Texture2D<int4> g_bindlessTextures2dI32[kMaxBindlessTextures]; \
 		[[vk::binding(0, s)]] Texture2D<RVec4> g_bindlessTextures2dF32[kMaxBindlessTextures]; \
 		[[vk::binding(0, s)]] Texture2DArray<RVec4> g_bindlessTextures2dArrayF32[kMaxBindlessTextures]; \
+		[[vk::binding(0, s)]] TextureCube<RVec4> g_bindlessTexturesCubeF32[kMaxBindlessTextures]; \
 		[[vk::binding(1, s)]] Buffer<float4> g_bindlessTextureBuffersF32[kMaxBindlessReadonlyTextureBuffers];
 
 #	define _ANKI_SCONST_X(type, n, id) [[vk::constant_id(id)]] const type n = (type)1;

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

@@ -85,7 +85,7 @@ static_assert(sizeof(GpuSceneSpotLight) == kSizeof_GpuSceneSpotLight);
 struct GpuSceneReflectionProbe
 {
 	Vec3 m_position; ///< Position of the probe in world space.
-	F32 m_cubemapIndex; ///< Index in the cubemap array texture.
+	U32 m_cubeTexture; ///< Bindless index of the reflection texture.
 
 	Vec3 m_aabbMin;
 	F32 m_padding0;

+ 12 - 7
AnKi/Shaders/IndirectSpecular.hlsl

@@ -31,13 +31,15 @@ constexpr Vec2 kNoiseTexSize = 64.0;
 #define CLUSTERED_SHADING_SET 0u
 #define CLUSTERED_SHADING_UNIFORMS_BINDING 11u
 #define CLUSTERED_SHADING_REFLECTIONS_BINDING 12u
-#define CLUSTERED_SHADING_CLUSTERS_BINDING 14u
+#define CLUSTERED_SHADING_CLUSTERS_BINDING 13u
 #include <AnKi/Shaders/ClusteredShadingCommon.hlsl>
 
 #if defined(ANKI_COMPUTE_SHADER)
-[[vk::binding(15)]] RWTexture2D<RVec4> g_outUav;
+[[vk::binding(14)]] RWTexture2D<RVec4> g_outUav;
 #endif
 
+ANKI_BINDLESS_SET(1)
+
 #if defined(ANKI_COMPUTE_SHADER)
 [numthreads(8, 8, 1)] void main(UVec3 svDispatchThreadId : SV_DISPATCHTHREADID)
 #else
@@ -203,7 +205,8 @@ RVec3 main(Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITION) : SV_TARGET0
 
 		Vec3 probeColor = 0.0;
 
-		if(countbits(cluster.m_reflectionProbesMask) == 1)
+		const U32 oneProbe = WaveActiveAllTrue(countbits(cluster.m_reflectionProbesMask) == 1);
+		if(oneProbe)
 		{
 			// Only one probe, do a fast path without blend weight
 
@@ -211,8 +214,9 @@ RVec3 main(Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITION) : SV_TARGET0
 
 			// Sample
 			const Vec3 cubeUv = intersectProbe(worldPos, reflDir, probe.m_aabbMin, probe.m_aabbMax, probe.m_position);
-			const Vec4 cubeArrUv = Vec4(cubeUv, probe.m_cubemapIndex);
-			probeColor = g_reflectionsTex.SampleLevel(g_trilinearClampSampler, cubeArrUv, reflLod).rgb;
+			probeColor = g_bindlessTexturesCubeF32[probe.m_cubeTexture]
+							 .SampleLevel(g_trilinearClampSampler, cubeUv, reflLod)
+							 .rgb;
 		}
 		else
 		{
@@ -234,8 +238,9 @@ RVec3 main(Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITION) : SV_TARGET0
 				// Sample reflections
 				const Vec3 cubeUv =
 					intersectProbe(worldPos, reflDir, probe.m_aabbMin, probe.m_aabbMax, probe.m_position);
-				const Vec4 cubeArrUv = Vec4(cubeUv, probe.m_cubemapIndex);
-				const Vec3 c = g_reflectionsTex.SampleLevel(g_trilinearClampSampler, cubeArrUv, reflLod).rgb;
+				const Vec3 c = g_bindlessTexturesCubeF32[NonUniformResourceIndex(probe.m_cubeTexture)]
+								   .SampleLevel(g_trilinearClampSampler, cubeUv, reflLod)
+								   .rgb;
 				probeColor += c * blendWeight;
 			}