Browse Source

Move the GI volumes to the GI component and remove some caches in the process

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
2341c92381

+ 5 - 1
AnKi/Gr/Vulkan/TextureImpl.cpp

@@ -547,8 +547,12 @@ VkImageLayout TextureImpl::computeLayout(TextureUsageBit usage, U level) const
 	{
 	{
 		out = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
 		out = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
 	}
 	}
+	else
+	{
+		// No idea so play it safe
+		out = VK_IMAGE_LAYOUT_GENERAL;
+	}
 
 
-	ANKI_ASSERT(out != VK_IMAGE_LAYOUT_MAX_ENUM);
 	return out;
 	return out;
 }
 }
 
 

+ 1 - 1
AnKi/Renderer/ClusterBinning.cpp

@@ -380,7 +380,7 @@ void ClusterBinning::writeClustererBuffersTask()
 
 
 			out.m_aabbMin = in.m_aabbMin;
 			out.m_aabbMin = in.m_aabbMin;
 			out.m_aabbMax = in.m_aabbMax;
 			out.m_aabbMax = in.m_aabbMax;
-			out.m_textureIndex = U32(&in - &rqueue.m_giProbes.getFront());
+			out.m_volumeTexture = in.m_volumeTextureBindlessIndex;
 			out.m_halfTexelSizeU = 1.0f / F32(F32(in.m_cellCounts.x()) * 6.0f) / 2.0f;
 			out.m_halfTexelSizeU = 1.0f / F32(F32(in.m_cellCounts.x()) * 6.0f) / 2.0f;
 			out.m_fadeDistance = in.m_fadeDistance;
 			out.m_fadeDistance = in.m_fadeDistance;
 		}
 		}

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

@@ -47,8 +47,6 @@ ANKI_CONFIG_VAR_F32(RSsrRoughnessCutoff, ((ANKI_PLATFORM_MOBILE) ? 0.7f : 1.0f),
 ANKI_CONFIG_VAR_U32(RIndirectDiffuseProbeTileResolution, ((ANKI_PLATFORM_MOBILE) ? 16 : 32), 8, 32,
 ANKI_CONFIG_VAR_U32(RIndirectDiffuseProbeTileResolution, ((ANKI_PLATFORM_MOBILE) ? 16 : 32), 8, 32,
 					"GI tile resolution")
 					"GI tile resolution")
 ANKI_CONFIG_VAR_U32(RIndirectDiffuseProbeShadowMapResolution, 128, 4, 2048, "GI shadowmap resolution")
 ANKI_CONFIG_VAR_U32(RIndirectDiffuseProbeShadowMapResolution, 128, 4, 2048, "GI shadowmap resolution")
-ANKI_CONFIG_VAR_U32(RIndirectDiffuseProbeMaxCachedProbes, 16, 4, 2048, "Max cached probes")
-ANKI_CONFIG_VAR_U32(RIndirectDiffuseProbeMaxVisibleProbes, 8, 1, 256, "Max visible GI probes")
 
 
 // GI
 // GI
 ANKI_CONFIG_VAR_U32(RIndirectDiffuseSsgiSampleCount, 8, 1, 1024, "SSGI sample count")
 ANKI_CONFIG_VAR_U32(RIndirectDiffuseSsgiSampleCount, 8, 1, 1024, "SSGI sample count")

+ 17 - 12
AnKi/Renderer/IndirectDiffuse.cpp

@@ -250,7 +250,11 @@ void IndirectDiffuse::populateRenderGraph(RenderingContext& ctx)
 
 
 		prpass->newTextureDependency(m_runCtx.m_mainRtHandles[kWrite], writeUsage);
 		prpass->newTextureDependency(m_runCtx.m_mainRtHandles[kWrite], writeUsage);
 
 
-		m_r->getIndirectDiffuseProbes().setRenderGraphDependencies(ctx, *prpass, readUsage);
+		if(m_r->getIndirectDiffuseProbes().hasCurrentlyRefreshedVolumeRt())
+		{
+			prpass->newTextureDependency(m_r->getIndirectDiffuseProbes().getCurrentlyRefreshedVolumeRt(), readUsage);
+		}
+
 		prpass->newTextureDependency(m_r->getGBuffer().getColorRt(2), readUsage);
 		prpass->newTextureDependency(m_r->getGBuffer().getColorRt(2), readUsage);
 		TextureSubresourceInfo hizSubresource;
 		TextureSubresourceInfo hizSubresource;
 		hizSubresource.m_mipmapCount = 1;
 		hizSubresource.m_mipmapCount = 1;
@@ -266,26 +270,27 @@ void IndirectDiffuse::populateRenderGraph(RenderingContext& ctx)
 
 
 			const ClusteredShadingContext& binning = ctx.m_clusteredShading;
 			const ClusteredShadingContext& binning = ctx.m_clusteredShading;
 			bindUniforms(cmdb, 0, 0, binning.m_clusteredShadingUniformsToken);
 			bindUniforms(cmdb, 0, 0, binning.m_clusteredShadingUniformsToken);
-			m_r->getIndirectDiffuseProbes().bindVolumeTextures(ctx, rgraphCtx, 0, 1);
-			bindUniforms(cmdb, 0, 2, binning.m_globalIlluminationProbesToken);
-			bindStorage(cmdb, 0, 3, binning.m_clustersToken);
+			bindUniforms(cmdb, 0, 1, binning.m_globalIlluminationProbesToken);
+			bindStorage(cmdb, 0, 2, binning.m_clustersToken);
 
 
-			cmdb->bindSampler(0, 4, m_r->getSamplers().m_trilinearClamp);
-			rgraphCtx.bindColorTexture(0, 5, m_r->getGBuffer().getColorRt(2));
+			cmdb->bindSampler(0, 3, m_r->getSamplers().m_trilinearClamp);
+			rgraphCtx.bindColorTexture(0, 4, m_r->getGBuffer().getColorRt(2));
 
 
 			TextureSubresourceInfo hizSubresource;
 			TextureSubresourceInfo hizSubresource;
 			hizSubresource.m_mipmapCount = 1;
 			hizSubresource.m_mipmapCount = 1;
-			rgraphCtx.bindTexture(0, 6, m_r->getDepthDownscale().getHiZRt(), hizSubresource);
-			rgraphCtx.bindColorTexture(0, 7, m_r->getDownscaleBlur().getRt());
-			rgraphCtx.bindColorTexture(0, 8, m_runCtx.m_mainRtHandles[kRead]);
-			rgraphCtx.bindColorTexture(0, 9, m_r->getMotionVectors().getMotionVectorsRt());
-			rgraphCtx.bindColorTexture(0, 10, m_r->getMotionVectors().getHistoryLengthRt());
+			rgraphCtx.bindTexture(0, 5, m_r->getDepthDownscale().getHiZRt(), hizSubresource);
+			rgraphCtx.bindColorTexture(0, 6, m_r->getDownscaleBlur().getRt());
+			rgraphCtx.bindColorTexture(0, 7, m_runCtx.m_mainRtHandles[kRead]);
+			rgraphCtx.bindColorTexture(0, 8, m_r->getMotionVectors().getMotionVectorsRt());
+			rgraphCtx.bindColorTexture(0, 9, m_r->getMotionVectors().getHistoryLengthRt());
 
 
 			if(getExternalSubsystems().m_config->getRPreferCompute())
 			if(getExternalSubsystems().m_config->getRPreferCompute())
 			{
 			{
-				rgraphCtx.bindImage(0, 11, m_runCtx.m_mainRtHandles[kWrite]);
+				rgraphCtx.bindImage(0, 10, m_runCtx.m_mainRtHandles[kWrite]);
 			}
 			}
 
 
+			cmdb->bindAllBindless(1);
+
 			// Bind uniforms
 			// Bind uniforms
 			IndirectDiffuseUniforms unis;
 			IndirectDiffuseUniforms unis;
 			unis.m_viewportSize = m_r->getInternalResolution() / 2u;
 			unis.m_viewportSize = m_r->getInternalResolution() / 2u;

+ 28 - 240
AnKi/Renderer/IndirectDiffuseProbes.cpp

@@ -13,40 +13,22 @@
 
 
 namespace anki {
 namespace anki {
 
 
-/// Given a cell index compute its world position.
-static Vec3 computeProbeCellPosition(U32 cellIdx, const GlobalIlluminationProbeQueueElement& probe)
-{
-	ANKI_ASSERT(cellIdx < probe.m_totalCellCount);
-
-	UVec3 cellCoords;
-	unflatten3dArrayIndex(probe.m_cellCounts.z(), probe.m_cellCounts.y(), probe.m_cellCounts.x(), cellIdx,
-						  cellCoords.z(), cellCoords.y(), cellCoords.x());
-
-	const Vec3 halfCellSize = probe.m_cellSizes / 2.0f;
-	const Vec3 cellPos = Vec3(cellCoords) * probe.m_cellSizes + halfCellSize + probe.m_aabbMin;
-	ANKI_ASSERT(cellPos < probe.m_aabbMax);
-
-	return cellPos;
-}
-
 class IndirectDiffuseProbes::InternalContext
 class IndirectDiffuseProbes::InternalContext
 {
 {
 public:
 public:
-	IndirectDiffuseProbes* m_gi ANKI_DEBUG_CODE(= numberToPtr<IndirectDiffuseProbes*>(1));
-	RenderingContext* m_ctx ANKI_DEBUG_CODE(= numberToPtr<RenderingContext*>(1));
+	IndirectDiffuseProbes* m_gi = nullptr;
+	RenderingContext* m_ctx = nullptr;
 
 
-	GlobalIlluminationProbeQueueElement*
-		m_probeToUpdateThisFrame ANKI_DEBUG_CODE(= numberToPtr<GlobalIlluminationProbeQueueElement*>(1));
-	UVec3 m_cellOfTheProbeToUpdateThisFrame ANKI_DEBUG_CODE(= UVec3(kMaxU32));
+	GlobalIlluminationProbeQueueElementForRefresh* m_probeToUpdateThisFrame = nullptr;
 
 
 	Array<RenderTargetHandle, kGBufferColorRenderTargetCount> m_gbufferColorRts;
 	Array<RenderTargetHandle, kGBufferColorRenderTargetCount> m_gbufferColorRts;
 	RenderTargetHandle m_gbufferDepthRt;
 	RenderTargetHandle m_gbufferDepthRt;
 	RenderTargetHandle m_shadowsRt;
 	RenderTargetHandle m_shadowsRt;
 	RenderTargetHandle m_lightShadingRt;
 	RenderTargetHandle m_lightShadingRt;
-	WeakArray<RenderTargetHandle> m_irradianceProbeRts;
+	RenderTargetHandle m_irradianceVolume;
 
 
-	U32 m_gbufferDrawcallCount ANKI_DEBUG_CODE(= kMaxU32);
-	U32 m_smDrawcallCount ANKI_DEBUG_CODE(= kMaxU32);
+	U32 m_gbufferDrawcallCount = kMaxU32;
+	U32 m_smDrawcallCount = kMaxU32;
 
 
 	static void foo()
 	static void foo()
 	{
 	{
@@ -56,43 +38,17 @@ public:
 
 
 IndirectDiffuseProbes::~IndirectDiffuseProbes()
 IndirectDiffuseProbes::~IndirectDiffuseProbes()
 {
 {
-	m_cacheEntries.destroy(getMemoryPool());
-	m_probeUuidToCacheEntryIdx.destroy(getMemoryPool());
 }
 }
 
 
-const RenderTargetHandle&
-IndirectDiffuseProbes::getVolumeRenderTarget(const GlobalIlluminationProbeQueueElement& probe) const
+RenderTargetHandle IndirectDiffuseProbes::getCurrentlyRefreshedVolumeRt() const
 {
 {
-	ANKI_ASSERT(m_giCtx);
-	ANKI_ASSERT(&probe >= &m_giCtx->m_ctx->m_renderQueue->m_giProbes.getFront()
-				&& &probe <= &m_giCtx->m_ctx->m_renderQueue->m_giProbes.getBack());
-	const U32 idx = U32(&probe - &m_giCtx->m_ctx->m_renderQueue->m_giProbes.getFront());
-	return m_giCtx->m_irradianceProbeRts[idx];
-}
-
-void IndirectDiffuseProbes::setRenderGraphDependencies(const RenderingContext& ctx, RenderPassDescriptionBase& pass,
-													   TextureUsageBit usage) const
-{
-	for(U32 idx = 0; idx < ctx.m_renderQueue->m_giProbes.getSize(); ++idx)
-	{
-		pass.newTextureDependency(getVolumeRenderTarget(ctx.m_renderQueue->m_giProbes[idx]), usage);
-	}
+	ANKI_ASSERT(m_giCtx && m_giCtx->m_irradianceVolume.isValid());
+	return m_giCtx->m_irradianceVolume;
 }
 }
 
 
-void IndirectDiffuseProbes::bindVolumeTextures(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx, U32 set,
-											   U32 binding) const
+Bool IndirectDiffuseProbes::hasCurrentlyRefreshedVolumeRt() const
 {
 {
-	for(U32 idx = 0; idx < kMaxVisibleGlobalIlluminationProbes; ++idx)
-	{
-		if(idx < ctx.m_renderQueue->m_giProbes.getSize())
-		{
-			rgraphCtx.bindColorTexture(set, binding, getVolumeRenderTarget(ctx.m_renderQueue->m_giProbes[idx]), idx);
-		}
-		else
-		{
-			rgraphCtx.m_commandBuffer->bindTexture(set, binding, m_r->getDummyTextureView3d(), idx);
-		}
-	}
+	return m_giCtx != nullptr;
 }
 }
 
 
 Error IndirectDiffuseProbes::init()
 Error IndirectDiffuseProbes::init()
@@ -109,10 +65,6 @@ Error IndirectDiffuseProbes::init()
 Error IndirectDiffuseProbes::initInternal()
 Error IndirectDiffuseProbes::initInternal()
 {
 {
 	m_tileSize = getExternalSubsystems().m_config->getRIndirectDiffuseProbeTileResolution();
 	m_tileSize = getExternalSubsystems().m_config->getRIndirectDiffuseProbeTileResolution();
-	m_cacheEntries.create(getMemoryPool(), getExternalSubsystems().m_config->getRIndirectDiffuseProbeMaxCachedProbes());
-	m_maxVisibleProbes = getExternalSubsystems().m_config->getRIndirectDiffuseProbeMaxVisibleProbes();
-	ANKI_ASSERT(m_maxVisibleProbes <= kMaxVisibleGlobalIlluminationProbes);
-	ANKI_ASSERT(m_cacheEntries.getSize() >= m_maxVisibleProbes);
 
 
 	ANKI_CHECK(initGBuffer());
 	ANKI_CHECK(initGBuffer());
 	ANKI_CHECK(initLightShading());
 	ANKI_CHECK(initLightShading());
@@ -229,21 +181,19 @@ void IndirectDiffuseProbes::populateRenderGraph(RenderingContext& rctx)
 {
 {
 	ANKI_TRACE_SCOPED_EVENT(RIndirectDiffuse);
 	ANKI_TRACE_SCOPED_EVENT(RIndirectDiffuse);
 
 
+	if(rctx.m_renderQueue->m_giProbeForRefresh == nullptr) [[likely]]
+	{
+		m_giCtx = nullptr;
+		return;
+	}
+
 	InternalContext* giCtx = newInstance<InternalContext>(*rctx.m_tempPool);
 	InternalContext* giCtx = newInstance<InternalContext>(*rctx.m_tempPool);
 	m_giCtx = giCtx;
 	m_giCtx = giCtx;
 	giCtx->m_gi = this;
 	giCtx->m_gi = this;
 	giCtx->m_ctx = &rctx;
 	giCtx->m_ctx = &rctx;
+	giCtx->m_probeToUpdateThisFrame = rctx.m_renderQueue->m_giProbeForRefresh;
 	RenderGraphDescription& rgraph = rctx.m_renderGraphDescr;
 	RenderGraphDescription& rgraph = rctx.m_renderGraphDescr;
 
 
-	// Prepare the probes
-	prepareProbes(*giCtx);
-	const Bool haveProbeToRender = giCtx->m_probeToUpdateThisFrame != nullptr;
-	if(!haveProbeToRender)
-	{
-		// Early exit
-		return;
-	}
-
 	// Compute task counts for some of the passes
 	// Compute task counts for some of the passes
 	U32 gbufferTaskCount, smTaskCount;
 	U32 gbufferTaskCount, smTaskCount;
 	{
 	{
@@ -365,6 +315,9 @@ void IndirectDiffuseProbes::populateRenderGraph(RenderingContext& rctx)
 
 
 	// Irradiance pass. First & 2nd bounce
 	// Irradiance pass. First & 2nd bounce
 	{
 	{
+		m_giCtx->m_irradianceVolume = rgraph.importRenderTarget(
+			TexturePtr(m_giCtx->m_probeToUpdateThisFrame->m_volumeTexture), TextureUsageBit::kNone);
+
 		ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("GI IR");
 		ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("GI IR");
 
 
 		pass.setWork([this, giCtx](RenderPassWorkContext& rgraphCtx) {
 		pass.setWork([this, giCtx](RenderPassWorkContext& rgraphCtx) {
@@ -378,170 +331,7 @@ void IndirectDiffuseProbes::populateRenderGraph(RenderingContext& rctx)
 			pass.newTextureDependency(giCtx->m_gbufferColorRts[i], TextureUsageBit::kSampledCompute);
 			pass.newTextureDependency(giCtx->m_gbufferColorRts[i], TextureUsageBit::kSampledCompute);
 		}
 		}
 
 
-		const U32 probeIdx = U32(giCtx->m_probeToUpdateThisFrame - &giCtx->m_ctx->m_renderQueue->m_giProbes.getFront());
-		pass.newTextureDependency(giCtx->m_irradianceProbeRts[probeIdx], TextureUsageBit::kImageComputeWrite);
-	}
-}
-
-void IndirectDiffuseProbes::prepareProbes(InternalContext& giCtx)
-{
-	RenderingContext& ctx = *giCtx.m_ctx;
-	giCtx.m_probeToUpdateThisFrame = nullptr;
-
-	if(ctx.m_renderQueue->m_giProbes.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<GlobalIlluminationProbeQueueElement> newListOfProbes;
-	newListOfProbes.create(*ctx.m_tempPool, ctx.m_renderQueue->m_giProbes.getSize());
-	DynamicArray<RenderTargetHandle> volumeRts;
-	volumeRts.create(*ctx.m_tempPool, ctx.m_renderQueue->m_giProbes.getSize());
-	U32 newListOfProbeCount = 0;
-	Bool foundProbeToUpdateNextFrame = false;
-	for(U32 probeIdx = 0; probeIdx < ctx.m_renderQueue->m_giProbes.getSize(); ++probeIdx)
-	{
-		if(newListOfProbeCount + 1 >= m_maxVisibleProbes)
-		{
-			ANKI_R_LOGW("Can't have more that %u visible probes. Increase the r_giMaxVisibleProbes or (somehow) "
-						"decrease the visible probes",
-						m_maxVisibleProbes);
-			break;
-		}
-
-		GlobalIlluminationProbeQueueElement& probe = ctx.m_renderQueue->m_giProbes[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_giMaxCachedProbes or (somehow) decrease the visible probes");
-			continue;
-		}
-
-		CacheEntry& entry = m_cacheEntries[cacheEntryIdx];
-
-		const Bool cacheEntryDirty = entry.m_uuid != probe.m_uuid || entry.m_volumeSize != probe.m_cellCounts
-									 || entry.m_probeAabbMin != probe.m_aabbMin
-									 || entry.m_probeAabbMax != probe.m_aabbMax;
-		const Bool needsUpdate = cacheEntryDirty || entry.m_renderedCells < probe.m_totalCellCount;
-
-		if(!needsUpdate) [[likely]]
-		{
-			// It's updated, early exit
-
-			entry.m_lastUsedTimestamp = *getExternalSubsystems().m_globTimestamp;
-			volumeRts[newListOfProbeCount] =
-				ctx.m_renderGraphDescr.importRenderTarget(entry.m_volumeTex, TextureUsageBit::kSampledFragment);
-			newListOfProbes[newListOfProbeCount++] = probe;
-			continue;
-		}
-
-		// It needs update
-
-		const Bool canUpdateThisFrame = giCtx.m_probeToUpdateThisFrame == nullptr && probe.m_renderQueues[0] != nullptr;
-		const Bool canUpdateNextFrame = !foundProbeToUpdateNextFrame;
-
-		if(!canUpdateThisFrame && canUpdateNextFrame)
-		{
-			// Probe will (possibly) be updated next frame, inform the probe
-
-			foundProbeToUpdateNextFrame = true;
-
-			const U32 cellToRender = (cacheEntryDirty) ? 0 : entry.m_renderedCells;
-			const Vec3 cellPos = computeProbeCellPosition(cellToRender, probe);
-			probe.m_feedbackCallback(true, probe.m_feedbackCallbackUserData, cellPos.xyz0());
-			continue;
-		}
-		else if(!canUpdateThisFrame)
-		{
-			// Can't be updated this frame so remove it from the list
-			continue;
-		}
-
-		// All good, can use this probe in this frame
-
-		if(cacheEntryDirty)
-		{
-			entry.m_renderedCells = 0;
-			entry.m_uuid = probe.m_uuid;
-			entry.m_probeAabbMin = probe.m_aabbMin;
-			entry.m_probeAabbMax = probe.m_aabbMax;
-			entry.m_volumeSize = probe.m_cellCounts;
-			m_probeUuidToCacheEntryIdx.emplace(getMemoryPool(), probe.m_uuid, cacheEntryIdx);
-		}
-
-		// Update the cache entry
-		entry.m_lastUsedTimestamp = *getExternalSubsystems().m_globTimestamp;
-
-		// Init the cache entry textures
-		const Bool shouldInitTextures = !entry.m_volumeTex.isCreated() || entry.m_volumeSize != probe.m_cellCounts;
-		if(shouldInitTextures)
-		{
-			TextureInitInfo texInit("GiProbeVolume");
-			texInit.m_type = TextureType::k3D;
-			texInit.m_format = m_r->getHdrFormat();
-			texInit.m_width = probe.m_cellCounts.x() * 6;
-			texInit.m_height = probe.m_cellCounts.y();
-			texInit.m_depth = probe.m_cellCounts.z();
-			texInit.m_usage = TextureUsageBit::kAllCompute | TextureUsageBit::kAllSampled;
-
-			entry.m_volumeTex = m_r->createAndClearRenderTarget(texInit, TextureUsageBit::kSampledFragment);
-		}
-
-		// Compute the render position
-		const U32 cellToRender = entry.m_renderedCells++;
-		ANKI_ASSERT(cellToRender < probe.m_totalCellCount);
-		unflatten3dArrayIndex(probe.m_cellCounts.z(), probe.m_cellCounts.y(), probe.m_cellCounts.x(), cellToRender,
-							  giCtx.m_cellOfTheProbeToUpdateThisFrame.z(), giCtx.m_cellOfTheProbeToUpdateThisFrame.y(),
-							  giCtx.m_cellOfTheProbeToUpdateThisFrame.x());
-
-		// Inform probe about its next frame
-		if(entry.m_renderedCells == probe.m_totalCellCount)
-		{
-			// Don't gather renderables next frame if it's done
-			probe.m_feedbackCallback(false, probe.m_feedbackCallbackUserData, Vec4(0.0f));
-		}
-		else if(!foundProbeToUpdateNextFrame)
-		{
-			// Gather rendederables from the same probe next frame
-			foundProbeToUpdateNextFrame = true;
-			const Vec3 cellPos = computeProbeCellPosition(entry.m_renderedCells, probe);
-			probe.m_feedbackCallback(true, probe.m_feedbackCallbackUserData, cellPos.xyz0());
-		}
-
-		// Push the probe to the new list
-		giCtx.m_probeToUpdateThisFrame = &newListOfProbes[newListOfProbeCount];
-		newListOfProbes[newListOfProbeCount] = probe;
-		volumeRts[newListOfProbeCount] =
-			ctx.m_renderGraphDescr.importRenderTarget(entry.m_volumeTex, TextureUsageBit::kSampledFragment);
-		++newListOfProbeCount;
-	}
-
-	// Replace the probe list in the queue
-	if(newListOfProbeCount > 0)
-	{
-		GlobalIlluminationProbeQueueElement* firstProbe;
-		U32 probeCount, storage;
-		newListOfProbes.moveAndReset(firstProbe, probeCount, storage);
-		ctx.m_renderQueue->m_giProbes = WeakArray<GlobalIlluminationProbeQueueElement>(firstProbe, newListOfProbeCount);
-
-		RenderTargetHandle* firstRt;
-		volumeRts.moveAndReset(firstRt, probeCount, storage);
-		m_giCtx->m_irradianceProbeRts = WeakArray<RenderTargetHandle>(firstRt, newListOfProbeCount);
-	}
-	else
-	{
-		ctx.m_renderQueue->m_giProbes = WeakArray<GlobalIlluminationProbeQueueElement>();
-		newListOfProbes.destroy(*ctx.m_tempPool);
-		volumeRts.destroy(*ctx.m_tempPool);
+		pass.newTextureDependency(m_giCtx->m_irradianceVolume, TextureUsageBit::kImageComputeWrite);
 	}
 	}
 }
 }
 
 
@@ -551,7 +341,7 @@ void IndirectDiffuseProbes::runGBufferInThread(RenderPassWorkContext& rgraphCtx,
 	ANKI_TRACE_SCOPED_EVENT(RIndirectDiffuse);
 	ANKI_TRACE_SCOPED_EVENT(RIndirectDiffuse);
 
 
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	const GlobalIlluminationProbeQueueElement& probe = *giCtx.m_probeToUpdateThisFrame;
+	const GlobalIlluminationProbeQueueElementForRefresh& probe = *giCtx.m_probeToUpdateThisFrame;
 
 
 	I32 start, end;
 	I32 start, end;
 	U32 startu, endu;
 	U32 startu, endu;
@@ -602,7 +392,7 @@ void IndirectDiffuseProbes::runShadowmappingInThread(RenderPassWorkContext& rgra
 	ANKI_ASSERT(giCtx.m_probeToUpdateThisFrame);
 	ANKI_ASSERT(giCtx.m_probeToUpdateThisFrame);
 	ANKI_TRACE_SCOPED_EVENT(RIndirectDiffuse);
 	ANKI_TRACE_SCOPED_EVENT(RIndirectDiffuse);
 
 
-	const GlobalIlluminationProbeQueueElement& probe = *giCtx.m_probeToUpdateThisFrame;
+	const GlobalIlluminationProbeQueueElementForRefresh& probe = *giCtx.m_probeToUpdateThisFrame;
 
 
 	I32 start, end;
 	I32 start, end;
 	U32 startu, endu;
 	U32 startu, endu;
@@ -657,7 +447,7 @@ void IndirectDiffuseProbes::runLightShading(RenderPassWorkContext& rgraphCtx, In
 	ANKI_TRACE_SCOPED_EVENT(RIndirectDiffuse);
 	ANKI_TRACE_SCOPED_EVENT(RIndirectDiffuse);
 
 
 	ANKI_ASSERT(giCtx.m_probeToUpdateThisFrame);
 	ANKI_ASSERT(giCtx.m_probeToUpdateThisFrame);
-	const GlobalIlluminationProbeQueueElement& probe = *giCtx.m_probeToUpdateThisFrame;
+	const GlobalIlluminationProbeQueueElementForRefresh& probe = *giCtx.m_probeToUpdateThisFrame;
 
 
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 
@@ -707,8 +497,7 @@ void IndirectDiffuseProbes::runIrradiance(RenderPassWorkContext& rgraphCtx, Inte
 
 
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 	ANKI_ASSERT(giCtx.m_probeToUpdateThisFrame);
 	ANKI_ASSERT(giCtx.m_probeToUpdateThisFrame);
-	const GlobalIlluminationProbeQueueElement& probe = *giCtx.m_probeToUpdateThisFrame;
-	const U32 probeIdx = U32(&probe - &giCtx.m_ctx->m_renderQueue->m_giProbes.getFront());
+	const GlobalIlluminationProbeQueueElementForRefresh& probe = *giCtx.m_probeToUpdateThisFrame;
 
 
 	cmdb->bindShaderProgram(m_irradiance.m_grProg);
 	cmdb->bindShaderProgram(m_irradiance.m_grProg);
 
 
@@ -721,7 +510,7 @@ void IndirectDiffuseProbes::runIrradiance(RenderPassWorkContext& rgraphCtx, Inte
 		rgraphCtx.bindColorTexture(0, 2, giCtx.m_gbufferColorRts[i], i);
 		rgraphCtx.bindColorTexture(0, 2, giCtx.m_gbufferColorRts[i], i);
 	}
 	}
 
 
-	rgraphCtx.bindImage(0, 3, giCtx.m_irradianceProbeRts[probeIdx], TextureSubresourceInfo());
+	rgraphCtx.bindImage(0, 3, giCtx.m_irradianceVolume, TextureSubresourceInfo());
 
 
 	class
 	class
 	{
 	{
@@ -730,8 +519,7 @@ void IndirectDiffuseProbes::runIrradiance(RenderPassWorkContext& rgraphCtx, Inte
 		I32 m_nextTexelOffsetInU;
 		I32 m_nextTexelOffsetInU;
 	} unis;
 	} unis;
 
 
-	unis.m_volumeTexel = IVec3(giCtx.m_cellOfTheProbeToUpdateThisFrame.x(), giCtx.m_cellOfTheProbeToUpdateThisFrame.y(),
-							   giCtx.m_cellOfTheProbeToUpdateThisFrame.z());
+	unis.m_volumeTexel = IVec3(probe.m_cellToRefresh.x(), probe.m_cellToRefresh.y(), probe.m_cellToRefresh.z());
 	unis.m_nextTexelOffsetInU = probe.m_cellCounts.x();
 	unis.m_nextTexelOffsetInU = probe.m_cellCounts.x();
 	cmdb->setPushConstants(&unis, sizeof(unis));
 	cmdb->setPushConstants(&unis, sizeof(unis));
 
 

+ 2 - 25
AnKi/Renderer/IndirectDiffuseProbes.h

@@ -34,31 +34,13 @@ public:
 	/// Populate the rendergraph.
 	/// Populate the rendergraph.
 	void populateRenderGraph(RenderingContext& ctx);
 	void populateRenderGraph(RenderingContext& ctx);
 
 
-	/// Return the volume RT given a cache entry index.
-	const RenderTargetHandle& getVolumeRenderTarget(const GlobalIlluminationProbeQueueElement& probe) const;
+	RenderTargetHandle getCurrentlyRefreshedVolumeRt() const;
 
 
-	/// Set the render graph dependencies.
-	void setRenderGraphDependencies(const RenderingContext& ctx, RenderPassDescriptionBase& pass,
-									TextureUsageBit usage) const;
-
-	/// Bind the volume textures to a command buffer.
-	void bindVolumeTextures(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx, U32 set, U32 binding) const;
+	Bool hasCurrentlyRefreshedVolumeRt() const;
 
 
 private:
 private:
 	class InternalContext;
 	class InternalContext;
 
 
-	class CacheEntry
-	{
-	public:
-		U64 m_uuid; ///< Probe UUID.
-		Timestamp m_lastUsedTimestamp = 0; ///< When it was last seen by the renderer.
-		TexturePtr m_volumeTex; ///< Contains the 6 directions.
-		UVec3 m_volumeSize = UVec3(0u);
-		Vec3 m_probeAabbMin = Vec3(0.0f);
-		Vec3 m_probeAabbMax = Vec3(0.0f);
-		U32 m_renderedCells = 0;
-	};
-
 	class
 	class
 	{
 	{
 	public:
 	public:
@@ -95,10 +77,7 @@ private:
 	} m_irradiance; ///< Irradiance.
 	} m_irradiance; ///< Irradiance.
 
 
 	InternalContext* m_giCtx = nullptr;
 	InternalContext* m_giCtx = nullptr;
-	DynamicArray<CacheEntry> m_cacheEntries;
-	HashMap<U64, U32> m_probeUuidToCacheEntryIdx;
 	U32 m_tileSize = 0;
 	U32 m_tileSize = 0;
-	U32 m_maxVisibleProbes = 0;
 
 
 	Error initInternal();
 	Error initInternal();
 	Error initGBuffer();
 	Error initGBuffer();
@@ -110,8 +89,6 @@ private:
 	void runShadowmappingInThread(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx) const;
 	void runShadowmappingInThread(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx) const;
 	void runLightShading(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx);
 	void runLightShading(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx);
 	void runIrradiance(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx);
 	void runIrradiance(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx);
-
-	void prepareProbes(InternalContext& giCtx);
 };
 };
 /// @}
 /// @}
 
 

+ 16 - 8
AnKi/Renderer/RenderQueue.h

@@ -211,24 +211,17 @@ public:
 	Texture* m_reflectionTexture;
 	Texture* m_reflectionTexture;
 };
 };
 
 
-/// See ReflectionProbeQueueElementFeedbackCallback for its purpose.
-using GlobalIlluminationProbeQueueElementFeedbackCallback = void (*)(Bool fillRenderQueuesOnNextFrame, void* userData,
-																	 const Vec4& eyeWorldPosition);
-
 // Probe for global illumination.
 // Probe for global illumination.
 class GlobalIlluminationProbeQueueElement final
 class GlobalIlluminationProbeQueueElement final
 {
 {
 public:
 public:
-	U64 m_uuid;
-	GlobalIlluminationProbeQueueElementFeedbackCallback m_feedbackCallback;
-	void* m_feedbackCallbackUserData;
-	Array<RenderQueue*, 6> m_renderQueues;
 	Vec3 m_aabbMin;
 	Vec3 m_aabbMin;
 	Vec3 m_aabbMax;
 	Vec3 m_aabbMax;
 	UVec3 m_cellCounts;
 	UVec3 m_cellCounts;
 	U32 m_totalCellCount;
 	U32 m_totalCellCount;
 	Vec3 m_cellSizes; ///< The cells might not be cubes so have different sizes per dimension.
 	Vec3 m_cellSizes; ///< The cells might not be cubes so have different sizes per dimension.
 	F32 m_fadeDistance;
 	F32 m_fadeDistance;
+	U32 m_volumeTextureBindlessIndex;
 
 
 	GlobalIlluminationProbeQueueElement()
 	GlobalIlluminationProbeQueueElement()
 	{
 	{
@@ -248,6 +241,20 @@ public:
 };
 };
 static_assert(std::is_trivially_destructible<GlobalIlluminationProbeQueueElement>::value == true);
 static_assert(std::is_trivially_destructible<GlobalIlluminationProbeQueueElement>::value == true);
 
 
+/// Contains info for a GI probe that the renderer will have to refresh.
+class GlobalIlluminationProbeQueueElementForRefresh final
+{
+public:
+	Array<RenderQueue*, 6> m_renderQueues;
+	Texture* m_volumeTexture;
+	UVec3 m_cellToRefresh;
+	UVec3 m_cellCounts;
+
+	GlobalIlluminationProbeQueueElementForRefresh()
+	{
+	}
+};
+
 /// Lens flare render queue element.
 /// Lens flare render queue element.
 class LensFlareQueueElement final
 class LensFlareQueueElement final
 {
 {
@@ -402,6 +409,7 @@ public:
 	void* m_fillCoverageBufferCallbackUserData = nullptr;
 	void* m_fillCoverageBufferCallbackUserData = nullptr;
 
 
 	ReflectionProbeQueueElementForRefresh* m_reflectionProbeForRefresh = nullptr;
 	ReflectionProbeQueueElementForRefresh* m_reflectionProbeForRefresh = nullptr;
+	GlobalIlluminationProbeQueueElementForRefresh* m_giProbeForRefresh = nullptr;
 
 
 	RenderQueue()
 	RenderQueue()
 	{
 	{

+ 10 - 5
AnKi/Renderer/VolumetricLightingAccumulation.cpp

@@ -92,7 +92,11 @@ void VolumetricLightingAccumulation::populateRenderGraph(RenderingContext& ctx)
 
 
 	pass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageComputeRead);
 	pass.newBufferDependency(ctx.m_clusteredShading.m_clustersBufferHandle, BufferUsageBit::kStorageComputeRead);
 
 
-	m_r->getIndirectDiffuseProbes().setRenderGraphDependencies(ctx, pass, TextureUsageBit::kSampledCompute);
+	if(m_r->getIndirectDiffuseProbes().hasCurrentlyRefreshedVolumeRt())
+	{
+		pass.newTextureDependency(m_r->getIndirectDiffuseProbes().getCurrentlyRefreshedVolumeRt(),
+								  TextureUsageBit::kSampledCompute);
+	}
 }
 }
 
 
 void VolumetricLightingAccumulation::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 void VolumetricLightingAccumulation::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
@@ -118,11 +122,12 @@ void VolumetricLightingAccumulation::run(const RenderingContext& ctx, RenderPass
 	bindUniforms(cmdb, 0, 8, rsrc.m_spotLightsToken);
 	bindUniforms(cmdb, 0, 8, rsrc.m_spotLightsToken);
 	rgraphCtx.bindColorTexture(0, 9, m_r->getShadowMapping().getShadowmapRt());
 	rgraphCtx.bindColorTexture(0, 9, m_r->getShadowMapping().getShadowmapRt());
 
 
-	m_r->getIndirectDiffuseProbes().bindVolumeTextures(ctx, rgraphCtx, 0, 10);
-	bindUniforms(cmdb, 0, 11, rsrc.m_globalIlluminationProbesToken);
+	bindUniforms(cmdb, 0, 10, rsrc.m_globalIlluminationProbesToken);
+
+	bindUniforms(cmdb, 0, 11, rsrc.m_fogDensityVolumesToken);
+	bindStorage(cmdb, 0, 12, rsrc.m_clustersToken);
 
 
-	bindUniforms(cmdb, 0, 12, rsrc.m_fogDensityVolumesToken);
-	bindStorage(cmdb, 0, 13, rsrc.m_clustersToken);
+	cmdb->bindAllBindless(1);
 
 
 	VolumetricLightingUniforms unis;
 	VolumetricLightingUniforms unis;
 	const SkyboxQueueElement& queueEl = ctx.m_renderQueue->m_skybox;
 	const SkyboxQueueElement& queueEl = ctx.m_renderQueue->m_skybox;

+ 77 - 7
AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp

@@ -8,12 +8,12 @@
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Core/ConfigSet.h>
 #include <AnKi/Core/ConfigSet.h>
+#include <AnKi/Resource/ResourceManager.h>
 
 
 namespace anki {
 namespace anki {
 
 
 GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* node)
 GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
-	, m_uuid(node->getSceneGraph().getNewUuid())
 	, m_spatial(this)
 	, m_spatial(this)
 {
 {
 	for(U32 i = 0; i < 6; ++i)
 	for(U32 i = 0; i < 6; ++i)
@@ -28,6 +28,13 @@ GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* no
 
 
 	m_gpuSceneOffset = U32(node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
 	m_gpuSceneOffset = U32(node->getSceneGraph().getAllGpuSceneContiguousArrays().allocate(
 		GpuSceneContiguousArrayType::kGlobalIlluminationProbes));
 		GpuSceneContiguousArrayType::kGlobalIlluminationProbes));
+
+	const Error err = getExternalSubsystems(*node).m_resourceManager->loadResource(
+		"ShaderBinaries/ClearTextureCompute.ankiprogbin", m_clearTextureProg);
+	if(err)
+	{
+		ANKI_LOGF("Failed to load shader");
+	}
 }
 }
 
 
 GlobalIlluminationProbeComponent::~GlobalIlluminationProbeComponent()
 GlobalIlluminationProbeComponent::~GlobalIlluminationProbeComponent()
@@ -46,12 +53,67 @@ Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, B
 {
 {
 	updated = info.m_node->movedThisFrame() || m_shapeDirty;
 	updated = info.m_node->movedThisFrame() || m_shapeDirty;
 
 
+	if(m_shapeDirty) [[unlikely]]
+	{
+		TextureInitInfo texInit("GiProbe");
+		texInit.m_format =
+			(getExternalSubsystems(*info.m_node).m_grManager->getDeviceCapabilities().m_unalignedBbpTextureFormats)
+				? Format::kR16G16B16_Sfloat
+				: Format::kR16G16B16A16_Sfloat;
+		texInit.m_width = m_cellCounts.x() * 6;
+		texInit.m_height = m_cellCounts.y();
+		texInit.m_depth = m_cellCounts.z();
+		texInit.m_type = TextureType::k3D;
+		texInit.m_usage =
+			TextureUsageBit::kAllSampled | TextureUsageBit::kImageComputeWrite | TextureUsageBit::kImageComputeRead;
+
+		m_volTex = getExternalSubsystems(*info.m_node).m_grManager->newTexture(texInit);
+
+		TextureViewInitInfo viewInit(m_volTex, "GiProbe");
+		m_volView = getExternalSubsystems(*info.m_node).m_grManager->newTextureView(viewInit);
+
+		m_volTexBindlessIdx = m_volView->getOrCreateBindlessTextureIndex();
+
+		// Zero the texture
+		const ShaderProgramResourceVariant* variant;
+		ShaderProgramResourceVariantInitInfo variantInit(m_clearTextureProg);
+		variantInit.addMutation("TEXTURE_DIMENSIONS", 3);
+		variantInit.addMutation("COMPONENT_TYPE", 0);
+		m_clearTextureProg->getOrCreateVariant(variantInit, variant);
+
+		CommandBufferInitInfo cmdbInit("ClearGIVol");
+		cmdbInit.m_flags = CommandBufferFlag::kSmallBatch | CommandBufferFlag::kGeneralWork;
+		CommandBufferPtr cmdb = getExternalSubsystems(*info.m_node).m_grManager->newCommandBuffer(cmdbInit);
+
+		TextureBarrierInfo texBarrier;
+		texBarrier.m_previousUsage = TextureUsageBit::kNone;
+		texBarrier.m_nextUsage = TextureUsageBit::kImageComputeWrite;
+		texBarrier.m_texture = m_volTex.get();
+		cmdb->setPipelineBarrier({&texBarrier, 1}, {}, {});
+
+		cmdb->bindShaderProgram(variant->getProgram());
+		cmdb->bindImage(0, 0, m_volView);
+
+		const Vec4 clearColor(0.0f);
+		cmdb->setPushConstants(&clearColor, sizeof(clearColor));
+
+		UVec3 wgSize;
+		wgSize.x() = (8 - 1 + m_volTex->getWidth()) / 8;
+		wgSize.y() = (8 - 1 + m_volTex->getHeight()) / 8;
+		wgSize.z() = (8 - 1 + m_volTex->getDepth()) / 8;
+		cmdb->dispatchCompute(wgSize.x(), wgSize.y(), wgSize.z());
+
+		texBarrier.m_previousUsage = TextureUsageBit::kImageComputeWrite;
+		texBarrier.m_nextUsage = m_volTex->getTextureUsage();
+		cmdb->setPipelineBarrier({&texBarrier, 1}, {}, {});
+
+		cmdb->flush();
+	}
+
 	if(updated) [[unlikely]]
 	if(updated) [[unlikely]]
 	{
 	{
 		m_shapeDirty = false;
 		m_shapeDirty = false;
-
-		// Set a new UUID to force the renderer to update the probe
-		m_uuid = info.m_node->getSceneGraph().getNewUuid();
+		m_cellIdxToRefresh = 0;
 
 
 		m_worldPos = info.m_node->getWorldTransform().getOrigin().xyz();
 		m_worldPos = info.m_node->getWorldTransform().getOrigin().xyz();
 
 
@@ -62,7 +124,7 @@ Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, B
 		GpuSceneGlobalIlluminationProbe gpuProbe;
 		GpuSceneGlobalIlluminationProbe gpuProbe;
 		gpuProbe.m_aabbMin = aabb.getMin().xyz();
 		gpuProbe.m_aabbMin = aabb.getMin().xyz();
 		gpuProbe.m_aabbMax = aabb.getMax().xyz();
 		gpuProbe.m_aabbMax = aabb.getMax().xyz();
-		gpuProbe.m_textureIndex = 0; // Unknown at this point
+		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()) / 2.0f;
 		gpuProbe.m_fadeDistance = m_fadeDistance;
 		gpuProbe.m_fadeDistance = m_fadeDistance;
 
 
@@ -70,10 +132,18 @@ Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, B
 			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneOffset, sizeof(gpuProbe), &gpuProbe);
 			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneOffset, sizeof(gpuProbe), &gpuProbe);
 	}
 	}
 
 
-	if(m_markedForRendering) [[unlikely]]
+	if(needsRefresh()) [[unlikely]]
 	{
 	{
 		updated = true;
 		updated = true;
 
 
+		// Compute the position of the cell
+		const Aabb aabb(-m_halfSize + m_worldPos, m_halfSize + m_worldPos);
+		U32 x, y, z;
+		unflatten3dArrayIndex(m_cellCounts.x(), m_cellCounts.y(), m_cellCounts.z(), m_cellIdxToRefresh, x, y, z);
+		const Vec3 cellSize = ((m_halfSize * 2.0f) / Vec3(m_cellCounts));
+		const Vec3 halfCellSize = cellSize / 2.0f;
+		const Vec3 cellCenter = aabb.getMin().xyz() + halfCellSize + cellSize * Vec3(UVec3(x, y, z));
+
 		F32 effectiveDistance = max(m_halfSize.x(), m_halfSize.y());
 		F32 effectiveDistance = max(m_halfSize.x(), m_halfSize.y());
 		effectiveDistance = max(effectiveDistance, m_halfSize.z());
 		effectiveDistance = max(effectiveDistance, m_halfSize.z());
 		effectiveDistance =
 		effectiveDistance =
@@ -85,7 +155,7 @@ Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, B
 		for(U32 i = 0; i < 6; ++i)
 		for(U32 i = 0; i < 6; ++i)
 		{
 		{
 			m_frustums[i].setWorldTransform(
 			m_frustums[i].setWorldTransform(
-				Transform(m_renderPosition.xyz0(), Frustum::getOmnidirectionalFrustumRotations()[i], 1.0f));
+				Transform(cellCenter.xyz0(), Frustum::getOmnidirectionalFrustumRotations()[i], 1.0f));
 
 
 			m_frustums[i].setFar(effectiveDistance);
 			m_frustums[i].setFar(effectiveDistance);
 			m_frustums[i].setShadowCascadeDistance(0, shadowCascadeDistance);
 			m_frustums[i].setShadowCascadeDistance(0, shadowCascadeDistance);

+ 31 - 34
AnKi/Scene/Components/GlobalIlluminationProbeComponent.h

@@ -63,20 +63,6 @@ public:
 		m_fadeDistance = max(0.0f, dist);
 		m_fadeDistance = max(0.0f, dist);
 	}
 	}
 
 
-	/// Returns true if it's marked for update this frame.
-	Bool getMarkedForRendering() const
-	{
-		return m_markedForRendering;
-	}
-
-	/// Get the cell position that will be rendered this frame.
-	Vec3 getRenderPosition() const
-	{
-		ANKI_ASSERT(m_renderPosition > -m_halfSize + m_worldPos && m_renderPosition < m_halfSize + m_worldPos);
-		ANKI_ASSERT(m_markedForRendering);
-		return m_renderPosition;
-	}
-
 	WeakArray<Frustum> getFrustums()
 	WeakArray<Frustum> getFrustums()
 	{
 	{
 		return m_frustums;
 		return m_frustums;
@@ -84,47 +70,57 @@ public:
 
 
 	void setupGlobalIlluminationProbeQueueElement(GlobalIlluminationProbeQueueElement& el)
 	void setupGlobalIlluminationProbeQueueElement(GlobalIlluminationProbeQueueElement& el)
 	{
 	{
-		el.m_uuid = m_uuid;
-		el.m_feedbackCallback = giProbeQueueElementFeedbackCallback;
-		el.m_feedbackCallbackUserData = this;
-		el.m_renderQueues = {};
 		el.m_aabbMin = -m_halfSize + m_worldPos;
 		el.m_aabbMin = -m_halfSize + m_worldPos;
 		el.m_aabbMax = m_halfSize + m_worldPos;
 		el.m_aabbMax = m_halfSize + m_worldPos;
 		el.m_cellCounts = m_cellCounts;
 		el.m_cellCounts = m_cellCounts;
-		el.m_totalCellCount = m_cellCounts.x() * m_cellCounts.y() * m_cellCounts.z();
+		el.m_totalCellCount = m_totalCellCount;
 		el.m_cellSizes = (m_halfSize * 2.0f) / Vec3(m_cellCounts);
 		el.m_cellSizes = (m_halfSize * 2.0f) / Vec3(m_cellCounts);
 		el.m_fadeDistance = m_fadeDistance;
 		el.m_fadeDistance = m_fadeDistance;
+		el.m_volumeTextureBindlessIndex = m_volTexBindlessIdx;
+	}
+
+	void setupGlobalIlluminationProbeQueueElementForRefresh(GlobalIlluminationProbeQueueElementForRefresh& el)
+	{
+		ANKI_ASSERT(m_cellIdxToRefresh < m_totalCellCount);
+		el.m_volumeTexture = m_volTex.get();
+		unflatten3dArrayIndex(m_cellCounts.x(), m_cellCounts.y(), m_cellCounts.z(), m_cellIdxToRefresh,
+							  el.m_cellToRefresh.x(), el.m_cellToRefresh.y(), el.m_cellToRefresh.z());
+		el.m_cellCounts = m_cellCounts;
+	}
+
+	Bool needsRefresh() const
+	{
+		return m_cellIdxToRefresh < m_totalCellCount;
+	}
+
+	void progressRefresh()
+	{
+		++m_cellIdxToRefresh;
 	}
 	}
 
 
 private:
 private:
-	U64 m_uuid;
 	Vec3 m_halfSize = Vec3(0.5f);
 	Vec3 m_halfSize = Vec3(0.5f);
 	Vec3 m_worldPos = Vec3(0.0f);
 	Vec3 m_worldPos = Vec3(0.0f);
-	Vec3 m_renderPosition = Vec3(0.0f);
 	UVec3 m_cellCounts = UVec3(2u);
 	UVec3 m_cellCounts = UVec3(2u);
+	U32 m_totalCellCount = 8u;
 	F32 m_cellSize = 4.0f; ///< Cell size in meters.
 	F32 m_cellSize = 4.0f; ///< Cell size in meters.
 	F32 m_fadeDistance = 0.2f;
 	F32 m_fadeDistance = 0.2f;
 
 
+	TexturePtr m_volTex;
+	TextureViewPtr m_volView;
+	U32 m_volTexBindlessIdx = 0;
+
 	U32 m_gpuSceneOffset = kMaxU32;
 	U32 m_gpuSceneOffset = kMaxU32;
 
 
 	Array<Frustum, 6> m_frustums;
 	Array<Frustum, 6> m_frustums;
 
 
 	Spatial m_spatial;
 	Spatial m_spatial;
 
 
-	Bool m_markedForRendering : 1 = false;
-	Bool m_shapeDirty : 1 = true;
+	ShaderProgramResourcePtr m_clearTextureProg;
 
 
-	static void giProbeQueueElementFeedbackCallback(Bool fillRenderQueuesOnNextFrame, void* userData,
-													const Vec4& eyeWorldPosition)
-	{
-		ANKI_ASSERT(userData);
-		GlobalIlluminationProbeComponent& self = *static_cast<GlobalIlluminationProbeComponent*>(userData);
-		ANKI_ASSERT(!(fillRenderQueuesOnNextFrame
-					  && (eyeWorldPosition.xyz() < -self.m_halfSize + self.m_worldPos
-						  || eyeWorldPosition.xyz() > self.m_halfSize + self.m_worldPos)));
-		self.m_markedForRendering = fillRenderQueuesOnNextFrame;
-		self.m_renderPosition = eyeWorldPosition.xyz();
-	}
+	U32 m_cellIdxToRefresh = 0;
+
+	Bool m_shapeDirty = true;
 
 
 	/// Recalc come values.
 	/// Recalc come values.
 	void updateMembers()
 	void updateMembers()
@@ -132,6 +128,7 @@ private:
 		const Vec3 dist = m_halfSize * 2.0f;
 		const Vec3 dist = m_halfSize * 2.0f;
 		m_cellCounts = UVec3(dist / m_cellSize);
 		m_cellCounts = UVec3(dist / m_cellSize);
 		m_cellCounts = m_cellCounts.max(UVec3(1));
 		m_cellCounts = m_cellCounts.max(UVec3(1));
+		m_totalCellCount = m_cellCounts.x() * m_cellCounts.y() * m_cellCounts.z();
 	}
 	}
 
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);

+ 13 - 8
AnKi/Scene/Visibility.cpp

@@ -574,14 +574,19 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 
 
 			GlobalIlluminationProbeComponent& giprobec = static_cast<GlobalIlluminationProbeComponent&>(comp);
 			GlobalIlluminationProbeComponent& giprobec = static_cast<GlobalIlluminationProbeComponent&>(comp);
 
 
-			GlobalIlluminationProbeQueueElement* el = result.m_giProbes.newElement(pool);
-			giprobec.setupGlobalIlluminationProbeQueueElement(*el);
-
-			if(giprobec.getMarkedForRendering())
+			if(giprobec.needsRefresh() && m_frcCtx->m_giProbesForRefreshCount.fetchAdd(1) == 0)
 			{
 			{
 				nextQueues = WeakArray<RenderQueue>(newArray<RenderQueue>(pool, 6), 6);
 				nextQueues = WeakArray<RenderQueue>(newArray<RenderQueue>(pool, 6), 6);
 				nextFrustums = WeakArray<VisibilityFrustum>(newArray<VisibilityFrustum>(pool, 6), 6);
 				nextFrustums = WeakArray<VisibilityFrustum>(newArray<VisibilityFrustum>(pool, 6), 6);
 
 
+				GlobalIlluminationProbeQueueElementForRefresh* el =
+					newInstance<GlobalIlluminationProbeQueueElementForRefresh>(pool);
+
+				m_frcCtx->m_giProbeForRefresh = el;
+
+				giprobec.setupGlobalIlluminationProbeQueueElementForRefresh(*el);
+				giprobec.progressRefresh();
+
 				for(U32 i = 0; i < 6; ++i)
 				for(U32 i = 0; i < 6; ++i)
 				{
 				{
 					el->m_renderQueues[i] = &nextQueues[i];
 					el->m_renderQueues[i] = &nextQueues[i];
@@ -589,10 +594,9 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 					static_cast<FrustumFlags&>(nextFrustums[i]) = getProbeFrustumFlags();
 					static_cast<FrustumFlags&>(nextFrustums[i]) = getProbeFrustumFlags();
 				}
 				}
 			}
 			}
-			else
-			{
-				memset(&el->m_renderQueues[0], 0, sizeof(el->m_renderQueues));
-			}
+
+			GlobalIlluminationProbeQueueElement* el = result.m_giProbes.newElement(pool);
+			giprobec.setupGlobalIlluminationProbeQueueElement(*el);
 		}
 		}
 		else if(comp.getClassId() == UiComponent::getStaticClassId())
 		else if(comp.getClassId() == UiComponent::getStaticClassId())
 		{
 		{
@@ -692,6 +696,7 @@ void CombineResultsTask::combine()
 #undef ANKI_VIS_COMBINE
 #undef ANKI_VIS_COMBINE
 
 
 	results.m_reflectionProbeForRefresh = m_frcCtx->m_reflectionProbeForRefresh;
 	results.m_reflectionProbeForRefresh = m_frcCtx->m_reflectionProbeForRefresh;
+	results.m_giProbeForRefresh = m_frcCtx->m_giProbeForRefresh;
 
 
 	for(U32 i = 0; i < threadCount; ++i)
 	for(U32 i = 0; i < threadCount; ++i)
 	{
 	{

+ 4 - 1
AnKi/Scene/VisibilityInternal.h

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

+ 3 - 6
AnKi/Shaders/ClusteredShadingCommon.hlsl

@@ -33,7 +33,7 @@
 #endif
 #endif
 
 
 //
 //
-// Reflection probes (2)
+// Reflection probes (1)
 //
 //
 #if defined(CLUSTERED_SHADING_REFLECTIONS_BINDING)
 #if defined(CLUSTERED_SHADING_REFLECTIONS_BINDING)
 [[vk::binding(CLUSTERED_SHADING_REFLECTIONS_BINDING, CLUSTERED_SHADING_SET)]] cbuffer b_reflectionProbes
 [[vk::binding(CLUSTERED_SHADING_REFLECTIONS_BINDING, CLUSTERED_SHADING_SET)]] cbuffer b_reflectionProbes
@@ -63,13 +63,10 @@
 #endif
 #endif
 
 
 //
 //
-// GI (2)
+// GI (1)
 //
 //
 #if defined(CLUSTERED_SHADING_GI_BINDING)
 #if defined(CLUSTERED_SHADING_GI_BINDING)
-[[vk::binding(CLUSTERED_SHADING_GI_BINDING, CLUSTERED_SHADING_SET)]] Texture3D<RVec4>
-	g_globalIlluminationTextures[kMaxVisibleGlobalIlluminationProbes];
-
-[[vk::binding(CLUSTERED_SHADING_GI_BINDING + 1u, CLUSTERED_SHADING_SET)]] cbuffer b_giProbes
+[[vk::binding(CLUSTERED_SHADING_GI_BINDING, CLUSTERED_SHADING_SET)]] cbuffer b_giProbes
 {
 {
 	GlobalIlluminationProbe g_giProbes[kMaxVisibleGlobalIlluminationProbes];
 	GlobalIlluminationProbe g_giProbes[kMaxVisibleGlobalIlluminationProbes];
 };
 };

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

@@ -177,7 +177,7 @@ struct GlobalIlluminationProbe
 	Vec3 m_aabbMax;
 	Vec3 m_aabbMax;
 	F32 m_padding1;
 	F32 m_padding1;
 
 
-	U32 m_textureIndex; ///< Index to the array of volume textures.
+	U32 m_volumeTexture; ///< Bindless index of the irradiance volume texture.
 	F32 m_halfTexelSizeU; ///< (1.0 / textureSize(texArr[textureIndex]).x) / 2.0
 	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.
 	/// 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;
 	RF32 m_fadeDistance;

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

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

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

@@ -105,7 +105,7 @@ struct GpuSceneGlobalIlluminationProbe
 	Vec3 m_aabbMax;
 	Vec3 m_aabbMax;
 	F32 m_padding1;
 	F32 m_padding1;
 
 
-	U32 m_textureIndex; ///< Index to the array of volume textures.
+	U32 m_volumeTexture; ///< Bindless index of the irradiance volume texture.
 	F32 m_halfTexelSizeU; ///< (1.0 / textureSize(texArr[textureIndex]).x) / 2.0
 	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.
 	/// 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;
 	RF32 m_fadeDistance;

+ 18 - 14
AnKi/Shaders/IndirectDiffuse.hlsl

@@ -22,21 +22,23 @@ ANKI_SPECIALIZATION_CONSTANT_U32(kSampleCount, 0u);
 #define CLUSTERED_SHADING_SET 0u
 #define CLUSTERED_SHADING_SET 0u
 #define CLUSTERED_SHADING_UNIFORMS_BINDING 0u
 #define CLUSTERED_SHADING_UNIFORMS_BINDING 0u
 #define CLUSTERED_SHADING_GI_BINDING 1u
 #define CLUSTERED_SHADING_GI_BINDING 1u
-#define CLUSTERED_SHADING_CLUSTERS_BINDING 3u
+#define CLUSTERED_SHADING_CLUSTERS_BINDING 2u
 #include <AnKi/Shaders/ClusteredShadingCommon.hlsl>
 #include <AnKi/Shaders/ClusteredShadingCommon.hlsl>
 
 
-[[vk::binding(4)]] SamplerState g_linearAnyClampSampler;
-[[vk::binding(5)]] Texture2D<RVec4> g_gbufferRt2;
-[[vk::binding(6)]] Texture2D g_depthTex;
-[[vk::binding(7)]] Texture2D<RVec4> g_lightBufferRt;
-[[vk::binding(8)]] Texture2D<RVec4> g_historyTex;
-[[vk::binding(9)]] Texture2D g_motionVectorsTex;
-[[vk::binding(10)]] Texture2D g_historyLengthTex;
+[[vk::binding(3)]] SamplerState g_linearAnyClampSampler;
+[[vk::binding(4)]] Texture2D<RVec4> g_gbufferRt2;
+[[vk::binding(5)]] Texture2D g_depthTex;
+[[vk::binding(6)]] Texture2D<RVec4> g_lightBufferRt;
+[[vk::binding(7)]] Texture2D<RVec4> g_historyTex;
+[[vk::binding(8)]] Texture2D g_motionVectorsTex;
+[[vk::binding(9)]] Texture2D g_historyLengthTex;
 
 
 #if defined(ANKI_COMPUTE_SHADER)
 #if defined(ANKI_COMPUTE_SHADER)
-[[vk::binding(11)]] RWTexture2D<RVec4> g_outUav;
+[[vk::binding(10)]] RWTexture2D<RVec4> g_outUav;
 #endif
 #endif
 
 
+ANKI_BINDLESS_SET(1)
+
 [[vk::push_constant]] ConstantBuffer<IndirectDiffuseUniforms> g_uniforms;
 [[vk::push_constant]] ConstantBuffer<IndirectDiffuseUniforms> g_uniforms;
 
 
 Vec4 cheapProject(Vec4 point_)
 Vec4 cheapProject(Vec4 point_)
@@ -154,15 +156,16 @@ RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITION
 		// Get the cluster
 		// Get the cluster
 		Cluster cluster = getClusterFragCoord(Vec3(fragCoord * 2.0, depth));
 		Cluster cluster = getClusterFragCoord(Vec3(fragCoord * 2.0, depth));
 
 
-		if(countbits(cluster.m_giProbesMask) == 1)
+		const U32 oneProbe = WaveActiveAllTrue(countbits(cluster.m_giProbesMask) == 1);
+		if(oneProbe)
 		{
 		{
 			// All subgroups point to the same probe and there is only one probe, do a fast path without blend weight
 			// All subgroups point to the same probe and there is only one probe, do a fast path without blend weight
 
 
 			const GlobalIlluminationProbe probe = g_giProbes[firstbitlow2(cluster.m_giProbesMask)];
 			const GlobalIlluminationProbe probe = g_giProbes[firstbitlow2(cluster.m_giProbesMask)];
 
 
 			// Sample
 			// Sample
-			probeColor = sampleGlobalIllumination(worldPos, worldNormal, probe, g_globalIlluminationTextures,
-												  g_linearAnyClampSampler);
+			probeColor = sampleGlobalIllumination(
+				worldPos, worldNormal, probe, g_bindlessTextures3dF32[probe.m_volumeTexture], g_linearAnyClampSampler);
 		}
 		}
 		else
 		else
 		{
 		{
@@ -183,8 +186,9 @@ RVec3 main([[vk::location(0)]] Vec2 uv : TEXCOORD, Vec4 svPosition : SV_POSITION
 				totalBlendWeight += blendWeight;
 				totalBlendWeight += blendWeight;
 
 
 				// Sample
 				// Sample
-				const RVec3 c = sampleGlobalIllumination(worldPos, worldNormal, probe, g_globalIlluminationTextures,
-														 g_linearAnyClampSampler);
+				const RVec3 c = sampleGlobalIllumination(
+					worldPos, worldNormal, probe,
+					g_bindlessTextures3dF32[NonUniformResourceIndex(probe.m_volumeTexture)], g_linearAnyClampSampler);
 				probeColor += c * blendWeight;
 				probeColor += c * blendWeight;
 			}
 			}
 
 

+ 2 - 5
AnKi/Shaders/LightFunctions.hlsl

@@ -417,8 +417,7 @@ RVec3 sampleAmbientDice(RVec3 posx, RVec3 negx, RVec3 posy, RVec3 negy, RVec3 po
 
 
 // Sample the irradiance term from the clipmap
 // Sample the irradiance term from the clipmap
 RVec3 sampleGlobalIllumination(const Vec3 worldPos, const Vec3 normal, const GlobalIlluminationProbe probe,
 RVec3 sampleGlobalIllumination(const Vec3 worldPos, const Vec3 normal, const GlobalIlluminationProbe probe,
-							   Texture3D<RVec4> textures[kMaxVisibleGlobalIlluminationProbes],
-							   SamplerState linearAnyClampSampler)
+							   Texture3D<RVec4> tex, SamplerState linearAnyClampSampler)
 {
 {
 	// Find the UVW
 	// Find the UVW
 	Vec3 uvw = (worldPos - probe.m_aabbMin) / (probe.m_aabbMax - probe.m_aabbMin);
 	Vec3 uvw = (worldPos - probe.m_aabbMin) / (probe.m_aabbMax - probe.m_aabbMin);
@@ -437,9 +436,7 @@ RVec3 sampleGlobalIllumination(const Vec3 worldPos, const Vec3 normal, const Glo
 		Vec3 shiftedUVw = uvw;
 		Vec3 shiftedUVw = uvw;
 		shiftedUVw.x += (1.0 / 6.0) * F32(dir);
 		shiftedUVw.x += (1.0 / 6.0) * F32(dir);
 
 
-		irradiancePerDir[dir] = textures[NonUniformResourceIndex(probe.m_textureIndex)]
-									.SampleLevel(linearAnyClampSampler, shiftedUVw, 0.0)
-									.rgb;
+		irradiancePerDir[dir] = tex.SampleLevel(linearAnyClampSampler, shiftedUVw, 0.0).rgb;
 	}
 	}
 
 
 	// Sample the irradiance
 	// Sample the irradiance

+ 11 - 7
AnKi/Shaders/VolumetricLightingAccumulation.ankiprog

@@ -15,8 +15,8 @@
 #define CLUSTERED_SHADING_UNIFORMS_BINDING 6u
 #define CLUSTERED_SHADING_UNIFORMS_BINDING 6u
 #define CLUSTERED_SHADING_LIGHTS_BINDING 7u
 #define CLUSTERED_SHADING_LIGHTS_BINDING 7u
 #define CLUSTERED_SHADING_GI_BINDING 10u
 #define CLUSTERED_SHADING_GI_BINDING 10u
-#define CLUSTERED_SHADING_FOG_BINDING 12u
-#define CLUSTERED_SHADING_CLUSTERS_BINDING 13u
+#define CLUSTERED_SHADING_FOG_BINDING 11u
+#define CLUSTERED_SHADING_CLUSTERS_BINDING 12u
 #include <AnKi/Shaders/ClusteredShadingCommon.hlsl>
 #include <AnKi/Shaders/ClusteredShadingCommon.hlsl>
 
 
 constexpr F32 kPhaseFunctionAnisotropy = 0.3;
 constexpr F32 kPhaseFunctionAnisotropy = 0.3;
@@ -31,6 +31,8 @@ constexpr F32 kPhaseFunctionAnisotropy = 0.3;
 
 
 [[vk::push_constant]] ConstantBuffer<VolumetricLightingUniforms> g_fogUnis;
 [[vk::push_constant]] ConstantBuffer<VolumetricLightingUniforms> g_fogUnis;
 
 
+ANKI_BINDLESS_SET(1)
+
 static Vec3 g_svDispatchThreadId;
 static Vec3 g_svDispatchThreadId;
 
 
 Vec3 readRand()
 Vec3 readRand()
@@ -168,15 +170,16 @@ Vec4 accumulateLightsAndFog(Cluster cluster, Vec3 worldPos, F32 negativeZViewSpa
 	{
 	{
 		Vec3 diffIndirect;
 		Vec3 diffIndirect;
 
 
-		if(countbits(cluster.m_giProbesMask) == 1)
+		const U32 oneProbe = WaveActiveAllTrue(countbits(cluster.m_giProbesMask) == 1);
+		if(oneProbe)
 		{
 		{
 			// Only one probe, do a fast path without blend weight
 			// Only one probe, do a fast path without blend weight
 
 
 			const GlobalIlluminationProbe probe = g_giProbes[firstbitlow2(cluster.m_giProbesMask)];
 			const GlobalIlluminationProbe probe = g_giProbes[firstbitlow2(cluster.m_giProbesMask)];
 
 
 			// Sample
 			// Sample
-			diffIndirect = sampleGlobalIllumination(worldPos, viewDir, probe, g_globalIlluminationTextures,
-													g_linearAnyClampSampler);
+			diffIndirect = sampleGlobalIllumination(
+				worldPos, viewDir, probe, g_bindlessTextures3dF32[probe.m_volumeTexture], g_linearAnyClampSampler);
 		}
 		}
 		else
 		else
 		{
 		{
@@ -198,8 +201,9 @@ Vec4 accumulateLightsAndFog(Cluster cluster, Vec3 worldPos, F32 negativeZViewSpa
 				totalBlendWeight += blendWeight;
 				totalBlendWeight += blendWeight;
 
 
 				// Sample
 				// Sample
-				const Vec3 c = sampleGlobalIllumination(worldPos, viewDir, probe, g_globalIlluminationTextures,
-														g_linearAnyClampSampler);
+				const Vec3 c = sampleGlobalIllumination(
+					worldPos, viewDir, probe, g_bindlessTextures3dF32[NonUniformResourceIndex(probe.m_volumeTexture)],
+					g_linearAnyClampSampler);
 				diffIndirect += c * blendWeight;
 				diffIndirect += c * blendWeight;
 			}
 			}
 
 

+ 5 - 0
AnKi/Util/Assert.cpp

@@ -40,6 +40,11 @@ void akassert(const char* exprTxt, const char* file, int line, const char* func)
 		printf("%.2u: %s\n", count++, symbol.cstr());
 		printf("%.2u: %s\n", count++, symbol.cstr());
 	});
 	});
 
 
+#	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);
+#	endif
 	ANKI_DEBUG_BREAK();
 	ANKI_DEBUG_BREAK();
 }
 }
 
 

+ 10 - 1
AnKi/Util/Win32Minimal.h

@@ -120,7 +120,8 @@ ANKI_WINBASEAPI BOOL ANKI_WINAPI GetConsoleScreenBufferInfo(HANDLE hConsoleOutpu
 															PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
 															PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
 ANKI_WINBASEAPI BOOL ANKI_WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes);
 ANKI_WINBASEAPI BOOL ANKI_WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes);
 ANKI_WINBASEAPI VOID ANKI_WINAPI GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
 ANKI_WINBASEAPI VOID ANKI_WINAPI GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
-ANKI_WINBASEAPI DWORD GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize);
+ANKI_WINBASEAPI DWORD ANKI_WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize);
+ANKI_WINBASEAPI int ANKI_WINAPI MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
 
 
 #undef ANKI_WINBASEAPI
 #undef ANKI_WINBASEAPI
 #undef ANKI_DECLARE_HANDLE
 #undef ANKI_DECLARE_HANDLE
@@ -155,6 +156,9 @@ constexpr WORD BACKGROUND_BLUE = 0x0010;
 constexpr WORD BACKGROUND_GREEN = 0x0020;
 constexpr WORD BACKGROUND_GREEN = 0x0020;
 constexpr WORD BACKGROUND_RED = 0x0040;
 constexpr WORD BACKGROUND_RED = 0x0040;
 
 
+constexpr WORD MB_OK = 0x00000000L;
+constexpr WORD MB_ICONWARNING = 0x00000030L;
+
 // Types
 // Types
 typedef union _LARGE_INTEGER
 typedef union _LARGE_INTEGER
 {
 {
@@ -451,4 +455,9 @@ inline DWORD GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize)
 	return ::GetModuleFileNameA(hModule, lpFilename, nSize);
 	return ::GetModuleFileNameA(hModule, lpFilename, nSize);
 }
 }
 
 
+inline int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
+{
+	return ::MessageBoxA(hWnd, lpText, lpCaption, uType);
+}
+
 } // end namespace anki
 } // end namespace anki