Browse Source

Feature complete

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
05c3c1a478

+ 0 - 0
shaders/ClusterLightCommon.glsl → shaders/ClusteredShadingCommon.glsl


+ 2 - 3
shaders/ForwardShadingCommonFrag.glsl

@@ -5,10 +5,9 @@
 
 #pragma once
 
-// Common code for all fragment shaders of BS
+// Common code for all fragment shaders of FS
 #include <shaders/Common.glsl>
 #include <shaders/Functions.glsl>
-#include <shaders/glsl_cpp_common/Clusterer.h>
 
 // Global resources
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D anki_msDepthRt;
@@ -18,7 +17,7 @@ layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D anki_msDepthRt;
 #define LIGHT_TEX_BINDING 1
 #define LIGHT_LIGHTS
 #define LIGHT_COMMON_UNIS
-#include <shaders/ClusterLightCommon.glsl>
+#include <shaders/ClusteredShadingCommon.glsl>
 
 #define anki_u_time u_time
 #define RENDERER_SIZE (u_rendererSize * 0.5)

+ 1 - 1
shaders/ForwardShadingCommonVert.glsl

@@ -14,7 +14,7 @@
 #define LIGHT_TEX_BINDING 1
 #define LIGHT_UBO_BINDING 0
 #define LIGHT_MINIMAL
-#include <shaders/ClusterLightCommon.glsl>
+#include <shaders/ClusteredShadingCommon.glsl>
 #undef LIGHT_SET
 #undef LIGHT_SS_BINDING
 #undef LIGHT_TEX_BINDING

+ 1 - 2
shaders/GBufferPost.glslp

@@ -30,7 +30,6 @@ void main()
 
 #pragma anki start frag
 #include <shaders/Pack.glsl>
-#include <shaders/glsl_cpp_common/Clusterer.h>
 #include <shaders/Functions.glsl>
 
 #define LIGHT_SET 0
@@ -39,7 +38,7 @@ void main()
 #define LIGHT_TEX_BINDING 1
 #define LIGHT_DECALS
 #define LIGHT_COMMON_UNIS
-#include <shaders/ClusterLightCommon.glsl>
+#include <shaders/ClusteredShadingCommon.glsl>
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msDepthRt;
 

+ 1 - 2
shaders/LightShading.glslp

@@ -32,7 +32,6 @@ void main()
 
 #pragma anki start frag
 #include <shaders/Pack.glsl>
-#include <shaders/glsl_cpp_common/Clusterer.h>
 #include <shaders/Functions.glsl>
 
 #define LIGHT_SET 0
@@ -42,7 +41,7 @@ void main()
 #define LIGHT_INDIRECT
 #define LIGHT_LIGHTS
 #define LIGHT_COMMON_UNIS
-#include <shaders/ClusterLightCommon.glsl>
+#include <shaders/ClusteredShadingCommon.glsl>
 
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_msRt0;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_msRt1;

+ 1 - 2
shaders/VolumetricFog.glslp

@@ -16,7 +16,6 @@
 #pragma anki start frag
 #include <shaders/Common.glsl>
 #include <shaders/Functions.glsl>
-#include <shaders/glsl_cpp_common/Clusterer.h>
 
 #define LIGHT_TEX_BINDING 3
 #define LIGHT_UBO_BINDING 0
@@ -24,7 +23,7 @@
 #define LIGHT_SET 0
 #define LIGHT_LIGHTS
 #define LIGHT_COMMON_UNIS
-#include <shaders/ClusterLightCommon.glsl>
+#include <shaders/ClusteredShadingCommon.glsl>
 
 layout(location = 0) in Vec2 in_uv;
 

+ 51 - 16
shaders/glsl_cpp_common/ClusteredShading.h

@@ -5,26 +5,15 @@
 
 #pragma once
 
-#include <shaders/glsl_cpp_common/Clusterer.h>
+#include <shaders/glsl_cpp_common/Common.h>
 
 ANKI_BEGIN_NAMESPACE
 
-// Common uniforms between lights
-struct LightingUniforms
+// See the documentation in the ClustererBin class.
+struct ClustererMagicValues
 {
-	Vec4 m_unprojectionParams;
-	Vec4 m_rendererSizeTimeNear;
-	Vec4 m_cameraPosFar;
-	ClustererMagicValues m_clustererMagicValues;
-	UVec4 m_tileCount;
-	Mat4 m_viewMat;
-	Mat4 m_invViewMat;
-	Mat4 m_projMat;
-	Mat4 m_invProjMat;
-	Mat4 m_viewProjMat;
-	Mat4 m_invViewProjMat;
-	Mat4 m_prevViewProjMat;
-	Mat4 m_prevViewProjMatMulInvViewProjMat; // Used to re-project previous frames
+	Vec4 m_val0;
+	Vec4 m_val1;
 };
 
 // Point light
@@ -67,4 +56,50 @@ struct Decal
 };
 const U32 SIZEOF_DECAL = 3 * SIZEOF_VEC4 + SIZEOF_MAT4;
 
+// Common uniforms for light shading passes
+struct LightingUniforms
+{
+	Vec4 m_unprojectionParams;
+	Vec4 m_rendererSizeTimeNear;
+	Vec4 m_cameraPosFar;
+	ClustererMagicValues m_clustererMagicValues;
+	UVec4 m_tileCount;
+	Mat4 m_viewMat;
+	Mat4 m_invViewMat;
+	Mat4 m_projMat;
+	Mat4 m_invProjMat;
+	Mat4 m_viewProjMat;
+	Mat4 m_invViewProjMat;
+	Mat4 m_prevViewProjMat;
+	Mat4 m_prevViewProjMatMulInvViewProjMat; // Used to re-project previous frames
+};
+
+ANKI_SHADER_FUNC_INLINE U32 computeClusterK(ClustererMagicValues magic, Vec3 worldPos)
+{
+	F32 fz = sqrt(dot(magic.m_val0.xyz(), worldPos) - magic.m_val0.w());
+	U32 z = U32(fz);
+	return z;
+}
+
+// Compute cluster index
+ANKI_SHADER_FUNC_INLINE U32 computeClusterIndex(
+	ClustererMagicValues magic, Vec2 uv, Vec3 worldPos, U32 clusterCountX, U32 clusterCountY)
+{
+	UVec2 xy = UVec2(uv * Vec2(clusterCountX, clusterCountY));
+
+	return computeClusterK(magic, worldPos) * (clusterCountX * clusterCountY) + xy.y() * clusterCountX + xy.x();
+}
+
+// Compute the Z of the near plane given a cluster idx
+ANKI_SHADER_FUNC_INLINE F32 computeClusterNear(ClustererMagicValues magic, U32 k)
+{
+	F32 fk = F32(k);
+	return magic.m_val1.x() * fk * fk + magic.m_val1.y();
+}
+
+ANKI_SHADER_FUNC_INLINE F32 computeClusterFar(ClustererMagicValues magic, U32 k)
+{
+	return computeClusterNear(magic, k + 1u);
+}
+
 ANKI_END_NAMESPACE

+ 0 - 47
shaders/glsl_cpp_common/Clusterer.h

@@ -1,47 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <shaders/glsl_cpp_common/Common.h>
-
-ANKI_BEGIN_NAMESPACE
-
-// See the documentation in the Clusterer class.
-struct ClustererMagicValues
-{
-	Vec4 m_val0;
-	Vec4 m_val1;
-};
-
-ANKI_SHADER_FUNC_INLINE U32 computeClusterK(ClustererMagicValues magic, Vec3 worldPos)
-{
-	F32 fz = sqrt(dot(magic.m_val0.xyz(), worldPos) - magic.m_val0.w());
-	U32 z = U32(fz);
-	return z;
-}
-
-// Compute cluster index
-ANKI_SHADER_FUNC_INLINE U32 computeClusterIndex(
-	ClustererMagicValues magic, Vec2 uv, Vec3 worldPos, U32 clusterCountX, U32 clusterCountY)
-{
-	UVec2 xy = UVec2(uv * Vec2(clusterCountX, clusterCountY));
-
-	return computeClusterK(magic, worldPos) * (clusterCountX * clusterCountY) + xy.y() * clusterCountX + xy.x();
-}
-
-// Compute the Z of the near plane given a cluster idx
-ANKI_SHADER_FUNC_INLINE F32 computeClusterNear(ClustererMagicValues magic, U32 k)
-{
-	F32 fk = F32(k);
-	return magic.m_val1.x() * fk * fk + magic.m_val1.y();
-}
-
-ANKI_SHADER_FUNC_INLINE F32 computeClusterFar(ClustererMagicValues magic, U32 k)
-{
-	return computeClusterNear(magic, k + 1u);
-}
-
-ANKI_END_NAMESPACE

+ 1 - 2
src/anki/Renderer.h

@@ -11,7 +11,7 @@
 #include <anki/renderer/Volumetric.h>
 #include <anki/renderer/ForwardShading.h>
 #include <anki/renderer/FinalComposite.h>
-#include <anki/renderer/LightBin.h>
+#include <anki/renderer/ClusterBin.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/MainRenderer.h>
@@ -25,7 +25,6 @@
 #include <anki/renderer/Dbg.h>
 #include <anki/renderer/Ssao.h>
 #include <anki/renderer/Drawer.h>
-#include <anki/renderer/Clusterer.h>
 #include <anki/renderer/UiStage.h>
 #include <anki/renderer/Tonemapping.h>
 #include <anki/renderer/RendererObject.h>

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

@@ -455,7 +455,7 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 	m_renderer = m_heapAlloc.newInstance<MainRenderer>();
 
 	ANKI_CHECK(m_renderer->init(
-		m_threadpool, m_resources, m_gr, m_stagingMem, m_ui, m_allocCb, m_allocCbData, config, &m_globalTimestamp));
+		m_threadHive, m_resources, m_gr, m_stagingMem, m_ui, m_allocCb, m_allocCbData, config, &m_globalTimestamp));
 
 	//
 	// Script

+ 2 - 2
src/anki/renderer/ClusterBin.cpp

@@ -69,7 +69,7 @@ public:
 	Vec4 m_unprojParams;
 };
 
-void ClusterBin::init(U32 clusterCountX, U32 clusterCountY, U32 clusterCountZ, const Config& cfg)
+void ClusterBin::init(U32 clusterCountX, U32 clusterCountY, U32 clusterCountZ, const ConfigSet& cfg)
 {
 	m_clusterCounts[0] = clusterCountX;
 	m_clusterCounts[1] = clusterCountY;
@@ -337,7 +337,7 @@ Bool ClusterBin::processNextCluster(BinCtx& ctx) const
 	*pObjectCount = pObjectIndex - pObjectCount - 1;
 
 	// Allocate and store indices for the cluster
-	U indexCount = pObjectCount - &objectIndices[0];
+	U indexCount = pObjectIndex - &objectIndices[0];
 	ANKI_ASSERT(indexCount >= TYPED_OBJECT_COUNT);
 
 	U firstIndex;

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

@@ -52,7 +52,7 @@ public:
 class ClusterBin
 {
 public:
-	void init(U32 clusterCountX, U32 clusterCountY, U32 clusterCountZ, const Config& cfg);
+	void init(U32 clusterCountX, U32 clusterCountY, U32 clusterCountZ, const ConfigSet& cfg);
 
 	void bin(ClusterBinIn& in, ClusterBinOut& out);
 

+ 0 - 802
src/anki/renderer/Clusterer.cpp

@@ -1,802 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/renderer/Clusterer.h>
-#include <anki/util/ThreadPool.h>
-#include <anki/Collision.h>
-
-namespace anki
-{
-
-class UpdatePlanesPerspectiveCameraTask : public ThreadPoolTask
-{
-public:
-	Clusterer* m_clusterer = nullptr;
-	Bool m_frustumChanged;
-
-	Error operator()(U32 threadId, PtrSize threadsCount)
-	{
-		m_clusterer->update(threadId, threadsCount, m_frustumChanged);
-		return Error::NONE;
-	}
-};
-
-static Vec4 unproject(const F32 depth, const Vec2& ndc, const Vec4& projParams)
-{
-	Vec4 view;
-	F32 z = projParams.z() / (projParams.w() + depth);
-	Vec2 viewxy = ndc * projParams.xy() * z;
-	view.x() = viewxy.x();
-	view.y() = viewxy.y();
-	view.z() = z;
-	view.w() = 0.0;
-
-	return view;
-}
-
-static Vec4 unprojectZViewSpace(const F32 zVSpace, const Vec2& ndc, const Vec4& projParams)
-{
-	Vec4 view;
-	view.x() = ndc.x() * projParams.x();
-	view.y() = ndc.y() * projParams.y();
-	view.z() = 1.0;
-	view.w() = 0.0;
-
-	return view * zVSpace;
-}
-
-Clusterer::~Clusterer()
-{
-	m_allPlanes.destroy(m_alloc);
-	m_clusterBoxes.destroy(m_alloc);
-}
-
-void Clusterer::initTestResults(const GenericMemoryPoolAllocator<U8>& alloc, ClustererTestResult& rez) const
-{
-	rez.m_clusterIds.create(alloc, getClusterCount());
-	rez.m_count = 0;
-	rez.m_alloc = alloc;
-}
-
-F32 Clusterer::calcNear(U k) const
-{
-	F32 neark = m_calcNearOpt * pow(k, 2.0) + m_near;
-	return neark;
-}
-
-U Clusterer::calcZ(F32 zVspace) const
-{
-	zVspace = clamp(zVspace, -m_far, -m_near);
-	zVspace = -zVspace;
-	F32 fk = sqrt((zVspace - m_near) / m_calcNearOpt);
-	ANKI_ASSERT(fk >= 0.0);
-	U k = min<U>(static_cast<U>(fk), m_counts[2]);
-
-	return k;
-}
-
-void Clusterer::calcPlaneY(U i, const Vec4& projParams)
-{
-	Plane& plane = m_planesY[i];
-	F32 y = F32(i + 1) / m_counts[1] * 2.0 - 1.0;
-
-	Vec4 viewA = unproject(1.0, Vec2(-1.0, y), projParams);
-	Vec4 viewB = unproject(1.0, Vec2(1.0, y), projParams);
-
-	Vec4 n = viewB.cross(viewA);
-	n.normalize();
-
-	plane = Plane(n, 0.0);
-}
-
-void Clusterer::calcPlaneX(U j, const Vec4& projParams)
-{
-	Plane& plane = m_planesX[j];
-	F32 x = F32(j + 1) / m_counts[0] * 2.0 - 1.0;
-
-	Vec4 viewA = unproject(1.0, Vec2(x, -1.0), projParams);
-	Vec4 viewB = unproject(1.0, Vec2(x, 1.0), projParams);
-
-	Vec4 n = viewA.cross(viewB);
-	n.normalize();
-
-	plane = Plane(n, 0.0);
-}
-
-void Clusterer::setClusterBoxes(const Vec4& projParams, U begin, U end)
-{
-	ANKI_ASSERT((m_counts[0] % 2) == 0);
-	ANKI_ASSERT((m_counts[1] % 2) == 0);
-	const U c = m_counts[0] * m_counts[1];
-
-	for(U i = begin; i < end; ++i)
-	{
-		U z = i / c;
-		U y = (i % c) / m_counts[0];
-		U x = (i % c) % m_counts[0];
-
-		F32 zMax = -calcNear(z);
-		F32 zMin = -calcNear(z + 1);
-
-		F32 xMin, xMax;
-		if(x < m_counts[0] / 2)
-		{
-			xMin = (F32(x) / m_counts[0] * 2.0 - 1.0) * projParams.x() * zMin;
-			xMax = (F32(x + 1) / m_counts[0] * 2.0 - 1.0) * projParams.x() * zMax;
-		}
-		else
-		{
-			xMin = (F32(x) / m_counts[0] * 2.0 - 1.0) * projParams.x() * zMax;
-			xMax = (F32(x + 1) / m_counts[0] * 2.0 - 1.0) * projParams.x() * zMin;
-		}
-
-		F32 yMin, yMax;
-		if(y < m_counts[1] / 2)
-		{
-			yMin = (F32(y) / m_counts[1] * 2.0 - 1.0) * projParams.y() * zMin;
-			yMax = (F32(y + 1) / m_counts[1] * 2.0 - 1.0) * projParams.y() * zMax;
-		}
-		else
-		{
-			yMin = (F32(y) / m_counts[1] * 2.0 - 1.0) * projParams.y() * zMax;
-			yMax = (F32(y + 1) / m_counts[1] * 2.0 - 1.0) * projParams.y() * zMin;
-		}
-
-		m_clusterBoxes[i] = Aabb(Vec4(xMin, yMin, zMin, 0.0), Vec4(xMax, yMax, zMax, 0.0));
-	}
-}
-
-void Clusterer::init(const GenericMemoryPoolAllocator<U8>& alloc, U clusterCountX, U clusterCountY, U clusterCountZ)
-{
-	m_alloc = alloc;
-	m_counts[0] = clusterCountX;
-	m_counts[1] = clusterCountY;
-	m_counts[2] = clusterCountZ;
-
-	// Init planes. One plane for each direction, plus near/far plus the world space of those
-	U planesCount = (m_counts[0] - 1) * 2 // planes J
-					+ (m_counts[1] - 1) * 2 // planes I
-					+ 2; // Near and far planes
-
-	m_allPlanes.create(m_alloc, planesCount);
-
-	Plane* base = &m_allPlanes[0];
-	U count = 0;
-
-	m_planesX = WeakArray<Plane>(base + count, m_counts[0] - 1);
-	count += m_planesX.getSize();
-
-	m_planesY = WeakArray<Plane>(base + count, m_counts[1] - 1);
-	count += m_planesY.getSize();
-
-	m_planesXW = WeakArray<Plane>(base + count, m_counts[0] - 1);
-	count += m_planesXW.getSize();
-
-	m_planesYW = WeakArray<Plane>(base + count, m_counts[1] - 1);
-	count += m_planesYW.getSize();
-
-	m_nearPlane = base + count;
-	++count;
-
-	m_farPlane = base + count;
-	++count;
-
-	ANKI_ASSERT(count == m_allPlanes.getSize());
-
-	m_clusterBoxes.create(m_alloc, m_counts[0] * m_counts[1] * m_counts[2]);
-}
-
-void Clusterer::prepare(ThreadPool& threadPool, const ClustererPrepareInfo& inf)
-{
-	Bool frustumChanged = m_projMat != inf.m_projMat;
-
-	// Compute cached values
-	m_projMat = inf.m_projMat;
-	m_viewMat = inf.m_viewMat;
-	m_camTrf = inf.m_camTrf;
-	m_near = inf.m_near;
-	m_far = inf.m_far;
-	ANKI_ASSERT(m_near < m_far && m_near > 0.0);
-
-	m_unprojParams = m_projMat.extractPerspectiveUnprojectionParams();
-	m_calcNearOpt = (m_far - m_near) / pow(m_counts[2], 2.0);
-
-	// Compute magic val 0
-	// It's been used to calculate the 'k' of a cluster given the world position
-	{
-		// Given a distance 'd' from the camera's near plane in world space the 'k' split is calculated like:
-		// k = sqrt(d / (f - n) * Cz2)  (1)
-		// where 'n' and 'f' are the near and far vals of the projection and Cz2 is the m_counts[2]^2
-		// If the 'd' is not known and the world position instead is known then 'd' is the distance from that position
-		// to the camera's near plane.
-		// d = dot(Pn, W) - Po  (2)
-		// where 'Pn' is the plane's normal, 'Po' is the plane's offset and 'W' is the world position.
-		// Substituting d from (2) in (1) we have:
-		// k = sqrt((dot(Pn, W) - Po) / (f - n) * Cz2) =>
-		// k = sqrt((dot(Pn, W) * Cz2 - Po * Cz2) / (f - n))
-		// k = sqrt(dot(Pn, W) * Cz2 / (f - n) - Po * Cz2 / (f - n))
-		// k = sqrt(dot(Pn * Cz2 / (f - n), W) - Po * Cz2 / (f - n))
-		// If we assume that:
-		// A = Pn * Cz2 / (f - n) and B =  Po * Cz2 / (f - n)
-		// Then:
-		// k = sqrt(dot(A, W) - B)
-
-		const Mat4& vp = inf.m_viewProjMat;
-		Plane nearPlane;
-		Array<Plane*, U(FrustumPlaneType::COUNT)> planes = {};
-		planes[FrustumPlaneType::NEAR] = &nearPlane;
-		extractClipPlanes(vp, planes);
-
-		Vec3 A = nearPlane.getNormal().xyz() * (m_counts[2] * m_counts[2]) / (m_far - m_near);
-		F32 B = nearPlane.getOffset() * (m_counts[2] * m_counts[2]) / (m_far - m_near);
-
-		m_shaderMagicVals.m_val0 = Vec4(A, B);
-	}
-
-	// Compute magic val 1
-	{
-		m_shaderMagicVals.m_val1.x() = m_calcNearOpt;
-		m_shaderMagicVals.m_val1.y() = m_near;
-	}
-
-	//
-	// Issue parallel jobs
-	//
-	UpdatePlanesPerspectiveCameraTask job;
-	job.m_clusterer = this;
-	job.m_frustumChanged = frustumChanged;
-
-	for(U i = 0; i < threadPool.getThreadCount(); i++)
-	{
-		threadPool.assignNewTask(i, &job);
-	}
-
-	// Sync threads
-	Error err = threadPool.waitForAllThreadsToFinish();
-	(void)err;
-}
-
-void Clusterer::computeSplitRange(const CollisionShape& cs, U& zBegin, U& zEnd) const
-{
-	// Find the distance between cs and near plane
-	F32 dist = cs.testPlane(*m_nearPlane);
-	dist = max(m_near, dist);
-
-	// Find split
-	zBegin = calcZ(-dist);
-	ANKI_ASSERT(zBegin <= m_counts[2]);
-
-	// Find the distance between cs and far plane
-	dist = cs.testPlane(*m_farPlane);
-	dist = max(0.0f, dist);
-	dist = m_far - dist;
-
-	// Find split
-	zEnd = min<U>(calcZ(-dist) + 1, m_counts[2]);
-	ANKI_ASSERT(zEnd <= m_counts[2]);
-}
-
-void Clusterer::bin(const CollisionShape& cs, const Aabb& csBox, ClustererTestResult& rez) const
-{
-	rez.m_count = 0;
-
-	if(cs.getType() == CollisionShapeType::SPHERE)
-	{
-		binSphere(static_cast<const Sphere&>(cs), csBox, rez);
-	}
-	else
-	{
-		binGeneric(cs, csBox, rez);
-	}
-}
-
-void Clusterer::totallyInsideAllTiles(U zBegin, U zEnd, ClustererTestResult& rez) const
-{
-	for(U z = zBegin; z < zEnd; ++z)
-	{
-		for(U y = 0; y < m_counts[1]; ++y)
-		{
-			for(U x = 0; x < m_counts[0]; ++x)
-			{
-				rez.pushBack(x, y, z);
-			}
-		}
-	}
-}
-
-void Clusterer::quickReduction(const Aabb& aabb, const Mat4& mvp, U& xBegin, U& xEnd, U& yBegin, U& yEnd) const
-{
-	// Compute projection points
-	const Vec4& minv = aabb.getMin();
-	const Vec4& maxv = aabb.getMax();
-	Array<Vec4, 8> points;
-	points[0] = minv.xyz1();
-	points[1] = Vec4(minv.x(), maxv.y(), minv.z(), 1.0);
-	points[2] = Vec4(minv.x(), maxv.y(), maxv.z(), 1.0);
-	points[3] = Vec4(minv.x(), minv.y(), maxv.z(), 1.0);
-	points[4] = maxv.xyz1();
-	points[5] = Vec4(maxv.x(), minv.y(), maxv.z(), 1.0);
-	points[6] = Vec4(maxv.x(), minv.y(), minv.z(), 1.0);
-	points[7] = Vec4(maxv.x(), maxv.y(), minv.z(), 1.0);
-	Vec2 min2(MAX_F32), max2(MIN_F32);
-	for(Vec4& p : points)
-	{
-		p = mvp * p;
-		ANKI_ASSERT(p.w() > 0.0 && "Should have cliped tha aabb before calling this");
-		p = p.perspectiveDivide();
-
-		for(U i = 0; i < 2; ++i)
-		{
-			min2[i] = min(min2[i], p[i]);
-			max2[i] = max(max2[i], p[i]);
-		}
-	}
-
-	min2 = min2 * 0.5 + 0.5;
-	max2 = max2 * 0.5 + 0.5;
-
-	// Compute ranges
-	xBegin = clamp<F32>(floor(m_counts[0] * min2.x()), 0.0, m_counts[0]);
-	xEnd = clamp<F32>(ceil(m_counts[0] * max2.x()), 0.0, m_counts[0]);
-	yBegin = clamp<F32>(floor(m_counts[1] * min2.y()), 0.0, m_counts[1]);
-	yEnd = clamp<F32>(ceil(m_counts[1] * max2.y()), 0.0, m_counts[1]);
-
-	ANKI_ASSERT(xBegin <= m_counts[0] && xEnd <= m_counts[0]);
-	ANKI_ASSERT(yBegin <= m_counts[1] && yEnd <= m_counts[1]);
-}
-
-template<typename TFunc>
-void Clusterer::boxReduction(
-	U xBegin, U xEnd, U yBegin, U yEnd, U zBegin, U zEnd, ClustererTestResult& rez, TFunc func) const
-{
-	U zcount = zEnd - zBegin;
-	U ycount = yEnd - yBegin;
-	U xcount = xEnd - xBegin;
-
-	if(xcount > ycount && xcount > zcount)
-	{
-		for(U z = zBegin; z < zEnd; ++z)
-		{
-			const U zc = (m_counts[0] * m_counts[1]) * z;
-
-			for(U y = yBegin; y < yEnd; ++y)
-			{
-				const U yc = zc + m_counts[0] * y;
-
-				// Do a reduction to avoid some checks
-				U firstX = MAX_U;
-
-				for(U x = xBegin; x < xEnd; ++x)
-				{
-					U i = yc + x;
-
-					if(func(m_clusterBoxes[i]))
-					{
-						firstX = x;
-						break;
-					}
-				}
-
-				for(U x = xEnd - 1; x >= firstX; --x)
-				{
-					U i = yc + x;
-
-					if(func(m_clusterBoxes[i]))
-					{
-						for(U a = firstX; a <= x; ++a)
-						{
-							rez.pushBack(a, y, z);
-						}
-
-						break;
-					}
-				}
-			}
-		}
-	}
-	else if(ycount > xcount && ycount > zcount)
-	{
-		for(U z = zBegin; z < zEnd; ++z)
-		{
-			const U zc = (m_counts[0] * m_counts[1]) * z;
-
-			for(U x = xBegin; x < xEnd; ++x)
-			{
-				// Do a reduction to avoid some checks
-				U firstY = MAX_U;
-
-				for(U y = yBegin; y < yEnd; ++y)
-				{
-					const U i = zc + m_counts[0] * y + x;
-
-					if(func(m_clusterBoxes[i]))
-					{
-						firstY = y;
-						break;
-					}
-				}
-
-				for(U y = yEnd - 1; y >= firstY; --y)
-				{
-					const U i = zc + m_counts[0] * y + x;
-
-					if(func(m_clusterBoxes[i]))
-					{
-						for(U a = firstY; a <= y; ++a)
-						{
-							rez.pushBack(x, a, z);
-						}
-
-						break;
-					}
-				}
-			}
-		}
-	}
-	else
-	{
-		for(U y = yBegin; y < yEnd; ++y)
-		{
-			for(U x = xBegin; x < xEnd; ++x)
-			{
-				// Do a reduction to avoid some checks
-				U firstZ = MAX_U;
-
-				for(U z = zBegin; z < zEnd; ++z)
-				{
-					const U i = (m_counts[0] * m_counts[1]) * z + m_counts[0] * y + x;
-
-					if(func(m_clusterBoxes[i]))
-					{
-						firstZ = z;
-						break;
-					}
-				}
-
-				for(U z = zEnd - 1; z >= firstZ; --z)
-				{
-					const U i = (m_counts[0] * m_counts[1]) * z + m_counts[0] * y + x;
-
-					if(func(m_clusterBoxes[i]))
-					{
-						for(U a = firstZ; a <= z; ++a)
-						{
-							rez.pushBack(x, y, a);
-						}
-
-						break;
-					}
-				}
-			}
-		}
-	}
-}
-
-void Clusterer::binSphere(const Sphere& s, const Aabb& aabb, ClustererTestResult& rez) const
-{
-	// Move the sphere to view space
-	Vec4 cVSpace = (m_viewMat * s.getCenter().xyz1()).xyz0();
-	Sphere sphere(cVSpace, s.getRadius());
-
-	// Compute a new AABB and clip it
-	Aabb box;
-	sphere.computeAabb(box);
-
-	F32 maxz = min(box.getMax().z(), -m_near - EPSILON);
-	box.setMax(Vec4(box.getMax().xy(), maxz, 0.0));
-	ANKI_ASSERT(box.getMax().xyz() > box.getMin().xyz());
-
-	// Quick reduction
-	U xBegin, xEnd, yBegin, yEnd, zBegin, zEnd;
-	computeSplitRange(s, zBegin, zEnd);
-	quickReduction(box, m_projMat, xBegin, xEnd, yBegin, yEnd);
-
-	// Detailed
-	boxReduction(xBegin, xEnd, yBegin, yEnd, zBegin, zEnd, rez, [&](const Aabb& aabb) {
-		return testCollisionShapes(sphere, aabb);
-	});
-}
-
-void Clusterer::binGeneric(const CollisionShape& cs, const Aabb& box0, ClustererTestResult& rez) const
-{
-	// Move the box to view space
-	Aabb box = box0.getTransformed(Transform(m_viewMat));
-
-	F32 maxz = min(box.getMax().z(), -m_near - EPSILON);
-	box.setMax(Vec4(box.getMax().xy(), maxz, 0.0));
-	ANKI_ASSERT(box.getMax().xyz() > box.getMin().xyz());
-
-	// Quick reduction
-	U xBegin, xEnd, yBegin, yEnd, zBegin, zEnd;
-	computeSplitRange(cs, zBegin, zEnd);
-	quickReduction(box, m_projMat, xBegin, xEnd, yBegin, yEnd);
-
-	// Detailed
-	binGenericRecursive(cs, xBegin, xEnd, yBegin, yEnd, zBegin, zEnd, rez);
-}
-
-void Clusterer::binGenericRecursive(
-	const CollisionShape& cs, U xBegin, U xEnd, U yBegin, U yEnd, U zBegin, U zEnd, ClustererTestResult& rez) const
-{
-	U my = (yEnd - yBegin) / 2;
-	U mx = (xEnd - xBegin) / 2;
-
-	// Handle final
-	if(ANKI_UNLIKELY(my == 0 && mx == 0))
-	{
-		for(U z = zBegin; z < zEnd; ++z)
-		{
-			rez.pushBack(xBegin, yBegin, z);
-		}
-		return;
-	}
-
-	// Handle the edge case
-	if(ANKI_UNLIKELY(mx == 0 || my == 0))
-	{
-		if(mx == 0)
-		{
-			const Plane& topPlane = m_planesYW[yBegin + my - 1];
-			F32 test = cs.testPlane(topPlane);
-
-			if(test <= 0.0)
-			{
-				binGenericRecursive(cs, xBegin, xEnd, yBegin, yBegin + my, zBegin, zEnd, rez);
-			}
-
-			if(test >= 0.0)
-			{
-				binGenericRecursive(cs, xBegin, xEnd, yBegin + my, yEnd, zBegin, zEnd, rez);
-			}
-		}
-		else
-		{
-			const Plane& rightPlane = m_planesXW[xBegin + mx - 1];
-			F32 test = cs.testPlane(rightPlane);
-
-			if(test <= 0.0)
-			{
-				binGenericRecursive(cs, xBegin, xBegin + mx, yBegin, yEnd, zBegin, zEnd, rez);
-			}
-
-			if(test >= 0.0)
-			{
-				binGenericRecursive(cs, xBegin + mx, xEnd, yBegin, yEnd, zBegin, zEnd, rez);
-			}
-		}
-
-		return;
-	}
-
-	// Do the checks
-	Bool inside[2][2] = {{false, false}, {false, false}};
-
-	// Top looking plane check
-	{
-		// Pick the correct top lookin plane (y)
-		const Plane& topPlane = m_planesYW[yBegin + my - 1];
-
-		F32 test = cs.testPlane(topPlane);
-		if(test < 0.0)
-		{
-			inside[0][0] = inside[0][1] = true;
-		}
-		else if(test > 0.0)
-		{
-			inside[1][0] = inside[1][1] = true;
-		}
-		else
-		{
-			// Possibly all inside
-			for(U i = 0; i < 2; i++)
-			{
-				for(U j = 0; j < 2; j++)
-				{
-					inside[i][j] = true;
-				}
-			}
-		}
-	}
-
-	// Right looking plane check
-	{
-		// Pick the correct right looking plane (x)
-		const Plane& rightPlane = m_planesXW[xBegin + mx - 1];
-
-		F32 test = cs.testPlane(rightPlane);
-		if(test < 0.0)
-		{
-			inside[0][1] = inside[1][1] = false;
-		}
-		else if(test > 0.0)
-		{
-			inside[0][0] = inside[1][0] = false;
-		}
-		else
-		{
-			// Do nothing and keep the top looking plane check results
-		}
-	}
-
-	// Now move lower to the hierarchy
-	if(inside[0][0])
-	{
-		binGenericRecursive(cs, xBegin, xBegin + mx, yBegin, yBegin + my, zBegin, zEnd, rez);
-	}
-
-	if(inside[0][1])
-	{
-		binGenericRecursive(cs, xBegin + mx, xEnd, yBegin, yBegin + my, zBegin, zEnd, rez);
-	}
-
-	if(inside[1][0])
-	{
-		binGenericRecursive(cs, xBegin, xBegin + mx, yBegin + my, yEnd, zBegin, zEnd, rez);
-	}
-
-	if(inside[1][1])
-	{
-		binGenericRecursive(cs, xBegin + mx, xEnd, yBegin + my, yEnd, zBegin, zEnd, rez);
-	}
-}
-
-void Clusterer::binPerspectiveFrustum(const PerspectiveFrustum& fr, const Aabb& box0, ClustererTestResult& rez) const
-{
-	rez.m_count = 0;
-
-	// Move the box to view space
-	Aabb box = box0.getTransformed(Transform(m_viewMat));
-
-	F32 maxz = min(box.getMax().z(), -m_near - EPSILON);
-	box.setMax(Vec4(box.getMax().xy(), maxz, 0.0));
-	ANKI_ASSERT(box.getMax().xyz() > box.getMin().xyz());
-
-	// Quick reduction
-	U xBegin, xEnd, yBegin, yEnd, zBegin, zEnd;
-	computeSplitRange(fr, zBegin, zEnd);
-	quickReduction(box, m_projMat, xBegin, xEnd, yBegin, yEnd);
-
-	// Detailed tests
-	Array<Plane, 5> vspacePlanes;
-	for(U i = 0; i < vspacePlanes.getSize(); ++i)
-	{
-		vspacePlanes[i] = fr.getPlanesWorldSpace()[i + 1].getTransformed(Transform(m_viewMat));
-	}
-
-	boxReduction(xBegin, xEnd, yBegin, yEnd, zBegin, zEnd, rez, [&](const Aabb& aabb) {
-		for(const Plane& p : vspacePlanes)
-		{
-			if(aabb.testPlane(p) < 0.0)
-			{
-				return false;
-			}
-		}
-
-		return true;
-	});
-}
-
-void Clusterer::update(U32 threadId, PtrSize threadsCount, Bool frustumChanged)
-{
-	PtrSize start, end;
-
-	const Transform& trf = m_camTrf;
-	const Vec4& projParams = m_unprojParams;
-
-	if(frustumChanged)
-	{
-		// Re-calculate the planes in local space
-
-		// First the top looking planes
-		ThreadPoolTask::choseStartEnd(threadId, threadsCount, m_planesYW.getSize(), start, end);
-
-		for(U i = start; i < end; i++)
-		{
-			calcPlaneY(i, projParams);
-
-			m_planesYW[i] = m_planesY[i].getTransformed(trf);
-		}
-
-		// Then the right looking planes
-		ThreadPoolTask::choseStartEnd(threadId, threadsCount, m_planesXW.getSize(), start, end);
-
-		for(U j = start; j < end; j++)
-		{
-			calcPlaneX(j, projParams);
-
-			m_planesXW[j] = m_planesX[j].getTransformed(trf);
-		}
-
-		// The boxes
-		ThreadPoolTask::choseStartEnd(threadId, threadsCount, m_clusterBoxes.getSize(), start, end);
-		setClusterBoxes(projParams, start, end);
-	}
-	else
-	{
-		// Only transform planes
-
-		// First the top looking planes
-		ThreadPoolTask::choseStartEnd(threadId, threadsCount, m_planesYW.getSize(), start, end);
-
-		for(U i = start; i < end; i++)
-		{
-			m_planesYW[i] = m_planesY[i].getTransformed(trf);
-		}
-
-		// Then the right looking planes
-		ThreadPoolTask::choseStartEnd(threadId, threadsCount, m_planesXW.getSize(), start, end);
-
-		for(U j = start; j < end; j++)
-		{
-			m_planesXW[j] = m_planesX[j].getTransformed(trf);
-		}
-	}
-
-	// Finaly tranform the near and far planes
-	if(threadId == 0)
-	{
-		*m_nearPlane = Plane(Vec4(0.0, 0.0, -1.0, 0.0), m_near);
-		m_nearPlane->transform(trf);
-
-		*m_farPlane = Plane(Vec4(0.0, 0.0, 1.0, 0.0), -m_far);
-		m_farPlane->transform(trf);
-	}
-}
-
-void Clusterer::debugDraw(ClustererDebugDrawer& drawer) const
-{
-}
-
-void Clusterer::debugDrawResult(const ClustererTestResult& rez, ClustererDebugDrawer& drawer) const
-{
-	const Vec4& projParams = m_unprojParams;
-
-	auto it = rez.getClustersBegin();
-	auto end = rez.getClustersEnd();
-	while(it != end)
-	{
-		const auto& id = *it;
-		Array<Vec3, 8> frustumPoints;
-		U count = 0;
-
-		for(U z = id.z(); z <= id.z() + 1u; ++z)
-		{
-			F32 zVSpace = -calcNear(z);
-
-			for(U y = id.y(); y <= id.y() + 1u; ++y)
-			{
-				F32 yNdc = F32(y) / m_counts[1] * 2.0 - 1.0;
-
-				for(U x = id.x(); x <= id.x() + 1u; ++x)
-				{
-					F32 xNdc = F32(x) / m_counts[0] * 2.0 - 1.0;
-
-					frustumPoints[count++] = unprojectZViewSpace(zVSpace, Vec2(xNdc, yNdc), projParams).xyz();
-				}
-			}
-		}
-
-		ANKI_ASSERT(count == 8);
-		static const Vec3 COLOR(1.0);
-		drawer(frustumPoints[0], frustumPoints[1], COLOR);
-		drawer(frustumPoints[1], frustumPoints[3], COLOR);
-		drawer(frustumPoints[3], frustumPoints[2], COLOR);
-		drawer(frustumPoints[0], frustumPoints[2], COLOR);
-
-		drawer(frustumPoints[4], frustumPoints[5], COLOR);
-		drawer(frustumPoints[5], frustumPoints[7], COLOR);
-		drawer(frustumPoints[7], frustumPoints[6], COLOR);
-		drawer(frustumPoints[4], frustumPoints[6], COLOR);
-
-		drawer(frustumPoints[0], frustumPoints[4], COLOR);
-		drawer(frustumPoints[1], frustumPoints[5], COLOR);
-		drawer(frustumPoints[2], frustumPoints[6], COLOR);
-		drawer(frustumPoints[3], frustumPoints[7], COLOR);
-
-		++it;
-	}
-}
-
-} // end namespace anki

+ 0 - 252
src/anki/renderer/Clusterer.h

@@ -1,252 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/renderer/Common.h>
-#include <anki/Math.h>
-#include <anki/collision/Aabb.h>
-#include <shaders/glsl_cpp_common/Clusterer.h>
-
-namespace anki
-{
-
-// Forward
-class ThreadPool;
-class PerspectiveFrustum;
-
-/// @addtogroup renderer
-/// @{
-
-class ClustererDebugDrawer
-{
-public:
-	virtual ~ClustererDebugDrawer() = default;
-
-	virtual void operator()(const Vec3& lineA, const Vec3& lineB, const Vec3& color) = 0;
-};
-
-class alignas(alignof(U32)) Cluster
-{
-	friend class Clusterer;
-	friend class ClustererTestResult;
-
-public:
-	U x() const
-	{
-		return m_v.m_x;
-	}
-
-	U y() const
-	{
-		return m_v.m_y;
-	}
-
-	U z() const
-	{
-		return m_v.m_z;
-	}
-
-private:
-	class S
-	{
-	public:
-		U8 m_x;
-		U8 m_y;
-		U8 m_z;
-		U8 m_pad_;
-	};
-
-	union
-	{
-		S m_v;
-		U32 m_u32;
-	};
-};
-
-static_assert(sizeof(Cluster) == sizeof(U32), "Wrong size");
-
-/// The result of the cluster tests.
-class ClustererTestResult
-{
-	friend class Clusterer;
-
-public:
-	ClustererTestResult()
-	{
-	}
-
-	~ClustererTestResult()
-	{
-		m_clusterIds.destroy(m_alloc);
-	}
-
-	DynamicArray<Cluster>::ConstIterator getClustersBegin() const
-	{
-		return m_clusterIds.getBegin();
-	}
-
-	DynamicArray<Cluster>::ConstIterator getClustersEnd() const
-	{
-		return m_clusterIds.getBegin() + m_count;
-	}
-
-	U getClusterCount() const
-	{
-		return m_count;
-	}
-
-private:
-	GenericMemoryPoolAllocator<U8> m_alloc;
-	DynamicArray<Cluster> m_clusterIds;
-	U32 m_count = 0;
-
-	void pushBack(U x, U y, U z)
-	{
-		ANKI_ASSERT(x <= 0xFF && y <= 0xFF && z <= 0xFF);
-#if 1
-		m_clusterIds[m_count].m_u32 = (z << 16) | (y << 8) | x;
-		ANKI_ASSERT(m_clusterIds[m_count].x() == x && m_clusterIds[m_count].y() == y && m_clusterIds[m_count].z() == z);
-#else
-		m_clusterIds[m_count].m_v.m_x = U8(x);
-		m_clusterIds[m_count].m_v.m_y = U8(y);
-		m_clusterIds[m_count].m_v.m_z = U8(z);
-#endif
-		++m_count;
-	}
-};
-
-/// Info that will prepare the clusterer.
-class ClustererPrepareInfo
-{
-public:
-	Mat4 m_viewMat;
-	Mat4 m_projMat; ///< Must be perspective projection.
-	Mat4 m_viewProjMat;
-	Transform m_camTrf;
-	F32 m_near;
-	F32 m_far;
-};
-
-/// Collection of clusters for visibility tests.
-class Clusterer
-{
-	friend class UpdatePlanesPerspectiveCameraTask;
-
-public:
-	Clusterer()
-	{
-	}
-
-	~Clusterer();
-
-	void init(const GenericMemoryPoolAllocator<U8>& alloc, U clusterCountX, U clusterCountY, U clusterCountZ);
-
-	/// Prepare for visibility tests.
-	void prepare(ThreadPool& threadpool, const ClustererPrepareInfo& inf);
-
-	void initTestResults(const GenericMemoryPoolAllocator<U8>& alloc, ClustererTestResult& rez) const;
-
-	/// Bin collision shape.
-	void bin(const CollisionShape& cs, const Aabb& csBox, ClustererTestResult& rez) const;
-
-	/// Bin a frustum.
-	void binPerspectiveFrustum(const PerspectiveFrustum& fr, const Aabb& csBox, ClustererTestResult& rez) const;
-
-	/// A value that will be used in shaders to calculate the cluster index. See the code that calculates it for info
-	/// on what it is.
-	const ClustererMagicValues& getShaderMagicValues() const
-	{
-		return m_shaderMagicVals;
-	}
-
-	U getClusterCountX() const
-	{
-		return m_counts[0];
-	}
-
-	U getClusterCountY() const
-	{
-		return m_counts[1];
-	}
-
-	U getClusterCountZ() const
-	{
-		return m_counts[2];
-	}
-
-	U getClusterCount() const
-	{
-		return m_counts[0] * m_counts[1] * m_counts[2];
-	}
-
-	/// Call this after prepare()
-	void debugDraw(ClustererDebugDrawer& drawer) const;
-
-	/// Call this with a result.
-	void debugDrawResult(const ClustererTestResult& rez, ClustererDebugDrawer& drawer) const;
-
-private:
-	GenericMemoryPoolAllocator<U8> m_alloc;
-
-	Array<U8, 3> m_counts;
-
-	/// Tile planes.
-	DynamicArray<Plane> m_allPlanes; ///< Do one allocation.
-	WeakArray<Plane> m_planesY; ///< Local space.
-	WeakArray<Plane> m_planesX; ///< Local space.
-	WeakArray<Plane> m_planesYW;
-	WeakArray<Plane> m_planesXW;
-	Plane* m_nearPlane; ///< In world space
-	Plane* m_farPlane; ///< In world space
-
-	/// Cluster boxes in view space.
-	DynamicArray<Aabb> m_clusterBoxes;
-
-	Mat4 m_viewMat = Mat4::getIdentity();
-	Mat4 m_projMat = Mat4::getIdentity();
-	Transform m_camTrf = Transform::getIdentity();
-	Vec4 m_unprojParams = Vec4(0.0);
-	F32 m_near = 0.0;
-	F32 m_far = 0.0;
-	F32 m_calcNearOpt = 0.0f;
-	ClustererMagicValues m_shaderMagicVals = {Vec4(0.0f), Vec4(0.0f)};
-
-	F32 calcNear(U k) const;
-
-	U calcZ(F32 zVspace) const;
-
-	void setClusterBoxes(const Vec4& projParams, U begin, U end);
-
-	void binGeneric(const CollisionShape& cs, const Aabb& box, ClustererTestResult& rez) const;
-	void binGenericRecursive(
-		const CollisionShape& cs, U xBegin, U xEnd, U yBegin, U yEnd, U zBegin, U zEnd, ClustererTestResult& rez) const;
-
-	/// Special fast path for binning spheres.
-	void binSphere(const Sphere& s, const Aabb& aabb, ClustererTestResult& rez) const;
-
-	/// Quick reduction.
-	void quickReduction(const Aabb& aabb, const Mat4& mvp, U& xBegin, U& xEnd, U& yBegin, U& yEnd) const;
-
-	/// Box based reduction.
-	template<typename TFunc>
-	void boxReduction(U xBegin, U xEnd, U yBegin, U yEnd, U zBegin, U zEnd, ClustererTestResult& rez, TFunc func) const;
-
-	void computeSplitRange(const CollisionShape& cs, U& zBegin, U& zEnd) const;
-
-	void update(U32 threadId, PtrSize threadsCount, Bool frustumChanged);
-
-	/// Calculate and set a top looking plane.
-	void calcPlaneY(U i, const Vec4& projParams);
-
-	/// Calculate and set a right looking plane.
-	void calcPlaneX(U j, const Vec4& projParams);
-
-	/// Call this when a shape is visible by all tiles.
-	void totallyInsideAllTiles(U zBegin, U zEnd, ClustererTestResult& rez) const;
-};
-/// @}
-
-} // end namespace anki

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

@@ -49,6 +49,7 @@ class ReflectionProbeQueueElement;
 class DecalQueueElement;
 
 class ShaderProgramResourceVariant;
+class ClusterBin;
 
 /// @addtogroup renderer
 /// @{

+ 4 - 4
src/anki/renderer/ForwardShading.cpp

@@ -113,19 +113,19 @@ void ForwardShading::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx
 	const U threadCount = rgraphCtx.m_secondLevelCommandBufferCount;
 	const U problemSize = ctx.m_renderQueue->m_forwardShadingRenderables.getSize();
 	PtrSize start, end;
-	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
+	splitThreadedProblem(threadId, threadCount, problemSize, start, end);
 
 	if(start != end)
 	{
-		const LightShadingResources& rsrc = m_r->getLightShading().getResources();
+		const ClusterBinOut& rsrc = ctx.m_clusterBinOut;
 		rgraphCtx.bindTextureAndSampler(
 			0, 0, m_r->getDepthDownscale().getHiZRt(), HIZ_HALF_DEPTH, m_r->getLinearSampler());
 		rgraphCtx.bindColorTextureAndSampler(0, 1, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
-		bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
+		bindUniforms(cmdb, 0, 0, ctx.m_lightShadingUniformsToken);
 		bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
 		bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
 		bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
-		bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
+		bindStorage(cmdb, 0, 1, rsrc.m_indicesToken);
 
 		cmdb->setViewport(0, 0, m_width, m_height);
 		cmdb->setBlendFactors(

+ 1 - 2
src/anki/renderer/GBuffer.cpp

@@ -8,7 +8,6 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/LensFlare.h>
 #include <anki/util/Logger.h>
-#include <anki/util/ThreadPool.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/core/Trace.h>
 
@@ -80,7 +79,7 @@ void GBuffer::runInThread(const RenderingContext& ctx, RenderPassWorkContext& rg
 	const PtrSize earlyZCount = ctx.m_renderQueue->m_earlyZRenderables.getSize();
 	const U problemSize = ctx.m_renderQueue->m_renderables.getSize() + earlyZCount;
 	PtrSize start, end;
-	ThreadPoolTask::choseStartEnd(threadId, threadCount, problemSize, start, end);
+	splitThreadedProblem(threadId, threadCount, problemSize, start, end);
 	ANKI_ASSERT(end != start);
 
 	// Set some state, leave the rest to default

+ 4 - 3
src/anki/renderer/GBufferPost.cpp

@@ -71,7 +71,8 @@ void GBufferPost::populateRenderGraph(RenderingContext& ctx)
 
 void GBufferPost::run(RenderPassWorkContext& rgraphCtx)
 {
-	const LightShadingResources& rsrc = m_r->getLightShading().getResources();
+	const RenderingContext& ctx = *m_runCtx.m_ctx;
+	const ClusterBinOut& rsrc = ctx.m_clusterBinOut;
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
@@ -98,12 +99,12 @@ void GBufferPost::run(RenderPassWorkContext& rgraphCtx)
 		TextureUsageBit::SAMPLED_FRAGMENT);
 
 	// Uniforms
-	bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
+	bindUniforms(cmdb, 0, 0, ctx.m_lightShadingUniformsToken);
 	bindUniforms(cmdb, 0, 1, rsrc.m_decalsToken);
 
 	// Storage
 	bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
-	bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
+	bindStorage(cmdb, 0, 1, rsrc.m_indicesToken);
 
 	cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
 

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

@@ -8,7 +8,7 @@
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/RendererObject.h>
 #include <anki/renderer/TraditionalDeferredShading.h>
-#include <anki/renderer/Clusterer.h>
+#include <anki/renderer/ClusterBin.h>
 #include <anki/resource/TextureResource.h>
 
 namespace anki

+ 0 - 795
src/anki/renderer/LightBin.cpp

@@ -1,795 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/renderer/LightBin.h>
-#include <anki/renderer/RenderQueue.h>
-#include <anki/core/Trace.h>
-#include <anki/util/ThreadPool.h>
-#include <anki/collision/Sphere.h>
-#include <anki/collision/Frustum.h>
-#include <shaders/glsl_cpp_common/ClusteredShading.h>
-
-namespace anki
-{
-
-/// This should be the number of light types. For now it's spots & points & probes & decals.
-const U SIZE_IDX_COUNT = 4;
-
-// Shader structs and block representations. All positions and directions in viewspace
-// For documentation see the shaders
-
-class LightBin::ShaderCluster
-{
-public:
-	U32 m_firstIdx;
-};
-
-static const U MAX_TYPED_LIGHTS_PER_CLUSTER = 16;
-static const U MAX_PROBES_PER_CLUSTER = 12;
-static const U MAX_DECALS_PER_CLUSTER = 8;
-static const F32 INVALID_TEXTURE_INDEX = -1.0;
-
-class LightBin::ClusterLightIndex
-{
-public:
-	ClusterLightIndex()
-	{
-		// Do nothing. No need to initialize
-	}
-
-	U getIndex() const
-	{
-		return m_index;
-	}
-
-	void setIndex(U i)
-	{
-		ANKI_ASSERT(i <= MAX_U16);
-		m_index = i;
-	}
-
-	friend Bool operator<(const ClusterLightIndex& a, const ClusterLightIndex& b)
-	{
-		return a.getIndex() < b.getIndex();
-	}
-
-private:
-	U16 m_index;
-};
-
-/// Store the probe radius for sorting the indices.
-/// WARNING: Keep it as small as possible, that's why the members are U16
-class LightBin::ClusterProbeIndex
-{
-public:
-	ClusterProbeIndex()
-	{
-		// Do nothing. No need to initialize
-		static_assert(sizeof(ClusterProbeIndex) == sizeof(U16), "Because we memcmp");
-	}
-
-	U getIndex() const
-	{
-		return m_index;
-	}
-
-	void setIndex(U i)
-	{
-		ANKI_ASSERT(i <= MAX_U16);
-		m_index = i;
-	}
-
-	Bool operator<(const ClusterProbeIndex& b) const
-	{
-		return m_index < b.m_index;
-	}
-
-private:
-	U16 m_index;
-};
-
-/// WARNING: Keep it as small as possible. The number of clusters is huge
-class alignas(U32) LightBin::ClusterData
-{
-public:
-	Atomic<U8> m_pointCount;
-	Atomic<U8> m_spotCount;
-	Atomic<U8> m_probeCount;
-	Atomic<U8> m_decalCount;
-
-	Array<ClusterLightIndex, MAX_TYPED_LIGHTS_PER_CLUSTER> m_pointIds;
-	Array<ClusterLightIndex, MAX_TYPED_LIGHTS_PER_CLUSTER> m_spotIds;
-	Array<ClusterProbeIndex, MAX_PROBES_PER_CLUSTER> m_probeIds;
-	Array<ClusterLightIndex, MAX_DECALS_PER_CLUSTER> m_decalIds;
-
-	ClusterData()
-	{
-		// Do nothing. No need to initialize
-	}
-
-	void reset()
-	{
-		// Set the counts to zero and try to be faster
-		*reinterpret_cast<U32*>(&m_pointCount) = 0;
-	}
-
-	void normalizeCounts()
-	{
-		normalize(m_pointCount, MAX_TYPED_LIGHTS_PER_CLUSTER, "point lights");
-		normalize(m_spotCount, MAX_TYPED_LIGHTS_PER_CLUSTER, "spot lights");
-		normalize(m_probeCount, MAX_PROBES_PER_CLUSTER, "probes");
-		normalize(m_decalCount, MAX_DECALS_PER_CLUSTER, "decals");
-	}
-
-	void sortLightIds()
-	{
-		const U pointCount = m_pointCount.get();
-		if(pointCount > 1)
-		{
-			std::sort(&m_pointIds[0], &m_pointIds[0] + pointCount);
-		}
-
-		const U spotCount = m_spotCount.get();
-		if(spotCount > 1)
-		{
-			std::sort(&m_spotIds[0], &m_spotIds[0] + spotCount);
-		}
-
-		const U probeCount = m_probeCount.get();
-		if(probeCount > 1)
-		{
-			std::sort(m_probeIds.getBegin(), m_probeIds.getBegin() + probeCount);
-		}
-
-		const U decalCount = m_decalCount.get();
-		if(decalCount > 1)
-		{
-			std::sort(&m_decalIds[0], &m_decalIds[0] + decalCount);
-		}
-	}
-
-	Bool operator==(const ClusterData& b) const
-	{
-		const U pointCount = m_pointCount.get();
-		const U spotCount = m_spotCount.get();
-		const U probeCount = m_probeCount.get();
-		const U decalCount = m_decalCount.get();
-		const U pointCount2 = b.m_pointCount.get();
-		const U spotCount2 = b.m_spotCount.get();
-		const U probeCount2 = b.m_probeCount.get();
-		const U decalCount2 = b.m_decalCount.get();
-
-		if(pointCount != pointCount2 || spotCount != spotCount2 || probeCount != probeCount2
-			|| decalCount != decalCount2)
-		{
-			return false;
-		}
-
-		if(pointCount > 0)
-		{
-			if(memcmp(&m_pointIds[0], &b.m_pointIds[0], sizeof(m_pointIds[0]) * pointCount) != 0)
-			{
-				return false;
-			}
-		}
-
-		if(spotCount > 0)
-		{
-			if(memcmp(&m_spotIds[0], &b.m_spotIds[0], sizeof(m_spotIds[0]) * spotCount) != 0)
-			{
-				return false;
-			}
-		}
-
-		if(probeCount > 0)
-		{
-			if(memcmp(&m_probeIds[0], &b.m_probeIds[0], sizeof(b.m_probeIds[0]) * probeCount) != 0)
-			{
-				return false;
-			}
-		}
-
-		if(decalCount > 0)
-		{
-			if(memcmp(&m_decalIds[0], &b.m_decalIds[0], sizeof(b.m_decalIds[0]) * decalCount) != 0)
-			{
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-private:
-	static void normalize(Atomic<U8>& count, const U maxCount, CString what)
-	{
-		U8 a = count.get();
-		count.set(a % maxCount);
-		if(ANKI_UNLIKELY(a >= maxCount))
-		{
-			ANKI_R_LOGW("Increase cluster limit: %s", &what[0]);
-		}
-	}
-};
-
-/// Common data for all tasks.
-class LightBin::BinContext
-{
-public:
-	BinContext(StackAllocator<U8> alloc)
-		: m_alloc(alloc)
-		, m_tempClusters(alloc)
-	{
-	}
-
-	Mat4 m_viewMat;
-	Mat4 m_viewProjMat;
-	Mat4 m_camTrf;
-
-	U32 m_maxLightIndices = 0;
-	Bool m_shadowsEnabled = false;
-	StackAllocator<U8> m_alloc;
-
-	// To fill the light buffers
-	WeakArray<PointLight> m_pointLights;
-	WeakArray<SpotLight> m_spotLights;
-	WeakArray<ReflectionProbe> m_probes;
-	WeakArray<Decal> m_decals;
-
-	WeakArray<U32> m_lightIds;
-	WeakArray<ShaderCluster> m_clusters;
-
-	Atomic<U32> m_pointLightsCount = {0};
-	Atomic<U32> m_spotLightsCount = {0};
-	Atomic<U32> m_probeCount = {0};
-	Atomic<U32> m_decalCount = {0};
-
-	// To fill the tile buffers
-	DynamicArrayAuto<ClusterData> m_tempClusters;
-
-	// To fill the light index buffer
-	Atomic<U32> m_lightIdsCount = {0};
-
-	// Misc
-	WeakArray<const PointLightQueueElement> m_vPointLights;
-	WeakArray<const SpotLightQueueElement> m_vSpotLights;
-	WeakArray<const ReflectionProbeQueueElement> m_vProbes;
-	WeakArray<const DecalQueueElement> m_vDecals;
-
-	Atomic<U32> m_count = {0};
-	Atomic<U32> m_count2 = {0};
-
-	TextureViewPtr m_diffDecalTexAtlas;
-	SpinLock m_diffDecalTexAtlasMtx;
-	TextureViewPtr m_specularRoughnessDecalTexAtlas;
-	SpinLock m_specularRoughnessDecalTexAtlasMtx;
-
-	LightBin* m_bin = nullptr;
-};
-
-/// Write the lights to the GPU buffers.
-class LightBin::WriteLightsTask : public ThreadPoolTask
-{
-public:
-	BinContext* m_ctx = nullptr;
-
-	Error operator()(U32 threadId, PtrSize threadsCount)
-	{
-		m_ctx->m_bin->binLights(threadId, threadsCount, *m_ctx);
-		return Error::NONE;
-	}
-};
-
-LightBin::LightBin(const GenericMemoryPoolAllocator<U8>& alloc,
-	U clusterCountX,
-	U clusterCountY,
-	U clusterCountZ,
-	ThreadPool* threadPool,
-	StagingGpuMemoryManager* stagingMem)
-	: m_alloc(alloc)
-	, m_clusterCount(clusterCountX * clusterCountY * clusterCountZ)
-	, m_threadPool(threadPool)
-	, m_stagingMem(stagingMem)
-	, m_barrier(threadPool->getThreadCount())
-{
-	m_clusterer.init(alloc, clusterCountX, clusterCountY, clusterCountZ);
-}
-
-LightBin::~LightBin()
-{
-}
-
-Error LightBin::bin(const Mat4& viewMat,
-	const Mat4& projMat,
-	const Mat4& viewProjMat,
-	const Mat4& camTrf,
-	const RenderQueue& rqueue,
-	StackAllocator<U8> frameAlloc,
-	U maxLightIndices,
-	Bool shadowsEnabled,
-	LightBinOut& out)
-{
-	ANKI_TRACE_SCOPED_EVENT(R_LIGHT_BINNING);
-
-	// Prepare the clusterer
-	ClustererPrepareInfo pinf;
-	pinf.m_viewMat = viewMat;
-	pinf.m_projMat = projMat;
-	pinf.m_viewProjMat = viewProjMat;
-	pinf.m_camTrf = Transform(camTrf);
-	pinf.m_near = rqueue.m_cameraNear;
-	pinf.m_far = rqueue.m_cameraFar;
-	m_clusterer.prepare(*m_threadPool, pinf);
-
-	//
-	// Quickly get the lights
-	//
-	const U visiblePointLightsCount = rqueue.m_pointLights.getSize();
-	const U visibleSpotLightsCount = rqueue.m_spotLights.getSize();
-	const U visibleProbeCount = rqueue.m_reflectionProbes.getSize();
-	const U visibleDecalCount = rqueue.m_decals.getSize();
-
-	ANKI_TRACE_INC_COUNTER(R_LIGHTS, visiblePointLightsCount + visibleSpotLightsCount);
-
-	//
-	// Write the lights and tiles UBOs
-	//
-	Array<WriteLightsTask, ThreadPool::MAX_THREADS> tasks;
-	BinContext ctx(frameAlloc);
-	ctx.m_viewMat = viewMat;
-	ctx.m_viewProjMat = viewProjMat;
-	ctx.m_camTrf = camTrf;
-	ctx.m_maxLightIndices = maxLightIndices;
-	ctx.m_shadowsEnabled = shadowsEnabled;
-	ctx.m_tempClusters.create(m_clusterCount);
-
-	if(visiblePointLightsCount)
-	{
-		PointLight* data = static_cast<PointLight*>(m_stagingMem->allocateFrame(
-			sizeof(PointLight) * visiblePointLightsCount, StagingGpuMemoryType::UNIFORM, out.m_pointLightsToken));
-
-		ctx.m_pointLights = WeakArray<PointLight>(data, visiblePointLightsCount);
-
-		ctx.m_vPointLights =
-			WeakArray<const PointLightQueueElement>(rqueue.m_pointLights.getBegin(), visiblePointLightsCount);
-	}
-	else
-	{
-		out.m_pointLightsToken.markUnused();
-	}
-
-	if(visibleSpotLightsCount)
-	{
-		SpotLight* data = static_cast<SpotLight*>(m_stagingMem->allocateFrame(
-			sizeof(SpotLight) * visibleSpotLightsCount, StagingGpuMemoryType::UNIFORM, out.m_spotLightsToken));
-
-		ctx.m_spotLights = WeakArray<SpotLight>(data, visibleSpotLightsCount);
-
-		ctx.m_vSpotLights =
-			WeakArray<const SpotLightQueueElement>(rqueue.m_spotLights.getBegin(), visibleSpotLightsCount);
-	}
-	else
-	{
-		out.m_spotLightsToken.markUnused();
-	}
-
-	if(visibleProbeCount)
-	{
-		ReflectionProbe* data = static_cast<ReflectionProbe*>(m_stagingMem->allocateFrame(
-			sizeof(ReflectionProbe) * visibleProbeCount, StagingGpuMemoryType::UNIFORM, out.m_probesToken));
-
-		ctx.m_probes = WeakArray<ReflectionProbe>(data, visibleProbeCount);
-
-		ctx.m_vProbes =
-			WeakArray<const ReflectionProbeQueueElement>(rqueue.m_reflectionProbes.getBegin(), visibleProbeCount);
-	}
-	else
-	{
-		out.m_probesToken.markUnused();
-	}
-
-	if(visibleDecalCount)
-	{
-		Decal* data = static_cast<Decal*>(m_stagingMem->allocateFrame(
-			sizeof(Decal) * visibleDecalCount, StagingGpuMemoryType::UNIFORM, out.m_decalsToken));
-
-		ctx.m_decals = WeakArray<Decal>(data, visibleDecalCount);
-
-		ctx.m_vDecals = WeakArray<const DecalQueueElement>(rqueue.m_decals.getBegin(), visibleDecalCount);
-	}
-	else
-	{
-		out.m_decalsToken.markUnused();
-	}
-
-	ctx.m_bin = this;
-
-	// Get mem for clusters
-	ShaderCluster* data = static_cast<ShaderCluster*>(m_stagingMem->allocateFrame(
-		sizeof(ShaderCluster) * m_clusterCount, StagingGpuMemoryType::STORAGE, out.m_clustersToken));
-
-	ctx.m_clusters = WeakArray<ShaderCluster>(data, m_clusterCount);
-
-	// Allocate light IDs
-	U32* data2 = static_cast<U32*>(m_stagingMem->allocateFrame(
-		maxLightIndices * sizeof(U32), StagingGpuMemoryType::STORAGE, out.m_lightIndicesToken));
-
-	ctx.m_lightIds = WeakArray<U32>(data2, maxLightIndices);
-
-	// Fill the first part of light ids with invalid indices. Will be used for empty clusters
-	for(U i = 0; i < SIZE_IDX_COUNT; ++i)
-	{
-		ctx.m_lightIds[i] = 0;
-	}
-	ctx.m_lightIdsCount.set(SIZE_IDX_COUNT);
-
-	// Fire the async job
-	for(U i = 0; i < m_threadPool->getThreadCount(); i++)
-	{
-		tasks[i].m_ctx = &ctx;
-
-		m_threadPool->assignNewTask(i, &tasks[i]);
-	}
-
-	// Sync
-	ANKI_CHECK(m_threadPool->waitForAllThreadsToFinish());
-
-	out.m_diffDecalTexView = ctx.m_diffDecalTexAtlas;
-	out.m_specularRoughnessDecalTexView = ctx.m_specularRoughnessDecalTexAtlas;
-
-	return Error::NONE;
-}
-
-void LightBin::binLights(U32 threadId, PtrSize threadsCount, BinContext& ctx)
-{
-	U clusterCount = m_clusterCount;
-	PtrSize start, end;
-
-	//
-	// Initialize the temp clusters
-	//
-	{
-		ANKI_TRACE_SCOPED_EVENT(R_LIGHT_BINNING);
-
-		ThreadPoolTask::choseStartEnd(threadId, threadsCount, clusterCount, start, end);
-
-		for(U i = start; i < end; ++i)
-		{
-			ctx.m_tempClusters[i].reset();
-		}
-	}
-
-	m_barrier.wait();
-
-	//
-	// Iterate lights and probes and bin them
-	//
-	{
-		ANKI_TRACE_SCOPED_EVENT(R_LIGHT_BINNING);
-
-		ClustererTestResult testResult;
-		m_clusterer.initTestResults(ctx.m_alloc, testResult);
-		U lightCount = ctx.m_vPointLights.getSize() + ctx.m_vSpotLights.getSize();
-		U totalCount = lightCount + ctx.m_vProbes.getSize() + ctx.m_vDecals.getSize();
-
-		const U TO_BIN_COUNT = 1;
-		while((start = ctx.m_count2.fetchAdd(TO_BIN_COUNT)) < totalCount)
-		{
-			end = min<U>(start + TO_BIN_COUNT, totalCount);
-
-			for(U j = start; j < end; ++j)
-			{
-				if(j >= lightCount + ctx.m_vDecals.getSize())
-				{
-					U i = j - (lightCount + ctx.m_vDecals.getSize());
-					writeAndBinProbe(ctx.m_vProbes[i], ctx, testResult);
-				}
-				else if(j >= ctx.m_vPointLights.getSize() + ctx.m_vDecals.getSize())
-				{
-					U i = j - (ctx.m_vPointLights.getSize() + ctx.m_vDecals.getSize());
-					writeAndBinSpotLight(ctx.m_vSpotLights[i], ctx, testResult);
-				}
-				else if(j >= ctx.m_vDecals.getSize())
-				{
-					U i = j - ctx.m_vDecals.getSize();
-					writeAndBinPointLight(ctx.m_vPointLights[i], ctx, testResult);
-				}
-				else
-				{
-					U i = j;
-					writeAndBinDecal(ctx.m_vDecals[i], ctx, testResult);
-				}
-			}
-		}
-	}
-
-	m_barrier.wait();
-
-	//
-	// Last thing, update the real clusters
-	//
-
-	{
-		ANKI_TRACE_SCOPED_EVENT(R_LIGHT_BINNING);
-
-		// Run per cluster
-		const U CLUSTER_GROUP = 16;
-		while((start = ctx.m_count.fetchAdd(CLUSTER_GROUP)) < clusterCount)
-		{
-			end = min<U>(start + CLUSTER_GROUP, clusterCount);
-
-			for(U i = start; i < end; ++i)
-			{
-				auto& cluster = ctx.m_tempClusters[i];
-				cluster.normalizeCounts();
-
-				const U countP = cluster.m_pointCount.get();
-				const U countS = cluster.m_spotCount.get();
-				const U countProbe = cluster.m_probeCount.get();
-				const U countDecal = cluster.m_decalCount.get();
-				const U count = countP + countS + countProbe + countDecal;
-
-				auto& c = ctx.m_clusters[i];
-				c.m_firstIdx = 0; // Point to the first empty indices
-
-				// Early exit
-				if(ANKI_UNLIKELY(count == 0))
-				{
-					continue;
-				}
-
-				// Check if the previous cluster contains the same lights as this one and if yes then merge them. This
-				// will avoid allocating new IDs (and thrashing GPU caches).
-				cluster.sortLightIds();
-				if(i != start)
-				{
-					const auto& clusterB = ctx.m_tempClusters[i - 1];
-
-					if(cluster == clusterB)
-					{
-						c.m_firstIdx = ctx.m_clusters[i - 1].m_firstIdx;
-						continue;
-					}
-				}
-
-				U offset = ctx.m_lightIdsCount.fetchAdd(count + SIZE_IDX_COUNT);
-				U initialOffset = offset;
-				(void)initialOffset;
-
-				if(offset + count + SIZE_IDX_COUNT <= ctx.m_maxLightIndices)
-				{
-					c.m_firstIdx = offset;
-
-					ctx.m_lightIds[offset++] = countDecal;
-					for(U i = 0; i < countDecal; ++i)
-					{
-						ctx.m_lightIds[offset++] = cluster.m_decalIds[i].getIndex();
-					}
-
-					ctx.m_lightIds[offset++] = countP;
-					for(U i = 0; i < countP; ++i)
-					{
-						ctx.m_lightIds[offset++] = cluster.m_pointIds[i].getIndex();
-					}
-
-					ctx.m_lightIds[offset++] = countS;
-					for(U i = 0; i < countS; ++i)
-					{
-						ctx.m_lightIds[offset++] = cluster.m_spotIds[i].getIndex();
-					}
-
-					ctx.m_lightIds[offset++] = countProbe;
-					for(U i = 0; i < countProbe; ++i)
-					{
-						ctx.m_lightIds[offset++] = cluster.m_probeIds[i].getIndex();
-					}
-
-					ANKI_ASSERT(offset - initialOffset == count + SIZE_IDX_COUNT);
-				}
-				else
-				{
-					ANKI_R_LOGW("Light IDs buffer too small");
-				}
-			} // end for
-		} // end while
-	} // scope
-}
-
-void LightBin::writeAndBinPointLight(
-	const PointLightQueueElement& lightEl, BinContext& ctx, ClustererTestResult& testResult)
-{
-	// Get GPU light
-	I idx = ctx.m_pointLightsCount.fetchAdd(1);
-
-	PointLight& slight = ctx.m_pointLights[idx];
-
-	slight.m_posRadius = Vec4(lightEl.m_worldPosition.xyz(), 1.0f / (lightEl.m_radius * lightEl.m_radius));
-	slight.m_diffuseColorTileSize = lightEl.m_diffuseColor.xyz0();
-
-	if(lightEl.m_shadowRenderQueues[0] == nullptr || !ctx.m_shadowsEnabled)
-	{
-		slight.m_diffuseColorTileSize.w() = INVALID_TEXTURE_INDEX;
-	}
-	else
-	{
-		slight.m_diffuseColorTileSize.w() = lightEl.m_atlasTileSize;
-		slight.m_atlasTiles = UVec2(lightEl.m_atlasTiles.x(), lightEl.m_atlasTiles.y());
-	}
-
-	slight.m_radiusPad1 = Vec2(lightEl.m_radius);
-
-	// Now bin it
-	Sphere sphere(lightEl.m_worldPosition.xyz0(), lightEl.m_radius);
-	Aabb box;
-	sphere.computeAabb(box);
-	m_clusterer.bin(sphere, box, testResult);
-
-	auto it = testResult.getClustersBegin();
-	auto end = testResult.getClustersEnd();
-	for(; it != end; ++it)
-	{
-		U x = (*it).x();
-		U y = (*it).y();
-		U z = (*it).z();
-
-		U i = m_clusterer.getClusterCountX() * (z * m_clusterer.getClusterCountY() + y) + x;
-
-		auto& cluster = ctx.m_tempClusters[i];
-
-		i = cluster.m_pointCount.fetchAdd(1) % MAX_TYPED_LIGHTS_PER_CLUSTER;
-		cluster.m_pointIds[i].setIndex(idx);
-	}
-}
-
-void LightBin::writeAndBinSpotLight(
-	const SpotLightQueueElement& lightEl, BinContext& ctx, ClustererTestResult& testResult)
-{
-	I idx = ctx.m_spotLightsCount.fetchAdd(1);
-
-	SpotLight& light = ctx.m_spotLights[idx];
-	F32 shadowmapIndex = INVALID_TEXTURE_INDEX;
-
-	if(lightEl.hasShadow() && ctx.m_shadowsEnabled)
-	{
-		// bias * proj_l * view_l
-		light.m_texProjectionMat = lightEl.m_textureMatrix;
-
-		shadowmapIndex = 1.0f; // Just set a value
-	}
-
-	// Pos & dist
-	light.m_posRadius =
-		Vec4(lightEl.m_worldTransform.getTranslationPart().xyz(), 1.0f / (lightEl.m_distance * lightEl.m_distance));
-
-	// Diff color and shadowmap ID now
-	light.m_diffuseColorShadowmapId = Vec4(lightEl.m_diffuseColor, shadowmapIndex);
-
-	// Light dir & radius
-	Vec3 lightDir = -lightEl.m_worldTransform.getRotationPart().getZAxis();
-	light.m_lightDirRadius = Vec4(lightDir, lightEl.m_distance);
-
-	// Angles
-	light.m_outerCosInnerCos = Vec4(cos(lightEl.m_outerAngle / 2.0f), cos(lightEl.m_innerAngle / 2.0f), 1.0f, 1.0f);
-
-	// Bin lights
-	PerspectiveFrustum shape(lightEl.m_outerAngle, lightEl.m_outerAngle, 0.01f, lightEl.m_distance);
-	shape.transform(Transform(lightEl.m_worldTransform));
-	Aabb box;
-	shape.computeAabb(box);
-	m_clusterer.binPerspectiveFrustum(shape, box, testResult);
-
-	auto it = testResult.getClustersBegin();
-	auto end = testResult.getClustersEnd();
-	for(; it != end; ++it)
-	{
-		U x = (*it).x();
-		U y = (*it).y();
-		U z = (*it).z();
-
-		U i = m_clusterer.getClusterCountX() * (z * m_clusterer.getClusterCountY() + y) + x;
-
-		auto& cluster = ctx.m_tempClusters[i];
-
-		i = cluster.m_spotCount.fetchAdd(1) % MAX_TYPED_LIGHTS_PER_CLUSTER;
-		cluster.m_spotIds[i].setIndex(idx);
-	}
-}
-
-void LightBin::writeAndBinProbe(
-	const ReflectionProbeQueueElement& probeEl, BinContext& ctx, ClustererTestResult& testResult)
-{
-	// Write it
-	ReflectionProbe probe;
-	probe.m_positionCubemapIndex = Vec4(probeEl.m_worldPosition, probeEl.m_textureArrayIndex);
-	probe.m_aabbMinPad1 = probeEl.m_aabbMin.xyz0();
-	probe.m_aabbMaxPad1 = probeEl.m_aabbMax.xyz0();
-
-	U idx = ctx.m_probeCount.fetchAdd(1);
-	ctx.m_probes[idx] = probe;
-
-	// Bin it
-	Aabb box(probeEl.m_aabbMin, probeEl.m_aabbMax);
-	m_clusterer.bin(box, box, testResult);
-
-	auto it = testResult.getClustersBegin();
-	auto end = testResult.getClustersEnd();
-	for(; it != end; ++it)
-	{
-		U x = (*it).x();
-		U y = (*it).y();
-		U z = (*it).z();
-
-		U i = m_clusterer.getClusterCountX() * (z * m_clusterer.getClusterCountY() + y) + x;
-
-		auto& cluster = ctx.m_tempClusters[i];
-
-		i = cluster.m_probeCount.fetchAdd(1) % MAX_PROBES_PER_CLUSTER;
-		cluster.m_probeIds[i].setIndex(idx);
-	}
-}
-
-void LightBin::writeAndBinDecal(const DecalQueueElement& decalEl, BinContext& ctx, ClustererTestResult& testResult)
-{
-	I idx = ctx.m_decalCount.fetchAdd(1);
-	Decal& decal = ctx.m_decals[idx];
-
-	TextureViewPtr atlas(const_cast<TextureView*>(decalEl.m_diffuseAtlas));
-	Vec4 uv = decalEl.m_diffuseAtlasUv;
-	decal.m_diffUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
-	decal.m_blendFactors[0] = decalEl.m_diffuseAtlasBlendFactor;
-
-	{
-		LockGuard<SpinLock> lock(ctx.m_diffDecalTexAtlasMtx);
-		if(ctx.m_diffDecalTexAtlas && ctx.m_diffDecalTexAtlas != atlas)
-		{
-			ANKI_R_LOGF("All decals should have the same tex atlas");
-		}
-
-		ctx.m_diffDecalTexAtlas = atlas;
-	}
-
-	atlas.reset(const_cast<TextureView*>(decalEl.m_specularRoughnessAtlas));
-	uv = decalEl.m_specularRoughnessAtlasUv;
-	decal.m_normRoughnessUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
-	decal.m_blendFactors[1] = decalEl.m_specularRoughnessAtlasBlendFactor;
-
-	if(atlas)
-	{
-		LockGuard<SpinLock> lock(ctx.m_specularRoughnessDecalTexAtlasMtx);
-		if(ctx.m_specularRoughnessDecalTexAtlas && ctx.m_specularRoughnessDecalTexAtlas != atlas)
-		{
-			ANKI_R_LOGF("All decals should have the same tex atlas");
-		}
-
-		ctx.m_specularRoughnessDecalTexAtlas = atlas;
-	}
-
-	// bias * proj_l * view_
-	decal.m_texProjectionMat = decalEl.m_textureMatrix;
-
-	// Bin it
-	Obb obb(decalEl.m_obbCenter.xyz0(), Mat3x4(decalEl.m_obbRotation), decalEl.m_obbExtend.xyz0());
-	Aabb box;
-	obb.computeAabb(box);
-	m_clusterer.bin(obb, box, testResult);
-
-	auto it = testResult.getClustersBegin();
-	auto end = testResult.getClustersEnd();
-	for(; it != end; ++it)
-	{
-		U x = (*it).x();
-		U y = (*it).y();
-		U z = (*it).z();
-
-		U i = m_clusterer.getClusterCountX() * (z * m_clusterer.getClusterCountY() + y) + x;
-
-		auto& cluster = ctx.m_tempClusters[i];
-
-		i = cluster.m_decalCount.fetchAdd(1) % MAX_DECALS_PER_CLUSTER;
-		cluster.m_decalIds[i].setIndex(idx);
-	}
-}
-
-} // end namespace anki

+ 0 - 88
src/anki/renderer/LightBin.h

@@ -1,88 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/renderer/Clusterer.h>
-
-namespace anki
-{
-
-/// @addtogroup renderer
-/// @{
-
-/// @memberof LightBin
-class LightBinOut
-{
-public:
-	StagingGpuMemoryToken m_pointLightsToken;
-	StagingGpuMemoryToken m_spotLightsToken;
-	StagingGpuMemoryToken m_probesToken;
-	StagingGpuMemoryToken m_decalsToken;
-	StagingGpuMemoryToken m_clustersToken;
-	StagingGpuMemoryToken m_lightIndicesToken;
-
-	TextureViewPtr m_diffDecalTexView;
-	TextureViewPtr m_specularRoughnessDecalTexView;
-};
-
-/// Bins lights and probes to clusters.
-class LightBin
-{
-	friend class WriteLightsTask;
-
-public:
-	LightBin(const GenericMemoryPoolAllocator<U8>& alloc,
-		U clusterCountX,
-		U clusterCountY,
-		U clusterCountZ,
-		ThreadPool* threadPool,
-		StagingGpuMemoryManager* stagingMem);
-
-	~LightBin();
-
-	ANKI_USE_RESULT Error bin(const Mat4& viewMat,
-		const Mat4& projMat,
-		const Mat4& viewProjMat,
-		const Mat4& camTrf,
-		const RenderQueue& rqueue,
-		StackAllocator<U8> frameAlloc,
-		U maxLightIndices,
-		Bool shadowsEnabled,
-		LightBinOut& out);
-
-	const Clusterer& getClusterer() const
-	{
-		return m_clusterer;
-	}
-
-private:
-	class BinContext;
-	class ShaderCluster;
-	class ClusterLightIndex;
-	class ClusterProbeIndex;
-	class ClusterData;
-	class WriteLightsTask;
-
-	GenericMemoryPoolAllocator<U8> m_alloc;
-	Clusterer m_clusterer;
-	U32 m_clusterCount = 0;
-	ThreadPool* m_threadPool = nullptr;
-	StagingGpuMemoryManager* m_stagingMem = nullptr;
-	Barrier m_barrier;
-
-	void binLights(U32 threadId, PtrSize threadsCount, BinContext& ctx);
-
-	void writeAndBinPointLight(const PointLightQueueElement& lightEl, BinContext& ctx, ClustererTestResult& testResult);
-
-	void writeAndBinSpotLight(const SpotLightQueueElement& lightEl, BinContext& ctx, ClustererTestResult& testResult);
-
-	void writeAndBinProbe(const ReflectionProbeQueueElement& probe, BinContext& ctx, ClustererTestResult& testResult);
-
-	void writeAndBinDecal(const DecalQueueElement& decal, BinContext& ctx, ClustererTestResult& testResult);
-};
-/// @}
-
-} // end namespace anki

+ 7 - 83
src/anki/renderer/LightShading.cpp

@@ -8,7 +8,6 @@
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/Indirect.h>
 #include <anki/renderer/GBuffer.h>
-#include <anki/renderer/LightBin.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/ForwardShading.h>
 #include <anki/renderer/DepthDownscale.h>
@@ -17,7 +16,6 @@
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
 #include <anki/collision/Functions.h>
-#include <shaders/glsl_cpp_common/ClusteredShading.h>
 
 namespace anki
 {
@@ -29,7 +27,6 @@ LightShading::LightShading(Renderer* r)
 
 LightShading::~LightShading()
 {
-	getAllocator().deleteInstance(m_lightBin);
 }
 
 Error LightShading::init(const ConfigSet& config)
@@ -47,36 +44,14 @@ Error LightShading::init(const ConfigSet& config)
 
 Error LightShading::initInternal(const ConfigSet& config)
 {
-	m_maxLightIds = config.getNumber("r.avgObjectsPerCluster");
-
-	if(m_maxLightIds == 0)
-	{
-		ANKI_R_LOGE("Incorrect number of max light indices");
-		return Error::USER_DATA;
-	}
-
-	m_clusterCounts[0] = config.getNumber("r.clusterSizeX");
-	m_clusterCounts[1] = config.getNumber("r.clusterSizeY");
-	m_clusterCounts[2] = config.getNumber("r.clusterSizeZ");
-	m_clusterCount = m_clusterCounts[0] * m_clusterCounts[1] * m_clusterCounts[2];
-
-	m_maxLightIds *= m_clusterCount;
-
-	m_lightBin = getAllocator().newInstance<LightBin>(getAllocator(),
-		m_clusterCounts[0],
-		m_clusterCounts[1],
-		m_clusterCounts[2],
-		&m_r->getThreadPool(),
-		&m_r->getStagingGpuMemoryManager());
-
 	// Load shaders and programs
 	ANKI_CHECK(getResourceManager().loadResource("shaders/LightShading.glslp", m_prog));
 
 	ShaderProgramResourceConstantValueInitList<5> consts(m_prog);
-	consts.add("CLUSTER_COUNT_X", U32(m_clusterCounts[0]))
-		.add("CLUSTER_COUNT_Y", U32(m_clusterCounts[1]))
-		.add("CLUSTER_COUNT_Z", U32(m_clusterCounts[2]))
-		.add("CLUSTER_COUNT", U32(m_clusterCount))
+	consts.add("CLUSTER_COUNT_X", U32(m_r->getClusterCount()[0]))
+		.add("CLUSTER_COUNT_Y", U32(m_r->getClusterCount()[1]))
+		.add("CLUSTER_COUNT_Z", U32(m_r->getClusterCount()[2]))
+		.add("CLUSTER_COUNT", U32(m_r->getClusterCount()[3]))
 		.add("IR_MIPMAP_COUNT", U32(m_r->getIndirect().getReflectionTextureMipmapCount()));
 
 	m_prog->getOrCreateVariant(consts.get(), m_progVariant);
@@ -111,27 +86,10 @@ Error LightShading::initInternal(const ConfigSet& config)
 	return Error::NONE;
 }
 
-Error LightShading::binLights(RenderingContext& ctx)
-{
-	updateCommonBlock(ctx);
-
-	ANKI_CHECK(m_lightBin->bin(ctx.m_renderQueue->m_viewMatrix,
-		ctx.m_renderQueue->m_projectionMatrix,
-		ctx.m_renderQueue->m_viewProjectionMatrix,
-		ctx.m_renderQueue->m_cameraTransform,
-		*ctx.m_renderQueue,
-		ctx.m_tempAllocator,
-		m_maxLightIds,
-		true,
-		m_runCtx.m_resources));
-
-	return Error::NONE;
-}
-
 void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
-	const LightShadingResources& rsrc = m_runCtx.m_resources;
+	const ClusterBinOut& rsrc = ctx.m_clusterBinOut;
 
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
@@ -163,14 +121,14 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 			TextureUsageBit::SAMPLED_FRAGMENT);
 
 		// Bind uniforms
-		bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
+		bindUniforms(cmdb, 0, 0, ctx.m_lightShadingUniformsToken);
 		bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
 		bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
 		bindUniforms(cmdb, 0, 3, rsrc.m_probesToken);
 
 		// Bind storage
 		bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
-		bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
+		bindStorage(cmdb, 0, 1, rsrc.m_indicesToken);
 
 		drawQuad(cmdb);
 	}
@@ -243,38 +201,4 @@ void LightShading::populateRenderGraph(RenderingContext& ctx)
 	pass.newDependency({m_r->getForwardShading().getRt(), TextureUsageBit::SAMPLED_FRAGMENT});
 }
 
-void LightShading::updateCommonBlock(RenderingContext& ctx)
-{
-	LightingUniforms* blk =
-		allocateUniforms<LightingUniforms*>(sizeof(LightingUniforms), m_runCtx.m_resources.m_commonUniformsToken);
-
-	// Start writing
-	blk->m_unprojectionParams = ctx.m_unprojParams;
-
-	blk->m_rendererSizeTimeNear =
-		Vec4(m_r->getWidth(), m_r->getHeight(), HighRezTimer::getCurrentTime(), ctx.m_renderQueue->m_cameraNear);
-
-	blk->m_tileCount = UVec4(m_clusterCounts[0], m_clusterCounts[1], m_clusterCounts[2], m_clusterCount);
-
-	blk->m_cameraPosFar =
-		Vec4(ctx.m_renderQueue->m_cameraTransform.getTranslationPart().xyz(), ctx.m_renderQueue->m_cameraFar);
-
-	blk->m_clustererMagicValues = m_lightBin->getClusterer().getShaderMagicValues();
-
-	// Matrices
-	blk->m_viewMat = ctx.m_renderQueue->m_viewMatrix;
-	blk->m_invViewMat = ctx.m_renderQueue->m_viewMatrix.getInverse();
-
-	blk->m_projMat = ctx.m_matrices.m_projectionJitter;
-	blk->m_invProjMat = ctx.m_matrices.m_projectionJitter.getInverse();
-
-	blk->m_viewProjMat = ctx.m_matrices.m_viewProjectionJitter;
-	blk->m_invViewProjMat = ctx.m_matrices.m_viewProjectionJitter.getInverse();
-
-	blk->m_prevViewProjMat = ctx.m_prevMatrices.m_viewProjectionJitter;
-
-	blk->m_prevViewProjMatMulInvViewProjMat =
-		ctx.m_prevMatrices.m_viewProjection * ctx.m_matrices.m_viewProjectionJitter.getInverse();
-}
-
 } // end namespace anki

+ 0 - 33
src/anki/renderer/LightShading.h

@@ -6,7 +6,6 @@
 #pragma once
 
 #include <anki/renderer/RendererObject.h>
-#include <anki/renderer/LightBin.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/resource/ShaderProgramResource.h>
 
@@ -16,13 +15,6 @@ namespace anki
 /// @addtogroup renderer
 /// @{
 
-/// @memberof LightShading
-class LightShadingResources : public LightBinOut
-{
-public:
-	StagingGpuMemoryToken m_commonUniformsToken;
-};
-
 /// Clustered deferred light pass.
 class LightShading : public RendererObject
 {
@@ -35,27 +27,12 @@ anki_internal:
 
 	void populateRenderGraph(RenderingContext& ctx);
 
-	ANKI_USE_RESULT Error binLights(RenderingContext& ctx);
-
 	RenderTargetHandle getRt() const
 	{
 		return m_runCtx.m_rt;
 	}
 
-	const LightBin& getLightBin() const
-	{
-		return *m_lightBin;
-	}
-
-	const LightShadingResources& getResources() const
-	{
-		return m_runCtx.m_resources;
-	}
-
 private:
-	Array<U32, 3> m_clusterCounts = {{0, 0, 0}};
-	U32 m_clusterCount = 0;
-
 	RenderTargetDescription m_rtDescr;
 	FramebufferDescription m_fbDescr;
 
@@ -63,13 +40,6 @@ private:
 	ShaderProgramResourcePtr m_prog;
 	const ShaderProgramResourceVariant* m_progVariant = nullptr;
 
-	LightBin* m_lightBin = nullptr;
-
-	/// @name Limits
-	/// @{
-	U32 m_maxLightIds;
-	/// @}
-
 	class
 	{
 	public:
@@ -83,14 +53,11 @@ private:
 	public:
 		RenderTargetHandle m_rt;
 		RenderingContext* m_ctx;
-		LightShadingResources m_resources;
 	} m_runCtx; ///< Run context.
 
 	/// Called by init
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
-	void updateCommonBlock(RenderingContext& ctx);
-
 	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
 
 	/// A RenderPassWorkCallback for the light pass.

+ 20 - 22
src/anki/renderer/MainRenderer.cpp

@@ -17,6 +17,7 @@
 #include <anki/core/App.h>
 #include <anki/misc/ConfigSet.h>
 #include <anki/util/HighRezTimer.h>
+#include <anki/util/ThreadHive.h>
 
 namespace anki
 {
@@ -30,7 +31,7 @@ MainRenderer::~MainRenderer()
 	ANKI_R_LOGI("Destroying main renderer");
 }
 
-Error MainRenderer::init(ThreadPool* threadpool,
+Error MainRenderer::init(ThreadHive* hive,
 	ResourceManager* resources,
 	GrManager* gr,
 	StagingGpuMemoryManager* stagingMem,
@@ -58,7 +59,7 @@ Error MainRenderer::init(ThreadPool* threadpool,
 	m_rDrawToDefaultFb = m_renderingQuality == 1.0;
 
 	m_r.reset(m_alloc.newInstance<Renderer>());
-	ANKI_CHECK(m_r->init(threadpool, resources, gr, stagingMem, ui, m_alloc, config2, globTimestamp));
+	ANKI_CHECK(m_r->init(hive, resources, gr, stagingMem, ui, m_alloc, config2, globTimestamp));
 
 	// Init other
 	if(!m_rDrawToDefaultFb)
@@ -94,6 +95,7 @@ Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 	// Run renderer
 	RenderingContext ctx(m_frameAlloc);
 	m_runCtx.m_ctx = &ctx;
+	m_runCtx.m_secondaryTaskId.set(0);
 
 	RenderTargetHandle presentRt = ctx.m_renderGraphDescr.importRenderTarget(presentTex, TextureUsageBit::NONE);
 
@@ -115,7 +117,6 @@ Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 	}
 
 	ctx.m_renderQueue = &rqueue;
-	ctx.m_unprojParams = ctx.m_renderQueue->m_projectionMatrix.extractPerspectiveUnprojectionParams();
 	ANKI_CHECK(m_r->populateRenderGraph(ctx));
 
 	// Blit renderer's result to default FB if needed
@@ -147,26 +148,14 @@ Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 	m_rgraph->compileNewGraph(ctx.m_renderGraphDescr, m_frameAlloc);
 
 	// Populate the 2nd level command buffers
-	class Task : public ThreadPoolTask
+	Array<ThreadHiveTask, ThreadHive::MAX_THREADS> tasks;
+	for(U i = 0; i < m_r->getThreadHive().getThreadCount(); ++i)
 	{
-	public:
-		RenderGraphPtr m_rgraph;
-
-		Error operator()(U32 taskId, PtrSize threadsCount)
-		{
-			m_rgraph->runSecondLevel(taskId);
-			return Error::NONE;
-		}
-	};
-
-	Task task;
-	task.m_rgraph = m_rgraph;
-	for(U i = 0; i < m_r->getThreadPool().getThreadCount(); ++i)
-	{
-		m_r->getThreadPool().assignNewTask(i, &task);
+		tasks[i].m_argument = this;
+		tasks[i].m_callback = executeSecondaryCallback;
 	}
-
-	ANKI_CHECK(m_r->getThreadPool().waitForAllThreadsToFinish());
+	m_r->getThreadHive().submitTasks(&tasks[0], m_r->getThreadHive().getThreadCount());
+	m_r->getThreadHive().waitAllTasks();
 
 	// Populate 1st level command buffers
 	m_rgraph->run();
@@ -174,7 +163,7 @@ Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 	// Flush
 	m_rgraph->flush();
 
-	// Reset render pass for the next frame
+	// Reset for the next frame
 	m_rgraph->reset();
 	m_r->finalize(ctx);
 
@@ -185,6 +174,15 @@ Error MainRenderer::render(RenderQueue& rqueue, TexturePtr presentTex)
 	return Error::NONE;
 }
 
+void MainRenderer::executeSecondaryCallback(
+	void* userData, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* signalSemaphore)
+{
+	MainRenderer& self = *static_cast<MainRenderer*>(userData);
+
+	const U taskId = self.m_runCtx.m_secondaryTaskId.fetchAdd(1);
+	self.m_rgraph->runSecondLevel(taskId);
+}
+
 void MainRenderer::runBlit(RenderPassWorkContext& rgraphCtx)
 {
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;

+ 5 - 2
src/anki/renderer/MainRenderer.h

@@ -15,7 +15,6 @@ namespace anki
 // Forward
 class ResourceManager;
 class ConfigSet;
-class ThreadPool;
 class StagingGpuMemoryManager;
 class UiManager;
 
@@ -37,7 +36,7 @@ public:
 
 	~MainRenderer();
 
-	ANKI_USE_RESULT Error init(ThreadPool* threadpool,
+	ANKI_USE_RESULT Error init(ThreadHive* hive,
 		ResourceManager* resources,
 		GrManager* gl,
 		StagingGpuMemoryManager* stagingMem,
@@ -92,6 +91,7 @@ private:
 	{
 	public:
 		const RenderingContext* m_ctx = nullptr;
+		Atomic<U32> m_secondaryTaskId = {0};
 	} m_runCtx;
 
 	void runBlit(RenderPassWorkContext& rgraphCtx);
@@ -109,6 +109,9 @@ private:
 	{
 		// Do nothing. This pass is dummy
 	}
+
+	static void executeSecondaryCallback(
+		void* userData, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* signalSemaphore);
 };
 /// @}
 

+ 55 - 3
src/anki/renderer/Renderer.cpp

@@ -27,6 +27,7 @@
 #include <anki/renderer/TemporalAA.h>
 #include <anki/renderer/UiStage.h>
 #include <anki/renderer/Ssr.h>
+#include <shaders/glsl_cpp_common/ClusteredShading.h>
 
 namespace anki
 {
@@ -40,7 +41,7 @@ Renderer::~Renderer()
 {
 }
 
-Error Renderer::init(ThreadPool* threadpool,
+Error Renderer::init(ThreadHive* hive,
 	ResourceManager* resources,
 	GrManager* gl,
 	StagingGpuMemoryManager* stagingMem,
@@ -52,7 +53,7 @@ Error Renderer::init(ThreadPool* threadpool,
 	ANKI_TRACE_SCOPED_EVENT(R_INIT);
 
 	m_globTimestamp = globTimestamp;
-	m_threadpool = threadpool;
+	m_threadHive = hive;
 	m_resources = resources;
 	m_gr = gl;
 	m_stagingMem = stagingMem;
@@ -80,6 +81,13 @@ Error Renderer::initInternal(const ConfigSet& config)
 	m_lodDistances[1] = config.getNumber("r.lodDistance1");
 	m_frameCount = 0;
 
+	m_clusterCount[0] = config.getNumber("r.clusterSizeX");
+	m_clusterCount[1] = config.getNumber("r.clusterSizeY");
+	m_clusterCount[2] = config.getNumber("r.clusterSizeZ");
+	m_clusterCount[3] = m_clusterCount[0] * m_clusterCount[1] * m_clusterCount[2];
+
+	m_clusterBin.init(m_clusterCount[0], m_clusterCount[1], m_clusterCount[2], config);
+
 	// A few sanity checks
 	if(m_width < 10 || m_height < 10)
 	{
@@ -251,6 +259,8 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 
 	ctx.m_prevMatrices = m_prevMatrices;
 
+	ctx.m_unprojParams = ctx.m_renderQueue->m_projectionMatrix.extractPerspectiveUnprojectionParams();
+
 	// Check if resources got loaded
 	if(m_prevLoadRequestCount != m_resources->getLoadingRequestCount()
 		|| m_prevAsyncTasksCompleted != m_resources->getAsyncTaskCompletedCount())
@@ -292,8 +302,16 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 
 	m_finalComposite->populateRenderGraph(ctx);
 
+	// Bin lights and update uniforms
 	m_stats.m_lightBinTime = HighRezTimer::getCurrentTime();
-	ANKI_CHECK(m_lightShading->binLights(ctx));
+	ClusterBinIn cin;
+	cin.m_renderQueue = ctx.m_renderQueue;
+	cin.m_shadowsEnabled = true; // TODO
+	cin.m_stagingMem = m_stagingMem;
+	cin.m_threadHive = m_threadHive;
+	m_clusterBin.bin(cin, ctx.m_clusterBinOut);
+
+	updateLightShadingUniforms(ctx);
 	m_stats.m_lightBinTime = HighRezTimer::getCurrentTime() - m_stats.m_lightBinTime;
 
 	return Error::NONE;
@@ -499,4 +517,38 @@ TexturePtr Renderer::createAndClearRenderTarget(const TextureInitInfo& inf, cons
 	return tex;
 }
 
+void Renderer::updateLightShadingUniforms(RenderingContext& ctx) const
+{
+	LightingUniforms* blk = static_cast<LightingUniforms*>(m_stagingMem->allocateFrame(
+		sizeof(LightingUniforms), StagingGpuMemoryType::UNIFORM, ctx.m_lightShadingUniformsToken));
+
+	// Start writing
+	blk->m_unprojectionParams = ctx.m_unprojParams;
+
+	blk->m_rendererSizeTimeNear =
+		Vec4(m_width, m_height, HighRezTimer::getCurrentTime(), ctx.m_renderQueue->m_cameraNear);
+
+	blk->m_tileCount = UVec4(m_clusterCount[0], m_clusterCount[1], m_clusterCount[2], m_clusterCount[3]);
+
+	blk->m_cameraPosFar =
+		Vec4(ctx.m_renderQueue->m_cameraTransform.getTranslationPart().xyz(), ctx.m_renderQueue->m_cameraFar);
+
+	blk->m_clustererMagicValues = ctx.m_clusterBinOut.m_shaderMagicValues;
+
+	// Matrices
+	blk->m_viewMat = ctx.m_renderQueue->m_viewMatrix;
+	blk->m_invViewMat = ctx.m_renderQueue->m_viewMatrix.getInverse();
+
+	blk->m_projMat = ctx.m_matrices.m_projectionJitter;
+	blk->m_invProjMat = ctx.m_matrices.m_projectionJitter.getInverse();
+
+	blk->m_viewProjMat = ctx.m_matrices.m_viewProjectionJitter;
+	blk->m_invViewProjMat = ctx.m_matrices.m_viewProjectionJitter.getInverse();
+
+	blk->m_prevViewProjMat = ctx.m_prevMatrices.m_viewProjectionJitter;
+
+	blk->m_prevViewProjMatMulInvViewProjMat =
+		ctx.m_prevMatrices.m_viewProjection * ctx.m_matrices.m_viewProjectionJitter.getInverse();
+}
+
 } // end namespace anki

+ 36 - 15
src/anki/renderer/Renderer.h

@@ -7,11 +7,11 @@
 
 #include <anki/renderer/Common.h>
 #include <anki/renderer/Drawer.h>
+#include <anki/renderer/ClusterBin.h>
 #include <anki/Math.h>
 #include <anki/Gr.h>
 #include <anki/resource/Forward.h>
 #include <anki/core/StagingGpuMemoryManager.h>
-#include <anki/util/ThreadPool.h>
 #include <anki/collision/Forward.h>
 
 namespace anki
@@ -45,7 +45,6 @@ class RenderingContext
 {
 public:
 	StackAllocator<U8> m_tempAllocator;
-
 	RenderQueue* m_renderQueue ANKI_DBG_NULLIFY;
 
 	RenderGraphDescription m_renderGraphDescr;
@@ -60,6 +59,10 @@ public:
 
 	Vec4 m_unprojParams;
 
+	ClusterBinOut m_clusterBinOut;
+
+	StagingGpuMemoryToken m_lightShadingUniformsToken;
+
 	RenderingContext(const StackAllocator<U8>& alloc)
 		: m_tempAllocator(alloc)
 		, m_renderGraphDescr(alloc)
@@ -190,7 +193,7 @@ public:
 	}
 
 	/// Init the renderer.
-	ANKI_USE_RESULT Error init(ThreadPool* threadpool,
+	ANKI_USE_RESULT Error init(ThreadHive* hive,
 		ResourceManager* resources,
 		GrManager* gr,
 		StagingGpuMemoryManager* stagingMem,
@@ -274,11 +277,6 @@ anki_internal:
 		return *m_gr;
 	}
 
-	StagingGpuMemoryManager& getStagingGpuMemoryManager()
-	{
-		return *m_stagingMem;
-	}
-
 	HeapAllocator<U8> getAllocator() const
 	{
 		return m_alloc;
@@ -289,11 +287,6 @@ anki_internal:
 		return *m_resources;
 	}
 
-	ThreadPool& getThreadPool()
-	{
-		return *m_threadpool;
-	}
-
 	Timestamp getGlobalTimestamp() const
 	{
 		return *m_globTimestamp;
@@ -340,11 +333,34 @@ anki_internal:
 		return m_nearesetNearestSampler;
 	}
 
+	const Array<U32, 4>& getClusterCount() const
+	{
+		return m_clusterCount;
+	}
+
+	StagingGpuMemoryManager& getStagingGpuMemoryManager()
+	{
+		ANKI_ASSERT(m_stagingMem);
+		return *m_stagingMem;
+	}
+
+	ThreadHive& getThreadHive()
+	{
+		ANKI_ASSERT(m_threadHive);
+		return *m_threadHive;
+	}
+
+	const ThreadHive& getThreadHive() const
+	{
+		ANKI_ASSERT(m_threadHive);
+		return *m_threadHive;
+	}
+
 private:
-	ThreadPool* m_threadpool = nullptr;
 	ResourceManager* m_resources = nullptr;
-	GrManager* m_gr = nullptr;
+	ThreadHive* m_threadHive = nullptr;
 	StagingGpuMemoryManager* m_stagingMem = nullptr;
+	GrManager* m_gr = nullptr;
 	UiManager* m_ui = nullptr;
 	Timestamp* m_globTimestamp;
 	HeapAllocator<U8> m_alloc;
@@ -371,6 +387,9 @@ private:
 	UniquePtr<UiStage> m_uiStage;
 	/// @}
 
+	Array<U32, 4> m_clusterCount;
+	ClusterBin m_clusterBin;
+
 	U32 m_width;
 	U32 m_height;
 
@@ -404,6 +423,8 @@ private:
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
 	void initJitteredMats();
+
+	void updateLightShadingUniforms(RenderingContext& ctx) const;
 };
 /// @}
 

+ 3 - 2
src/anki/renderer/RendererObject.cpp

@@ -6,6 +6,7 @@
 #include <anki/renderer/RendererObject.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/util/Enum.h>
+#include <anki/util/ThreadHive.h>
 
 namespace anki
 {
@@ -61,7 +62,7 @@ void RendererObject::bindStorage(CommandBufferPtr& cmdb, U set, U binding, const
 
 U32 RendererObject::computeNumberOfSecondLevelCommandBuffers(U32 drawcallCount) const
 {
-	const U drawcallsPerThread = drawcallCount / m_r->getThreadPool().getThreadCount();
+	const U drawcallsPerThread = drawcallCount / m_r->getThreadHive().getThreadCount();
 	U secondLevelCmdbCount;
 	if(drawcallsPerThread < MIN_DRAWCALLS_PER_2ND_LEVEL_COMMAND_BUFFER)
 	{
@@ -69,7 +70,7 @@ U32 RendererObject::computeNumberOfSecondLevelCommandBuffers(U32 drawcallCount)
 	}
 	else
 	{
-		secondLevelCmdbCount = m_r->getThreadPool().getThreadCount();
+		secondLevelCmdbCount = m_r->getThreadHive().getThreadCount();
 	}
 
 	return secondLevelCmdbCount;

+ 3 - 3
src/anki/renderer/ShadowMapping.cpp

@@ -9,7 +9,7 @@
 #include <anki/core/App.h>
 #include <anki/core/Trace.h>
 #include <anki/misc/ConfigSet.h>
-#include <anki/util/ThreadPool.h>
+#include <anki/util/ThreadHive.h>
 
 namespace anki
 {
@@ -226,7 +226,7 @@ void ShadowMapping::populateRenderGraph(RenderingContext& ctx)
 			m_scratchRt = rgraph.newRenderTarget(m_scratchRtDescr);
 			pass.setFramebufferInfo(m_scratchFbDescr, {}, m_scratchRt, minx, miny, width, height);
 			ANKI_ASSERT(
-				threadCountForScratchPass && threadCountForScratchPass <= m_r->getThreadPool().getThreadCount());
+				threadCountForScratchPass && threadCountForScratchPass <= m_r->getThreadHive().getThreadCount());
 			pass.setWork(runShadowmappingCallback, this, threadCountForScratchPass);
 
 			TextureSubresourceInfo subresource = TextureSubresourceInfo(DepthStencilAspectBit::DEPTH);
@@ -417,7 +417,7 @@ void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScra
 		for(U taskId = 0; taskId < threadCount; ++taskId)
 		{
 			PtrSize start, end;
-			ThreadPoolTask::choseStartEnd(taskId, threadCount, drawcallCount, start, end);
+			splitThreadedProblem(taskId, threadCount, drawcallCount, start, end);
 
 			// While there are drawcalls in this task emit new work items
 			U taskDrawcallCount = end - start;

+ 4 - 8
src/anki/renderer/Volumetric.cpp

@@ -8,7 +8,6 @@
 #include <anki/renderer/DepthDownscale.h>
 #include <anki/renderer/ShadowMapping.h>
 #include <anki/renderer/LightShading.h>
-#include <anki/renderer/LightBin.h>
 #include <anki/renderer/RenderQueue.h>
 
 namespace anki
@@ -27,10 +26,7 @@ Error Volumetric::initMain(const ConfigSet& config)
 
 	ShaderProgramResourceConstantValueInitList<3> consts(m_main.m_prog);
 	consts.add("FB_SIZE", UVec2(m_width, m_height))
-		.add("CLUSTER_COUNT",
-			UVec3(m_r->getLightShading().getLightBin().getClusterer().getClusterCountX(),
-				m_r->getLightShading().getLightBin().getClusterer().getClusterCountY(),
-				m_r->getLightShading().getLightBin().getClusterer().getClusterCountZ()))
+		.add("CLUSTER_COUNT", UVec3(m_r->getClusterCount()[0], m_r->getClusterCount()[1], m_r->getClusterCount()[2]))
 		.add("NOISE_MAP_SIZE", U32(m_main.m_noiseTex->getWidth()));
 
 	const ShaderProgramResourceVariant* variant;
@@ -135,8 +131,8 @@ void Volumetric::runMain(const RenderingContext& ctx, RenderPassWorkContext& rgr
 	rgraphCtx.bindColorTextureAndSampler(0, 2, m_runCtx.m_rts[(m_r->getFrameCount() + 1) & 1], m_r->getLinearSampler());
 	rgraphCtx.bindColorTextureAndSampler(0, 3, m_r->getShadowMapping().getShadowmapRt(), m_r->getLinearSampler());
 
-	const LightShadingResources& rsrc = m_r->getLightShading().getResources();
-	bindUniforms(cmdb, 0, 0, rsrc.m_commonUniformsToken);
+	const ClusterBinOut& rsrc = ctx.m_clusterBinOut;
+	bindUniforms(cmdb, 0, 0, ctx.m_lightShadingUniformsToken);
 	bindUniforms(cmdb, 0, 1, rsrc.m_pointLightsToken);
 	bindUniforms(cmdb, 0, 2, rsrc.m_spotLightsToken);
 
@@ -157,7 +153,7 @@ void Volumetric::runMain(const RenderingContext& ctx, RenderPassWorkContext& rgr
 	uniforms->m_fogParticleColorPad1 = Vec4(m_main.m_fogParticleColor, 0.0);
 
 	bindStorage(cmdb, 0, 0, rsrc.m_clustersToken);
-	bindStorage(cmdb, 0, 1, rsrc.m_lightIndicesToken);
+	bindStorage(cmdb, 0, 1, rsrc.m_indicesToken);
 
 	cmdb->bindShaderProgram(m_main.m_grProg);
 

+ 11 - 0
src/anki/util/Functions.h

@@ -226,6 +226,17 @@ inline void unflatten3dArrayIndex(const U sizeA, const U sizeB, const U sizeC, c
 	c = flatIdx % sizeC;
 }
 
+/// Given a threaded problem split it into smaller ones.
+inline void splitThreadedProblem(
+	PtrSize threadId, PtrSize threadCount, PtrSize problemSize, PtrSize& start, PtrSize& end)
+{
+	ANKI_ASSERT(threadCount > 0 && threadId < threadCount);
+	const PtrSize div = problemSize / threadCount;
+	start = threadId * div;
+	end = (threadId == threadCount - 1) ? problemSize : (threadId + 1u) * div;
+	ANKI_ASSERT(!(threadId == threadCount - 1 && end != problemSize));
+}
+
 /// Equivelent to static_cast.
 template<typename T, typename Y>
 inline T scast(Y from)

+ 0 - 10
src/anki/util/ThreadPool.h

@@ -29,16 +29,6 @@ public:
 	}
 
 	virtual Error operator()(U32 taskId, PtrSize threadsCount) = 0;
-
-	/// Chose a starting and end index
-	static void choseStartEnd(U32 taskId, PtrSize threadCount, PtrSize elementCount, PtrSize& start, PtrSize& end)
-	{
-		ANKI_ASSERT(threadCount > 0 && taskId < threadCount);
-		const PtrSize div = elementCount / threadCount;
-		start = taskId * div;
-		end = (taskId == threadCount - 1) ? elementCount : (taskId + 1u) * div;
-		ANKI_ASSERT(!(taskId == threadCount - 1 && end != elementCount));
-	}
 };
 
 /// Parallel task dispatcher. You feed it with tasks and sends them for execution in parallel and then waits for all to

+ 0 - 150
tests/renderer/Clusterer.cpp

@@ -1,150 +0,0 @@
-// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <tests/framework/Framework.h>
-#include <anki/renderer/Clusterer.h>
-#include <anki/Collision.h>
-#include <anki/util/ThreadPool.h>
-#include "anki/util/HighRezTimer.h"
-
-namespace anki
-{
-
-ANKI_TEST(Renderer, Clusterer)
-{
-	const U CLUSTER_COUNT_X = 32;
-	const U CLUSTER_COUNT_Y = 24;
-	const U CLUSTER_COUNT_Z = 32;
-	const U ITERATION_COUNT = 32;
-	const U SPHERE_COUNT = 1024;
-	const F32 SPHERE_MAX_RADIUS = 1000.0;
-	const F32 E = 0.01;
-	const U FRUSTUM_COUNT = 1024;
-	const F32 FRUSTUM_MAX_ANGLE = toRad(70.0);
-	const F32 FRUSTUM_MAX_DIST = 200.0;
-
-	HeapAllocator<U8> alloc(allocAligned, nullptr);
-
-	Clusterer c;
-	c.init(alloc, CLUSTER_COUNT_X, CLUSTER_COUNT_Y, CLUSTER_COUNT_Z);
-
-	PerspectiveFrustum fr(toRad(70.0), toRad(60.0), 0.1, 1000.0);
-	Mat4 projMat = fr.calculateProjectionMatrix();
-	Vec4 unprojParams = projMat.extractPerspectiveUnprojectionParams();
-
-	ThreadPool threadpool(4);
-
-	// Gen spheres
-	DynamicArrayAuto<Sphere> spheres(alloc);
-	spheres.create(SPHERE_COUNT);
-	DynamicArrayAuto<Aabb> sphereBoxes(alloc);
-	sphereBoxes.create(SPHERE_COUNT);
-	for(U i = 0; i < SPHERE_COUNT; ++i)
-	{
-		Vec2 ndc;
-		ndc.x() = clamp((i % 64) / 64.0f, E, 1.0f - E) * 2.0f - 1.0f;
-		ndc.y() = ndc.x();
-		F32 depth = clamp((i % 128) / 128.0f, E, 1.0f - E);
-
-		F32 z = unprojParams.z() / (unprojParams.w() + depth);
-		Vec2 xy = ndc.xy() * unprojParams.xy() * z;
-		Vec4 sphereC(xy, z, 0.0);
-
-		F32 radius = max((i % 64) / 64.0f, 0.1f) * SPHERE_MAX_RADIUS;
-
-		spheres[i] = Sphere(sphereC, radius);
-		spheres[i].computeAabb(sphereBoxes[i]);
-	}
-
-	// Bin spheres
-	HighRezTimer timer;
-	timer.start();
-	U clusterBinCount = 0;
-	for(U i = 0; i < ITERATION_COUNT; ++i)
-	{
-		Transform camTrf(Vec4(0.1, 0.1, 0.1, 0.0), Mat3x4::getIdentity(), 1.0);
-
-		ClustererPrepareInfo pinf;
-		pinf.m_viewMat = Mat4(camTrf).getInverse();
-		pinf.m_projMat = projMat;
-		pinf.m_camTrf = camTrf;
-
-		c.prepare(threadpool, pinf);
-		ClustererTestResult rez;
-		c.initTestResults(alloc, rez);
-
-		for(U s = 0; s < SPHERE_COUNT; ++s)
-		{
-			c.bin(spheres[s], sphereBoxes[s], rez);
-			ANKI_TEST_EXPECT_GT(rez.getClusterCount(), 0);
-			clusterBinCount += rez.getClusterCount();
-		}
-	}
-	timer.stop();
-	F64 ms = timer.getElapsedTime() * 1000.0;
-	printf("Cluster count: %u.\n"
-		   "Binned %f spheres/ms.\n"
-		   "Avg clusters per sphere %f\n",
-		unsigned(c.getClusterCount()),
-		F64(SPHERE_COUNT) * F64(ITERATION_COUNT) / ms,
-		clusterBinCount / F32(ITERATION_COUNT * SPHERE_COUNT));
-
-	// Gen spheres
-	DynamicArrayAuto<PerspectiveFrustum> frs(alloc);
-	frs.create(FRUSTUM_COUNT);
-	DynamicArrayAuto<Aabb> frBoxes(alloc);
-	frBoxes.create(FRUSTUM_COUNT);
-	for(U i = 0; i < FRUSTUM_COUNT; ++i)
-	{
-		Vec2 ndc;
-		ndc.x() = clamp((i % 64) / 64.0f, E, 1.0f - E) * 2.0f - 1.0f;
-		ndc.y() = ndc.x();
-		F32 depth = clamp((i % 128) / 128.0f, E, 1.0f - E);
-
-		F32 z = unprojParams.z() / (unprojParams.w() + depth);
-		Vec2 xy = ndc.xy() * unprojParams.xy() * z;
-		Vec4 c(xy, z, 0.0);
-
-		F32 dist = max((i % 64) / 64.0f, 0.1f) * FRUSTUM_MAX_DIST;
-		F32 ang = max((i % 64) / 64.0f, 0.2f) * FRUSTUM_MAX_ANGLE;
-
-		frs[i] = PerspectiveFrustum(ang, ang, 0.1, dist);
-		frs[i].transform(Transform(c, Mat3x4::getIdentity(), 1.0));
-
-		frs[i].computeAabb(frBoxes[i]);
-	}
-
-	// Bin frustums
-	timer.start();
-	clusterBinCount = 0;
-	for(U i = 0; i < ITERATION_COUNT; ++i)
-	{
-		Transform camTrf(Vec4(0.1, 0.1, 0.1, 0.0), Mat3x4::getIdentity(), 1.0);
-
-		ClustererPrepareInfo pinf;
-		pinf.m_viewMat = Mat4(camTrf).getInverse();
-		pinf.m_projMat = projMat;
-		pinf.m_camTrf = camTrf;
-
-		c.prepare(threadpool, pinf);
-		ClustererTestResult rez;
-		c.initTestResults(alloc, rez);
-
-		for(U s = 0; s < FRUSTUM_COUNT; ++s)
-		{
-			c.binPerspectiveFrustum(frs[s], frBoxes[s], rez);
-			ANKI_TEST_EXPECT_GT(rez.getClusterCount(), 0);
-			clusterBinCount += rez.getClusterCount();
-		}
-	}
-	timer.stop();
-	ms = timer.getElapsedTime() * 1000.0;
-	printf("Binned %f frustums/ms.\n"
-		   "Avg clusters per frustum %f\n",
-		F64(FRUSTUM_COUNT) * F64(ITERATION_COUNT) / ms,
-		clusterBinCount / F32(ITERATION_COUNT * FRUSTUM_COUNT));
-}
-
-} // end namespace anki

+ 2 - 2
tests/util/Thread.cpp

@@ -148,7 +148,7 @@ ANKI_TEST(Util, ThreadPool)
 
 	delete tp;
 
-	// Test choseStartEnd()
+	// Test splitThreadedProblem()
 	{
 		const U ITERATIONS = 100000;
 
@@ -160,7 +160,7 @@ ANKI_TEST(Util, ThreadPool)
 			for(U tid = 0; tid < threadCount; ++tid)
 			{
 				PtrSize start, end;
-				ThreadPoolTask::choseStartEnd(tid, threadCount, problemSize, start, end);
+				splitThreadedProblem(tid, threadCount, problemSize, start, end);
 
 				if(tid == 0)
 				{