Browse Source

More parallelization of the renderer

Panagiotis Christopoulos Charitos 10 years ago
parent
commit
cedccc8fa7

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

@@ -14,6 +14,7 @@ namespace anki
 // Forward
 class Renderer;
 class Ms;
+class Sm;
 class Is;
 class Fs;
 class Lf;

+ 8 - 17
include/anki/renderer/Drawer.h

@@ -5,8 +5,6 @@
 
 #pragma once
 
-#include <anki/util/StdTypes.h>
-#include <anki/util/Ptr.h>
 #include <anki/resource/RenderingKey.h>
 #include <anki/scene/Forward.h>
 #include <anki/Gr.h>
@@ -16,18 +14,11 @@ namespace anki
 
 // Forward
 class Renderer;
-struct RenderContext;
+class DrawContext;
 
 /// @addtogroup renderer
 /// @{
 
-/// The rendering stage
-enum class RenderingStage : U8
-{
-	MATERIAL,
-	BLEND
-};
-
 /// It includes all the functions to render a Renderable
 class RenderableDrawer
 {
@@ -42,20 +33,20 @@ public:
 
 	~RenderableDrawer();
 
-	ANKI_USE_RESULT Error render(FrustumComponent& frc,
-		RenderingStage stage,
-		Pass pass,
-		SArray<CommandBufferPtr>& cmdbs,
-		const UVec2& screenSize);
+	ANKI_USE_RESULT Error drawRange(Pass pass,
+		const FrustumComponent& frc,
+		CommandBufferPtr cmdb,
+		VisibleNode* begin,
+		VisibleNode* end);
 
 private:
 	Renderer* m_r;
 
-	void setupUniforms(RenderContext& ctx,
+	void setupUniforms(DrawContext& ctx,
 		const RenderComponent& renderable,
 		const RenderingKey& key);
 
-	ANKI_USE_RESULT Error renderSingle(RenderContext& ctx);
+	ANKI_USE_RESULT Error drawSingle(DrawContext& ctx);
 };
 /// @}
 

+ 7 - 2
include/anki/renderer/Fs.h

@@ -25,7 +25,13 @@ anki_internal:
 	~Fs();
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
-	ANKI_USE_RESULT Error run(RenderingContext& ctx);
+
+	void prepareBuildCommandBuffers(RenderingContext& ctx);
+
+	ANKI_USE_RESULT Error buildCommandBuffers(
+		RenderingContext& ctx, U threadId, U threadCount) const;
+
+	void run(RenderingContext& ctx);
 
 	TexturePtr getRt() const
 	{
@@ -35,7 +41,6 @@ anki_internal:
 private:
 	FramebufferPtr m_fb;
 	TexturePtr m_rt;
-
 	ResourceGroupPtr m_globalResources;
 };
 /// @}

+ 1 - 11
include/anki/renderer/Is.h

@@ -10,7 +10,6 @@
 #include <anki/resource/ShaderResource.h>
 #include <anki/Gr.h>
 #include <anki/Math.h>
-#include <anki/renderer/Sm.h>
 #include <anki/util/StdTypes.h>
 #include <anki/util/Array.h>
 #include <anki/core/Timestamp.h>
@@ -68,11 +67,6 @@ anki_internal:
 		m_ambientColor = color;
 	}
 
-	Sm& getSm()
-	{
-		return m_sm;
-	}
-
 	DynamicBufferToken getCommonVarsToken() const
 	{
 		return m_commonVarsToken;
@@ -118,9 +112,6 @@ private:
 	ShaderResourcePtr m_lightFrag;
 	PipelinePtr m_lightPpline;
 
-	/// Shadow mapping
-	Sm m_sm;
-
 	/// Opt because many ask for it
 	FrustumComponent* m_frc = nullptr;
 
@@ -151,8 +142,7 @@ private:
 	/// Prepare GL for rendering
 	void setState(CommandBufferPtr& cmdBuff);
 
-	void updateCommonBlock(
-		CommandBufferPtr& cmdBuff, const FrustumComponent& frc);
+	void updateCommonBlock(const FrustumComponent& frc);
 
 	// Binning
 	void binLights(U32 threadId, PtrSize threadsCount, TaskCommonData& data);

+ 8 - 2
include/anki/renderer/Ms.h

@@ -31,7 +31,14 @@ anki_internal:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
 
-	ANKI_USE_RESULT Error run(RenderingContext& ctx);
+	void prepareBuildCommandBuffers(RenderingContext& ctx)
+	{
+	}
+
+	ANKI_USE_RESULT Error buildCommandBuffers(
+		RenderingContext& ctx, U threadId, U threadCount) const;
+
+	void run(RenderingContext& ctx);
 
 	TexturePtr& getRt0()
 	{
@@ -80,7 +87,6 @@ private:
 	/// Create a G buffer FBO
 	ANKI_USE_RESULT Error createRt(U32 samples);
 };
-
 /// @}
 
 } // end namespace anki

+ 68 - 1
include/anki/renderer/Renderer.h

@@ -34,7 +34,60 @@ public:
 
 	CommandBufferPtr m_commandBuffer; ///< Primary command buffer.
 
-	DArray<CommandBufferPtr> m_fsCommandBuffers;
+	StackAllocator<U8> m_tempAllocator;
+
+	/// @name MS
+	/// @{
+	class Ms
+	{
+	public:
+		Array<CommandBufferPtr, ThreadPool::MAX_THREADS> m_commandBuffers;
+	} m_ms;
+	/// @}
+
+	/// @name Shadow mapping
+	/// @{
+	class Sm
+	{
+	public:
+		DArrayAuto<FramebufferPtr> m_spotFramebuffers;
+		DArrayAuto<Array<FramebufferPtr, 6>> m_omniFramebuffers;
+
+		/// [casterIdx][threadIdx]
+		DArrayAuto<CommandBufferPtr> m_spotCommandBuffers;
+		/// [casterIdx][threadIdx][faceIdx]
+		DArrayAuto<CommandBufferPtr> m_omniCommandBuffers;
+
+		DArrayAuto<SceneNode*> m_spots;
+		DArrayAuto<SceneNode*> m_omnis;
+
+		Sm(const StackAllocator<U8>& alloc)
+			: m_spotFramebuffers(alloc)
+			, m_omniFramebuffers(alloc)
+			, m_spotCommandBuffers(alloc)
+			, m_omniCommandBuffers(alloc)
+			, m_spots(alloc)
+			, m_omnis(alloc)
+		{
+		}
+	} m_sm;
+	/// @}
+
+	/// @name FS
+	/// @{
+	class Fs
+	{
+	public:
+		DynamicBufferInfo m_set1DynInfo;
+		Array<CommandBufferPtr, ThreadPool::MAX_THREADS> m_commandBuffers;
+	} m_fs;
+	/// @}
+
+	RenderingContext(const StackAllocator<U8>& alloc)
+		: m_tempAllocator(alloc)
+		, m_sm(alloc)
+	{
+	}
 };
 
 /// Offscreen renderer. It is a class and not a namespace because we may need
@@ -56,6 +109,16 @@ public:
 		return *m_ir;
 	}
 
+	Sm& getSm()
+	{
+		return *m_sm;
+	}
+
+	Bool getSmEnabled() const
+	{
+		return m_sm.isCreated();
+	}
+
 	Ms& getMs()
 	{
 		return *m_ms;
@@ -326,6 +389,7 @@ private:
 	/// @name Rendering stages
 	/// @{
 	UniquePtr<Ir> m_ir;
+	UniquePtr<Sm> m_sm; ///< Shadow mapping.
 	UniquePtr<Ms> m_ms; ///< Material rendering stage
 	UniquePtr<Is> m_is; ///< Illumination rendering stage
 	UniquePtr<Fs> m_fs; ///< Forward shading.
@@ -360,6 +424,9 @@ private:
 	UVec2 m_outputFbSize;
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
+
+	ANKI_USE_RESULT Error buildCommandBuffersSmMs(RenderingContext& ctx);
+	ANKI_USE_RESULT Error buildCommandBuffersFs(RenderingContext& ctx);
 };
 /// @}
 

+ 17 - 26
include/anki/renderer/Sm.h

@@ -20,11 +20,8 @@ class SceneNode;
 /// @{
 
 /// Shadowmapping pass
-class Sm : private RenderingPass
+class Sm : public RenderingPass
 {
-public:
-	static const U32 MAX_SHADOW_CASTERS = 16;
-
 anki_internal:
 	static const PixelFormat DEPTH_RT_PIXEL_FORMAT;
 
@@ -40,32 +37,25 @@ anki_internal:
 	}
 
 	ANKI_USE_RESULT Error init(const ConfigSet& initializer);
-	ANKI_USE_RESULT Error run(SArray<SceneNode*> spotShadowCasters,
-		SArray<SceneNode*> omniShadowCasters,
-		RenderingContext& ctx);
 
-	Bool getEnabled() const
-	{
-		return m_enabled;
-	}
+	void prepareBuildCommandBuffers(RenderingContext& ctx);
+
+	ANKI_USE_RESULT Error buildCommandBuffers(
+		RenderingContext& ctx, U threadId, U threadCount) const;
+
+	void run(RenderingContext& ctx);
 
 	Bool getPoissonEnabled() const
 	{
 		return m_poissonEnabled;
 	}
 
-	/// Get max shadow casters
-	U32 getMaxLightsCount()
-	{
-		return m_spots.getSize();
-	}
-
-	TexturePtr& getSpotTextureArray()
+	TexturePtr getSpotTextureArray() const
 	{
 		return m_spotTexArray;
 	}
 
-	TexturePtr& getOmniTextureArray()
+	TexturePtr getOmniTextureArray() const
 	{
 		return m_omniTexArray;
 	}
@@ -97,9 +87,6 @@ private:
 	DArray<ShadowmapSpot> m_spots;
 	DArray<ShadowmapOmni> m_omnis;
 
-	/// If false then disable SM at all
-	Bool8 m_enabled;
-
 	/// Enable Poisson for all the levels
 	Bool8 m_poissonEnabled = false;
 
@@ -116,11 +103,15 @@ private:
 	/// Check if a shadow pass can be skipped.
 	Bool skip(SceneNode& light, ShadowmapBase& sm);
 
-	ANKI_USE_RESULT Error doSpotLight(
-		SceneNode& light, CommandBufferPtr& cmdBuff);
+	ANKI_USE_RESULT Error doSpotLight(SceneNode& light,
+		CommandBufferPtr& cmdBuff,
+		U threadId,
+		U threadCount) const;
 
-	ANKI_USE_RESULT Error doOmniLight(
-		SceneNode& light, CommandBufferPtr& cmdBuff);
+	ANKI_USE_RESULT Error doOmniLight(SceneNode& light,
+		CommandBufferPtr cmdbs[],
+		U threadId,
+		U threadCount) const;
 };
 
 /// @}

+ 4 - 7
include/anki/scene/Visibility.h

@@ -77,14 +77,15 @@ public:
 /// The group of nodes that a VisibilityTestResults holds.
 enum class VisibilityGroupType
 {
-	RENDERABLES,
+	RENDERABLES_MS,
+	RENDERABLES_FS,
 	LIGHTS,
 	FLARES,
 	REFLECTION_PROBES,
 	REFLECTION_PROXIES,
 
 	TYPE_COUNT,
-	FIRST = RENDERABLES
+	FIRST = RENDERABLES_MS
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VisibilityGroupType, inline)
 
@@ -97,11 +98,7 @@ public:
 		ANKI_ASSERT(0 && "It's supposed to be deallocated on frame start");
 	}
 
-	void create(SceneFrameAllocator<U8> alloc,
-		U32 renderablesReservedSize,
-		U32 lightsReservedSize,
-		U32 lensFlaresReservedSize,
-		U32 reflectionProbesReservedSize);
+	void create(SceneFrameAllocator<U8> alloc);
 
 	void prepareMerge();
 

+ 5 - 5
src/core/Config.cpp

@@ -15,11 +15,11 @@ Config::Config()
 	// Renderer
 	//
 
-	newOption("is.sm.enabled", true);
-	newOption("is.sm.poissonEnabled", true);
-	newOption("is.sm.bilinearEnabled", true);
-	newOption("is.sm.resolution", 512);
-	newOption("is.sm.maxLights", 4);
+	newOption("sm.enabled", true);
+	newOption("sm.poissonEnabled", true);
+	newOption("sm.bilinearEnabled", true);
+	newOption("sm.resolution", 512);
+	newOption("sm.maxLights", 4);
 
 	newOption("is.groundLightEnabled", true);
 	newOption("is.maxPointLights", 384);

+ 3 - 1
src/core/Trace.cpp

@@ -48,7 +48,7 @@ static Array<const char*, U(TraceCounterType::COUNT)> counterNames = {
 		ANKI_LOGE("Error writing the trace file");                             \
 	}
 
-const U MAX_EVENTS_DEPTH = 10;
+const U MAX_EVENTS_DEPTH = 20;
 thread_local HighRezTimer::Scalar g_traceEventStartTime[MAX_EVENTS_DEPTH];
 thread_local I g_traceEventsInFlight = 0;
 
@@ -117,6 +117,8 @@ void TraceManager::startEvent()
 //==============================================================================
 void TraceManager::stopEvent(TraceEventType type)
 {
+	ANKI_ASSERT(g_traceEventsInFlight > 0 
+		&& g_traceEventsInFlight < I(MAX_EVENTS_DEPTH));
 	I i = --g_traceEventsInFlight;
 	ANKI_ASSERT(i >= 0 && i < I(MAX_EVENTS_DEPTH));
 	auto startedTime = g_traceEventStartTime[i];

+ 4 - 0
src/gr/gl/CommandBuffer.cpp

@@ -86,7 +86,9 @@ public:
 
 void CommandBuffer::setViewport(U16 minx, U16 miny, U16 maxx, U16 maxy)
 {
+#if ANKI_ASSERTS_ENABLED
 	m_impl->m_stateSet.m_viewport = true;
+#endif
 	m_impl->pushBackNewCommand<ViewportCommand>(minx, miny, maxx, maxy);
 }
 
@@ -121,7 +123,9 @@ public:
 
 void CommandBuffer::setPolygonOffset(F32 offset, F32 units)
 {
+#if ANKI_ASSERTS_ENABLED
 	m_impl->m_stateSet.m_polygonOffset = true;
+#endif
 	m_impl->pushBackNewCommand<SetPolygonOffsetCommand>(offset, units);
 }
 

+ 32 - 106
src/renderer/Drawer.cpp

@@ -25,27 +25,27 @@ namespace anki
 //==============================================================================
 
 /// Common stuff to pass to renderSingle
-struct RenderContext
+class DrawContext
 {
-	FrustumComponent* m_frc ANKI_DBG_NULLIFY_PTR;
-	RenderingStage m_stage;
+public:
+	const FrustumComponent* m_frc = nullptr;
 	Pass m_pass;
 	CommandBufferPtr m_cmdb;
-	const VisibleNode* m_visibleNode ANKI_DBG_NULLIFY_PTR;
-	const VisibleNode* m_nextVisibleNode ANKI_DBG_NULLIFY_PTR;
-	const MaterialVariant* m_variant ANKI_DBG_NULLIFY_PTR;
+
 	Array<Mat4, MAX_INSTANCES> m_cachedTrfs;
 	U m_cachedTrfCount = 0;
-	F32 m_flod;
+	const MaterialVariant* m_variant = nullptr;
 	DynamicBufferInfo m_dynBufferInfo;
-	UVec2 m_screenSize;
+	F32 m_flod = 0.0;
+	VisibleNode* m_visibleNode = nullptr;
+	VisibleNode* m_nextVisibleNode = nullptr;
 };
 
 /// Visitor that sets a uniform
 class SetupRenderableVariableVisitor
 {
 public:
-	RenderContext* m_ctx ANKI_DBG_NULLIFY_PTR;
+	DrawContext* m_ctx ANKI_DBG_NULLIFY_PTR;
 	const RenderableDrawer* m_drawer ANKI_DBG_NULLIFY_PTR;
 	SArray<U8> m_uniformBuffer;
 
@@ -195,60 +195,6 @@ void SetupRenderableVariableVisitor::uniSet<TextureResourcePtr>(
 	// Do nothing
 }
 
-/// Task to render a single node.
-class RenderTask : public ThreadPool::Task
-{
-public:
-	RenderableDrawer* m_drawer;
-	RenderContext m_ctx;
-
-	Error operator()(U32 threadId, PtrSize threadsCount) override
-	{
-		ANKI_TRACE_START_EVENT(RENDER_DRAWER);
-		VisibilityTestResults& vis = m_ctx.m_frc->getVisibilityTestResults();
-
-		PtrSize start, end;
-		U problemSize = vis.getEnd(VisibilityGroupType::RENDERABLES)
-			- vis.getBegin(VisibilityGroupType::RENDERABLES);
-		choseStartEnd(threadId, threadsCount, problemSize, start, end);
-
-		// Set the state of the command buffer
-		if(m_ctx.m_pass == Pass::SM)
-		{
-			m_ctx.m_cmdb->setPolygonOffset(1.0, 2.0);
-		}
-		else
-		{
-			m_ctx.m_cmdb->setPolygonOffset(0.0, 0.0);
-		}
-
-		m_ctx.m_cmdb->setViewport(
-			0, 0, m_ctx.m_screenSize.x(), m_ctx.m_screenSize.y());
-
-		for(U i = start; i < end; ++i)
-		{
-			m_ctx.m_visibleNode =
-				vis.getBegin(VisibilityGroupType::RENDERABLES) + i;
-
-			if(i + 1 < end)
-			{
-				m_ctx.m_nextVisibleNode =
-					vis.getBegin(VisibilityGroupType::RENDERABLES) + i + 1;
-			}
-			else
-			{
-				m_ctx.m_nextVisibleNode = nullptr;
-			}
-
-			ANKI_CHECK(m_drawer->renderSingle(m_ctx));
-		}
-
-		ANKI_TRACE_STOP_EVENT(RENDER_DRAWER);
-
-		return ErrorCode::NONE;
-	}
-};
-
 //==============================================================================
 // RenderableDrawer                                                            =
 //==============================================================================
@@ -259,7 +205,7 @@ RenderableDrawer::~RenderableDrawer()
 }
 
 //==============================================================================
-void RenderableDrawer::setupUniforms(RenderContext& ctx,
+void RenderableDrawer::setupUniforms(DrawContext& ctx,
 	const RenderComponent& renderable,
 	const RenderingKey& key)
 {
@@ -296,66 +242,46 @@ void RenderableDrawer::setupUniforms(RenderContext& ctx,
 }
 
 //==============================================================================
-Error RenderableDrawer::render(FrustumComponent& frc,
-	RenderingStage stage,
-	Pass pass,
-	SArray<CommandBufferPtr>& cmdbs,
-	const UVec2& screenSize)
+Error RenderableDrawer::drawRange(Pass pass,
+	const FrustumComponent& frc,
+	CommandBufferPtr cmdb,
+	VisibleNode* begin,
+	VisibleNode* end)
 {
-	Error err = ErrorCode::NONE;
-	ANKI_ASSERT(cmdbs.getSize() == m_r->getThreadPool().getThreadsCount()
-		|| cmdbs.getSize() == 1);
+	ANKI_ASSERT(begin && end && begin < end);
 
-	if(cmdbs.getSize() > 1)
+	DrawContext ctx;
+	ctx.m_frc = &frc;
+	ctx.m_pass = pass;
+	ctx.m_cmdb = cmdb;
+
+	for(; begin != end; ++begin)
 	{
-		Array<RenderTask, ThreadPool::MAX_THREADS> tasks;
+		ctx.m_visibleNode = begin;
 
-		ThreadPool& threadPool = m_r->getThreadPool();
-		for(U i = 0; i < threadPool.getThreadsCount(); i++)
+		if(begin + 1 < end)
+		{
+			ctx.m_nextVisibleNode = begin + 1;
+		}
+		else
 		{
-			auto& task = tasks[i];
-			task.m_drawer = this;
-			task.m_ctx.m_cmdb = cmdbs[i];
-			task.m_ctx.m_frc = &frc;
-			task.m_ctx.m_stage = stage;
-			task.m_ctx.m_pass = pass;
-			task.m_ctx.m_screenSize = screenSize;
-
-			threadPool.assignNewTask(i, &task);
+			ctx.m_nextVisibleNode = nullptr;
 		}
 
-		err = threadPool.waitForAllThreadsToFinish();
-	}
-	else
-	{
-		RenderTask task;
-		task.m_drawer = this;
-		task.m_ctx.m_cmdb = cmdbs[0];
-		task.m_ctx.m_frc = &frc;
-		task.m_ctx.m_stage = stage;
-		task.m_ctx.m_pass = pass;
-		task.m_ctx.m_screenSize = screenSize;
-
-		err = task(0, 1);
+		ANKI_CHECK(drawSingle(ctx));
 	}
 
-	return err;
+	return ErrorCode::NONE;
 }
 
 //==============================================================================
-Error RenderableDrawer::renderSingle(RenderContext& ctx)
+Error RenderableDrawer::drawSingle(DrawContext& ctx)
 {
 	// Get components
 	const RenderComponent& renderable =
 		ctx.m_visibleNode->m_node->getComponent<RenderComponent>();
 	const Material& mtl = renderable.getMaterial();
 
-	if((ctx.m_stage == RenderingStage::BLEND && !mtl.getForwardShading())
-		|| (ctx.m_stage == RenderingStage::MATERIAL && mtl.getForwardShading()))
-	{
-		return ErrorCode::NONE;
-	}
-
 	// Check if it can merge drawcalls
 	if(ctx.m_nextVisibleNode)
 	{

+ 60 - 20
src/renderer/Fs.cpp

@@ -7,6 +7,7 @@
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Ms.h>
 #include <anki/renderer/Is.h>
+#include <anki/renderer/Sm.h>
 #include <anki/scene/SceneGraph.h>
 #include <anki/scene/Camera.h>
 
@@ -45,10 +46,12 @@ Error Fs::init(const ConfigSet&)
 	{
 		ResourceGroupInitInfo init;
 		init.m_textures[0].m_texture = m_r->getMs().getDepthRt();
-		init.m_textures[1].m_texture =
-			m_r->getIs().getSm().getSpotTextureArray();
-		init.m_textures[2].m_texture =
-			m_r->getIs().getSm().getOmniTextureArray();
+
+		if(m_r->getSmEnabled())
+		{
+			init.m_textures[1].m_texture = m_r->getSm().getSpotTextureArray();
+			init.m_textures[2].m_texture = m_r->getSm().getOmniTextureArray();
+		}
 
 		init.m_storageBuffers[0].m_dynamic = true;
 		init.m_storageBuffers[1].m_dynamic = true;
@@ -64,31 +67,68 @@ Error Fs::init(const ConfigSet&)
 }
 
 //==============================================================================
-Error Fs::run(RenderingContext& ctx)
+void Fs::prepareBuildCommandBuffers(RenderingContext& ctx)
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-	cmdb->bindFramebuffer(m_fb);
-	cmdb->setViewport(0, 0, m_r->getWidth() / 2, m_r->getHeight() / 2);
-
-	FrustumComponent& camFr = *ctx.m_frustumComponent;
-
-	DynamicBufferInfo dyn;
+	DynamicBufferInfo& dyn = ctx.m_fs.m_set1DynInfo;
 	dyn.m_storageBuffers[0] = m_r->getIs().getCommonVarsToken();
 	dyn.m_storageBuffers[1] = m_r->getIs().getPointLightsToken();
 	dyn.m_storageBuffers[2] = m_r->getIs().getSpotLightsToken();
 	dyn.m_storageBuffers[3] = m_r->getIs().getClustersToken();
 	dyn.m_storageBuffers[4] = m_r->getIs().getLightIndicesToken();
+}
 
-	cmdb->bindResourceGroup(m_globalResources, 1, &dyn);
+//==============================================================================
+Error Fs::buildCommandBuffers(
+	RenderingContext& ctx, U threadId, U threadCount) const
+{
+	// Get some stuff
+	VisibilityTestResults& vis =
+		ctx.m_frustumComponent->getVisibilityTestResults();
 
-	SArray<CommandBufferPtr> cmdbs(&cmdb, 1);
-	ANKI_CHECK(m_r->getSceneDrawer().render(camFr,
-		RenderingStage::BLEND,
-		Pass::MS_FS,
-		cmdbs,
-		UVec2(m_r->getWidth() / 2, m_r->getHeight() / 2)));
+	U problemSize = vis.getCount(VisibilityGroupType::RENDERABLES_FS);
+	PtrSize start, end;
+	ThreadPool::Task::choseStartEnd(
+		threadId, threadCount, problemSize, start, end);
 
-	return ErrorCode::NONE;
+	if(start == end)
+	{
+		// Early exit
+		return ErrorCode::NONE;
+	}
+
+	// Create the command buffer and set some state
+	CommandBufferPtr cmdb = m_r->getGrManager().newInstance<CommandBuffer>();
+	ctx.m_fs.m_commandBuffers[threadId] = cmdb;
+
+	cmdb->setViewport(0, 0, m_r->getWidth() / 2, m_r->getHeight() / 2);
+	cmdb->setPolygonOffset(0.0, 0.0);
+	cmdb->bindResourceGroup(m_globalResources, 1, &ctx.m_fs.m_set1DynInfo);
+
+	// Start drawing
+	Error err = m_r->getSceneDrawer().drawRange(Pass::MS_FS,
+		*ctx.m_frustumComponent,
+		cmdb,
+		vis.getBegin(VisibilityGroupType::RENDERABLES_FS) + start,
+		vis.getBegin(VisibilityGroupType::RENDERABLES_FS) + end);
+
+	return err;
+}
+
+//==============================================================================
+void Fs::run(RenderingContext& ctx)
+{
+	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	cmdb->bindFramebuffer(m_fb);
+	cmdb->setViewport(0, 0, m_r->getWidth() / 2, m_r->getHeight() / 2);
+	cmdb->setPolygonOffset(0.0, 0.0);
+
+	for(U i = 0; i < m_r->getThreadPool().getThreadsCount(); ++i)
+	{
+		if(ctx.m_fs.m_commandBuffers[i].isCreated())
+		{
+			cmdb->pushSecondLevelCommandBuffer(ctx.m_fs.m_commandBuffers[i]);
+		}
+	}
 }
 
 } // end namespace anki

+ 2 - 6
src/renderer/Ir.cpp

@@ -179,12 +179,8 @@ Error Ir::init(const ConfigSet& config)
 	// Init the renderer
 	Config nestedRConfig;
 	nestedRConfig.set("dbg.enabled", false);
-	nestedRConfig.set("is.sm.bilinearEnabled", true);
 	nestedRConfig.set("is.groundLightEnabled", false);
-	nestedRConfig.set("is.sm.enabled", false);
-	nestedRConfig.set("is.sm.maxLights", 8);
-	nestedRConfig.set("is.sm.poissonEnabled", false);
-	nestedRConfig.set("is.sm.resolution", 16);
+	nestedRConfig.set("sm.enabled", false);
 	nestedRConfig.set("lf.maxFlares", 8);
 	nestedRConfig.set("pps.enabled", false);
 	nestedRConfig.set("renderingQuality", 1.0);
@@ -607,7 +603,7 @@ Error Ir::renderReflection(RenderingContext& ctx,
 	for(U i = 0; i < 6; ++i)
 	{
 		// Render
-		RenderingContext nestedCtx;
+		RenderingContext nestedCtx(ctx.m_tempAllocator);
 		nestedCtx.m_frustumComponent = frustumComponents[i];
 		nestedCtx.m_commandBuffer = cmdb;
 

+ 12 - 37
src/renderer/Is.cpp

@@ -6,6 +6,7 @@
 #include <anki/renderer/Is.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Ms.h>
+#include <anki/renderer/Sm.h>
 #include <anki/renderer/Pps.h>
 #include <anki/renderer/Ir.h>
 #include <anki/scene/Camera.h>
@@ -184,7 +185,6 @@ const PixelFormat Is::RT_PIXEL_FORMAT(
 //==============================================================================
 Is::Is(Renderer* r)
 	: RenderingPass(r)
-	, m_sm(r)
 {
 }
 
@@ -234,11 +234,6 @@ Error Is::initInternal(const ConfigSet& config)
 
 	m_maxLightIds *= m_r->getClusterCount();
 
-	//
-	// Init the passes
-	//
-	ANKI_CHECK(m_sm.init(config));
-
 	//
 	// Load the programs
 	//
@@ -265,7 +260,7 @@ Error Is::initInternal(const ConfigSet& config)
 		m_maxSpotLights,
 		m_maxLightIds,
 		m_groundLightEnabled,
-		m_sm.getPoissonEnabled(),
+		m_r->getSmEnabled() ? m_r->getSm().getPoissonEnabled() : 0,
 		m_r->getIrEnabled(),
 		(m_r->getIrEnabled()) ? m_r->getIr().getCubemapArrayMipmapCount() : 0);
 
@@ -314,8 +309,12 @@ Error Is::initInternal(const ConfigSet& config)
 		init.m_textures[1].m_texture = m_r->getMs().getRt1();
 		init.m_textures[2].m_texture = m_r->getMs().getRt2();
 		init.m_textures[3].m_texture = m_r->getMs().getDepthRt();
-		init.m_textures[4].m_texture = m_sm.getSpotTextureArray();
-		init.m_textures[5].m_texture = m_sm.getOmniTextureArray();
+
+		if(m_r->getSmEnabled())
+		{
+			init.m_textures[4].m_texture = m_r->getSm().getSpotTextureArray();
+			init.m_textures[5].m_texture = m_r->getSm().getOmniTextureArray();
+		}
 
 		init.m_storageBuffers[0].m_dynamic = true;
 		init.m_storageBuffers[1].m_dynamic = true;
@@ -353,10 +352,6 @@ Error Is::lightPass(RenderingContext& ctx)
 	//
 	U visiblePointLightsCount = 0;
 	U visibleSpotLightsCount = 0;
-	Array<SceneNode*, Sm::MAX_SHADOW_CASTERS> spotCasters;
-	Array<SceneNode*, Sm::MAX_SHADOW_CASTERS> omniCasters;
-	U spotCastersCount = 0;
-	U omniCastersCount = 0;
 
 	auto it = vi.getBegin(VisibilityGroupType::LIGHTS);
 	auto lend = vi.getEnd(VisibilityGroupType::LIGHTS);
@@ -368,19 +363,9 @@ Error Is::lightPass(RenderingContext& ctx)
 		{
 		case LightComponent::LightType::POINT:
 			++visiblePointLightsCount;
-
-			if(light.getShadowEnabled())
-			{
-				omniCasters[omniCastersCount++] = node;
-			}
 			break;
 		case LightComponent::LightType::SPOT:
 			++visibleSpotLightsCount;
-
-			if(light.getShadowEnabled())
-			{
-				spotCasters[spotCastersCount++] = node;
-			}
 			break;
 		default:
 			ANKI_ASSERT(0);
@@ -395,16 +380,6 @@ Error Is::lightPass(RenderingContext& ctx)
 	ANKI_TRACE_INC_COUNTER(
 		RENDERER_LIGHTS, visiblePointLightsCount + visibleSpotLightsCount);
 
-	//
-	// Do shadows pass
-	//
-	if(m_sm.getEnabled())
-	{
-		ANKI_CHECK(m_sm.run({&spotCasters[0], spotCastersCount},
-			{&omniCasters[0], omniCastersCount},
-			ctx));
-	}
-
 	//
 	// Write the lights and tiles UBOs
 	//
@@ -475,7 +450,7 @@ Error Is::lightPass(RenderingContext& ctx)
 	}
 
 	// Update uniforms
-	updateCommonBlock(cmdb, *m_frc);
+	updateCommonBlock(*m_frc);
 
 	// In the meantime set the state
 	setState(cmdb);
@@ -642,7 +617,7 @@ I Is::writePointLight(const LightComponent& lightc,
 		Vec4(pos.xyz(), 1.0 / (lightc.getRadius() * lightc.getRadius()));
 	slight.m_diffuseColorShadowmapId = lightc.getDiffuseColor();
 
-	if(!lightc.getShadowEnabled() || !m_sm.getEnabled())
+	if(!lightc.getShadowEnabled() || !m_r->getSmEnabled())
 	{
 		slight.m_diffuseColorShadowmapId.w() = INVALID_TEXTURE_INDEX;
 	}
@@ -673,7 +648,7 @@ I Is::writeSpotLight(const LightComponent& lightc,
 	ShaderSpotLight& light = task.m_spotLights[i];
 	F32 shadowmapIndex = INVALID_TEXTURE_INDEX;
 
-	if(lightc.getShadowEnabled() && m_sm.getEnabled())
+	if(lightc.getShadowEnabled() && m_r->getSmEnabled())
 	{
 		// Write matrix
 		static const Mat4 biasMat4(0.5,
@@ -800,7 +775,7 @@ Error Is::run(RenderingContext& ctx)
 }
 
 //==============================================================================
-void Is::updateCommonBlock(CommandBufferPtr& cmdb, const FrustumComponent& fr)
+void Is::updateCommonBlock(const FrustumComponent& fr)
 {
 	ShaderCommonUniforms* blk = static_cast<ShaderCommonUniforms*>(
 		getGrManager().allocateFrameHostVisibleMemory(

+ 2 - 2
src/renderer/MainRenderer.cpp

@@ -152,9 +152,9 @@ Error MainRenderer::render(SceneGraph& scene)
 	// Run renderer
 	m_r->getIs().setAmbientColor(scene.getAmbientColor());
 
-	RenderingContext ctx;
+	RenderingContext ctx(m_frameAlloc);
 	ctx.m_commandBuffer = cmdb;
-	ctx.m_frustumComponent = 
+	ctx.m_frustumComponent =
 		&scene.getActiveCamera().getComponent<FrustumComponent>();
 	ANKI_CHECK(m_r->render(ctx));
 

+ 42 - 23
src/renderer/Ms.cpp

@@ -116,45 +116,64 @@ Error Ms::initInternal(const ConfigSet& initializer)
 }
 
 //==============================================================================
-Error Ms::run(RenderingContext& ctx)
+Error Ms::buildCommandBuffers(
+	RenderingContext& ctx, U threadId, U threadCount) const
 {
 	ANKI_TRACE_START_EVENT(RENDER_MS);
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
-	// Create 2nd level cmdbs
-	U threadCount = m_r->getThreadPool().getThreadsCount();
-	GrManager& gr = m_r->getGrManager();
-	for(U i = 0; i < threadCount; ++i)
+	// Get some stuff
+	VisibilityTestResults& vis =
+		ctx.m_frustumComponent->getVisibilityTestResults();
+
+	U problemSize = vis.getCount(VisibilityGroupType::RENDERABLES_MS);
+	PtrSize start, end;
+	ThreadPool::Task::choseStartEnd(
+		threadId, threadCount, problemSize, start, end);
+
+	if(start == end)
 	{
-		// TODO Add hints
-		m_secondLevelCmdbs[i] = gr.newInstance<CommandBuffer>();
+		// Early exit
+		return ErrorCode::NONE;
 	}
 
+	// Create the command buffer and set some state
+	CommandBufferPtr cmdb = m_r->getGrManager().newInstance<CommandBuffer>();
+	ctx.m_ms.m_commandBuffers[threadId] = cmdb;
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
+	cmdb->setPolygonOffset(0.0, 0.0);
+
+	// Start drawing
+	Error err = m_r->getSceneDrawer().drawRange(Pass::MS_FS,
+		*ctx.m_frustumComponent,
+		cmdb,
+		vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
+		vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end);
+
+	ANKI_TRACE_STOP_EVENT(RENDER_MS);
+	return err;
+}
 
+//==============================================================================
+void Ms::run(RenderingContext& ctx)
+{
+	ANKI_TRACE_START_EVENT(RENDER_MS);
+
+	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	cmdb->bindFramebuffer(m_fb);
 
-	// render all
-	FrustumComponent& frc = *ctx.m_frustumComponent;
-	SArray<CommandBufferPtr> cmdbs(
-		&m_secondLevelCmdbs[0], m_secondLevelCmdbs.getSize());
-	ANKI_CHECK(m_r->getSceneDrawer().render(frc,
-		RenderingStage::MATERIAL,
-		Pass::MS_FS,
-		cmdbs,
-		UVec2(m_r->getWidth(), m_r->getHeight())));
-
-	for(U i = 0; i < m_secondLevelCmdbs.getSize(); ++i)
+	// Set some state anyway because other stages may depend on it
+	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
+	cmdb->setPolygonOffset(0.0, 0.0);
+
+	for(U i = 0; i < m_r->getThreadPool().getThreadsCount(); ++i)
 	{
-		if(!m_secondLevelCmdbs[i]->isEmpty())
+		if(ctx.m_ms.m_commandBuffers[i].isCreated())
 		{
-			cmdb->pushSecondLevelCommandBuffer(m_secondLevelCmdbs[i]);
-			m_secondLevelCmdbs[i].reset(nullptr);
+			cmdb->pushSecondLevelCommandBuffer(ctx.m_ms.m_commandBuffers[i]);
 		}
 	}
 
 	ANKI_TRACE_STOP_EVENT(RENDER_MS);
-	return ErrorCode::NONE;
 }
 
 } // end namespace anki

+ 93 - 4
src/renderer/Renderer.cpp

@@ -12,6 +12,7 @@
 #include <anki/renderer/Ir.h>
 #include <anki/renderer/Ms.h>
 #include <anki/renderer/Is.h>
+#include <anki/renderer/Sm.h>
 #include <anki/renderer/Pps.h>
 #include <anki/renderer/Ssao.h>
 #include <anki/renderer/Sslf.h>
@@ -144,6 +145,12 @@ Error Renderer::initInternal(const ConfigSet& config)
 	m_ms.reset(m_alloc.newInstance<Ms>(this));
 	ANKI_CHECK(m_ms->init(config));
 
+	if(config.getNumber("sm.enabled"))
+	{
+		m_sm.reset(m_alloc.newInstance<Sm>(this));
+		ANKI_CHECK(m_sm->init(config));
+	}
+
 	m_tiler.reset(m_alloc.newInstance<Tiler>(this));
 	ANKI_CHECK(m_tiler->init());
 
@@ -236,18 +243,24 @@ Error Renderer::render(RenderingContext& ctx)
 		ANKI_CHECK(m_ir->run(ctx));
 	}
 
-	ANKI_CHECK(m_ms->run(ctx));
+	ANKI_CHECK(buildCommandBuffersSmMs(ctx));
 
-	m_lf->runOcclusionTests(ctx);
+	if(m_sm)
+	{
+		m_sm->run(ctx);
+	}
+
+	m_ms->run(ctx);
 
-	// m_tiler->run(cmdb);
+	m_lf->runOcclusionTests(ctx);
 
 	ANKI_CHECK(m_is->run(ctx));
 
 	cmdb->generateMipmaps(m_ms->getDepthRt());
 	cmdb->generateMipmaps(m_ms->getRt2());
 
-	ANKI_CHECK(m_fs->run(ctx));
+	ANKI_CHECK(buildCommandBuffersFs(ctx));
+	m_fs->run(ctx);
 	m_lf->run(ctx);
 
 	m_upsample->run(ctx);
@@ -387,4 +400,80 @@ Bool Renderer::doGpuVisibilityTest(
 	return m_tiler->test(cs, aabb);
 }
 
+//==============================================================================
+Error Renderer::buildCommandBuffersSmMs(RenderingContext& ctx)
+{
+	// Prepare
+	m_ms->prepareBuildCommandBuffers(ctx);
+	if(m_sm)
+	{
+		m_sm->prepareBuildCommandBuffers(ctx);
+	}
+
+	// Build
+	class Task : public ThreadPool::Task
+	{
+	public:
+		Renderer* m_r ANKI_DBG_NULLIFY_PTR;
+		RenderingContext* m_ctx ANKI_DBG_NULLIFY_PTR;
+
+		Error operator()(U32 threadId, PtrSize threadsCount)
+		{
+			ANKI_CHECK(m_r->getMs().buildCommandBuffers(
+				*m_ctx, threadId, threadsCount));
+
+			if(m_r->getSmEnabled())
+			{
+				ANKI_CHECK(m_r->getSm().buildCommandBuffers(
+					*m_ctx, threadId, threadsCount));
+			}
+
+			return ErrorCode::NONE;
+		}
+	};
+
+	ThreadPool& threadPool = getThreadPool();
+	Task task;
+	task.m_r = this;
+	task.m_ctx = &ctx;
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	{
+		threadPool.assignNewTask(i, &task);
+	}
+
+	return threadPool.waitForAllThreadsToFinish();
+}
+
+//==============================================================================
+Error Renderer::buildCommandBuffersFs(RenderingContext& ctx)
+{
+	m_fs->prepareBuildCommandBuffers(ctx);
+
+	class Task : public ThreadPool::Task
+	{
+	public:
+		Renderer* m_r ANKI_DBG_NULLIFY_PTR;
+		RenderingContext* m_ctx ANKI_DBG_NULLIFY_PTR;
+
+		Error operator()(U32 threadId, PtrSize threadsCount)
+		{
+			ANKI_CHECK(m_r->getFs().buildCommandBuffers(
+				*m_ctx, threadId, threadsCount));
+
+			return ErrorCode::NONE;
+		}
+	};
+
+	ThreadPool& threadPool = getThreadPool();
+	Task task;
+	task.m_r = this;
+	task.m_ctx = &ctx;
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	{
+		threadPool.assignNewTask(i, &task);
+	}
+
+	return threadPool.waitForAllThreadsToFinish();
+}
+
 } // end namespace anki

+ 214 - 67
src/renderer/Sm.cpp

@@ -22,32 +22,20 @@ const PixelFormat Sm::DEPTH_RT_PIXEL_FORMAT(
 //==============================================================================
 Error Sm::init(const ConfigSet& config)
 {
-	m_enabled = config.getNumber("is.sm.enabled");
-
-	if(!m_enabled)
-	{
-		return ErrorCode::NONE;
-	}
-
-	m_poissonEnabled = config.getNumber("is.sm.poissonEnabled");
-	m_bilinearEnabled = config.getNumber("is.sm.bilinearEnabled");
-	m_resolution = config.getNumber("is.sm.resolution");
+	m_poissonEnabled = config.getNumber("sm.poissonEnabled");
+	m_bilinearEnabled = config.getNumber("sm.bilinearEnabled");
+	m_resolution = config.getNumber("sm.resolution");
 
 	//
 	// Init the shadowmaps
 	//
-	if(config.getNumber("is.sm.maxLights") > MAX_SHADOW_CASTERS)
-	{
-		ANKI_LOGE("Too many shadow casters");
-		return ErrorCode::FUNCTION_FAILED;
-	}
 
 	// Create shadowmaps array
 	TextureInitInfo sminit;
 	sminit.m_type = TextureType::_2D_ARRAY;
 	sminit.m_width = m_resolution;
 	sminit.m_height = m_resolution;
-	sminit.m_depth = config.getNumber("is.sm.maxLights");
+	sminit.m_depth = config.getNumber("sm.maxLights");
 	sminit.m_format = DEPTH_RT_PIXEL_FORMAT;
 	sminit.m_mipmapsCount = 1;
 	sminit.m_sampling.m_minMagFilter =
@@ -60,7 +48,7 @@ Error Sm::init(const ConfigSet& config)
 	m_omniTexArray = getGrManager().newInstance<Texture>(sminit);
 
 	// Init 2D layers
-	m_spots.create(getAllocator(), config.getNumber("is.sm.maxLights"));
+	m_spots.create(getAllocator(), config.getNumber("sm.maxLights"));
 
 	FramebufferInitInfo fbInit;
 	fbInit.m_depthStencilAttachment.m_texture = m_spotTexArray;
@@ -80,7 +68,7 @@ Error Sm::init(const ConfigSet& config)
 	}
 
 	// Init cube layers
-	m_omnis.create(getAllocator(), config.getNumber("is.sm.maxLights"));
+	m_omnis.create(getAllocator(), config.getNumber("sm.maxLights"));
 
 	fbInit.m_depthStencilAttachment.m_texture = m_omniTexArray;
 
@@ -104,33 +92,48 @@ Error Sm::init(const ConfigSet& config)
 }
 
 //==============================================================================
-Error Sm::run(SArray<SceneNode*> spotShadowCasters,
-	SArray<SceneNode*> omniShadowCasters,
-	RenderingContext& ctx)
+void Sm::run(RenderingContext& ctx)
 {
-	ANKI_ASSERT(m_enabled);
 	ANKI_TRACE_START_EVENT(RENDER_SM);
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
-	if(omniShadowCasters.getSize() > m_omnis.getSize()
-		|| spotShadowCasters.getSize() > m_spots.getSize())
-	{
-		ANKI_LOGW("Too many shadow casters");
-	}
+	const U threadCount = m_r->getThreadPool().getThreadsCount();
 
-	// render all
-	for(SceneNode* node : spotShadowCasters)
+	// Spot lights
+	for(U i = 0; i < ctx.m_sm.m_spots.getSize(); ++i)
 	{
-		ANKI_CHECK(doSpotLight(*node, cmdb));
+		cmdb->bindFramebuffer(ctx.m_sm.m_spotFramebuffers[i]);
+		for(U j = 0; j < threadCount; ++j)
+		{
+			U idx = i * threadCount + j;
+			CommandBufferPtr& cmdb2 = ctx.m_sm.m_spotCommandBuffers[idx];
+			if(cmdb2.isCreated())
+			{
+				cmdb->pushSecondLevelCommandBuffer(cmdb2);
+			}
+		}
 	}
 
-	for(SceneNode* node : omniShadowCasters)
+	// Omni lights
+	for(U i = 0; i < ctx.m_sm.m_omnis.getSize(); ++i)
 	{
-		ANKI_CHECK(doOmniLight(*node, cmdb));
+		for(U j = 0; j < 6; ++j)
+		{
+			cmdb->bindFramebuffer(ctx.m_sm.m_omniFramebuffers[i][j]);
+
+			for(U k = 0; k < threadCount; ++k)
+			{
+				U idx = i * threadCount * 6 + k * 6 + j;
+				CommandBufferPtr& cmdb2 = ctx.m_sm.m_omniCommandBuffers[idx];
+				if(cmdb2.isCreated())
+				{
+					cmdb->pushSecondLevelCommandBuffer(cmdb2);
+				}
+			}
+		}
 	}
 
 	ANKI_TRACE_STOP_EVENT(RENDER_SM);
-	return ErrorCode::NONE;
 }
 
 //==============================================================================
@@ -196,71 +199,215 @@ Bool Sm::skip(SceneNode& light, ShadowmapBase& sm)
 	if(shouldUpdate)
 	{
 		sm.m_timestamp = m_r->getGlobalTimestamp();
-		LightComponent& lcomp = light.getComponent<LightComponent>();
-		lcomp.setShadowMapIndex(sm.m_layerId);
 	}
 
+	// Update the layer ID anyway
+	LightComponent& lcomp = light.getComponent<LightComponent>();
+	lcomp.setShadowMapIndex(sm.m_layerId);
+
 	return !shouldUpdate;
 }
 
 //==============================================================================
-Error Sm::doSpotLight(SceneNode& light, CommandBufferPtr& cmdBuff)
+Error Sm::buildCommandBuffers(
+	RenderingContext& ctx, U threadId, U threadCount) const
 {
-	ShadowmapSpot* sm;
-	bestCandidate(light, m_spots, sm);
+	ANKI_TRACE_START_EVENT(RENDER_SM);
 
-	if(skip(light, *sm))
+	for(U i = 0; i < ctx.m_sm.m_spots.getSize(); ++i)
 	{
-		return ErrorCode::NONE;
+		U idx = i * threadCount + threadId;
+
+		ANKI_CHECK(doSpotLight(*ctx.m_sm.m_spots[i],
+			ctx.m_sm.m_spotCommandBuffers[idx],
+			threadId,
+			threadCount));
 	}
 
-	cmdBuff->bindFramebuffer(sm->m_fb);
-	cmdBuff->setViewport(0, 0, m_resolution, m_resolution);
+	for(U i = 0; i < ctx.m_sm.m_omnis.getSize(); ++i)
+	{
+		U idx = i * threadCount * 6 + threadId * 6 + 0;
 
-	FrustumComponent& fr = light.getComponent<FrustumComponent>();
-	SArray<CommandBufferPtr> cmdbs(&cmdBuff, 1);
-	ANKI_CHECK(m_r->getSceneDrawer().render(fr,
-		RenderingStage::MATERIAL,
-		Pass::SM,
-		cmdbs,
-		UVec2(m_resolution, m_resolution)));
+		ANKI_CHECK(doOmniLight(*ctx.m_sm.m_omnis[i],
+			&ctx.m_sm.m_omniCommandBuffers[idx],
+			threadId,
+			threadCount));
+	}
 
-	ANKI_TRACE_INC_COUNTER(RENDERER_SHADOW_PASSES, 1);
+	ANKI_TRACE_STOP_EVENT(RENDER_SM);
 	return ErrorCode::NONE;
 }
 
 //==============================================================================
-Error Sm::doOmniLight(SceneNode& light, CommandBufferPtr& cmdBuff)
+Error Sm::doSpotLight(
+	SceneNode& light, CommandBufferPtr& cmdb, U threadId, U threadCount) const
 {
-	ShadowmapOmni* sm;
-	bestCandidate(light, m_omnis, sm);
-
-	if(skip(light, *sm))
+	FrustumComponent& frc = light.getComponent<FrustumComponent>();
+	VisibilityTestResults& vis = frc.getVisibilityTestResults();
+	U problemSize = vis.getCount(VisibilityGroupType::RENDERABLES_MS);
+	PtrSize start, end;
+	ThreadPool::Task::choseStartEnd(
+		threadId, threadCount, problemSize, start, end);
+
+	if(start == end)
 	{
 		return ErrorCode::NONE;
 	}
 
-	cmdBuff->setViewport(0, 0, m_resolution, m_resolution);
+	cmdb = m_r->getGrManager().newInstance<CommandBuffer>();
+	cmdb->setViewport(0, 0, m_resolution, m_resolution);
+	cmdb->setPolygonOffset(1.0, 2.0);
+
+	Error err = m_r->getSceneDrawer().drawRange(Pass::SM,
+		frc,
+		cmdb,
+		vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
+		vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end);
+
+	return err;
+}
+
+//==============================================================================
+Error Sm::doOmniLight(
+	SceneNode& light, CommandBufferPtr cmdbs[], U threadId, U threadCount) const
+{
 	U frCount = 0;
 
 	Error err = light.iterateComponentsOfType<FrustumComponent>(
-		[&](FrustumComponent& fr) -> Error {
-			cmdBuff->bindFramebuffer(sm->m_fb[frCount]);
-
-			SArray<CommandBufferPtr> cmdbs(&cmdBuff, 1);
-			ANKI_CHECK(m_r->getSceneDrawer().render(fr,
-				RenderingStage::MATERIAL,
-				Pass::SM,
-				cmdbs,
-				UVec2(m_resolution, m_resolution)));
+		[&](FrustumComponent& frc) -> Error {
+			VisibilityTestResults& vis = frc.getVisibilityTestResults();
+			U problemSize = vis.getCount(VisibilityGroupType::RENDERABLES_MS);
+			PtrSize start, end;
+			ThreadPool::Task::choseStartEnd(
+				threadId, threadCount, problemSize, start, end);
+
+			if(start != end)
+			{
+				cmdbs[frCount] = getGrManager().newInstance<CommandBuffer>();
+				cmdbs[frCount]->setViewport(0, 0, m_resolution, m_resolution);
+				cmdbs[frCount]->setPolygonOffset(1.0, 2.0);
+
+				ANKI_CHECK(m_r->getSceneDrawer().drawRange(Pass::SM,
+					frc,
+					cmdbs[frCount],
+					vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + start,
+					vis.getBegin(VisibilityGroupType::RENDERABLES_MS) + end));
+			}
 
 			++frCount;
 			return ErrorCode::NONE;
 		});
 
-	ANKI_TRACE_INC_COUNTER(RENDERER_SHADOW_PASSES, 6);
-
 	return err;
 }
 
+//==============================================================================
+void Sm::prepareBuildCommandBuffers(RenderingContext& ctx)
+{
+	ANKI_TRACE_START_EVENT(RENDER_SM);
+
+	// Gather the lights
+	VisibilityTestResults& vi =
+		ctx.m_frustumComponent->getVisibilityTestResults();
+
+	const U MAX = 64;
+	Array<SceneNode*, MAX> spotCasters;
+	Array<SceneNode*, MAX> omniCasters;
+	U spotCastersCount = 0;
+	U omniCastersCount = 0;
+
+	auto it = vi.getBegin(VisibilityGroupType::LIGHTS);
+	const auto lend = vi.getEnd(VisibilityGroupType::LIGHTS);
+	for(; it != lend; ++it)
+	{
+		SceneNode* node = (*it).m_node;
+		LightComponent& light = node->getComponent<LightComponent>();
+		switch(light.getLightType())
+		{
+		case LightComponent::LightType::POINT:
+			if(light.getShadowEnabled())
+			{
+				ShadowmapOmni* sm;
+				bestCandidate(*node, m_omnis, sm);
+
+				if(!skip(*node, *sm))
+				{
+					omniCasters[omniCastersCount++] = node;
+				}
+			}
+			break;
+		case LightComponent::LightType::SPOT:
+			if(light.getShadowEnabled())
+			{
+				ShadowmapSpot* sm;
+				bestCandidate(*node, m_spots, sm);
+
+				if(!skip(*node, *sm))
+				{
+					spotCasters[spotCastersCount++] = node;
+				}
+			}
+			break;
+		default:
+			ANKI_ASSERT(0);
+			break;
+		}
+	}
+
+	if(omniCastersCount > m_omnis.getSize()
+		|| spotCastersCount > m_spots.getSize())
+	{
+		ANKI_LOGW("Too many shadow casters");
+		omniCastersCount = min(omniCastersCount, m_omnis.getSize());
+		spotCastersCount = min(spotCastersCount, m_spots.getSize());
+	}
+
+	if(spotCastersCount > 0)
+	{
+		ctx.m_sm.m_spots.create(spotCastersCount);
+		memcpy(&ctx.m_sm.m_spots[0],
+			&spotCasters[0],
+			sizeof(SceneNode*) * spotCastersCount);
+
+		ctx.m_sm.m_spotCommandBuffers.create(
+			spotCastersCount * m_r->getThreadPool().getThreadsCount());
+
+		ctx.m_sm.m_spotFramebuffers.create(spotCastersCount);
+		for(U i = 0; i < spotCastersCount; ++i)
+		{
+			const LightComponent& lightc =
+				ctx.m_sm.m_spots[i]->getComponent<LightComponent>();
+			U idx = lightc.getShadowMapIndex();
+
+			ctx.m_sm.m_spotFramebuffers[i] = m_spots[idx].m_fb;
+		}
+	}
+
+	if(omniCastersCount > 0)
+	{
+		ctx.m_sm.m_omnis.create(omniCastersCount);
+		memcpy(&ctx.m_sm.m_omnis[0],
+			&omniCasters[0],
+			sizeof(SceneNode*) * omniCastersCount);
+
+		ctx.m_sm.m_omniCommandBuffers.create(
+			omniCastersCount * 6 * m_r->getThreadPool().getThreadsCount());
+
+		ctx.m_sm.m_omniFramebuffers.create(omniCastersCount);
+		for(U i = 0; i < omniCastersCount; ++i)
+		{
+			const LightComponent& lightc =
+				ctx.m_sm.m_omnis[i]->getComponent<LightComponent>();
+			U idx = lightc.getShadowMapIndex();
+
+			for(U j = 0; j < 6; ++j)
+			{
+				ctx.m_sm.m_omniFramebuffers[i][j] = m_omnis[idx].m_fb[j];
+			}
+		}
+	}
+
+	ANKI_TRACE_STOP_EVENT(RENDER_SM);
+}
+
 } // end namespace anki

+ 1 - 1
src/scene/FrustumComponent.cpp

@@ -31,7 +31,7 @@ void FrustumComponent::setVisibilityTestResults(VisibilityTestResults* visible)
 	m_visible = visible;
 
 	m_stats.m_renderablesCount =
-		visible->getCount(VisibilityGroupType::RENDERABLES);
+		visible->getCount(VisibilityGroupType::RENDERABLES_MS);
 	m_stats.m_lightsCount = visible->getCount(VisibilityGroupType::LIGHTS);
 }
 

+ 17 - 23
src/scene/Visibility.cpp

@@ -153,10 +153,7 @@ void VisibilityTestTask::test(
 	// Init test results
 	VisibilityTestResults* visible = alloc.newInstance<VisibilityTestResults>();
 
-	FrustumComponent::VisibilityStats stats =
-		testedFrc.getLastVisibilityStats();
-
-	visible->create(alloc, stats.m_renderablesCount, stats.m_lightsCount, 4, 4);
+	visible->create(alloc);
 
 	m_shared->m_testResults[threadId] = visible;
 
@@ -331,8 +328,11 @@ void VisibilityTestTask::test(
 			if(wantsRenderComponents
 				|| (wantsShadowCasters && rc->getCastsShadow()))
 			{
-				visible->moveBack(
-					alloc, VisibilityGroupType::RENDERABLES, visibleNode);
+				visible->moveBack(alloc,
+					rc->getMaterial().getForwardShading()
+						? VisibilityGroupType::RENDERABLES_FS
+						: VisibilityGroupType::RENDERABLES_MS,
+					visibleNode);
 
 				if(wantsShadowCasters)
 				{
@@ -436,8 +436,13 @@ void VisibilityTestTask::combineTestResults(
 
 	// Sort some of the arrays
 	DistanceSortFunctor comp;
-	std::sort(visible->getBegin(VisibilityGroupType::RENDERABLES),
-		visible->getEnd(VisibilityGroupType::RENDERABLES),
+	std::sort(visible->getBegin(VisibilityGroupType::RENDERABLES_MS),
+		visible->getEnd(VisibilityGroupType::RENDERABLES_MS),
+		comp);
+
+	// TODO: Reverse the sort
+	std::sort(visible->getBegin(VisibilityGroupType::RENDERABLES_FS),
+		visible->getEnd(VisibilityGroupType::RENDERABLES_FS),
 		comp);
 
 	std::sort(visible->getBegin(VisibilityGroupType::REFLECTION_PROBES),
@@ -450,22 +455,11 @@ void VisibilityTestTask::combineTestResults(
 //==============================================================================
 
 //==============================================================================
-void VisibilityTestResults::create(SceneFrameAllocator<U8> alloc,
-	U32 renderablesReservedSize,
-	U32 lightsReservedSize,
-	U32 lensFlaresReservedSize,
-	U32 reflectionProbesReservedSize)
+void VisibilityTestResults::create(SceneFrameAllocator<U8> alloc)
 {
-	m_groups[VisibilityGroupType::RENDERABLES].m_nodes.create(
-		alloc, renderablesReservedSize);
-	m_groups[VisibilityGroupType::LIGHTS].m_nodes.create(
-		alloc, lightsReservedSize);
-	m_groups[VisibilityGroupType::FLARES].m_nodes.create(
-		alloc, lensFlaresReservedSize);
-	m_groups[VisibilityGroupType::REFLECTION_PROBES].m_nodes.create(
-		alloc, reflectionProbesReservedSize);
-	m_groups[VisibilityGroupType::REFLECTION_PROXIES].m_nodes.create(
-		alloc, reflectionProbesReservedSize);
+	m_groups[VisibilityGroupType::RENDERABLES_MS].m_nodes.create(alloc, 64);
+	m_groups[VisibilityGroupType::RENDERABLES_FS].m_nodes.create(alloc, 8);
+	m_groups[VisibilityGroupType::LIGHTS].m_nodes.create(alloc, 32);
 }
 
 //==============================================================================

+ 5 - 5
testapp/Main.cpp

@@ -495,12 +495,12 @@ Error initSubsystems(int argc, char* argv[])
 	// Config
 	Config config;
 	config.set("dbg.enabled", false);
-	config.set("is.sm.bilinearEnabled", true);
+	config.set("sm.bilinearEnabled", true);
 	config.set("is.groundLightEnabled", false);
-	config.set("is.sm.enabled", true);
-	config.set("is.sm.maxLights", 16);
-	config.set("is.sm.poissonEnabled", true);
-	config.set("is.sm.resolution", 1024);
+	config.set("sm.enabled", true);
+	config.set("sm.maxLights", 16);
+	config.set("sm.poissonEnabled", true);
+	config.set("sm.resolution", 1024);
 	config.set("lf.maxFlares", 32);
 	config.set("pps.enabled", true);
 	config.set("bloom.enabled", true);