浏览代码

Parallelizing the drawer

Panagiotis Christopoulos Charitos 10 年之前
父节点
当前提交
6aef69e3ae

+ 6 - 3
CMakeLists.txt

@@ -39,6 +39,7 @@ endif()
 
 
 option(ANKI_OPTIMIZE "Optimize" ON)
 option(ANKI_OPTIMIZE "Optimize" ON)
 option(ANKI_DEBUG_SYMBOLS "Debug symbols" OFF)
 option(ANKI_DEBUG_SYMBOLS "Debug symbols" OFF)
+option(ANKI_DEBUG "Debugging checks" OFF)
 
 
 option(ANKI_BUILD_TOOLS "Build tools" ON)
 option(ANKI_BUILD_TOOLS "Build tools" ON)
 option(ANKI_BUILD_TESTS "Build unit tests" OFF)
 option(ANKI_BUILD_TESTS "Build unit tests" OFF)
@@ -121,10 +122,12 @@ if(ANKI_ENABLE_SIMD)
 	endif()
 	endif()
 endif()
 endif()
 
 
-# Add debug sumbols
+# Add debug
 if(ANKI_DEBUG_SYMBOLS)
 if(ANKI_DEBUG_SYMBOLS)
 	set(COMPILER_FLAGS "${COMPILER_FLAGS} -g3 ")
 	set(COMPILER_FLAGS "${COMPILER_FLAGS} -g3 ")
+endif()
 
 
+if(ANKI_DEBUG)
 	if(${CMAKE_C_COMPILER_ID} MATCHES "GNU")
 	if(${CMAKE_C_COMPILER_ID} MATCHES "GNU")
 		set(COMPILER_FLAGS "${COMPILER_FLAGS} -fstack-check ")
 		set(COMPILER_FLAGS "${COMPILER_FLAGS} -fstack-check ")
 	endif()
 	endif()
@@ -206,7 +209,7 @@ if(${ANKI_WINDOW_BACKEND} STREQUAL "SDL")
 		DOWNLOAD_COMMAND ""
 		DOWNLOAD_COMMAND ""
 		PREFIX "${CMAKE_CURRENT_BINARY_DIR}/sdl2_build"
 		PREFIX "${CMAKE_CURRENT_BINARY_DIR}/sdl2_build"
 		SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/SDL2"
 		SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/SDL2"
-		CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/sdl2_build -DSDL_SHARED=OFF -DSDL_STATIC=ON)
+		CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/sdl2_build -DSDL_SHARED=OFF -DSDL_STATIC=ON -DCMAKE_BUILD_TYPE=Release)
 
 
 	ExternalProject_Get_Property(SDL2_PROJECT install_dir)
 	ExternalProject_Get_Property(SDL2_PROJECT install_dir)
 	set(SDL2_INSTALL_DIR ${install_dir})
 	set(SDL2_INSTALL_DIR ${install_dir})
@@ -258,7 +261,7 @@ set(ANKI_VERSION_MAJOR 0)
 set(ANKI_VERSION_MINOR 1)
 set(ANKI_VERSION_MINOR 1)
 message("++ AnKi version: ${ANKI_VERSION_MAJOR}.${ANKI_VERSION_MINOR}")
 message("++ AnKi version: ${ANKI_VERSION_MAJOR}.${ANKI_VERSION_MINOR}")
 
 
-if(ANKI_DEBUG_SYMBOLS)
+if(ANKI_DEBUG)
 	set(ANKI_DEBUG 1)
 	set(ANKI_DEBUG 1)
 else()
 else()
 	set(ANKI_DEBUG 0)
 	set(ANKI_DEBUG 0)

+ 1 - 4
include/anki/Renderer.h

@@ -3,13 +3,10 @@
 // Code licensed under the BSD License.
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
-#ifndef ANKI_RENDERER_H
-#define ANKI_RENDERER_H
+#pragma once
 
 
 #include "anki/renderer/MainRenderer.h"
 #include "anki/renderer/MainRenderer.h"
 #include "anki/renderer/Dbg.h"
 #include "anki/renderer/Dbg.h"
 
 
 /// @defgroup renderer Renderering system
 /// @defgroup renderer Renderering system
 
 
-#endif
-

+ 3 - 0
include/anki/gr/CommandBuffer.h

@@ -178,6 +178,9 @@ public:
 
 
 	/// End query.
 	/// End query.
 	void endOcclusionQuery(OcclusionQueryPtr query);
 	void endOcclusionQuery(OcclusionQueryPtr query);
+
+	/// Append a second level command buffer.
+	void pushSecondLevelCommandBuffer(CommandBufferPtr cmdb);
 	/// @}
 	/// @}
 
 
 private:
 private:

+ 18 - 31
include/anki/renderer/Drawer.h

@@ -15,7 +15,6 @@ namespace anki {
 
 
 // Forward
 // Forward
 class Renderer;
 class Renderer;
-class SetupRenderableVariableVisitor;
 
 
 /// @addtogroup renderer
 /// @addtogroup renderer
 /// @{
 /// @{
@@ -31,47 +30,35 @@ enum class RenderingStage: U8
 class RenderableDrawer
 class RenderableDrawer
 {
 {
 	friend class SetupRenderableVariableVisitor;
 	friend class SetupRenderableVariableVisitor;
+	friend class RenderTask;
 
 
 public:
 public:
-	RenderableDrawer();
+	RenderableDrawer(Renderer* r)
+		: m_r(r)
+	{}
 
 
 	~RenderableDrawer();
 	~RenderableDrawer();
 
 
-	/// The one and only constructor
-	ANKI_USE_RESULT Error create(Renderer* r);
-
-	void prepareDraw(RenderingStage stage, Pass pass, CommandBufferPtr& cmdBuff)
-	{
-		m_stage = stage;
-		m_pass = pass;
-		m_cmdBuff = cmdBuff;
-	}
-
-	ANKI_USE_RESULT Error render(FrustumComponent& frc, VisibleNode& visible);
-
-	void finishDraw()
-	{
-		m_cmdBuff = CommandBufferPtr();
-	}
+	ANKI_USE_RESULT Error render(FrustumComponent& frc, RenderingStage stage,
+		Pass pass, SArray<CommandBufferPtr>& cmdbs);
 
 
 private:
 private:
 	Renderer* m_r;
 	Renderer* m_r;
-	UniquePtr<SetupRenderableVariableVisitor> m_variableVisitor;
-
-	/// @name State
-	/// @{
-	CommandBufferPtr m_cmdBuff;
-	RenderingStage m_stage;
-	Pass m_pass;
-	/// @}
 
 
 	void setupUniforms(
 	void setupUniforms(
-		VisibleNode& visibleNode,
-		RenderComponent& renderable,
-		FrustumComponent& fr,
-		F32 flod);
+		const VisibleNode& visibleNode,
+		const RenderComponent& renderable,
+		const FrustumComponent& fr,
+		F32 flod,
+		CommandBufferPtr cmdb);
+
+	ANKI_USE_RESULT Error renderSingle(
+		const FrustumComponent& fr,
+		RenderingStage stage,
+		Pass pass,
+		CommandBufferPtr cmdb,
+		const VisibleNode& visibleNode);
 };
 };
-
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

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

@@ -80,6 +80,7 @@ private:
 
 
 	/// One for multisampled and one for not. 0: multisampled, 1: not
 	/// One for multisampled and one for not. 0: multisampled, 1: not
 	Array<Plane, 2> m_planes;
 	Array<Plane, 2> m_planes;
+	DArray<CommandBufferPtr> m_secondLevelCmdbs;
 
 
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
 

+ 0 - 3
include/anki/renderer/Sm.h

@@ -108,9 +108,6 @@ private:
 	/// Shadowmap resolution
 	/// Shadowmap resolution
 	U32 m_resolution;
 	U32 m_resolution;
 
 
-	void prepareDraw(CommandBufferPtr& cmdBuff);
-	void finishDraw(CommandBufferPtr& cmdBuff);
-
 	/// Find the best shadowmap for that light
 	/// Find the best shadowmap for that light
 	template<typename TShadowmap, typename TContainer>
 	template<typename TShadowmap, typename TContainer>
 	void bestCandidate(SceneNode& light, TContainer& arr, TShadowmap*& out);
 	void bestCandidate(SceneNode& light, TContainer& arr, TShadowmap*& out);

+ 1 - 0
include/anki/resource/Model.h

@@ -79,6 +79,7 @@ private:
 	Array<MeshResourcePtr, MAX_LODS> m_meshes; ///< One for each LOD
 	Array<MeshResourcePtr, MAX_LODS> m_meshes; ///< One for each LOD
 	U8 m_meshCount = 0;
 	U8 m_meshCount = 0;
 	MaterialResourcePtr m_mtl;
 	MaterialResourcePtr m_mtl;
+	mutable SpinLock m_lock;
 
 
 	mutable Array3d<PipelinePtr, U(Pass::COUNT), MAX_LODS, 2> m_pplines;
 	mutable Array3d<PipelinePtr, U(Pass::COUNT), MAX_LODS, 2> m_pplines;
 	Array<ResourceGroupPtr, MAX_LODS> m_grResources;
 	Array<ResourceGroupPtr, MAX_LODS> m_grResources;

+ 2 - 2
include/anki/scene/ModelNode.h

@@ -44,9 +44,9 @@ private:
 		const MoveComponent* instanceMoves[],
 		const MoveComponent* instanceMoves[],
 		U32 instanceMovesCount);
 		U32 instanceMovesCount);
 
 
-	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
+	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data) const;
 
 
-	void getRenderWorldTransform(U index, Transform& trf);
+	void getRenderWorldTransform(U index, Transform& trf) const;
 };
 };
 
 
 /// The model scene node
 /// The model scene node

+ 2 - 2
include/anki/scene/ParticleEmitter.h

@@ -203,8 +203,8 @@ private:
 
 
 	ANKI_USE_RESULT Error doInstancingCalcs();
 	ANKI_USE_RESULT Error doInstancingCalcs();
 
 
-	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
-	void getRenderWorldTransform(U index, Transform& trf);
+	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data) const;
+	void getRenderWorldTransform(U index, Transform& trf) const;
 
 
 	void onMoveComponentUpdate(MoveComponent& move);
 	void onMoveComponentUpdate(MoveComponent& move);
 };
 };

+ 23 - 8
include/anki/scene/RenderComponent.h

@@ -137,7 +137,7 @@ public:
 	typedef T Type;
 	typedef T Type;
 
 
 	RenderComponentVariableTemplate(const MaterialVariable* mvar)
 	RenderComponentVariableTemplate(const MaterialVariable* mvar)
-	:	RenderComponentVariable(mvar)
+		: RenderComponentVariable(mvar)
 	{
 	{
 		setupVisitable(this);
 		setupVisitable(this);
 	}
 	}
@@ -229,7 +229,7 @@ public:
 	}
 	}
 
 
 	/// @param node Pass node to steal it's allocator
 	/// @param node Pass node to steal it's allocator
-	RenderComponent(SceneNode* node);
+	RenderComponent(SceneNode* node, const Material* mtl);
 
 
 	~RenderComponent();
 	~RenderComponent();
 
 
@@ -245,18 +245,33 @@ public:
 		return m_vars.end();
 		return m_vars.end();
 	}
 	}
 
 
+	Variables::ConstIterator getVariablesBegin() const
+	{
+		return m_vars.begin();
+	}
+
+	Variables::ConstIterator getVariablesEnd() const
+	{
+		return m_vars.end();
+	}
+
 	/// Build up the rendering.
 	/// Build up the rendering.
 	/// Given an array of submeshes that are visible append jobs to the GL
 	/// Given an array of submeshes that are visible append jobs to the GL
 	/// job chain
 	/// job chain
-	virtual ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data) = 0;
+	virtual ANKI_USE_RESULT Error buildRendering(
+		RenderingBuildData& data) const = 0;
 
 
 	/// Access the material
 	/// Access the material
-	virtual const Material& getMaterial() = 0;
+	const Material& getMaterial() const
+	{
+		ANKI_ASSERT(m_mtl);
+		return *m_mtl;
+	}
 
 
 	/// Information for movables. It's actualy an array of transformations.
 	/// Information for movables. It's actualy an array of transformations.
 	/// @param index The index of the transform to get
 	/// @param index The index of the transform to get
 	/// @param[out] trf The transform to set
 	/// @param[out] trf The transform to set
-	virtual void getRenderWorldTransform(U index, Transform& trf)
+	virtual void getRenderWorldTransform(U index, Transform& trf) const
 	{
 	{
 		ANKI_ASSERT(getHasWorldTransforms());
 		ANKI_ASSERT(getHasWorldTransforms());
 		(void)index;
 		(void)index;
@@ -264,12 +279,12 @@ public:
 	}
 	}
 
 
 	/// Return true if the renderable has world transforms
 	/// Return true if the renderable has world transforms
-	virtual Bool getHasWorldTransforms()
+	virtual Bool getHasWorldTransforms() const
 	{
 	{
 		return false;
 		return false;
 	}
 	}
 
 
-	Bool getCastsShadow()
+	Bool getCastsShadow() const
 	{
 	{
 		const Material& mtl = getMaterial();
 		const Material& mtl = getMaterial();
 		return mtl.getShadowEnabled();
 		return mtl.getShadowEnabled();
@@ -290,8 +305,8 @@ public:
 	}
 	}
 
 
 private:
 private:
-	SceneAllocator<U8> m_alloc;
 	Variables m_vars;
 	Variables m_vars;
+	const Material* m_mtl;
 };
 };
 /// @}
 /// @}
 
 

+ 1 - 1
include/anki/scene/StaticGeometryNode.h

@@ -31,7 +31,7 @@ public:
 private:
 private:
 	const ModelPatch* m_modelPatch;
 	const ModelPatch* m_modelPatch;
 
 
-	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data);
+	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data) const;
 };
 };
 
 
 /// Static geometry scene node
 /// Static geometry scene node

+ 1 - 1
include/anki/scene/Visibility.h

@@ -63,7 +63,7 @@ public:
 		return *this;
 		return *this;
 	}
 	}
 
 
-	U8 getSpatialIndex(U i)
+	U8 getSpatialIndex(U i) const
 	{
 	{
 		ANKI_ASSERT(m_spatialsCount != 0 && i < m_spatialsCount);
 		ANKI_ASSERT(m_spatialsCount != 0 && i < m_spatialsCount);
 		return m_spatialIndices[i];
 		return m_spatialIndices[i];

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

@@ -527,5 +527,26 @@ CommandBufferInitHints CommandBuffer::computeInitHints() const
 	return m_impl->computeInitHints();
 	return m_impl->computeInitHints();
 }
 }
 
 
+//==============================================================================
+class ExecCmdbCommand final: public GlCommand
+{
+public:
+	CommandBufferPtr m_cmdb;
+
+	ExecCmdbCommand(const CommandBufferPtr& cmdb)
+		: m_cmdb(cmdb)
+	{}
+
+	Error operator()(GlState&)
+	{
+		return m_cmdb->getImplementation().executeAllCommands();
+	}
+};
+
+void CommandBuffer::pushSecondLevelCommandBuffer(CommandBufferPtr cmdb)
+{
+	m_impl->pushBackNewCommand<ExecCmdbCommand>(cmdb);
+}
+
 } // end namespace anki
 } // end namespace anki
 
 

+ 112 - 50
src/renderer/Drawer.cpp

@@ -15,9 +15,12 @@
 #include "anki/renderer/Renderer.h"
 #include "anki/renderer/Renderer.h"
 #include "anki/core/Counters.h"
 #include "anki/core/Counters.h"
 #include "anki/util/Logger.h"
 #include "anki/util/Logger.h"
+#include "anki/util/Thread.h"
 
 
 namespace anki {
 namespace anki {
 
 
+//==============================================================================
+// Misc                                                                        =
 //==============================================================================
 //==============================================================================
 static const U MATERIAL_BLOCK_MAX_SIZE = 1024 * 4;
 static const U MATERIAL_BLOCK_MAX_SIZE = 1024 * 4;
 
 
@@ -26,31 +29,21 @@ class SetupRenderableVariableVisitor
 {
 {
 public:
 public:
 	// Used to get the visible spatials
 	// Used to get the visible spatials
-	WeakPtr<VisibleNode> m_visibleNode;
+	const VisibleNode* m_visibleNode;
 
 
-	WeakPtr<RenderComponent> m_renderable; ///< To get the transforms
-	WeakPtr<RenderComponentVariable> m_rvar;
-	WeakPtr<const FrustumComponent> m_fr;
-	WeakPtr<RenderableDrawer> m_drawer;
+	const RenderComponent* m_renderable; ///< To get the transforms
+	const RenderComponentVariable* m_rvar;
+	const FrustumComponent* m_fr;
+	const RenderableDrawer* m_drawer;
 	U8 m_instanceCount;
 	U8 m_instanceCount;
 	CommandBufferPtr m_cmdBuff;
 	CommandBufferPtr m_cmdBuff;
 	SArray<U8> m_uniformBuffer;
 	SArray<U8> m_uniformBuffer;
 
 
 	F32 m_flod;
 	F32 m_flod;
 
 
-	SetupRenderableVariableVisitor(RenderableDrawer* drawer)
-		: m_drawer(drawer)
-	{}
-
-	HeapAllocator<U8> getAllocator() const
-	{
-		return m_drawer->m_r->getAllocator();
-	}
-
 	/// Set a uniform in a client block
 	/// Set a uniform in a client block
 	template<typename T>
 	template<typename T>
-	void uniSet(const MaterialVariable& mtlVar,
-		const T* value, U32 size)
+	void uniSet(const MaterialVariable& mtlVar, const T* value, U32 size)
 	{
 	{
 		mtlVar.writeShaderBlockMemory<T>(
 		mtlVar.writeShaderBlockMemory<T>(
 			value,
 			value,
@@ -223,70 +216,139 @@ void SetupRenderableVariableVisitor::uniSet<TextureResourcePtr>(
 	// Do nothing
 	// Do nothing
 }
 }
 
 
+/// Task to render a single node.
+class RenderTask: public Threadpool::Task
+{
+public:
+	RenderableDrawer* m_drawer;
+	CommandBufferPtr m_cmdb;
+	FrustumComponent* m_frc;
+	RenderingStage m_stage;
+	Pass m_pass;
+
+	Error operator()(U32 threadId, PtrSize threadsCount) override
+	{
+		VisibilityTestResults& vis = m_frc->getVisibilityTestResults();
+
+		PtrSize start, end;
+		U problemSize = vis.getRenderablesEnd() - vis.getRenderablesBegin();
+		choseStartEnd(threadId, threadsCount, problemSize, start, end);
+
+		for(U i = start; i < end; ++i)
+		{
+			VisibleNode* node = vis.getRenderablesBegin() + i;
+			ANKI_CHECK(
+				m_drawer->renderSingle(*m_frc, m_stage, m_pass, m_cmdb, *node));
+		}
+
+		return ErrorCode::NONE;
+	}
+};
+
+//==============================================================================
+// RenderableDrawer                                                            =
 //==============================================================================
 //==============================================================================
-RenderableDrawer::RenderableDrawer()
-{}
 
 
 //==============================================================================
 //==============================================================================
 RenderableDrawer::~RenderableDrawer()
 RenderableDrawer::~RenderableDrawer()
 {}
 {}
 
 
-//==============================================================================
-Error RenderableDrawer::create(Renderer* r)
-{
-	m_r = r;
-	m_variableVisitor.reset(
-		m_r->getAllocator().newInstance<SetupRenderableVariableVisitor>(this));
-	return ErrorCode::NONE;
-}
-
 //==============================================================================
 //==============================================================================
 void RenderableDrawer::setupUniforms(
 void RenderableDrawer::setupUniforms(
-	VisibleNode& visibleNode,
-	RenderComponent& renderable,
-	FrustumComponent& fr,
-	F32 flod)
+	const VisibleNode& visibleNode,
+	const RenderComponent& renderable,
+	const FrustumComponent& fr,
+	F32 flod,
+	CommandBufferPtr cmdb)
 {
 {
 	const Material& mtl = renderable.getMaterial();
 	const Material& mtl = renderable.getMaterial();
 
 
 	// Get some memory for uniforms
 	// Get some memory for uniforms
 	ANKI_ASSERT(mtl.getDefaultBlockSize() < MATERIAL_BLOCK_MAX_SIZE);
 	ANKI_ASSERT(mtl.getDefaultBlockSize() < MATERIAL_BLOCK_MAX_SIZE);
 	U8* uniforms = nullptr;
 	U8* uniforms = nullptr;
-	m_cmdBuff->updateDynamicUniforms(mtl.getDefaultBlockSize(), uniforms);
+	cmdb->updateDynamicUniforms(mtl.getDefaultBlockSize(), uniforms);
 
 
 	// Call the visitor
 	// Call the visitor
-	m_variableVisitor->m_visibleNode = &visibleNode;
-	m_variableVisitor->m_renderable = &renderable;
-	m_variableVisitor->m_fr = &fr;
-	m_variableVisitor->m_instanceCount = visibleNode.m_spatialsCount;
-	m_variableVisitor->m_cmdBuff = m_cmdBuff;
-	m_variableVisitor->m_flod = flod;
-	m_variableVisitor->m_uniformBuffer =
-		SArray<U8>(uniforms, mtl.getDefaultBlockSize());
+	SetupRenderableVariableVisitor visitor;
+	visitor.m_visibleNode = &visibleNode;
+	visitor.m_renderable = &renderable;
+	visitor.m_fr = &fr;
+	visitor.m_drawer = this;
+	visitor.m_instanceCount = visibleNode.m_spatialsCount;
+	visitor.m_cmdBuff = cmdb;
+	visitor.m_uniformBuffer = SArray<U8>(uniforms, mtl.getDefaultBlockSize());
+	visitor.m_flod = flod;
 
 
 	for(auto it = renderable.getVariablesBegin();
 	for(auto it = renderable.getVariablesBegin();
 		it != renderable.getVariablesEnd(); ++it)
 		it != renderable.getVariablesEnd(); ++it)
 	{
 	{
 		RenderComponentVariable* rvar = *it;
 		RenderComponentVariable* rvar = *it;
 
 
-		m_variableVisitor->m_rvar = rvar;
-		Error err = rvar->acceptVisitor(*m_variableVisitor);
+		visitor.m_rvar = rvar;
+		Error err = rvar->acceptVisitor(visitor);
 		(void)err;
 		(void)err;
 	}
 	}
 }
 }
 
 
 //==============================================================================
 //==============================================================================
-Error RenderableDrawer::render(FrustumComponent& fr, VisibleNode& visibleNode)
+Error RenderableDrawer::render(FrustumComponent& frc,
+	RenderingStage stage, Pass pass, SArray<CommandBufferPtr>& cmdbs)
+{
+	Error err = ErrorCode::NONE;
+	ANKI_ASSERT(cmdb.getSize() == threadPool.getThreadsCount() ||
+		cmdb.getSize() == 1);
+
+	if(cmdbs.getSize() > 1)
+	{
+		Array<RenderTask, Threadpool::MAX_THREADS> tasks;
+
+		Threadpool& threadPool = m_r->getThreadpool();
+		for(U i = 0; i < threadPool.getThreadsCount(); i++)
+		{
+			auto& task = tasks[i];
+			task.m_drawer = this;
+			task.m_cmdb = cmdbs[i];
+			task.m_frc = &frc;
+			task.m_stage = stage;
+			task.m_pass = pass;
+
+			threadPool.assignNewTask(i, &task);
+		}
+
+		err = threadPool.waitForAllThreadsToFinish();
+	}
+	else
+	{
+		RenderTask task;
+		task.m_drawer = this;
+		task.m_cmdb = cmdbs[0];
+		task.m_frc = &frc;
+		task.m_stage = stage;
+		task.m_pass = pass;
+
+		err = task(0, 1);
+	}
+
+	return err;
+}
+
+//==============================================================================
+Error RenderableDrawer::renderSingle(
+	const FrustumComponent& fr,
+	RenderingStage stage,
+	Pass pass,
+	CommandBufferPtr cmdb,
+	const VisibleNode& visibleNode)
 {
 {
 	RenderingBuildData build;
 	RenderingBuildData build;
 
 
 	// Get components
 	// Get components
-	RenderComponent& renderable =
+	const RenderComponent& renderable =
 		visibleNode.m_node->getComponent<RenderComponent>();
 		visibleNode.m_node->getComponent<RenderComponent>();
 	const Material& mtl = renderable.getMaterial();
 	const Material& mtl = renderable.getMaterial();
 
 
-	if((m_stage == RenderingStage::BLEND && !mtl.getForwardShading())
-		|| (m_stage == RenderingStage::MATERIAL && mtl.getForwardShading()))
+	if((stage == RenderingStage::BLEND && !mtl.getForwardShading())
+		|| (stage == RenderingStage::MATERIAL && mtl.getForwardShading()))
 	{
 	{
 		return ErrorCode::NONE;
 		return ErrorCode::NONE;
 	}
 	}
@@ -300,24 +362,24 @@ Error RenderableDrawer::render(FrustumComponent& fr, VisibleNode& visibleNode)
 		getSpatialOrigin() - camPos).getLength();
 		getSpatialOrigin() - camPos).getLength();
 	F32 flod = m_r->calculateLod(dist);
 	F32 flod = m_r->calculateLod(dist);
 	build.m_key.m_lod = flod;
 	build.m_key.m_lod = flod;
-	build.m_key.m_pass = m_pass;
+	build.m_key.m_pass = pass;
 	build.m_key.m_tessellation =
 	build.m_key.m_tessellation =
 		m_r->getTessellationEnabled()
 		m_r->getTessellationEnabled()
 		&& mtl.getTessellationEnabled()
 		&& mtl.getTessellationEnabled()
 		&& build.m_key.m_lod == 0;
 		&& build.m_key.m_lod == 0;
 
 
-	if(m_pass == Pass::SM)
+	if(pass == Pass::SM)
 	{
 	{
 		build.m_key.m_tessellation = false;
 		build.m_key.m_tessellation = false;
 	}
 	}
 
 
 	// Enqueue uniform state updates
 	// Enqueue uniform state updates
-	setupUniforms(visibleNode, renderable, fr, flod);
+	setupUniforms(visibleNode, renderable, fr, flod, cmdb);
 
 
 	// Enqueue vertex, program and drawcall
 	// Enqueue vertex, program and drawcall
 	build.m_subMeshIndicesArray = &visibleNode.m_spatialIndices[0];
 	build.m_subMeshIndicesArray = &visibleNode.m_spatialIndices[0];
 	build.m_subMeshIndicesCount = visibleNode.m_spatialsCount;
 	build.m_subMeshIndicesCount = visibleNode.m_spatialsCount;
-	build.m_cmdb = m_cmdBuff;
+	build.m_cmdb = cmdb;
 
 
 	ANKI_CHECK(renderable.buildRendering(build));
 	ANKI_CHECK(renderable.buildRendering(build));
 
 

+ 4 - 17
src/renderer/Fs.cpp

@@ -35,29 +35,16 @@ Error Fs::init(const ConfigSet&)
 //==============================================================================
 //==============================================================================
 Error Fs::run(CommandBufferPtr& cmdb)
 Error Fs::run(CommandBufferPtr& cmdb)
 {
 {
-	Error err = ErrorCode::NONE;
-
 	cmdb->bindFramebuffer(m_fb);
 	cmdb->bindFramebuffer(m_fb);
 
 
-	RenderableDrawer& drawer = m_r->getSceneDrawer();
-	drawer.prepareDraw(RenderingStage::BLEND, Pass::MS_FS, cmdb);
-
 	SceneNode& cam = m_r->getActiveCamera();
 	SceneNode& cam = m_r->getActiveCamera();
 	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
 	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
 
 
-	auto it = camFr.getVisibilityTestResults().getRenderablesBegin();
-	auto end = camFr.getVisibilityTestResults().getRenderablesEnd();
-	for(; !err && it != end; ++it)
-	{
-		err = drawer.render(camFr, *it);
-	}
+	SArray<CommandBufferPtr> cmdbs(&cmdb, 1);
+	ANKI_CHECK(m_r->getSceneDrawer().render(
+		camFr, RenderingStage::BLEND, Pass::MS_FS, cmdbs));
 
 
-	if(!err)
-	{
-		drawer.finishDraw();
-	}
-
-	return err;
+	return ErrorCode::NONE;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 22 - 13
src/renderer/Ms.cpp

@@ -92,6 +92,9 @@ Error Ms::initInternal(const ConfigSet& initializer)
 
 
 	ANKI_CHECK(createRt(1, 1));
 	ANKI_CHECK(createRt(1, 1));
 
 
+	m_secondLevelCmdbs.create(
+		getAllocator(), m_r->getThreadpool().getThreadsCount());
+
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
@@ -105,27 +108,28 @@ Error Ms::run(CommandBufferPtr& cmdb)
 		planeId = 1;
 		planeId = 1;
 	}
 	}
 
 
+	// Create 2nd level cmdbs
+	U threadCount = m_r->getThreadpool().getThreadsCount();
+	GrManager& gr = m_r->getGrManager();
+	for(U i = 0; i < threadCount; ++i)
+	{
+		// TODO Add hints
+		m_secondLevelCmdbs[i] = gr.newInstance<CommandBuffer>();
+	}
+
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
 
 
 	cmdb->bindFramebuffer(m_planes[planeId].m_fb);
 	cmdb->bindFramebuffer(m_planes[planeId].m_fb);
 
 
 	// render all
 	// render all
-	m_r->getSceneDrawer().prepareDraw(
-		RenderingStage::MATERIAL, Pass::MS_FS, cmdb);
-
 	SceneNode& cam = m_r->getActiveCamera();
 	SceneNode& cam = m_r->getActiveCamera();
 
 
 	FrustumComponent& frc = cam.getComponent<FrustumComponent>();
 	FrustumComponent& frc = cam.getComponent<FrustumComponent>();
-	VisibilityTestResults& vi = frc.getVisibilityTestResults();
-
-	auto it = vi.getRenderablesBegin();
-	auto end = vi.getRenderablesEnd();
-	for(; it != end; ++it)
-	{
-		ANKI_CHECK(m_r->getSceneDrawer().render(frc, *it));
-	}
-
-	m_r->getSceneDrawer().finishDraw();
+	SArray<CommandBufferPtr> cmdbs(
+		&m_secondLevelCmdbs[0], m_secondLevelCmdbs.getSize());
+	//SArray<CommandBufferPtr> cmdbs(&cmdb, 1);
+	ANKI_CHECK(m_r->getSceneDrawer().render(
+		frc, RenderingStage::MATERIAL, Pass::MS_FS, cmdbs));
 
 
 	// If there is multisampling then resolve to singlesampled
 	// If there is multisampling then resolve to singlesampled
 	if(m_r->getSamples() > 1)
 	if(m_r->getSamples() > 1)
@@ -139,6 +143,11 @@ Error Ms::run(CommandBufferPtr& cmdb)
 		ANKI_ASSERT(0 && "TODO");
 		ANKI_ASSERT(0 && "TODO");
 	}
 	}
 
 
+	for(U i = 0; i < m_secondLevelCmdbs.getSize(); ++i)
+	{
+		cmdb->pushSecondLevelCommandBuffer(m_secondLevelCmdbs[i]);
+	}
+
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 

+ 1 - 3
src/renderer/Renderer.cpp

@@ -21,6 +21,7 @@ namespace anki {
 
 
 //==============================================================================
 //==============================================================================
 Renderer::Renderer()
 Renderer::Renderer()
+	: m_sceneDrawer(this)
 {}
 {}
 
 
 //==============================================================================
 //==============================================================================
@@ -94,9 +95,6 @@ Error Renderer::initInternal(const ConfigSet& config)
 		return ErrorCode::USER_DATA;
 		return ErrorCode::USER_DATA;
 	}
 	}
 
 
-	// Drawer
-	ANKI_CHECK(m_sceneDrawer.create(this));
-
 	// quad setup
 	// quad setup
 	ANKI_CHECK(
 	ANKI_CHECK(
 		m_resources->loadResource("shaders/Quad.vert.glsl", m_drawQuadVert));
 		m_resources->loadResource("shaders/Quad.vert.glsl", m_drawQuadVert));

+ 8 - 36
src/renderer/Sm.cpp

@@ -101,27 +101,11 @@ Error Sm::init(const ConfigSet& config)
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
-//==============================================================================
-void Sm::prepareDraw(CommandBufferPtr& cmdBuff)
-{
-	m_r->getSceneDrawer().prepareDraw(
-		RenderingStage::MATERIAL, Pass::SM, cmdBuff);
-}
-
-//==============================================================================
-void Sm::finishDraw(CommandBufferPtr& cmdBuff)
-{
-	m_r->getSceneDrawer().finishDraw();
-}
-
 //==============================================================================
 //==============================================================================
 Error Sm::run(SArray<SceneNode*> spotShadowCasters,
 Error Sm::run(SArray<SceneNode*> spotShadowCasters,
 	SArray<SceneNode*> omniShadowCasters, CommandBufferPtr& cmdBuff)
 	SArray<SceneNode*> omniShadowCasters, CommandBufferPtr& cmdBuff)
 {
 {
 	ANKI_ASSERT(m_enabled);
 	ANKI_ASSERT(m_enabled);
-	Error err = ErrorCode::NONE;
-
-	prepareDraw(cmdBuff);
 
 
 	// render all
 	// render all
 	for(SceneNode* node : spotShadowCasters)
 	for(SceneNode* node : spotShadowCasters)
@@ -134,9 +118,7 @@ Error Sm::run(SArray<SceneNode*> spotShadowCasters,
 		ANKI_CHECK(doOmniLight(*node, cmdBuff));
 		ANKI_CHECK(doOmniLight(*node, cmdBuff));
 	}
 	}
 
 
-	finishDraw(cmdBuff);
-
-	return err;
+	return ErrorCode::NONE;
 }
 }
 
 
 //==============================================================================
 //==============================================================================
@@ -242,21 +224,15 @@ Error Sm::doSpotLight(SceneNode& light, CommandBufferPtr& cmdBuff)
 		return ErrorCode::NONE;
 		return ErrorCode::NONE;
 	}
 	}
 
 
-	FrustumComponent& fr = light.getComponent<FrustumComponent>();
-	VisibilityTestResults& vi = fr.getVisibilityTestResults();
-
 	cmdBuff->bindFramebuffer(sm->m_fb);
 	cmdBuff->bindFramebuffer(sm->m_fb);
 	cmdBuff->setViewport(0, 0, m_resolution, m_resolution);
 	cmdBuff->setViewport(0, 0, m_resolution, m_resolution);
 
 
-	auto it = vi.getRenderablesBegin();
-	auto end = vi.getRenderablesEnd();
-	for(; it != end; ++it)
-	{
-		ANKI_CHECK(m_r->getSceneDrawer().render(fr, *it));
-	}
+	FrustumComponent& fr = light.getComponent<FrustumComponent>();
+	SArray<CommandBufferPtr> cmdbs(&cmdBuff, 1);
+	ANKI_CHECK(m_r->getSceneDrawer().render(
+		fr, RenderingStage::MATERIAL, Pass::SM, cmdbs));
 
 
 	ANKI_COUNTER_INC(RENDERER_SHADOW_PASSES, U64(1));
 	ANKI_COUNTER_INC(RENDERER_SHADOW_PASSES, U64(1));
-
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
@@ -278,14 +254,10 @@ Error Sm::doOmniLight(SceneNode& light, CommandBufferPtr& cmdBuff)
 		[&](FrustumComponent& fr) -> Error
 		[&](FrustumComponent& fr) -> Error
 	{
 	{
 		cmdBuff->bindFramebuffer(sm->m_fb[frCount]);
 		cmdBuff->bindFramebuffer(sm->m_fb[frCount]);
-		VisibilityTestResults& vi = fr.getVisibilityTestResults();
 
 
-		auto it = vi.getRenderablesBegin();
-		auto end = vi.getRenderablesEnd();
-		for(; it != end; ++it)
-		{
-			ANKI_CHECK(m_r->getSceneDrawer().render(fr, *it));
-		}
+		SArray<CommandBufferPtr> cmdbs(&cmdBuff, 1);
+		ANKI_CHECK(m_r->getSceneDrawer().render(
+			fr, RenderingStage::MATERIAL, Pass::SM, cmdbs));
 
 
 		++frCount;
 		++frCount;
 		return ErrorCode::NONE;
 		return ErrorCode::NONE;

+ 21 - 16
src/resource/Model.cpp

@@ -133,27 +133,32 @@ PipelinePtr ModelPatch::getPipeline(const RenderingKey& key) const
 	// Lazily create it
 	// Lazily create it
 	if(ANKI_UNLIKELY(!ppline.isCreated()))
 	if(ANKI_UNLIKELY(!ppline.isCreated()))
 	{
 	{
-		PipelineInitializer pplineInit;
-		computePipelineInitializer(key, pplineInit);
+		LockGuard<SpinLock> lock(m_lock);
 
 
-		pplineInit.m_shaders[U(ShaderType::VERTEX)] =
-			m_mtl->getShader(key, ShaderType::VERTEX);
-
-		if(key.m_tessellation)
+		if(!ppline.isCreated())
 		{
 		{
-			pplineInit.m_shaders[U(ShaderType::TESSELLATION_CONTROL)] =
-				m_mtl->getShader(key, ShaderType::TESSELLATION_CONTROL);
+			PipelineInitializer pplineInit;
+			computePipelineInitializer(key, pplineInit);
 
 
-			pplineInit.m_shaders[U(ShaderType::TESSELLATION_EVALUATION)] =
-				m_mtl->getShader(key, ShaderType::TESSELLATION_EVALUATION);
-		}
+			pplineInit.m_shaders[U(ShaderType::VERTEX)] =
+				m_mtl->getShader(key, ShaderType::VERTEX);
+
+			if(key.m_tessellation)
+			{
+				pplineInit.m_shaders[U(ShaderType::TESSELLATION_CONTROL)] =
+					m_mtl->getShader(key, ShaderType::TESSELLATION_CONTROL);
 
 
-		pplineInit.m_shaders[U(ShaderType::FRAGMENT)] =
-			m_mtl->getShader(key, ShaderType::FRAGMENT);
+				pplineInit.m_shaders[U(ShaderType::TESSELLATION_EVALUATION)] =
+					m_mtl->getShader(key, ShaderType::TESSELLATION_EVALUATION);
+			}
 
 
-		// Create
-		ppline = m_model->getManager().getGrManager()
-			.newInstance<Pipeline>(pplineInit);
+			pplineInit.m_shaders[U(ShaderType::FRAGMENT)] =
+				m_mtl->getShader(key, ShaderType::FRAGMENT);
+
+			// Create
+			ppline = m_model->getManager().getGrManager()
+				.newInstance<Pipeline>(pplineInit);
+		}
 	}
 	}
 
 
 	return ppline;
 	return ppline;

+ 11 - 15
src/scene/ModelNode.cpp

@@ -26,26 +26,22 @@ public:
 	ModelPatchNode* m_node;
 	ModelPatchNode* m_node;
 
 
 	ModelPatchRenderComponent(ModelPatchNode* node)
 	ModelPatchRenderComponent(ModelPatchNode* node)
-		: RenderComponent(node)
+		: RenderComponent(node, &node->m_modelPatch->getMaterial())
 		, m_node(node)
 		, m_node(node)
 	{}
 	{}
 
 
-	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data) override
+	ANKI_USE_RESULT Error buildRendering(
+		RenderingBuildData& data) const override
 	{
 	{
 		return m_node->buildRendering(data);
 		return m_node->buildRendering(data);
 	}
 	}
 
 
-	const Material& getMaterial() override
-	{
-		return m_node->m_modelPatch->getMaterial();
-	}
-
-	void getRenderWorldTransform(U index, Transform& trf) override
+	void getRenderWorldTransform(U index, Transform& trf) const override
 	{
 	{
 		m_node->getRenderWorldTransform(index, trf);
 		m_node->getRenderWorldTransform(index, trf);
 	}
 	}
 
 
-	Bool getHasWorldTransforms() override
+	Bool getHasWorldTransforms() const override
 	{
 	{
 		return true;
 		return true;
 	}
 	}
@@ -98,7 +94,7 @@ Error ModelPatchNode::create(const CString& name,
 }
 }
 
 
 //==============================================================================
 //==============================================================================
-Error ModelPatchNode::buildRendering(RenderingBuildData& data)
+Error ModelPatchNode::buildRendering(RenderingBuildData& data) const
 {
 {
 	// That will not work on multi-draw and instanced at the same time. Make
 	// That will not work on multi-draw and instanced at the same time. Make
 	// sure that there is no multi-draw anywhere
 	// sure that there is no multi-draw anywhere
@@ -140,11 +136,11 @@ Error ModelPatchNode::buildRendering(RenderingBuildData& data)
 }
 }
 
 
 //==============================================================================
 //==============================================================================
-void ModelPatchNode::getRenderWorldTransform(U index, Transform& trf)
+void ModelPatchNode::getRenderWorldTransform(U index, Transform& trf) const
 {
 {
-	SceneNode* parent = getParent();
+	const SceneNode* parent = getParent();
 	ANKI_ASSERT(parent);
 	ANKI_ASSERT(parent);
-	MoveComponent& move = parent->getComponent<MoveComponent>();
+	const MoveComponent& move = parent->getComponent<MoveComponent>();
 
 
 	if(index == 0)
 	if(index == 0)
 	{
 	{
@@ -154,9 +150,9 @@ void ModelPatchNode::getRenderWorldTransform(U index, Transform& trf)
 	else
 	else
 	{
 	{
 		// Asking for a next instance
 		// Asking for a next instance
-		SceneNode* parent = getParent();
+		const SceneNode* parent = getParent();
 		ANKI_ASSERT(parent);
 		ANKI_ASSERT(parent);
-		ModelNode* mnode = staticCastPtr<ModelNode*>(parent);
+		const ModelNode* mnode = staticCastPtr<const ModelNode*>(parent);
 
 
 		--index;
 		--index;
 		trf = mnode->m_transforms[index];
 		trf = mnode->m_transforms[index];

+ 9 - 12
src/scene/ParticleEmitter.cpp

@@ -201,29 +201,26 @@ void Particle::revive(const ParticleEmitter& pe,
 class ParticleEmitterRenderComponent: public RenderComponent
 class ParticleEmitterRenderComponent: public RenderComponent
 {
 {
 public:
 public:
-	ParticleEmitter* m_node;
+	ParticleEmitter* m_node = nullptr;
 
 
 	ParticleEmitterRenderComponent(ParticleEmitter* node)
 	ParticleEmitterRenderComponent(ParticleEmitter* node)
-		: RenderComponent(node)
+		: RenderComponent(node,
+			&node->m_particleEmitterResource->getMaterial())
 		, m_node(node)
 		, m_node(node)
 	{}
 	{}
 
 
-	ANKI_USE_RESULT Error buildRendering(RenderingBuildData& data) override
+	ANKI_USE_RESULT Error buildRendering(
+		RenderingBuildData& data) const override
 	{
 	{
 		return m_node->buildRendering(data);
 		return m_node->buildRendering(data);
 	}
 	}
 
 
-	const Material& getMaterial() override
-	{
-		return m_node->m_particleEmitterResource->getMaterial();
-	}
-
-	void getRenderWorldTransform(U index, Transform& trf) override
+	void getRenderWorldTransform(U index, Transform& trf) const override
 	{
 	{
 		m_node->getRenderWorldTransform(index, trf);
 		m_node->getRenderWorldTransform(index, trf);
 	}
 	}
 
 
-	Bool getHasWorldTransforms() override
+	Bool getHasWorldTransforms() const override
 	{
 	{
 		return true;
 		return true;
 	}
 	}
@@ -359,7 +356,7 @@ Error ParticleEmitter::create(
 }
 }
 
 
 //==============================================================================
 //==============================================================================
-Error ParticleEmitter::buildRendering(RenderingBuildData& data)
+Error ParticleEmitter::buildRendering(RenderingBuildData& data) const
 {
 {
 	ANKI_ASSERT(data.m_subMeshIndicesCount <= m_transforms.getSize() + 1);
 	ANKI_ASSERT(data.m_subMeshIndicesCount <= m_transforms.getSize() + 1);
 
 
@@ -703,7 +700,7 @@ Error ParticleEmitter::doInstancingCalcs()
 }
 }
 
 
 //==============================================================================
 //==============================================================================
-void ParticleEmitter::getRenderWorldTransform(U index, Transform& trf)
+void ParticleEmitter::getRenderWorldTransform(U index, Transform& trf) const
 {
 {
 	if(index == 0)
 	if(index == 0)
 	{
 	{

+ 9 - 7
src/scene/RenderComponent.cpp

@@ -90,36 +90,38 @@ RenderComponentVariable::~RenderComponentVariable()
 //==============================================================================
 //==============================================================================
 
 
 //==============================================================================
 //==============================================================================
-RenderComponent::RenderComponent(SceneNode* node)
+RenderComponent::RenderComponent(SceneNode* node, const Material* mtl)
 	: SceneComponent(Type::RENDER, node)
 	: SceneComponent(Type::RENDER, node)
-	, m_alloc(node->getSceneAllocator())
+	, m_mtl(mtl)
 {}
 {}
 
 
 //==============================================================================
 //==============================================================================
 RenderComponent::~RenderComponent()
 RenderComponent::~RenderComponent()
 {
 {
+	auto alloc = m_node->getSceneAllocator();
 	for(RenderComponentVariable* var : m_vars)
 	for(RenderComponentVariable* var : m_vars)
 	{
 	{
-		var->destroy(m_alloc);
-		m_alloc.deleteInstance(var);
+		var->destroy(alloc);
+		alloc.deleteInstance(var);
 	}
 	}
 
 
-	m_vars.destroy(m_alloc);
+	m_vars.destroy(alloc);
 }
 }
 
 
 //==============================================================================
 //==============================================================================
 Error RenderComponent::create()
 Error RenderComponent::create()
 {
 {
 	const Material& mtl = getMaterial();
 	const Material& mtl = getMaterial();
+	auto alloc = m_node->getSceneAllocator();
 
 
 	// Create the material variables using a visitor
 	// Create the material variables using a visitor
 	CreateNewRenderComponentVariableVisitor vis;
 	CreateNewRenderComponentVariableVisitor vis;
 	U32 count = 0;
 	U32 count = 0;
 	vis.m_vars = &m_vars;
 	vis.m_vars = &m_vars;
 	vis.m_count = &count;
 	vis.m_count = &count;
-	vis.m_alloc = m_alloc;
+	vis.m_alloc = alloc;
 
 
-	m_vars.create(m_alloc, mtl.getVariables().getSize());
+	m_vars.create(alloc, mtl.getVariables().getSize());
 
 
 	auto it = mtl.getVariables().getBegin();
 	auto it = mtl.getVariables().getBegin();
 	auto end = mtl.getVariables().getEnd();
 	auto end = mtl.getVariables().getEnd();

+ 3 - 8
src/scene/StaticGeometryNode.cpp

@@ -20,19 +20,14 @@ public:
 	StaticGeometryPatchNode* m_node;
 	StaticGeometryPatchNode* m_node;
 
 
 	StaticGeometryRenderComponent(StaticGeometryPatchNode* node)
 	StaticGeometryRenderComponent(StaticGeometryPatchNode* node)
-		: RenderComponent(node)
+		: RenderComponent(node, &node->m_modelPatch->getMaterial())
 		, m_node(node)
 		, m_node(node)
 	{}
 	{}
 
 
-	Error buildRendering(RenderingBuildData& data) override
+	Error buildRendering(RenderingBuildData& data) const override
 	{
 	{
 		return m_node->buildRendering(data);
 		return m_node->buildRendering(data);
 	}
 	}
-
-	const Material& getMaterial()
-	{
-		return m_node->m_modelPatch->getMaterial();
-	}
 };
 };
 
 
 //==============================================================================
 //==============================================================================
@@ -80,7 +75,7 @@ Error StaticGeometryPatchNode::create(
 }
 }
 
 
 //==============================================================================
 //==============================================================================
-Error StaticGeometryPatchNode::buildRendering(RenderingBuildData& data)
+Error StaticGeometryPatchNode::buildRendering(RenderingBuildData& data) const
 {
 {
 	Array<U32, ANKI_GL_MAX_SUB_DRAWCALLS> indicesCountArray;
 	Array<U32, ANKI_GL_MAX_SUB_DRAWCALLS> indicesCountArray;
 	Array<PtrSize, ANKI_GL_MAX_SUB_DRAWCALLS> indicesOffsetArray;
 	Array<PtrSize, ANKI_GL_MAX_SUB_DRAWCALLS> indicesOffsetArray;

+ 7 - 8
src/scene/Visibility.cpp

@@ -81,7 +81,7 @@ public:
 	void combineTestResults(FrustumComponent& frc, PtrSize threadsCount);
 	void combineTestResults(FrustumComponent& frc, PtrSize threadsCount);
 
 
 	/// Do the tests
 	/// Do the tests
-	Error operator()(U32 threadId, PtrSize threadsCount)
+	Error operator()(U32 threadId, PtrSize threadsCount) override
 	{
 	{
 		auto& list = m_shared->m_frustumsList;
 		auto& list = m_shared->m_frustumsList;
 		auto alloc = m_shared->m_scene->getFrameAllocator();
 		auto alloc = m_shared->m_scene->getFrameAllocator();
@@ -266,14 +266,13 @@ void VisibilityTestTask::test(FrustumComponent& testedFrc,
 			visibleNode.m_spatialIndices[i] = sps[i].m_idx;
 			visibleNode.m_spatialIndices[i] = sps[i].m_idx;
 		}
 		}
 
 
-		if(rc && wantsRenderComponents)
-		{
-			visible->moveBackRenderable(alloc, visibleNode);
-		}
-
-		if(rc && wantsShadowCasters && rc->getCastsShadow())
+		if(rc)
 		{
 		{
-			visible->moveBackRenderable(alloc, visibleNode);
+			if(wantsRenderComponents ||
+				(wantsShadowCasters && rc->getCastsShadow()))
+			{
+				visible->moveBackRenderable(alloc, visibleNode);
+			}
 		}
 		}
 
 
 		if(lc && wantsLightComponents)
 		if(lc && wantsLightComponents)

+ 6 - 5
testapp/Main.cpp

@@ -43,7 +43,8 @@ App* app;
 ModelNode* horse;
 ModelNode* horse;
 PerspectiveCamera* cam;
 PerspectiveCamera* cam;
 
 
-#define NO_PLAYER 0
+#define NO_PLAYER 1
+#define MOUSE 0
 
 
 Bool profile = false;
 Bool profile = false;
 
 
@@ -87,7 +88,7 @@ Error init()
 #if NO_PLAYER
 #if NO_PLAYER
 	cam->getComponent<MoveComponent>().
 	cam->getComponent<MoveComponent>().
 		setLocalTransform(Transform(Vec4(147.392776, -10.132728, 16.607138, 0.0),
 		setLocalTransform(Transform(Vec4(147.392776, -10.132728, 16.607138, 0.0),
-		Mat3x4(Euler(toRad(0.0), toRad(0.0), toRad(0.0))),
+		Mat3x4(Euler(toRad(0.0), toRad(90.0), toRad(0.0))),
 		1.0));
 		1.0));
 #endif
 #endif
 
 
@@ -436,7 +437,7 @@ Error mainLoopExtra(App& app, void*, Bool& quit)
 	}
 	}
 #endif
 #endif
 
 
-#if NO_PLAYER
+#if NO_PLAYER && MOUSE
 	if(in.getMousePosition() != Vec2(0.0) && !profile)
 	if(in.getMousePosition() != Vec2(0.0) && !profile)
 	{
 	{
 		//printf("%f %f\n", in.getMousePosition().x(), in.getMousePosition().y());
 		//printf("%f %f\n", in.getMousePosition().x(), in.getMousePosition().y());
@@ -506,8 +507,8 @@ Error initSubsystems(int argc, char* argv[])
 	config.set("debugContext", false);
 	config.set("debugContext", false);
 	config.set("dataPaths", "assets");
 	config.set("dataPaths", "assets");
 	config.set("sceneFrameAllocatorSize", 1024 * 1024 * 10);
 	config.set("sceneFrameAllocatorSize", 1024 * 1024 * 10);
-	config.set("maxTextureSize", 256);
-	config.set("lodDistance", 3.0);
+	//config.set("maxTextureSize", 256);
+	//config.set("lodDistance", 3.0);
 
 
 	app = new App;
 	app = new App;
 	err = app->create(config, allocAligned, nullptr);
 	err = app->create(config, allocAligned, nullptr);

+ 1 - 1
tools/scene/Exporter.cpp

@@ -1218,7 +1218,7 @@ void Exporter::exportAll()
 		// Write instance nodes
 		// Write instance nodes
 		for(unsigned j = 1; j < node.m_transforms.size(); j++)
 		for(unsigned j = 1; j < node.m_transforms.size(); j++)
 		{
 		{
-			file << "inst = scene:newInstanceNode(\""
+			file << "\ninst = scene:newInstanceNode(\""
 				<< nodeName << "_inst" << (j - 1) << "\")\n"
 				<< nodeName << "_inst" << (j - 1) << "\")\n"
 				<< "node:getSceneNodeBase():addChild("
 				<< "node:getSceneNodeBase():addChild("
 				<< "inst:getSceneNodeBase())\n";
 				<< "inst:getSceneNodeBase())\n";