Browse Source

IS refactoring

Panagiotis Christopoulos Charitos 12 years ago
parent
commit
77a75ed609
3 changed files with 194 additions and 181 deletions
  1. 17 1
      include/anki/gl/BufferObject.h
  2. 3 12
      include/anki/renderer/Is.h
  3. 174 168
      src/renderer/Is.cpp

+ 17 - 1
include/anki/gl/BufferObject.h

@@ -162,10 +162,26 @@ public:
 		ANKI_ASSERT(correctTarget);
 		ANKI_ASSERT(correctTarget);
 		(void)correctTarget;
 		(void)correctTarget;
 		
 		
-		bind();
 		glBindBufferBase(target, binding, glId);
 		glBindBufferBase(target, binding, glId);
 	}
 	}
 
 
+	/// Set the binding point of this buffer with range
+	void setBindingRange(GLuint binding, PtrSize offset, PtrSize size) const
+	{
+		Bool correctTarget = target == GL_TRANSFORM_FEEDBACK_BUFFER 
+#if ANKI_GL == ANKI_GL_DESKTOP
+			|| target == GL_SHADER_STORAGE_BUFFER
+#endif
+			|| target == GL_UNIFORM_BUFFER;
+
+		ANKI_ASSERT(correctTarget);
+		(void)correctTarget;
+
+		ANKI_ASSERT(offset + size <= sizeInBytes);
+
+		glBindBufferRange(target, binding, glId, offset, size);
+	}
+
 private:
 private:
 	/// Used in glBindBuffer(target, glId) and its for easy access so we
 	/// Used in glBindBuffer(target, glId) and its for easy access so we
 	/// wont have to query the GL driver. Its the type of the buffer eg
 	/// wont have to query the GL driver. Its the type of the buffer eg

+ 3 - 12
include/anki/renderer/Is.h

@@ -29,16 +29,6 @@ class Is: private RenderingPass
 	friend struct UpdateTilesJob;
 	friend struct UpdateTilesJob;
 
 
 public:
 public:
-	static const U MAX_POINT_LIGHTS_PER_TILE = 24;
-	static const U MAX_SPOT_LIGHTS_PER_TILE = 4;
-	static const U MAX_SPOT_TEX_LIGHTS_PER_TILE = 4;
-
-	static const U MAX_POINT_LIGHTS = 512 - 16;
-	static const U MAX_SPOT_LIGHTS = 8;
-	static const U MAX_SPOT_TEX_LIGHTS = 8;
-	static const U MAX_LIGHTS = MAX_POINT_LIGHTS + MAX_SPOT_LIGHTS 
-		+ MAX_SPOT_TEX_LIGHTS;
-
 	Is(Renderer* r);
 	Is(Renderer* r);
 	~Is();
 	~Is();
 
 
@@ -57,7 +47,8 @@ private:
 	static const U COMMON_UNIFORMS_BLOCK_BINDING = 0;
 	static const U COMMON_UNIFORMS_BLOCK_BINDING = 0;
 	static const U POINT_LIGHTS_BLOCK_BINDING = 1;
 	static const U POINT_LIGHTS_BLOCK_BINDING = 1;
 	static const U SPOT_LIGHTS_BLOCK_BINDING = 2;
 	static const U SPOT_LIGHTS_BLOCK_BINDING = 2;
-	static const U TILES_BLOCK_BINDING = 3;
+	static const U SPOT_TEX_LIGHTS_BLOCK_BINDING = 3;
+	static const U TILES_BLOCK_BINDING = 4;
 
 
 	/// The IS FAI
 	/// The IS FAI
 	Texture fai;
 	Texture fai;
@@ -74,7 +65,7 @@ private:
 	/// Track the updates of commonUbo
 	/// Track the updates of commonUbo
 	U32 commonUboUpdateTimestamp = Timestamp::getTimestamp();
 	U32 commonUboUpdateTimestamp = Timestamp::getTimestamp();
 
 
-	/// Contains info of lights
+	/// Contains all the lights
 	Ubo lightsUbo;
 	Ubo lightsUbo;
 
 
 	/// Contains the indices of lights per tile
 	/// Contains the indices of lights per tile

+ 174 - 168
src/renderer/Is.cpp

@@ -8,6 +8,22 @@
 namespace anki {
 namespace anki {
 
 
 //==============================================================================
 //==============================================================================
+static const U MAX_POINT_LIGHTS_PER_TILE = 
+	ANKI_RENDERER_MAX_POINT_LIGHTS_PER_TILE;
+static const U MAX_SPOT_LIGHTS_PER_TILE = 
+	ANKI_RENDERER_MAX_SPOT_LIGHTS_PER_TILE;
+static const U MAX_SPOT_TEX_LIGHTS_PER_TILE = 
+	ANKI_RENDERER_MAX_SPOT_TEX_LIGHTS_PER_TILE;
+
+static const U MAX_POINT_LIGHTS = ANKI_RENDERER_MAX_POINT_LIGHTS;
+static const U MAX_SPOT_LIGHTS = ANKI_RENDERER_MAX_SPOT_LIGHTS;
+static const U MAX_SPOT_TEX_LIGHTS = ANKI_RENDERER_MAX_SPOT_TEX_LIGHTS;
+static const U MAX_LIGHTS = 
+	MAX_POINT_LIGHTS + MAX_SPOT_LIGHTS + MAX_SPOT_TEX_LIGHTS;
+
+static const U TILES_X_COUNT = ANKI_RENDERER_TILES_X_COUNT;
+static const U TILES_Y_COUNT = ANKI_RENDERER_TILES_Y_COUNT;
+static const U TILES_COUNT = TILES_X_COUNT * TILES_Y_COUNT;
 
 
 //==============================================================================
 //==============================================================================
 static Bool groundVectorsEqual(const Vec3& prev, const Vec3& crnt)
 static Bool groundVectorsEqual(const Vec3& prev, const Vec3& crnt)
@@ -79,14 +95,6 @@ struct SpotTexLight: SpotLight
 	Mat4 texProjectionMat; ///< Texture projection matrix
 	Mat4 texProjectionMat; ///< Texture projection matrix
 };
 };
 
 
-struct Lights
-{
-	U32 count[4];
-	PointLight pointLights[Is::MAX_POINT_LIGHTS];
-	SpotLight spotLights[Is::MAX_SPOT_LIGHTS];
-	SpotTexLight spotTexLights[Is::MAX_SPOT_TEX_LIGHTS];
-};
-
 struct CommonUniforms
 struct CommonUniforms
 {
 {
 	Vec4 planes;
 	Vec4 planes;
@@ -96,21 +104,39 @@ struct CommonUniforms
 
 
 } // end namespace shader
 } // end namespace shader
 
 
+static const PtrSize LIGHTS_UBO_SIZE = 
+		MAX_POINT_LIGHTS * sizeof(shader::PointLight)
+		+ MAX_SPOT_LIGHTS * sizeof(shader::SpotLight)
+		+ MAX_SPOT_TEX_LIGHTS * sizeof(shader::SpotTexLight);
+
 //==============================================================================
 //==============================================================================
 
 
 // Threading jobs
 // Threading jobs
 
 
 /// Write the lights to a client buffer
 /// Write the lights to a client buffer
-struct WriteLights: ThreadJob
+struct WriteLightsJob: ThreadJob
 {
 {
-	shader::Lights* gpuLights;
-	SceneVector<Light*>::iterator lightsBegin; // XXX Make them const
+	shader::PointLight* pointLights = nullptr;
+	shader::SpotLight* spotLights = nullptr;
+	shader::SpotTexLight* spotTexLights = nullptr;
+	shader::Tiles* tiles = nullptr;
+
+	SceneVector<Light*>::iterator lightsBegin; // XXX Make them const and ShaderNode
 	SceneVector<Light*>::iterator lightsEnd;
 	SceneVector<Light*>::iterator lightsEnd;
-	Is* is = nullptr;
 
 
 	std::atomic<U32>* pointLightsCount = nullptr;
 	std::atomic<U32>* pointLightsCount = nullptr;
 	std::atomic<U32>* spotLightsCount = nullptr;
 	std::atomic<U32>* spotLightsCount = nullptr;
 	std::atomic<U32>* spotTexLightsCount = nullptr;
 	std::atomic<U32>* spotTexLightsCount = nullptr;
+		
+	std::atomic<U32> 
+		(*tilePointLightsCount)[TILES_Y_COUNT][TILES_X_COUNT];
+	std::atomic<U32> 
+		(*tileSpotLightsCount)[TILES_Y_COUNT][TILES_X_COUNT];
+	std::atomic<U32> 
+		(*tileSpotTexLightsCount)[TILES_Y_COUNT][TILES_X_COUNT];
+
+	Tiler* tiler = nullptr;
+	Is* is = nullptr;
 
 
 	void operator()(U threadId, U threadsCount)
 	void operator()(U threadId, U threadsCount)
 	{
 	{
@@ -138,14 +164,13 @@ struct WriteLights: ThreadJob
 	}
 	}
 
 
 	/// Copy CPU light to GPU buffer
 	/// Copy CPU light to GPU buffer
-	void doLight(PointLight& light)
+	U doLight(PointLight& light)
 	{
 	{
 		// Get GPU light
 		// Get GPU light
 		U32 pos = pointLightsCount->fetch_add(1);
 		U32 pos = pointLightsCount->fetch_add(1);
-		// Use % to avoid overflows
-		pos = pos % Is::MAX_POINT_LIGHTS;
+		ANKI_ASSERT(pos < MAX_POINT_LIGHTS);
 
 
-		shader::PointLight& slight = gpuLights->pointLights[pos];
+		shader::PointLight& slight = pointLights[pos];
 
 
 		const Camera* cam = is->cam;
 		const Camera* cam = is->cam;
 		ANKI_ASSERT(cam);
 		ANKI_ASSERT(cam);
@@ -156,10 +181,12 @@ struct WriteLights: ThreadJob
 		slight.posRadius = Vec4(pos, light.getRadius());
 		slight.posRadius = Vec4(pos, light.getRadius());
 		slight.diffuseColorShadowmapId = light.getDiffuseColor();
 		slight.diffuseColorShadowmapId = light.getDiffuseColor();
 		slight.specularColorTexId = light.getSpecularColor();
 		slight.specularColorTexId = light.getSpecularColor();
+
+		return pos;
 	}
 	}
 
 
 	/// Copy CPU spot light to GPU buffer
 	/// Copy CPU spot light to GPU buffer
-	void doLight(SpotLight& light)
+	U doLight(SpotLight& light)
 	{
 	{
 		Bool isTexLight = light.getShadowEnabled();
 		Bool isTexLight = light.getShadowEnabled();
 
 
@@ -170,9 +197,9 @@ struct WriteLights: ThreadJob
 			// Spot tex light
 			// Spot tex light
 
 
 			pos = spotTexLightsCount->fetch_add(1);
 			pos = spotTexLightsCount->fetch_add(1);
-			pos = pos % Is::MAX_SPOT_TEX_LIGHTS;
+			ANKI_ASSERT(pos < MAX_SPOT_TEX_LIGHTS);
 
 
-			shader::SpotTexLight& slight = gpuLights->spotTexLights[pos];
+			shader::SpotTexLight& slight = spotTexLights[pos];
 			baseslight = &slight;
 			baseslight = &slight;
 
 
 			// Write matrix
 			// Write matrix
@@ -194,12 +221,12 @@ struct WriteLights: ThreadJob
 		}
 		}
 		else
 		else
 		{
 		{
-			// Spot light
+			// Spot light without texture
 
 
 			pos = spotLightsCount->fetch_add(1);
 			pos = spotLightsCount->fetch_add(1);
-			pos = pos % Is::MAX_SPOT_LIGHTS;
+			ANKI_ASSERT(pos < MAX_SPOT_LIGHTS);
 
 
-			shader::SpotLight& slight = gpuLights->spotLights[pos];
+			shader::SpotLight& slight = spotLights[pos];
 			baseslight = &slight;
 			baseslight = &slight;
 		}
 		}
 
 
@@ -238,71 +265,66 @@ struct WriteLights: ThreadJob
 			dir.transform(cam->getViewMatrix());
 			dir.transform(cam->getViewMatrix());
 			baseslight->extendPoints[i] = Vec4(dir, 1.0);
 			baseslight->extendPoints[i] = Vec4(dir, 1.0);
 		}
 		}
-	}
-};
 
 
-/// A job to write the tiles UBO
-struct UpdateTilesJob: ThreadJob
-{
-	Tiles* tiles = nullptr;
-	Lights* lights = nullptr;
-	Is* is = nullptr;
-	Tiler* tiler = nullptr;
-
-	std::atomic<U32> (*pointLightsCount)[Is::TILES_Y_COUNT][Is::TILES_X_COUNT];
-	std::atomic<U32> (*spotLightsCount)[Is::TILES_Y_COUNT][Is::TILES_X_COUNT];
-	std::atomic<U32> 
-		(*spotTexLightsCount)[Is::TILES_Y_COUNT][Is::TILES_X_COUNT];
+		return pos;
+	}
 
 
-	void operator()(U threadId, U threadsCount)
+	// Bin point light
+	void binLight(const PointLight& light, U pos)
 	{
 	{
-		U pointLightsCount = lights->count[0];
-		U spotLightsCount = lights->count[2];
-		U spotTexLightsCount = lights->count[3];
+		// Do the tests
+		Tiler::Bitset bitset;
+		tiler->test(light->getSpatialCollisonShape(), true, &bitset);
 
 
-		U lightsCount = pointLightsCount + spotLightsCount + spotTexLightsCount;
-		ANKI_ASSERT(lightsCount <= Is::MAX_LIGHTS);
+		// Bin to the correct tiles
+		for(U t = 0; t < TILES_COUNT; t++)
+		{
+			// If not in tile bye
+			if(!bitset.test(t))
+			{
+				continue;
+			}
 
 
-		U64 start, end;
-		choseStartEnd(threadId, threadsCount, lightsCount, start, end);
+			U x = t % TILES_X_COUNT;
+			U y = t / TILES_X_COUNT;
 
 
-		const Tiler& tiler = is->r.getTiler();
+			U tilePos = (*tilePointLightsCount)[y][x].fetch_add(1);
 
 
-		for(U32 i = start; i < end; i++)
-		{
-			if(i < pointLightsCount)
+			if(pos < MAX_POINT_LIGHTS_PER_TILE)
 			{
 			{
-				Sphere s(light.posRadius.xyz(), light.posRadius.w());
-				Tiler::Bitset bitset;
-				tiler->test(s, true, &bitset);
+				tiles->tiles[y][x].pointLightIndices[tilePos] = pos;
+			}
+		}
+	}
 
 
-				// For all tiles
-				for(U t = 0; t < Tiler::TILES_COUNT; t++)
-				{
-					// If not in tile bye
-					if(!bitset.test(t))
-					{
-						continue;
-					}
+	// Bin spot light
+	void binLight(const SpotLight& light, U pos)
+	{
+		// Do the tests
+		Tiler::Bitset bitset;
+		tiler->test(light->getSpatialCollisonShape(), true, &bitset);
 
 
-					U x = t % Tiler::TILES_X_COUNT;
-					U y = t / Tiler::TILES_X_COUNT;
+		// Bin to the correct tiles
+		for(U t = 0; t < TILES_COUNT; t++)
+		{
+			// If not in tile bye
+			if(!bitset.test(t))
+			{
+				continue;
+			}
 
 
-					U pos = (*pointLightsCount)[y][x].fetch_add(1);
+			U x = t % TILES_X_COUNT;
+			U y = t / TILES_X_COUNT;
 
 
-					if(pos < Is::MAX_POINT_LIGHTS_PER_TILE)
-					{
-						tiles->tiles[y][x].pointLightIndices[pos] = i;
-					}
-				}
-			}
-			else if(i < pointLightsCount + spotLightsCount)
+			if(light.getShadowEnabled())
 			{
 			{
-
+				U tilePos = (*tileSpotTexLightsCount)[y][x].fetch_add(1);
+				tiles->tiles[y][x].spotTexLightIndices[tilePos] = pos;
 			}
 			}
 			else
 			else
 			{
 			{
-				ANKI_ASSERT(i < lightsCount);
+				U tilePos = (*tileSpotLightsCount)[y][x].fetch_add(1);
+				tiles->tiles[y][x].spotLightIndices[tilePos] = pos;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -412,8 +434,9 @@ void Is::initInternal(const RendererInitializer& initializer)
 	commonUbo.create(sizeof(shader::CommonUniforms), nullptr);
 	commonUbo.create(sizeof(shader::CommonUniforms), nullptr);
 
 
 	// lights UBO
 	// lights UBO
-	pointLightsUbo.create(sizeof(shader::PointLights), nullptr);
-	spotLightsUbo.create(sizeof(shader::SpotLights), nullptr);
+	lightsUbo.create()
+
+	pointLightsUbo.create(LIGHTS_UBO_SIZE, nullptr);
 
 
 	// tiles UBO
 	// tiles UBO
 	tilesUbo.create(sizeof(shader::Tiles), nullptr);
 	tilesUbo.create(sizeof(shader::Tiles), nullptr);
@@ -435,7 +458,7 @@ void Is::initInternal(const RendererInitializer& initializer)
 
 
 	ublock = &lightPassProg->findUniformBlock("pointLightsBlock");
 	ublock = &lightPassProg->findUniformBlock("pointLightsBlock");
 	ublock->setBinding(POINT_LIGHTS_BLOCK_BINDING);
 	ublock->setBinding(POINT_LIGHTS_BLOCK_BINDING);
-	if(ublock->getSize() != sizeof(shader::PointLights)
+	if(ublock->getSize() != sizeof(shader::PointLight) * MAX_POINT_LIGHTS
 		|| ublock->getBinding() != POINT_LIGHTS_BLOCK_BINDING)
 		|| ublock->getBinding() != POINT_LIGHTS_BLOCK_BINDING)
 	{
 	{
 		throw ANKI_EXCEPTION("Problem with the pointLightsBlock");
 		throw ANKI_EXCEPTION("Problem with the pointLightsBlock");
@@ -443,12 +466,20 @@ void Is::initInternal(const RendererInitializer& initializer)
 
 
 	ublock = &lightPassProg->findUniformBlock("spotLightsBlock");
 	ublock = &lightPassProg->findUniformBlock("spotLightsBlock");
 	ublock->setBinding(SPOT_LIGHTS_BLOCK_BINDING);
 	ublock->setBinding(SPOT_LIGHTS_BLOCK_BINDING);
-	if(ublock->getSize() != sizeof(shader::SpotLights)
+	if(ublock->getSize() != sizeof(shader::SpotLight) * MAX_SPOT_LIGHTS
 		|| ublock->getBinding() != SPOT_LIGHTS_BLOCK_BINDING)
 		|| ublock->getBinding() != SPOT_LIGHTS_BLOCK_BINDING)
 	{
 	{
 		throw ANKI_EXCEPTION("Problem with the spotLightsBlock");
 		throw ANKI_EXCEPTION("Problem with the spotLightsBlock");
 	}
 	}
 
 
+	ublock = &lightPassProg->findUniformBlock("spotTexLightsBlock");
+	ublock->setBinding(SPOT_TEX_LIGHTS_BLOCK_BINDING);
+	if(ublock->getSize() != sizeof(shader::SpotTexLight) * MAX_SPOT_TEX_LIGHTS
+		|| ublock->getBinding() != SPOT_TEX_LIGHTS_BLOCK_BINDING)
+	{
+		throw ANKI_EXCEPTION("Problem with the spotTexLightsBlock");
+	}
+
 	ublock = &lightPassProg->findUniformBlock("tilesBlock");
 	ublock = &lightPassProg->findUniformBlock("tilesBlock");
 	ublock->setBinding(TILES_BLOCK_BINDING);
 	ublock->setBinding(TILES_BLOCK_BINDING);
 	if(ublock->getSize() != sizeof(shader::Tiles)
 	if(ublock->getSize() != sizeof(shader::Tiles)
@@ -465,18 +496,14 @@ void Is::lightPass()
 	VisibilityTestResults& vi = 
 	VisibilityTestResults& vi = 
 		*cam->getFrustumable()->getVisibilityTestResults();
 		*cam->getFrustumable()->getVisibilityTestResults();
 
 
-	Array<PointLight*, MAX_POINT_LIGHTS> visiblePointLights;
-	U visiblePointLightsCount = 0;
-	Array<SpotLight*, MAX_SPOT_LIGHTS> visibleSpotLights;
-	U visibleSpotLightsCount = 0;
-
-	U spotsNoShadowCount = 0, spotsShadowCount = 0;
-	Array<SpotLight*, MAX_SPOT_LIGHTS> visibleSpotNoShadowLights, 
-		visibleSpotShadowLights;
-
 	//
 	//
 	// Quickly get the lights
 	// Quickly get the lights
 	//
 	//
+	U visiblePointLightsCount = 0;
+	U visibleSpotLightsCount = 0;
+	U visibleSpotTexLightsCount = 0;
+	Array<Light*, Sm::MAX_SHADOW_CASTERS> shadowCasters;
+
 	for(auto it = vi.getLightsBegin(); it != vi.getLightsEnd(); ++it)
 	for(auto it = vi.getLightsBegin(); it != vi.getLightsEnd(); ++it)
 	{
 	{
 		Light* light = (*it)->getLight();
 		Light* light = (*it)->getLight();
@@ -484,8 +511,6 @@ void Is::lightPass()
 		{
 		{
 		case Light::LT_POINT:
 		case Light::LT_POINT:
 			// Use % to avoid overflows
 			// Use % to avoid overflows
-			visiblePointLights[visiblePointLightsCount % MAX_POINT_LIGHTS] = 
-				static_cast<PointLight*>(light);
 			++visiblePointLightsCount;
 			++visiblePointLightsCount;
 			break;
 			break;
 		case Light::LT_SPOT:
 		case Light::LT_SPOT:
@@ -494,54 +519,34 @@ void Is::lightPass()
 				
 				
 				if(slight->getShadowEnabled())
 				if(slight->getShadowEnabled())
 				{
 				{
-					visibleSpotShadowLights[
-						spotsShadowCount % MAX_SPOT_LIGHTS] = slight;
-					++spotsShadowCount;
+					shadowCasters[visibleSpotTexLightsCount++] = slight;
 				}
 				}
 				else
 				else
 				{
 				{
-					visibleSpotNoShadowLights[
-						spotsNoShadowCount % MAX_SPOT_LIGHTS] = slight;
-					++spotsNoShadowCount;
+					++visibleSpotLightsCount;
 				}
 				}
 				break;
 				break;
 			}
 			}
-		case Light::LT_NUM:
+		default:
+			ANKI_ASSERT(0);
 			break;
 			break;
 		}
 		}
 	}
 	}
 
 
-	visibleSpotLightsCount = spotsShadowCount + spotsNoShadowCount;
-
-	if(visiblePointLightsCount > MAX_POINT_LIGHTS 
-		|| visibleSpotLightsCount > MAX_SPOT_LIGHTS)
-	{
-		throw ANKI_EXCEPTION("Too many visible lights");
-	}
-
-	for(U i = 0; i < spotsNoShadowCount; i++)
-	{
-		visibleSpotLights[i] = visibleSpotNoShadowLights[i];
-	}
-
-	for(U i = 0; i < spotsShadowCount; i++)
-	{
-		visibleSpotLights[i + spotsNoShadowCount] = visibleSpotShadowLights[i];
-	}
+	// Sanitize the counts
+	visiblePointLightsCount = MAX_POINT_LIGHTS % visiblePointLightsCount;
+	visibleSpotLightsCount = MAX_SPOT_LIGHTS % visibleSpotLightsCount;
+	visibleSpotTexLightsCount = MAX_SPOT_TEX_LIGHTS % visibleSpotTexLightsCount;
 
 
 	//
 	//
 	// Do shadows pass
 	// Do shadows pass
 	//
 	//
-	Array<Light*, Sm::MAX_SHADOW_CASTERS> shadowCasters;
-	for(U i = 0; i < spotsShadowCount; i++)
-	{
-		shadowCasters[i] = visibleSpotShadowLights[i];
-	}
-
 	Array<U32, Sm::MAX_SHADOW_CASTERS> shadowmapLayers;
 	Array<U32, Sm::MAX_SHADOW_CASTERS> shadowmapLayers;
 	sm.run(&shadowCasters[0], spotsShadowCount, shadowmapLayers);
 	sm.run(&shadowCasters[0], spotsShadowCount, shadowmapLayers);
 
 
+	//
 	// Prepare state
 	// Prepare state
+	//
 	fbo.bind();
 	fbo.bind();
 	r->clearAfterBindingFbo(
 	r->clearAfterBindingFbo(
 		GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 		GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
@@ -551,74 +556,75 @@ void Is::lightPass()
 	GlStateSingleton::get().disable(GL_BLEND);
 	GlStateSingleton::get().disable(GL_BLEND);
 
 
 	//
 	//
-	// Write the lights UBOs
+	// Write the lights UBO
 	//
 	//
 
 
-	// Write pointlights
-	if(visiblePointLightsCount > 0)
-	{
-		shader::PointLights clientBuffer;
+	U8 clientBuffer[LIGHTS_UBO_SIZE];
 
 
-		WritePointLightsUbo jobs[ThreadPool::MAX_THREADS];
-		for(U i = 0; i < threadPool.getThreadsCount(); i++)
-		{
-			jobs[i].shaderLights = &clientBuffer.lights[0];
-			jobs[i].visibleLights = &visiblePointLights[0];
-			jobs[i].visibleLightsCount = visiblePointLightsCount;
-			jobs[i].is = this;
+	// Get the offsets and sizes of each uniform block
+	PtrSize pointLightsOffset = 0;
+	PtrSize pointLightsSize = 
+		sizeof(shader::PointLight) * visiblePointLightsCount;
 
 
-			threadPool.assignNewJob(i, &jobs[i]);
-		}
+	PtrSize spotLightsOffset = pointLightsSize;
+	PtrSize spotLightsSize = 
+		sizeof(shader::SpotLight) * visibleSpotLightsCount;
 
 
-		// Done
-		threadPool.waitForAllJobsToFinish();
+	PtrSize spotTexLightsOffset = spotLightsOffset + spotLightsSize;
+	PtrSize spotTexLightsSize = 
+		sizeof(shader::SpotTexLight) * visibleSpotTexLightsCount;
 
 
-		pointLightsUbo.write(
-			&clientBuffer,
-			0, 
-			sizeof(shader::PointLight) * visiblePointLightsCount);
-	}
+	ANKI_ASSERT(spotTexLightsOffset + spotTexLightsSize <= LIGHTS_UBO_SIZE);
 
 
-	// Write spots
-	if(visibleSpotLightsCount > 0)
+	// Fire the super jobs
+	WriteLightsJob jobs[ThreadPool::MAX_THREADS];
+
+	shader::Tiles tilesClient;
+
+	std::atomic<U32> pointLightsAtomicCount;
+	std::atomic<U32> spotLightsAtomicCount;
+	std::atomic<U32> spotTexLightsAtomicCount;
+
+	std::atomic<U32> 
+		tilePointLightsCount[TILES_Y_COUNT][TILES_X_COUNT];
+	std::atomic<U32> 
+		tileSpotLightsCount[TILES_Y_COUNT][TILES_X_COUNT];
+	std::atomic<U32> 
+		tileSpotTexLightsCount[TILES_Y_COUNT][TILES_X_COUNT];
+
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
 	{
 	{
-		shader::SpotLights clientBuffer;
+		WriteLightsJob& job = jobs[i];
 
 
-		WriteSpotLightsUbo jobs2[ThreadPool::MAX_THREADS];
-		for(U i = 0; i < threadPool.getThreadsCount(); i++)
-		{
-			jobs2[i].shaderLights = &clientBuffer.lights[0];
-			jobs2[i].visibleLights = &visibleSpotLights[0];
-			jobs2[i].visibleLightsCount = visibleSpotLightsCount;
-			jobs2[i].is = this;
+		job.pointLigths = 
+			(shader::PointLight*)(&clientBuffer[0] + pointLightsOffset);
+		job.spotLigths = 
+			(shader::SpotLight*)(&clientBuffer[0] + spotLightsOffset);
+		job.spotTexLigths = 
+			(shader::SpotTexLight*)(&clientBuffer[0] + spotTexLightsOffset);
 
 
-			threadPool.assignNewJob(i, &jobs2[i]);
-		}
+		job.tiles = &tilesClient;
 
 
-		// Done
-		threadPool.waitForAllJobsToFinish();
+		// XXX add more
 
 
-		// Set shadow layer IDs
-		U32 i = 0;
-		shader::SpotLight* shaderSpotLight = 
-			&clientBuffer.lights[0] + spotsNoShadowCount;
-		shader::SpotLight* end = 
-			&clientBuffer.lights[0] + visibleSpotLightsCount;
+		job.pointLightsCount = &pointLightsAtomicCount;
+		job.spotLightsCount = &spotLightsAtomicCount;
+		job.spotTexLightsCount = &spotTexLightsAtomicCount;
 
 
-		for(; shaderSpotLight != end; ++shaderSpotLight)
-		{
-			shaderSpotLight->diffuseColorShadowmapId.w() = 
-				(F32)shadowmapLayers[i];
-			++i;
-		}
+		job.tilePointLightsCount = &tilePointLightsCount;
+		job.tileSpotLightsCount = &tileSpotLightsCount;
+		job.tileSpotTexLightsCount = &tileSpotTexLightsCount;
 
 
-		// Write GPU buffer
-		spotLightsUbo.write(
-			&clientBuffer, 
-			0, 
-			sizeof(shader::SpotLight) * visibleSpotLightsCount);
+		job.tiler = r->getTiler();
+		job.is = this;
 	}
 	}
 
 
+	// Sync
+	threadPool.waitForAllJobsToFinish();
+
+	// Write BO
+	lightsUbo.write(&clientBuffer[0], spotTexLightsOffset + spotTexLightsSize);
+
 	//
 	//
 	// Update the tiles UBO
 	// Update the tiles UBO
 	//
 	//