Browse Source

More GI work

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
9f0f550a42

+ 1 - 0
shaders/Common.glsl

@@ -41,6 +41,7 @@ const U32 MAX_U32 = 0xFFFFFFFFu;
 
 const F32 PI = 3.14159265358979323846;
 const U32 UBO_MAX_SIZE = 16384u;
+#define MAX_SHARED_MEMORY (32u * 1024u)
 
 // Macros
 #define UV_TO_NDC(x_) ((x_)*2.0 - 1.0)

+ 107 - 62
shaders/IrradianceDice.glslp

@@ -13,7 +13,7 @@
 #include <shaders/Pack.glsl>
 #include <shaders/LightFunctions.glsl>
 
-layout(local_size_x = 6, local_size_y = 1, local_size_z = 1) in;
+layout(local_size_x = INPUT_TEXTURES_HEIGHT, local_size_y = INPUT_TEXTURES_HEIGHT, local_size_z = 1) in;
 
 layout(set = 0, binding = 0) uniform sampler u_nearestAnyClampSampler;
 layout(set = 0, binding = 1) uniform texture2D u_lightShadingTex;
@@ -22,96 +22,141 @@ layout(set = 0, binding = 3) uniform texture2D u_gbufferTex1;
 layout(set = 0, binding = 4) uniform texture2D u_gbufferTex2;
 layout(set = 0, binding = 5) uniform writeonly image3D u_irradianceVolumes[6];
 
+// This is a temporary buffer used instead of shared memory because it's too large
+layout(set = 0, binding = 6) buffer ssbo_
+{
+	Vec3 u_integrationResults[6][INPUT_TEXTURES_HEIGHT * INPUT_TEXTURES_HEIGHT];
+};
+
 layout(push_constant, std430) uniform pc_
 {
 	IVec3 u_volumeTexel;
 	I32 u_padding;
 };
 
-shared Vec3 s_diceColors[6];
+shared Vec3 s_diceIrradiance[6];
 
 void main()
 {
 	const F32 INPUT_TEXTURES_HEIGHT_F = F32(INPUT_TEXTURES_HEIGHT);
-	const U32 diceFace = gl_LocalInvocationID.x;
+	const Vec2 INPUT_TEXTURES_SIZE = Vec2(INPUT_TEXTURES_HEIGHT * 6u, INPUT_TEXTURES_HEIGHT);
 
-	// Get the r coordinate of the current direction of the dice
-	const Vec3 ri = getCubemapDirection(Vec2(0.0), diceFace);
+	// Compute the NDC used in cubeCoordSolidAngle
+	const Vec2 faceUv = (Vec2(gl_LocalInvocationID.x, gl_LocalInvocationID.y) + 0.5) / INPUT_TEXTURES_HEIGHT_F;
+	const Vec2 ndc = UV_TO_NDC(faceUv);
 
-	// For all the faces and texels of the environment map integrate to compute the irradiance for a face
-	Vec3 irradiance = Vec3(0.0);
-	ANKI_LOOP for(U32 f = 0u; f < 6u; ++f)
+	// Initialize
+	ANKI_UNROLL for(U32 f = 0u; f < 6u; ++f)
 	{
-		ANKI_LOOP for(U32 i = 0u; i < INPUT_TEXTURES_HEIGHT; ++i)
+		const Vec2 uv = (Vec2(gl_LocalInvocationID.x + INPUT_TEXTURES_HEIGHT_F * f, gl_LocalInvocationID.y) + 0.5)
+						/ INPUT_TEXTURES_SIZE;
+
+		// Get the direction of the dice face
+		const Vec3 diceDir = getCubemapDirection(Vec2(0.0), f);
+
+		const Vec3 r = getCubemapDirection(ndc, f);
+
+		// Compute integral part
+		const F32 lambert = max(0.0, dot(r, diceDir));
+		const Vec3 lightShading = textureLod(u_lightShadingTex, u_nearestAnyClampSampler, uv, 0.0).rgb;
+		const Vec3 irradiance = lightShading * lambert * cubeCoordSolidAngle(ndc, INPUT_TEXTURES_HEIGHT_F);
+
+		// Store
+		u_integrationResults[f][gl_LocalInvocationID.y * INPUT_TEXTURES_HEIGHT + gl_LocalInvocationID.x] = irradiance;
+	}
+
+	memoryBarrierBuffer();
+	barrier();
+
+	// Reduce using prefix sum
+	ANKI_UNROLL for(U32 f = 0u; f < 6u; ++f)
+	{
+		const U32 WG_SIZE = INPUT_TEXTURES_HEIGHT * INPUT_TEXTURES_HEIGHT;
+		ANKI_LOOP for(U32 s = WG_SIZE / 2u; s > 0u; s >>= 1u)
 		{
-			ANKI_LOOP for(U32 j = 0u; j < INPUT_TEXTURES_HEIGHT; ++j)
+			if(gl_LocalInvocationIndex < s)
 			{
-				const Vec2 uv = Vec2(j + f * INPUT_TEXTURES_HEIGHT_F, i)
-								/ Vec2(6.0 * INPUT_TEXTURES_HEIGHT_F, INPUT_TEXTURES_HEIGHT_F);
-				const Vec2 ndc = UV_TO_NDC(uv);
-
-				const Vec3 r = getCubemapDirection(ndc, f);
-				const F32 lambert = dot(r, ri);
-
-				if(lambert > 0.0)
-				{
-					const Vec3 lightShading = textureLod(u_lightShadingTex, u_nearestAnyClampSampler, uv, 0.0).rgb;
-					irradiance += lightShading * lambert * cubeCoordSolidAngle(ndc, INPUT_TEXTURES_HEIGHT_F);
-				}
+				u_integrationResults[f][gl_LocalInvocationIndex] +=
+					u_integrationResults[f][gl_LocalInvocationIndex + s];
 			}
+
+			memoryBarrierBuffer();
+			barrier();
 		}
+
+		s_diceIrradiance[f] = u_integrationResults[f][0];
 	}
 
-	s_diceColors[diceFace] = irradiance;
 	memoryBarrierShared();
 	barrier();
 
-	// 2nd bounce
-	irradiance = Vec3(0.0);
-	ANKI_LOOP for(U32 f = 0u; f < 6u; ++f)
+	// Initialize again for the 2nd bounce
+	ANKI_UNROLL for(U32 f = 0u; f < 6u; ++f)
+	{
+		const Vec2 uv = (Vec2(gl_LocalInvocationID.x + INPUT_TEXTURES_HEIGHT_F * f, gl_LocalInvocationID.y) + 0.5)
+						/ INPUT_TEXTURES_SIZE;
+
+		// Get the direction of the dice face
+		const Vec3 diceDir = getCubemapDirection(Vec2(0.0), f);
+
+		const Vec3 r = getCubemapDirection(ndc, f);
+
+		// Compute integral part
+		const F32 lambert = max(0.0, dot(r, diceDir));
+
+		// Read the gbuffer
+		GbufferInfo gbuffer;
+		readGBuffer(u_gbufferTex0, u_gbufferTex1, u_gbufferTex2, u_nearestAnyClampSampler, uv, 0.0, gbuffer);
+
+		// Sample irradiance
+		Vec3 firstBounceIrradiance = sampleAmbientDice(s_diceIrradiance[0],
+			s_diceIrradiance[1],
+			s_diceIrradiance[2],
+			s_diceIrradiance[3],
+			s_diceIrradiance[4],
+			s_diceIrradiance[5],
+			gbuffer.m_normal);
+		firstBounceIrradiance = gbuffer.m_diffuse * firstBounceIrradiance / PI;
+
+		// Compute 2nd bounce
+		const Vec3 lightShading = textureLod(u_lightShadingTex, u_nearestAnyClampSampler, uv, 0.0).rgb;
+		const Vec3 irradiance =
+			firstBounceIrradiance + lightShading * lambert * cubeCoordSolidAngle(ndc, INPUT_TEXTURES_HEIGHT_F);
+
+		// Store
+		u_integrationResults[f][gl_LocalInvocationID.y * INPUT_TEXTURES_HEIGHT + gl_LocalInvocationID.x] = irradiance;
+	}
+
+	memoryBarrierBuffer();
+	barrier();
+
+	// Reduce using prefix sum again
+	ANKI_UNROLL for(U32 f = 0u; f < 6u; ++f)
 	{
-		ANKI_LOOP for(U32 i = 0u; i < INPUT_TEXTURES_HEIGHT; ++i)
+		const U32 WG_SIZE = INPUT_TEXTURES_HEIGHT * INPUT_TEXTURES_HEIGHT;
+		ANKI_LOOP for(U32 s = WG_SIZE / 2u; s > 0u; s >>= 1u)
 		{
-			ANKI_LOOP for(U32 j = 0u; j < INPUT_TEXTURES_HEIGHT; ++j)
+			if(gl_LocalInvocationIndex < s)
 			{
-				const Vec2 uv = Vec2(j + f * INPUT_TEXTURES_HEIGHT_F, i)
-								/ Vec2(6.0 * INPUT_TEXTURES_HEIGHT_F, INPUT_TEXTURES_HEIGHT_F);
-				const Vec2 ndc = UV_TO_NDC(uv);
-
-				const Vec3 r = getCubemapDirection(ndc, f);
-				const F32 lambert = dot(r, ri);
-
-				if(lambert > 0.0)
-				{
-					// Read the gbuffer
-					GbufferInfo gbuffer;
-					readGBuffer(
-						u_gbufferTex0, u_gbufferTex1, u_gbufferTex2, u_nearestAnyClampSampler, uv, 0.0, gbuffer);
-
-					// Sample irradiance
-					Vec3 firstBounceIrradiance = sampleAmbientDice(s_diceColors[0],
-						s_diceColors[1],
-						s_diceColors[2],
-						s_diceColors[3],
-						s_diceColors[4],
-						s_diceColors[5],
-						gbuffer.m_normal);
-					firstBounceIrradiance = gbuffer.m_diffuse * firstBounceIrradiance / PI;
-
-					// Compute 2nd bounce
-					const Vec3 lightShading = textureLod(u_lightShadingTex, u_nearestAnyClampSampler, uv, 0.0).rgb;
-					irradiance += firstBounceIrradiance
-								  + lightShading * lambert * cubeCoordSolidAngle(ndc, INPUT_TEXTURES_HEIGHT_F);
-				}
+				u_integrationResults[f][gl_LocalInvocationIndex] +=
+					u_integrationResults[f][gl_LocalInvocationIndex + s];
 			}
+
+			memoryBarrierBuffer();
+			barrier();
 		}
 	}
 
-	// Pre-divide
-	irradiance *= (1.0 / PI);
-
-	// Store the result
-	imageStore(u_irradianceVolumes[nonuniformEXT(diceFace)], u_volumeTexel, Vec4(irradiance, 0.0));
+	// Store the results
+	ANKI_BRANCH if(gl_LocalInvocationIndex == 0u)
+	{
+		ANKI_UNROLL for(U32 f = 0u; f < 6u; ++f)
+		{
+			Vec3 irradiance = u_integrationResults[f][0];
+			irradiance /= PI; // Pre-divide
+			imageStore(u_irradianceVolumes[f], u_volumeTexel, Vec4(irradiance, 0.0));
+		}
+	}
 }
 
 #pragma anki end

+ 1 - 2
shaders/LightShading.glslp

@@ -214,8 +214,7 @@ void main()
 		const Vec3 env =
 			envBRDF(gbuffer.m_specular, gbuffer.m_roughness, u_integrationLut, u_trilinearClampSampler, NoV);
 
-		//out_color += diffIndirect * gbuffer.m_diffuse + finalRefl * env;
-		out_color = diffIndirect;
+		out_color += diffIndirect * gbuffer.m_diffuse + finalRefl * env;
 	}
 }
 #pragma anki end

+ 1 - 1
src/anki/core/Config.cpp

@@ -45,7 +45,7 @@ Config::Config()
 	newOption("r.indirect.maxSimultaneousProbeCount", 32);
 	newOption("r.indirect.shadowMapResolution", 64);
 
-	newOption("r.gi.tileResolution", 64);
+	newOption("r.gi.tileResolution", 32);
 	newOption("r.gi.maxSimultaneousProbeCount", 16);
 	newOption("r.gi.shadowMapResolution", 128);
 

+ 5 - 1
src/anki/renderer/GlobalIllumination.cpp

@@ -641,7 +641,7 @@ void GlobalIllumination::runLightShading(RenderPassWorkContext& rgraphCtx, Inter
 		cmdb);
 }
 
-void GlobalIllumination::runIrradiance(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx) const
+void GlobalIllumination::runIrradiance(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx)
 {
 	ANKI_TRACE_SCOPED_EVENT(R_GI);
 
@@ -666,6 +666,10 @@ void GlobalIllumination::runIrradiance(RenderPassWorkContext& rgraphCtx, Interna
 		rgraphCtx.bindImage(
 			0, binding, m_cacheEntries[probe.m_cacheEntryIndex].m_rtHandles[i], TextureSubresourceInfo(), i);
 	}
+	++binding;
+
+	// Bind temporary memory
+	allocateAndBindStorage<Vec3*>(sizeof(Vec3) * 6 * m_tileSize * m_tileSize, cmdb, 0, binding);
 
 	const IVec4 volumeTexel = IVec4(giCtx.m_cell.x(), giCtx.m_cell.y(), giCtx.m_cell.z(), 0);
 	cmdb->setPushConstants(&volumeTexel, sizeof(volumeTexel));

+ 1 - 1
src/anki/renderer/GlobalIllumination.h

@@ -106,7 +106,7 @@ private:
 	void runGBufferInThread(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx) const;
 	void runShadowmappingInThread(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx) const;
 	void runLightShading(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx);
-	void runIrradiance(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx) const;
+	void runIrradiance(RenderPassWorkContext& rgraphCtx, InternalContext& giCtx);
 
 	void prepareProbes(RenderingContext& rctx,
 		GlobalIlluminationProbeQueueElement*& probeToUpdateThisFrame,

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

@@ -179,6 +179,7 @@ class GlobalIlluminationProbeQueueElement final
 public:
 	U64 m_uuid;
 	GlobalIlluminationProbeQueueElementFeedbackCallback m_feedbackCallback;
+	RenderQueueDrawCallback m_debugDrawCallback;
 	void* m_userData;
 	Array<RenderQueue*, 6> m_renderQueues;
 	Vec3 m_aabbMin;

+ 54 - 0
src/anki/scene/DebugDrawer.cpp

@@ -238,4 +238,58 @@ void PhysicsDebugDrawer::drawLines(const Vec3* lines, const U32 vertCount, const
 	}
 }
 
+void allocateAndPopulateDebugBox(StagingGpuMemoryManager& stagingGpuAllocator,
+	StagingGpuMemoryToken& vertsToken,
+	StagingGpuMemoryToken& indicesToken,
+	U32& indexCount)
+{
+	Vec3* verts = static_cast<Vec3*>(
+		stagingGpuAllocator.allocateFrame(sizeof(Vec3) * 8, StagingGpuMemoryType::VERTEX, vertsToken));
+
+	const F32 SIZE = 1.0f;
+	verts[0] = Vec3(SIZE, SIZE, SIZE); // front top right
+	verts[1] = Vec3(-SIZE, SIZE, SIZE); // front top left
+	verts[2] = Vec3(-SIZE, -SIZE, SIZE); // front bottom left
+	verts[3] = Vec3(SIZE, -SIZE, SIZE); // front bottom right
+	verts[4] = Vec3(SIZE, SIZE, -SIZE); // back top right
+	verts[5] = Vec3(-SIZE, SIZE, -SIZE); // back top left
+	verts[6] = Vec3(-SIZE, -SIZE, -SIZE); // back bottom left
+	verts[7] = Vec3(SIZE, -SIZE, -SIZE); // back bottom right
+
+	const U INDEX_COUNT = 12 * 2;
+	U16* indices = static_cast<U16*>(
+		stagingGpuAllocator.allocateFrame(sizeof(U16) * INDEX_COUNT, StagingGpuMemoryType::VERTEX, indicesToken));
+
+	U c = 0;
+	indices[c++] = 0;
+	indices[c++] = 1;
+	indices[c++] = 1;
+	indices[c++] = 2;
+	indices[c++] = 2;
+	indices[c++] = 3;
+	indices[c++] = 3;
+	indices[c++] = 0;
+
+	indices[c++] = 4;
+	indices[c++] = 5;
+	indices[c++] = 5;
+	indices[c++] = 6;
+	indices[c++] = 6;
+	indices[c++] = 7;
+	indices[c++] = 7;
+	indices[c++] = 4;
+
+	indices[c++] = 0;
+	indices[c++] = 4;
+	indices[c++] = 1;
+	indices[c++] = 5;
+	indices[c++] = 2;
+	indices[c++] = 6;
+	indices[c++] = 3;
+	indices[c++] = 7;
+
+	ANKI_ASSERT(c == INDEX_COUNT);
+	indexCount = INDEX_COUNT;
+}
+
 } // end namespace anki

+ 8 - 0
src/anki/scene/DebugDrawer.h

@@ -17,6 +17,8 @@ namespace anki
 
 // Forward
 class RenderQueueDrawContext;
+class StagingGpuMemoryManager;
+class StagingGpuMemoryToken;
 
 /// @addtogroup renderer
 /// @{
@@ -118,6 +120,12 @@ public:
 private:
 	DebugDrawer* m_dbg; ///< The debug drawer
 };
+
+/// Allocate memory for a line cube and populate it.
+void allocateAndPopulateDebugBox(StagingGpuMemoryManager& stagingGpuAllocator,
+	StagingGpuMemoryToken& vertsToken,
+	StagingGpuMemoryToken& indicesToken,
+	U32& indexCount);
 /// @}
 
 } // end namespace anki

+ 1 - 0
src/anki/scene/GlobalIlluminationProbeNode.cpp

@@ -82,6 +82,7 @@ Error GlobalIlluminationProbeNode::init()
 	// GI probe comp
 	GlobalIlluminationProbeComponent* giprobec =
 		newComponent<GlobalIlluminationProbeComponent>(getSceneGraph().getNewUuid());
+	ANKI_CHECK(giprobec->init(getResourceManager()));
 	giprobec->setBoundingBox(m_spatialAabb.getMin(), m_spatialAabb.getMax());
 
 	// Second feedback component

+ 6 - 50
src/anki/scene/ModelNode.cpp

@@ -5,6 +5,7 @@
 
 #include <anki/scene/ModelNode.h>
 #include <anki/scene/SceneGraph.h>
+#include <anki/scene/DebugDrawer.h>
 #include <anki/scene/components/BodyComponent.h>
 #include <anki/scene/components/SkinComponent.h>
 #include <anki/scene/components/RenderComponent.h>
@@ -126,7 +127,7 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
-	if(!ctx.m_debugDraw)
+	if(ANKI_LIKELY(!ctx.m_debugDraw))
 	{
 		const ModelPatch* patch = m_model->getModelPatches()[m_modelPatchIdx];
 
@@ -210,54 +211,9 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 		// Draw the bounding volumes
 
 		// Allocate staging memory
-		StagingGpuMemoryToken vertToken;
-		Vec3* verts = static_cast<Vec3*>(
-			ctx.m_stagingGpuAllocator->allocateFrame(sizeof(Vec3) * 8, StagingGpuMemoryType::VERTEX, vertToken));
-
-		const F32 SIZE = 1.0f;
-		verts[0] = Vec3(SIZE, SIZE, SIZE); // front top right
-		verts[1] = Vec3(-SIZE, SIZE, SIZE); // front top left
-		verts[2] = Vec3(-SIZE, -SIZE, SIZE); // front bottom left
-		verts[3] = Vec3(SIZE, -SIZE, SIZE); // front bottom right
-		verts[4] = Vec3(SIZE, SIZE, -SIZE); // back top right
-		verts[5] = Vec3(-SIZE, SIZE, -SIZE); // back top left
-		verts[6] = Vec3(-SIZE, -SIZE, -SIZE); // back bottom left
-		verts[7] = Vec3(SIZE, -SIZE, -SIZE); // back bottom right
-
-		StagingGpuMemoryToken indicesToken;
-		const U INDEX_COUNT = 12 * 2;
-		U16* indices = static_cast<U16*>(ctx.m_stagingGpuAllocator->allocateFrame(
-			sizeof(U16) * INDEX_COUNT, StagingGpuMemoryType::VERTEX, indicesToken));
-
-		U c = 0;
-		indices[c++] = 0;
-		indices[c++] = 1;
-		indices[c++] = 1;
-		indices[c++] = 2;
-		indices[c++] = 2;
-		indices[c++] = 3;
-		indices[c++] = 3;
-		indices[c++] = 0;
-
-		indices[c++] = 4;
-		indices[c++] = 5;
-		indices[c++] = 5;
-		indices[c++] = 6;
-		indices[c++] = 6;
-		indices[c++] = 7;
-		indices[c++] = 7;
-		indices[c++] = 4;
-
-		indices[c++] = 0;
-		indices[c++] = 4;
-		indices[c++] = 1;
-		indices[c++] = 5;
-		indices[c++] = 2;
-		indices[c++] = 6;
-		indices[c++] = 3;
-		indices[c++] = 7;
-
-		ANKI_ASSERT(c == INDEX_COUNT);
+		StagingGpuMemoryToken vertToken, indicesToken;
+		U32 indexCount;
+		allocateAndPopulateDebugBox(*ctx.m_stagingGpuAllocator, vertToken, indicesToken, indexCount);
 
 		// Set the uniforms
 		StagingGpuMemoryToken unisToken;
@@ -311,7 +267,7 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 
 		cmdb->bindUniformBuffer(1, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
 
-		cmdb->drawElements(PrimitiveTopology::LINES, INDEX_COUNT, userData.getSize());
+		cmdb->drawElements(PrimitiveTopology::LINES, indexCount, userData.getSize());
 
 		// Restore state
 		if(!enableDepthTest)

+ 88 - 0
src/anki/scene/components/GlobalIlluminationProbeComponent.cpp

@@ -0,0 +1,88 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/components/GlobalIlluminationProbeComponent.h>
+#include <anki/scene/DebugDrawer.h>
+#include <anki/resource/ResourceManager.h>
+
+namespace anki
+{
+
+Error GlobalIlluminationProbeComponent::init(ResourceManager& rsrc)
+{
+	return rsrc.loadResource("shaders/SceneDebug.glslp", m_dbgProg);
+}
+
+void GlobalIlluminationProbeComponent::debugDraw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
+{
+	StagingGpuMemoryToken vertToken, indicesToken;
+	U32 indexCount;
+	allocateAndPopulateDebugBox(*ctx.m_stagingGpuAllocator, vertToken, indicesToken, indexCount);
+
+	// Set the uniforms
+	StagingGpuMemoryToken unisToken;
+	Mat4* mvps = static_cast<Mat4*>(ctx.m_stagingGpuAllocator->allocateFrame(
+		sizeof(Mat4) * userData.getSize() + sizeof(Vec4), StagingGpuMemoryType::UNIFORM, unisToken));
+
+	for(U i = 0; i < userData.getSize(); ++i)
+	{
+		const GlobalIlluminationProbeComponent& self =
+			*static_cast<const GlobalIlluminationProbeComponent*>(userData[i]);
+
+		const Vec3 tsl = (self.m_aabbMin + self.m_aabbMax) / 2.0f;
+		const Vec3 scale = (tsl - self.m_aabbMin) / 2.0f;
+
+		// Set non uniform scale.
+		Mat3 rot = Mat3::getIdentity();
+		rot(0, 0) *= scale.x();
+		rot(1, 1) *= scale.y();
+		rot(2, 2) *= scale.z();
+
+		*mvps = ctx.m_viewProjectionMatrix * Mat4(tsl.xyz1(), rot, 1.0f);
+		++mvps;
+	}
+
+	Vec4* color = reinterpret_cast<Vec4*>(mvps);
+	*color = Vec4(0.729f, 0.635f, 0.196f, 1.0f);
+
+	// Setup state
+	const GlobalIlluminationProbeComponent& self = *static_cast<const GlobalIlluminationProbeComponent*>(userData[0]);
+	ShaderProgramResourceMutationInitList<2> mutators(self.m_dbgProg);
+	mutators.add("COLOR_TEXTURE", 0);
+	mutators.add("DITHERED_DEPTH_TEST", ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON));
+	ShaderProgramResourceConstantValueInitList<1> consts(self.m_dbgProg);
+	consts.add("INSTANCE_COUNT", U32(userData.getSize()));
+	const ShaderProgramResourceVariant* variant;
+	self.m_dbgProg->getOrCreateVariant(mutators.get(), consts.get(), variant);
+
+	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	cmdb->bindShaderProgram(variant->getProgram());
+
+	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
+	if(enableDepthTest)
+	{
+		cmdb->setDepthCompareOperation(CompareOperation::LESS);
+	}
+	else
+	{
+		cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
+	}
+
+	cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
+	cmdb->bindVertexBuffer(0, vertToken.m_buffer, vertToken.m_offset, sizeof(Vec3));
+	cmdb->bindIndexBuffer(indicesToken.m_buffer, indicesToken.m_offset, IndexType::U16);
+
+	cmdb->bindUniformBuffer(1, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
+
+	cmdb->drawElements(PrimitiveTopology::LINES, indexCount, userData.getSize());
+
+	// Restore state
+	if(!enableDepthTest)
+	{
+		cmdb->setDepthCompareOperation(CompareOperation::LESS);
+	}
+}
+
+} // end namespace anki

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

@@ -27,6 +27,8 @@ public:
 		ANKI_ASSERT(uuid > 0);
 	}
 
+	ANKI_USE_RESULT Error init(ResourceManager& rsrc);
+
 	/// Set the bounding box in world coordinates.
 	void setBoundingBox(const Vec4& min, const Vec4& max)
 	{
@@ -79,6 +81,7 @@ public:
 	{
 		el.m_uuid = m_uuid;
 		el.m_feedbackCallback = giProbeQueueElementFeedbackCallback;
+		el.m_debugDrawCallback = debugDrawCallback;
 		el.m_userData = this;
 		el.m_renderQueues = {};
 		el.m_aabbMin = m_aabbMin;
@@ -96,6 +99,7 @@ public:
 	}
 
 private:
+	ShaderProgramResourcePtr m_dbgProg;
 	U64 m_uuid;
 	Vec3 m_aabbMin = Vec3(-1.0f);
 	Vec3 m_aabbMax = Vec3(+1.0f);
@@ -117,9 +121,11 @@ private:
 
 	static void debugDrawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
 	{
-		// TODO
+		debugDraw(ctx, userData);
 	}
 
+	static void debugDraw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData);
+
 	/// Recalc come values.
 	void updateMembers()
 	{