Browse Source

More GI work

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
78ab33efb7

+ 6 - 0
shaders/Functions.glsl

@@ -475,6 +475,12 @@ F32 rayAabbIntersectionInside(Vec3 rayOrigin, Vec3 rayDir, Vec3 aabbMin, Vec3 aa
 	return distToIntersect;
 	return distToIntersect;
 }
 }
 
 
+// Return true if to AABBs overlap
+Bool aabbsOverlap(const Vec3 aMin, const Vec3 aMax, const Vec3 bMin, const Vec3 bMax)
+{
+	return all(lessThan(aMin, bMax)) && all(lessThan(bMin, aMax));
+}
+
 // A convenience macro to skip out of bounds invocations on post-process compute shaders.
 // A convenience macro to skip out of bounds invocations on post-process compute shaders.
 #define SKIP_OUT_OF_BOUNDS_INVOCATIONS() \
 #define SKIP_OUT_OF_BOUNDS_INVOCATIONS() \
 	if((FB_SIZE.x % WORKGROUP_SIZE.x) != 0u || (FB_SIZE.y % WORKGROUP_SIZE.y) != 0u) \
 	if((FB_SIZE.x % WORKGROUP_SIZE.x) != 0u || (FB_SIZE.y % WORKGROUP_SIZE.y) != 0u) \

+ 65 - 11
shaders/GlobalIlluminationClipmapPopulation.glslp

@@ -5,8 +5,9 @@
 
 
 // Populates a clipmap with the irradiance values of probes.
 // Populates a clipmap with the irradiance values of probes.
 
 
-#pragma anki input const U32 CLIPMAP_COUNT
+#pragma anki input const U32 CLIPMAP_LEVEL_COUNT
 #pragma anki input const UVec3 WORKGROUP_SIZE
 #pragma anki input const UVec3 WORKGROUP_SIZE
+#pragma anki input const U32 PROBE_COUNT
 
 
 #pragma anki start comp
 #pragma anki start comp
 
 
@@ -20,31 +21,45 @@ layout(set = 0, binding = 0) buffer ssbo_
 };
 };
 
 
 layout(set = 0, binding = 1) uniform sampler u_linearAnyClampSampler;
 layout(set = 0, binding = 1) uniform sampler u_linearAnyClampSampler;
-layout(set = 0, binding = 2) uniform texture3D u_irradianceTextures[6];
+layout(set = 0, binding = 2) uniform texture3D u_probeIrradianceTextures[6u * PROBE_COUNT];
+layout(set = 0, binding = 3) uniform writeonly image3D u_clipmapImages[6u * CLIPMAP_LEVEL_COUNT];
 
 
 struct Clipmap
 struct Clipmap
 {
 {
 	Vec3 m_aabbMin;
 	Vec3 m_aabbMin;
-	F32 m_padding0;
+	F32 m_cellSize;
 	Vec3 m_aabbMax;
 	Vec3 m_aabbMax;
-	F32 m_padding1;
+	F32 m_padding0;
 	UVec3 m_cellCounts;
 	UVec3 m_cellCounts;
 	U32 m_padding2;
 	U32 m_padding2;
 };
 };
 
 
-layout(set = 0, binding = 3, std140) uniform ubo_
+layout(set = 0, binding = 4, std140) uniform ubo_
 {
 {
-	Clipmap u_clipmaps[CLIPMAP_COUNT];
+	Clipmap u_clipmaps[CLIPMAP_LEVEL_COUNT];
 	Vec3 u_cameraPos;
 	Vec3 u_cameraPos;
-	F32 u_padding;
+	U32 u_padding;
 };
 };
 
 
+Bool aabbsOverlap(const Clipmap clipmap, const GlobalIlluminationProbe probe)
+{
+	return aabbsOverlap(clipmap.m_aabbMin, clipmap.m_aabbMax, probe.m_aabbMin, probe.m_aabbMax);
+}
+
+// Compute the texture coordinates inside a probe
+Vec3 computeUvwCoordsInsideProbe(const GlobalIlluminationProbe probe, const Vec3 positionInsideTheProbe)
+{
+	const Vec3 probeSize = probe.m_aabbMax - probe.m_aabbMin;
+	const Vec3 uvw = (positionInsideTheProbe - probe.m_aabbMin) / probeSize;
+	return uvw;
+}
+
 void main()
 void main()
 {
 {
 	// Populate all clipmaps
 	// Populate all clipmaps
-	ANKI_UNROLL for(U32 clipmapIdx = 0u; clipmapIdx < CLIPMAP_COUNT; ++clipmapIdx)
+	ANKI_UNROLL for(U32 clipmapIdx = 0u; clipmapIdx < CLIPMAP_LEVEL_COUNT; ++clipmapIdx)
 	{
 	{
-		Clipmap clipmap = u_clipmaps[clipmapIdx];
+		const Clipmap clipmap = u_clipmaps[clipmapIdx];
 
 
 		// Check bounds
 		// Check bounds
 		if(any(greaterThanEqual(gl_GlobalInvocationID, clipmap.m_cellCounts)))
 		if(any(greaterThanEqual(gl_GlobalInvocationID, clipmap.m_cellCounts)))
@@ -52,8 +67,47 @@ void main()
 			continue;
 			continue;
 		}
 		}
 
 
-		// TODO
+		// For all probes
+		F32 weight = EPSILON;
+		Vec3 accumulatedIrradiance[6u] = Vec3[](Vec3(0.0), Vec3(0.0), Vec3(0.0), Vec3(0.0), Vec3(0.0), Vec3(0.0));
+		ANKI_UNROLL for(U32 probeIdx = 0u; probeIdx < PROBE_COUNT; ++probeIdx)
+		{
+			const GlobalIlluminationProbe probe = u_probes[probeIdx];
+
+			ANKI_FLATTEN if(aabbsOverlap(clipmap, probe))
+			{
+				// Compute the world position of the cell in the clipmap
+				Vec3 cellPosition = Vec3(gl_GlobalInvocationID) * clipmap.m_cellSize;
+				cellPosition += clipmap.m_cellSize / 2.0; // Move to the center of the cell
+				cellPosition += clipmap.m_aabbMin;
+
+				// Compute the UVW coords
+				const Vec3 texCoordsInProbe = computeUvwCoordsInsideProbe(probe, cellPosition);
+
+				// Read the texture
+				ANKI_UNROLL for(U32 dirIdx = 0u; dirIdx < 6u; ++dirIdx)
+				{
+					// Read the color from the probe
+					const U32 inputTexIdx = propeIdx * 6u + dirIdx;
+					const Vec3 inColor = textureLod(
+						u_probeIrradianceTextures[inputTexIdx], u_linearAnyClampSampler, texCoordsInProbe, 0.0);
+
+					accumulatedIrradiance[dirIdx] += inColor;
+				}
+
+				weight += 1.0;
+			}
+		}
+
+		// Write the result
+		ANKI_UNROLL for(U32 dirIdx = 0u; dirIdx < 6u; ++dirIdx)
+		{
+			const U32 clipmapImageIdx = clipmapIdx * 6u + dirIdx;
+
+			const Vec3 storedColor = accumulatedIrradiance[dirIdx] / weight;
+			imageStore(u_clipmapImages[clipmapImageIdx], IVec3(gl_GlobalInvocationID), Vec4(storedColor, 0.0));
+		}
 	}
 	}
 }
 }
 
 
-#pragma anki end
+#pragma anki end

+ 1 - 1
shaders/glsl_cpp_common/ClusteredShading.h

@@ -110,7 +110,7 @@ struct GlobalIlluminationProbe
 	Vec3 m_aabbMin;
 	Vec3 m_aabbMin;
 	F32 m_cellSize;
 	F32 m_cellSize;
 	Vec3 m_aabbMax;
 	Vec3 m_aabbMax;
-	U32 m_textureIndex;
+	F32 m_padding;
 };
 };
 
 
 // Common uniforms for light shading passes
 // Common uniforms for light shading passes

+ 2 - 2
src/anki/gr/RenderGraph.h

@@ -178,14 +178,14 @@ public:
 	}
 	}
 
 
 	/// Convenience method to bind the whole texture as color.
 	/// Convenience method to bind the whole texture as color.
-	void bindColorTexture(U32 set, U32 binding, RenderTargetHandle handle)
+	void bindColorTexture(U32 set, U32 binding, RenderTargetHandle handle, U arrayIdx = 0)
 	{
 	{
 		TexturePtr tex = getTexture(handle);
 		TexturePtr tex = getTexture(handle);
 		TextureViewInitInfo viewInit(tex); // Use the whole texture
 		TextureViewInitInfo viewInit(tex); // Use the whole texture
 		TextureUsageBit usage;
 		TextureUsageBit usage;
 		getRenderTargetState(handle, viewInit, tex, usage);
 		getRenderTargetState(handle, viewInit, tex, usage);
 		TextureViewPtr view = m_commandBuffer->getManager().newTextureView(viewInit);
 		TextureViewPtr view = m_commandBuffer->getManager().newTextureView(viewInit);
-		m_commandBuffer->bindTexture(set, binding, view, usage);
+		m_commandBuffer->bindTexture(set, binding, view, usage, arrayIdx);
 	}
 	}
 
 
 	/// Convenience method.
 	/// Convenience method.

+ 253 - 3
src/anki/renderer/GlobalIllumination.cpp

@@ -8,6 +8,8 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/core/Trace.h>
 #include <anki/core/Trace.h>
+#include <anki/collision/Aabb.h>
+#include <anki/collision/Functions.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -44,16 +46,23 @@ public:
 	GlobalIlluminationProbeQueueElement* m_probe ANKI_DEBUG_CODE(= nullptr);
 	GlobalIlluminationProbeQueueElement* m_probe ANKI_DEBUG_CODE(= nullptr);
 	UVec3 m_cell ANKI_DEBUG_CODE(= UVec3(MAX_U32));
 	UVec3 m_cell ANKI_DEBUG_CODE(= UVec3(MAX_U32));
 
 
+	Array<RenderTargetDescription, CLIPMAP_LEVEL_COUNT> m_clipmapRtDescriptors;
+
 	Array<RenderTargetHandle, GBUFFER_COLOR_ATTACHMENT_COUNT> m_gbufferColorRts;
 	Array<RenderTargetHandle, GBUFFER_COLOR_ATTACHMENT_COUNT> m_gbufferColorRts;
 	RenderTargetHandle m_gbufferDepthRt;
 	RenderTargetHandle m_gbufferDepthRt;
 	RenderTargetHandle m_shadowsRt;
 	RenderTargetHandle m_shadowsRt;
 	RenderTargetHandle m_lightShadingRt;
 	RenderTargetHandle m_lightShadingRt;
+	Array2d<RenderTargetHandle, CLIPMAP_LEVEL_COUNT, 6> m_clipmapRts;
+
+	Array<Aabb, CLIPMAP_LEVEL_COUNT> m_clipmapLevelAabbs; ///< AABBs in world space.
+	UVec3 m_maxClipmapVolumeSize = UVec3(0u);
 };
 };
 
 
 GlobalIllumination::~GlobalIllumination()
 GlobalIllumination::~GlobalIllumination()
 {
 {
 	m_cacheEntries.destroy(getAllocator());
 	m_cacheEntries.destroy(getAllocator());
 	m_probeUuidToCacheEntryIdx.destroy(getAllocator());
 	m_probeUuidToCacheEntryIdx.destroy(getAllocator());
+	m_clipmap.m_grProgs.destroy(getAllocator());
 }
 }
 
 
 Error GlobalIllumination::init(const ConfigSet& cfg)
 Error GlobalIllumination::init(const ConfigSet& cfg)
@@ -72,12 +81,14 @@ Error GlobalIllumination::init(const ConfigSet& cfg)
 Error GlobalIllumination::initInternal(const ConfigSet& cfg)
 Error GlobalIllumination::initInternal(const ConfigSet& cfg)
 {
 {
 	m_tileSize = cfg.getNumber("r.gi.tileResolution");
 	m_tileSize = cfg.getNumber("r.gi.tileResolution");
-	m_cacheEntries.create(getAllocator(), cfg.getNumber("r.gi.maxSimultaneousProbeCount"));
+	m_cacheEntries.create(getAllocator(), cfg.getNumber("r.gi.maxCachedProbes"));
+	m_maxVisibleProbes = cfg.getNumber("r.gi.maxVisibleProbes");
 
 
 	ANKI_CHECK(initGBuffer(cfg));
 	ANKI_CHECK(initGBuffer(cfg));
 	ANKI_CHECK(initLightShading(cfg));
 	ANKI_CHECK(initLightShading(cfg));
 	ANKI_CHECK(initShadowMapping(cfg));
 	ANKI_CHECK(initShadowMapping(cfg));
 	ANKI_CHECK(initIrradiance(cfg));
 	ANKI_CHECK(initIrradiance(cfg));
+	ANKI_CHECK(initClipmap(cfg));
 
 
 	return Error::NONE;
 	return Error::NONE;
 }
 }
@@ -190,6 +201,37 @@ Error GlobalIllumination::initIrradiance(const ConfigSet& cfg)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
+Error GlobalIllumination::initClipmap(const ConfigSet& cfg)
+{
+	// Init the program
+	ANKI_CHECK(
+		m_r->getResourceManager().loadResource("shaders/GlobalIlluminationClimpamPopulation.glslp", m_clipmap.m_prog));
+
+	m_clipmap.m_grProgs.create(getAllocator(), m_maxVisibleProbes + 1);
+
+	ShaderProgramResourceConstantValueInitList<3> consts(m_clipmap.m_prog);
+	consts.add("CLIPMAP_LEVEL_COUNT", U32(CLIPMAP_LEVEL_COUNT));
+	consts.add("WORKGROUP_SIZE",
+		UVec3(m_clipmap.m_workgroupSize[0], m_clipmap.m_workgroupSize[1], m_clipmap.m_workgroupSize[2]));
+	consts.add("PROBE_COUNT", U32(0));
+
+	for(U probeCount = 0; probeCount < m_clipmap.m_grProgs.getSize(); ++probeCount)
+	{
+		const ShaderProgramResourceVariant* variant;
+		consts[2].m_uint = U32(probeCount);
+		m_clipmap.m_prog->getOrCreateVariant(consts.get(), variant);
+		m_clipmap.m_grProgs[probeCount] = variant->getProgram();
+	}
+
+	// Init more
+	static_assert(CLIPMAP_LEVEL_COUNT == 2, "The following line assume that");
+	m_clipmap.m_cellSizes[0] = cfg.getNumber("r.gi.firstClipmapLevelCellSize");
+	m_clipmap.m_cellSizes[1] = cfg.getNumber("r.gi.secondClipmapLevelCellSize");
+	m_clipmap.m_levelMaxDistances[0] = cfg.getNumber("r.gi.firstClipmapMaxDistance");
+
+	return Error::NONE;
+}
+
 void GlobalIllumination::populateRenderGraph(RenderingContext& rctx)
 void GlobalIllumination::populateRenderGraph(RenderingContext& rctx)
 {
 {
 	ANKI_TRACE_SCOPED_EVENT(R_GI);
 	ANKI_TRACE_SCOPED_EVENT(R_GI);
@@ -351,6 +393,9 @@ void GlobalIllumination::populateRenderGraph(RenderingContext& rctx)
 				TextureUsageBit::IMAGE_COMPUTE_WRITE});
 				TextureUsageBit::IMAGE_COMPUTE_WRITE});
 		}
 		}
 	}
 	}
+
+	// Clipmap population
+	populateRenderGraphClipmap(*giCtx);
 }
 }
 
 
 void GlobalIllumination::prepareProbes(RenderingContext& ctx,
 void GlobalIllumination::prepareProbes(RenderingContext& ctx,
@@ -374,6 +419,14 @@ void GlobalIllumination::prepareProbes(RenderingContext& ctx,
 	Bool foundProbeToUpdateNextFrame = false;
 	Bool foundProbeToUpdateNextFrame = false;
 	for(U32 probeIdx = 0; probeIdx < ctx.m_renderQueue->m_giProbes.getSize(); ++probeIdx)
 	for(U32 probeIdx = 0; probeIdx < ctx.m_renderQueue->m_giProbes.getSize(); ++probeIdx)
 	{
 	{
+		if(probeIdx == m_maxVisibleProbes)
+		{
+			ANKI_R_LOGW("Can't have more that %u visible probes. Increase the r.gi.maxVisibleProbes or (somehow) "
+						"decrease the visible probes",
+				m_maxVisibleProbes);
+			break;
+		}
+
 		GlobalIlluminationProbeQueueElement& probe = ctx.m_renderQueue->m_giProbes[probeIdx];
 		GlobalIlluminationProbeQueueElement& probe = ctx.m_renderQueue->m_giProbes[probeIdx];
 
 
 		// Find cache entry
 		// Find cache entry
@@ -383,7 +436,7 @@ void GlobalIllumination::prepareProbes(RenderingContext& ctx,
 		{
 		{
 			// Failed
 			// Failed
 			ANKI_R_LOGW("There is not enough space in the indirect lighting atlas for more probes. "
 			ANKI_R_LOGW("There is not enough space in the indirect lighting atlas for more probes. "
-						"Increase the r.gi.maxSimultaneousProbeCount or (somehow) decrease the visible probes");
+						"Increase the r.gi.maxCachedProbes or (somehow) decrease the visible probes");
 			continue;
 			continue;
 		}
 		}
 
 
@@ -402,7 +455,7 @@ void GlobalIllumination::prepareProbes(RenderingContext& ctx,
 			for(U i = 0; i < 6; i++)
 			for(U i = 0; i < 6; i++)
 			{
 			{
 				entry.m_rtHandles[i] = ctx.m_renderGraphDescr.importRenderTarget(
 				entry.m_rtHandles[i] = ctx.m_renderGraphDescr.importRenderTarget(
-					entry.m_volumeTextures[i], TextureUsageBit::SAMPLED_FRAGMENT);
+					entry.m_volumeTextures[i], TextureUsageBit::SAMPLED_COMPUTE);
 			}
 			}
 
 
 			probe.m_cacheEntryIndex = cacheEntryIdx;
 			probe.m_cacheEntryIndex = cacheEntryIdx;
@@ -677,4 +730,201 @@ void GlobalIllumination::runIrradiance(RenderPassWorkContext& rgraphCtx, Interna
 	cmdb->dispatchCompute(1, 1, 1);
 	cmdb->dispatchCompute(1, 1, 1);
 }
 }
 
 
+void GlobalIllumination::runClipmapPopulation(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx)
+{
+	ANKI_TRACE_SCOPED_EVENT(R_GI);
+
+	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+
+	// Allocate and bin the probes
+	GlobalIlluminationProbe* probes = allocateAndBindStorage<GlobalIlluminationProbe*>(
+		sizeof(GlobalIlluminationProbe) * giCtx.m_ctx->m_renderQueue->m_giProbes.getSize(), cmdb, 0, 0);
+	for(U i = 0; i < giCtx.m_ctx->m_renderQueue->m_giProbes.getSize(); ++i)
+	{
+		const GlobalIlluminationProbeQueueElement& in = giCtx.m_ctx->m_renderQueue->m_giProbes[i];
+		GlobalIlluminationProbe& out = probes[i];
+
+		out.m_aabbMin = in.m_aabbMin;
+		out.m_aabbMax = in.m_aabbMax;
+		out.m_cellSize = in.m_cellSize;
+	}
+
+	// Bind sampler
+	cmdb->bindSampler(0, 1, m_r->getSamplers().m_trilinearClamp);
+
+	// Bind input textures
+	for(U i = 0; i < giCtx.m_ctx->m_renderQueue->m_giProbes.getSize(); ++i)
+	{
+		const GlobalIlluminationProbeQueueElement& element = giCtx.m_ctx->m_renderQueue->m_giProbes[i];
+
+		for(U dir = 0; dir < 6; ++dir)
+		{
+			const RenderTargetHandle& rt = m_cacheEntries[element.m_cacheEntryIndex].m_rtHandles[dir];
+
+			rgraphCtx.bindColorTexture(0, 2, rt, i * 6 + dir);
+		}
+	}
+
+	// Bind out textures
+	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		for(U dir = 0; dir < 6; ++dir)
+		{
+			rgraphCtx.bindColorTexture(0, 3, giCtx.m_clipmapRts[level][dir], level * 6 + dir);
+		}
+	}
+
+	// Allocate and bin uniforms
+	struct Clipmap
+	{
+		Vec3 m_aabbMin;
+		F32 m_cellSize;
+		Vec3 m_aabbMax;
+		F32 m_padding0;
+		UVec3 m_cellCounts;
+		U32 m_padding2;
+	};
+
+	struct Unis
+	{
+		Clipmap m_clipmaps[CLIPMAP_LEVEL_COUNT];
+		Vec3 m_cameraPos;
+		U32 m_padding;
+	};
+	Unis* unis = allocateAndBindUniforms<Unis*>(sizeof(Unis), cmdb, 0, 4);
+
+	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		unis->m_clipmaps[level].m_aabbMin = giCtx.m_clipmapLevelAabbs[level].getMin().xyz();
+		unis->m_clipmaps[level].m_cellSize = m_clipmap.m_cellSizes[level];
+		unis->m_clipmaps[level].m_aabbMax = giCtx.m_clipmapLevelAabbs[level].getMax().xyz();
+		unis->m_clipmaps[level].m_cellCounts = UVec3(giCtx.m_clipmapRtDescriptors[level].m_width,
+			giCtx.m_clipmapRtDescriptors[level].m_height,
+			giCtx.m_clipmapRtDescriptors[level].m_depth);
+	}
+	unis->m_cameraPos = giCtx.m_ctx->m_renderQueue->m_cameraTransform.getTranslationPart().xyz();
+
+	// Bind the program
+	cmdb->bindShaderProgram(m_clipmap.m_grProgs[giCtx.m_ctx->m_renderQueue->m_giProbes.getSize()]);
+
+	// Dispatch
+	dispatchPPCompute(cmdb,
+		m_clipmap.m_workgroupSize[0],
+		m_clipmap.m_workgroupSize[1],
+		m_clipmap.m_workgroupSize[2],
+		giCtx.m_maxClipmapVolumeSize[0],
+		giCtx.m_maxClipmapVolumeSize[1],
+		giCtx.m_maxClipmapVolumeSize[2]);
+}
+
+void GlobalIllumination::populateRenderGraphClipmap(InternalContext& giCtx)
+{
+	RenderGraphDescription& rgraph = giCtx.m_ctx->m_renderGraphDescr;
+	const RenderQueue& rqueue = *giCtx.m_ctx->m_renderQueue;
+
+	// Compute the size of the clipmap levels
+	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		UVec3 clipmapVolumeSize;
+
+		// Get the edges of the sub frustum in local space
+		const F32 near = (level == 0) ? EPSILON : m_clipmap.m_levelMaxDistances[level - 1];
+		const F32 far = (level != CLIPMAP_LEVEL_COUNT - 1) ? m_clipmap.m_levelMaxDistances[level] : rqueue.m_cameraFar;
+		Array<Vec4, 8> frustumEdges;
+		computeEdgesOfFrustum(near, rqueue.m_cameraFovX, rqueue.m_cameraFovY, &frustumEdges[0]);
+		computeEdgesOfFrustum(far, rqueue.m_cameraFovX, rqueue.m_cameraFovY, &frustumEdges[4]);
+
+		// Transform the edges to world space
+		for(Vec4& edge : frustumEdges)
+		{
+			edge = rqueue.m_cameraTransform * edge.xyz1();
+		}
+
+		// Compute the AABB
+		giCtx.m_clipmapLevelAabbs[level].setFromPointCloud(reinterpret_cast<Vec3*>(&frustumEdges[0]),
+			frustumEdges.getSize(),
+			sizeof(frustumEdges[0]),
+			sizeof(frustumEdges));
+
+		// Align the AABB to the cell grid
+		Vec4 min(0.0f), max(0.0f);
+		for(U comp = 0; comp < 3; ++comp)
+		{
+			min[comp] =
+				getAlignedRoundDown(m_clipmap.m_cellSizes[level], giCtx.m_clipmapLevelAabbs[level].getMin()[comp]);
+
+			max[comp] =
+				getAlignedRoundUp(m_clipmap.m_cellSizes[level], giCtx.m_clipmapLevelAabbs[level].getMax()[comp]);
+		}
+
+		giCtx.m_clipmapLevelAabbs[level].setMin(min);
+		giCtx.m_clipmapLevelAabbs[level].setMax(max);
+
+		// Compute the volume sizes
+		for(U comp = 0; comp < 3; ++comp)
+		{
+			const Vec4& min = giCtx.m_clipmapLevelAabbs[level].getMin();
+			const Vec4& max = giCtx.m_clipmapLevelAabbs[level].getMax();
+			clipmapVolumeSize[comp] = U32((max[comp] - min[comp]) / m_clipmap.m_cellSizes[level]);
+			ANKI_ASSERT(clipmapVolumeSize[comp] >= m_clipmap.m_cellSizes[level]);
+		}
+
+		giCtx.m_maxClipmapVolumeSize = giCtx.m_maxClipmapVolumeSize.max(clipmapVolumeSize);
+
+		// Maximize the size of the volumes to avoid creating new RTs every frame
+		m_clipmap.m_volumeSizes[level] = clipmapVolumeSize.max(m_clipmap.m_volumeSizes[level]);
+	}
+
+	// Create a few RT descriptors
+	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		RenderTargetDescription& descr = giCtx.m_clipmapRtDescriptors[level];
+
+		descr = m_r->create2DRenderTargetDescription(m_clipmap.m_volumeSizes[level][0],
+			m_clipmap.m_volumeSizes[level][1],
+			Format::B10G11R11_UFLOAT_PACK32,
+			"GI clipmap");
+		descr.m_depth = m_clipmap.m_volumeSizes[level][2];
+		descr.m_type = TextureType::_3D;
+		descr.bake();
+	}
+
+	// Ask for render targets
+	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		for(U dir = 0; dir < 6; ++dir)
+		{
+			giCtx.m_clipmapRts[level][dir] = rgraph.newRenderTarget(giCtx.m_clipmapRtDescriptors[level]);
+		}
+	}
+
+	// Create the pass
+	ComputeRenderPassDescription& pass = rgraph.newComputeRenderPass("GI clipmap");
+
+	pass.setWork(
+		[](RenderPassWorkContext& rgraphCtx) {
+			InternalContext* giCtx = static_cast<InternalContext*>(rgraphCtx.m_userData);
+			giCtx->m_gi->runClipmapPopulation(rgraphCtx, *giCtx);
+		},
+		&giCtx,
+		0);
+
+	for(const GlobalIlluminationProbeQueueElement& probe : giCtx.m_ctx->m_renderQueue->m_giProbes)
+	{
+		const CacheEntry& entry = m_cacheEntries[probe.m_cacheEntryIndex];
+		for(U i = 0; i < 6; ++i)
+		{
+			pass.newDependency({entry.m_rtHandles[i], TextureUsageBit::SAMPLED_COMPUTE});
+		}
+	}
+
+	for(U level = 0; level < CLIPMAP_LEVEL_COUNT; ++level)
+	{
+		for(U i = 0; i < 6; ++i)
+		{
+			pass.newDependency({giCtx.m_clipmapRts[level][i], TextureUsageBit::IMAGE_COMPUTE_WRITE});
+		}
+	}
+}
+
 } // end namespace anki
 } // end namespace anki

+ 10 - 6
src/anki/renderer/GlobalIllumination.h

@@ -19,8 +19,6 @@ namespace anki
 class GlobalIllumination : public RendererObject
 class GlobalIllumination : public RendererObject
 {
 {
 anki_internal:
 anki_internal:
-	static constexpr U CLIPMAP_CASCADE_COUNT = 2;
-
 	GlobalIllumination(Renderer* r)
 	GlobalIllumination(Renderer* r)
 		: RendererObject(r)
 		: RendererObject(r)
 		, m_lightShading(r)
 		, m_lightShading(r)
@@ -44,6 +42,7 @@ anki_internal:
 	}
 	}
 
 
 private:
 private:
+	static constexpr U CLIPMAP_LEVEL_COUNT = 2;
 	class InternalContext;
 	class InternalContext;
 
 
 	class CacheEntry
 	class CacheEntry
@@ -98,16 +97,18 @@ private:
 	class
 	class
 	{
 	{
 	public:
 	public:
-		Array2d<RenderTargetDescription, CLIPMAP_CASCADE_COUNT, 6> m_rtDescrs;
 		ShaderProgramResourcePtr m_prog;
 		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
-		Array<UVec3, CLIPMAP_CASCADE_COUNT> m_volumeSizes;
-		Array<F32, CLIPMAP_CASCADE_COUNT> m_cellSizes;
+		DynamicArray<ShaderProgramPtr> m_grProgs; ///< One program for a number of probe counts.
+		Array<UVec3, CLIPMAP_LEVEL_COUNT> m_volumeSizes; ///< This is dynamic.
+		Array<F32, CLIPMAP_LEVEL_COUNT> m_cellSizes;
+		Array<U8, 3> m_workgroupSize = {{8, 8, 8}};
+		Array<F32, CLIPMAP_LEVEL_COUNT - 1> m_levelMaxDistances;
 	} m_clipmap; ///< Clipmap population.
 	} m_clipmap; ///< Clipmap population.
 
 
 	DynamicArray<CacheEntry> m_cacheEntries;
 	DynamicArray<CacheEntry> m_cacheEntries;
 	HashMap<U64, U32> m_probeUuidToCacheEntryIdx;
 	HashMap<U64, U32> m_probeUuidToCacheEntryIdx;
 	U32 m_tileSize = 0;
 	U32 m_tileSize = 0;
+	U32 m_maxVisibleProbes = 0;
 
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initGBuffer(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initGBuffer(const ConfigSet& cfg);
@@ -116,10 +117,13 @@ private:
 	ANKI_USE_RESULT Error initIrradiance(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initIrradiance(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initClipmap(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initClipmap(const ConfigSet& cfg);
 
 
+	void populateRenderGraphClipmap(InternalContext& ctx);
+
 	void runGBufferInThread(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx) const;
 	void runGBufferInThread(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx) const;
 	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 runClipmapPopulation(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx);
 
 
 	void prepareProbes(RenderingContext& rctx,
 	void prepareProbes(RenderingContext& rctx,
 		GlobalIlluminationProbeQueueElement*& probeToUpdateThisFrame,
 		GlobalIlluminationProbeQueueElement*& probeToUpdateThisFrame,

+ 3 - 0
src/anki/renderer/RenderQueue.h

@@ -186,6 +186,7 @@ public:
 	Vec3 m_aabbMax;
 	Vec3 m_aabbMax;
 	UVec3 m_cellCounts;
 	UVec3 m_cellCounts;
 	U32 m_totalCellCount;
 	U32 m_totalCellCount;
+	F32 m_cellSize;
 	U32 m_cacheEntryIndex; ///< Renderer internal.
 	U32 m_cacheEntryIndex; ///< Renderer internal.
 
 
 	GlobalIlluminationProbeQueueElement()
 	GlobalIlluminationProbeQueueElement()
@@ -312,6 +313,8 @@ public:
 
 
 	F32 m_cameraNear;
 	F32 m_cameraNear;
 	F32 m_cameraFar;
 	F32 m_cameraFar;
+	F32 m_cameraFovX;
+	F32 m_cameraFovY;
 	F32 m_effectiveShadowDistance;
 	F32 m_effectiveShadowDistance;
 
 
 	FillCoverageBufferCallback m_fillCoverageBufferCallback = nullptr;
 	FillCoverageBufferCallback m_fillCoverageBufferCallback = nullptr;

+ 5 - 0
src/anki/resource/ShaderProgramResource.h

@@ -407,6 +407,11 @@ public:
 		return ConstWeakArray<ShaderProgramResourceConstantValue>(&m_constantValues[0], m_count);
 		return ConstWeakArray<ShaderProgramResourceConstantValue>(&m_constantValues[0], m_count);
 	}
 	}
 
 
+	ShaderProgramResourceConstantValue& operator[](U idx)
+	{
+		return m_constantValues[idx];
+	}
+
 private:
 private:
 	ShaderProgramResourcePtr m_ptr;
 	ShaderProgramResourcePtr m_ptr;
 	U m_count = 0;
 	U m_count = 0;

+ 2 - 0
src/anki/scene/Visibility.cpp

@@ -62,6 +62,8 @@ void VisibilityContext::submitNewWork(const FrustumComponent& frc, RenderQueue&
 	rqueue.m_previousViewProjectionMatrix = frc.getPreviousViewProjectionMatrix();
 	rqueue.m_previousViewProjectionMatrix = frc.getPreviousViewProjectionMatrix();
 	rqueue.m_cameraNear = frc.getNear();
 	rqueue.m_cameraNear = frc.getNear();
 	rqueue.m_cameraFar = frc.getFar();
 	rqueue.m_cameraFar = frc.getFar();
+	rqueue.m_cameraFovX = frc.getFovX();
+	rqueue.m_cameraFovY = frc.getFovY();
 	rqueue.m_effectiveShadowDistance = frc.getEffectiveShadowDistance();
 	rqueue.m_effectiveShadowDistance = frc.getEffectiveShadowDistance();
 
 
 	auto alloc = m_scene->getFrameAllocator();
 	auto alloc = m_scene->getFrameAllocator();

+ 2 - 1
src/anki/scene/components/GlobalIlluminationProbeComponent.h

@@ -88,6 +88,7 @@ public:
 		el.m_aabbMax = m_aabbMax;
 		el.m_aabbMax = m_aabbMax;
 		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_cellCounts.x() * m_cellCounts.y() * m_cellCounts.z();
+		el.m_cellSize = m_cellSize;
 		ANKI_DEBUG_CODE(el.m_cacheEntryIndex = MAX_U32);
 		ANKI_DEBUG_CODE(el.m_cacheEntryIndex = MAX_U32);
 	}
 	}
 
 
@@ -104,7 +105,7 @@ private:
 	Vec3 m_aabbMin = Vec3(-1.0f);
 	Vec3 m_aabbMin = Vec3(-1.0f);
 	Vec3 m_aabbMax = Vec3(+1.0f);
 	Vec3 m_aabbMax = Vec3(+1.0f);
 	Vec3 m_renderPosition = Vec3(0.0f);
 	Vec3 m_renderPosition = Vec3(0.0f);
-	F32 m_cellSize = 1.0f; ///< Cell size in meters.
+	F32 m_cellSize = 2.0f; ///< Cell size in meters.
 	UVec3 m_cellCounts = UVec3(2u);
 	UVec3 m_cellCounts = UVec3(2u);
 	Bool m_markedForRendering = false;
 	Bool m_markedForRendering = false;
 	Bool m_dirty = true;
 	Bool m_dirty = true;