Panagiotis Christopoulos Charitos 13 anos atrás
pai
commit
a39aeb4dab

+ 1 - 2
include/anki/collision/Collision.h

@@ -1,8 +1,7 @@
 #ifndef ANKI_COLLISION_COLLISION_H
 #define ANKI_COLLISION_COLLISION_H
 
-/// @defgroup Collision
-/// Collision detection module
+/// @defgroup Collision Collision detection module
 
 #include "anki/collision/Plane.h"
 #include "anki/collision/Sphere.h"

+ 25 - 15
include/anki/scene/Common.h

@@ -7,23 +7,33 @@
 
 namespace anki {
 
-/// @addtogroup Scene
+/// @addtogroup config 
+/// @{
+/// @addtogroup config_scene Scene configuration constants
+/// @{
+
+#define ANKI_CFG_SCENE_NODES_AVERAGE_COUNT 1024
+
+/// 1MB
+#define ANKI_CFG_SCENE_ALLOCATOR_SIZE 0x100000 
+
+// 512K
+#define ANKI_CFG_SCENE_FRAME_ALLOCATOR_SIZE 0x80000
+
 /// @{
+/// Used to optimize the initial vectors of VisibilityTestResults
+#define ANKI_CFG_FRUSTUMABLE_AVERAGE_VISIBLE_RENDERABLES_COUNT 16
+#define ANKI_CFG_FRUSTUMABLE_AVERAGE_VISIBLE_LIGHTS_COUNT 8
+/// @}
+
+/// If true then we can place spatials in a thread-safe way
+#define ANKI_CFG_OCTREE_THREAD_SAFE 1
 
-/// Scene configuration constants. The enum contains absolute values and other
-/// constants that help to optimize allocations
-struct SceneConfig
-{
-	enum 
-	{
-		SCENE_NODES_AVERAGE_COUNT = 1024,
-		SCENE_ALLOCATOR_SIZE = 0x100000, // 1MB
-		SCENE_FRAME_ALLOCATOR_SIZE = 0x80000, // 512K
-
-		/// Used to optimize the initial vectors of VisibilityTestResults
-		FRUSTUMABLE_AVERAGE_VISIBLE_SCENE_NODES_COUNT = 10
-	};
-};
+/// @}
+/// @}
+
+/// @addtogroup Scene
+/// @{
 
 /// The type of the scene's allocator
 template<typename T>

+ 6 - 0
include/anki/scene/Octree.h

@@ -6,6 +6,9 @@
 #include "anki/scene/Common.h"
 #include "anki/scene/Visibility.h"
 #include <memory>
+#if ANKI_CFG_OCTREE_THREAD_SAFE
+#	include <mutex>
+#endif
 
 namespace anki {
 
@@ -81,6 +84,9 @@ private:
 	OctreeNode* parent;
 	Aabb aabb; ///< Including AABB
 	SceneVector<SceneNode*> sceneNodes;
+#if ANKI_CFG_OCTREE_THREAD_SAFE
+	std::mutex mtx;
+#endif
 
 	void addSceneNode(SceneNode* sn);
 

+ 13 - 8
include/anki/scene/Spatial.h

@@ -36,9 +36,10 @@ public:
 	};
 
 	/// Pass the collision shape here so we can avoid the virtuals
-	Spatial(CollisionShape* cs)
-		: spatialCs(cs)
-	{}
+	Spatial(const CollisionShape* cs)
+	{
+		spatialProtected.spatialCs = cs;
+	}
 
 	// Remove from current OctreeNode
 	~Spatial();
@@ -47,7 +48,7 @@ public:
 	/// @{
 	const CollisionShape& getSpatialCollisionShape() const
 	{
-		return *spatialCs;
+		return *spatialProtected.spatialCs;
 	}
 
 	const Aabb& getAabb() const
@@ -58,9 +59,10 @@ public:
 	/// Get optimal collision shape for visibility tests
 	const CollisionShape& getOptimalCollisionShape() const
 	{
-		if(spatialCs->getCollisionShapeType() == CollisionShape::CST_SPHERE)
+		if(spatialProtected.spatialCs->getCollisionShapeType() 
+			== CollisionShape::CST_SPHERE)
 		{
-			return *spatialCs;
+			return *spatialProtected.spatialCs;
 		}
 		else
 		{
@@ -95,12 +97,15 @@ public:
 	void spatialMarkForUpdate()
 	{
 		timestamp = Timestamp::getTimestamp();
-		spatialCs->toAabb(aabb);
+		spatialProtected.spatialCs->toAabb(aabb);
 		origin = (aabb.getMax() + aabb.getMin()) * 0.5;
 	}
 
 protected:
-	CollisionShape* spatialCs = nullptr;
+	struct
+	{
+		const CollisionShape* spatialCs = nullptr;
+	} spatialProtected;
 
 private:
 	U32 timestamp = Timestamp::getTimestamp();

+ 17 - 5
include/anki/scene/StaticGeometryNode.h

@@ -20,12 +20,9 @@ public:
 	StaticGeometrySpatialNode(const Obb& obb,
 		const char* name, Scene* scene); // Scene
 	/// @}
-
-public:
-	Obb obb;
 };
 
-/// Static geometry scene node
+/// Static geometry scene node patch
 class StaticGeometryPatchNode: public SceneNode, public Spatial,
 	public Renderable
 {
@@ -34,6 +31,8 @@ public:
 	/// @{
 	StaticGeometryPatchNode(const ModelPatchBase* modelPatch,
 		const char* name, Scene* scene); // Scene
+
+	~StaticGeometryPatchNode();
 	/// @}
 
 	/// @name Renderable virtuals
@@ -54,10 +53,23 @@ public:
 
 private:
 	const ModelPatchBase* modelPatch;
-	Obb obb; ///< In world space
 	SceneVector<StaticGeometrySpatialNode*> spatials;
 };
 
+/// Static geometry scene node
+class StaticGeometryNode: public SceneNode
+{
+public:
+	StaticGeometryNode(const char* filename,
+		const char* name, Scene* scene); // Scene
+
+	~StaticGeometryNode();
+
+private:
+	ModelResourcePointer model;
+	SceneVector<StaticGeometryPatchNode*> patches;
+};
+
 /// @}
 
 } // end namespace anki

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

@@ -42,9 +42,9 @@ struct VisibilityTestResults
 
 	VisibilityTestResults(const SceneAllocator<U8>& frameAlloc,
 		U32 renderablesReservedSize = 
-		SceneConfig::FRUSTUMABLE_AVERAGE_VISIBLE_SCENE_NODES_COUNT,
+		ANKI_CFG_FRUSTUMABLE_AVERAGE_VISIBLE_RENDERABLES_COUNT,
 		U32 lightsReservedSize =
-		SceneConfig::FRUSTUMABLE_AVERAGE_VISIBLE_SCENE_NODES_COUNT)
+		ANKI_CFG_FRUSTUMABLE_AVERAGE_VISIBLE_LIGHTS_COUNT)
 		: renderables(frameAlloc), lights(frameAlloc)
 	{
 		renderables.reserve(renderablesReservedSize);

+ 46 - 16
src/scene/Octree.cpp

@@ -63,7 +63,13 @@ void OctreeNode::addSceneNode(SceneNode* sn)
 		}
 
 		// ... and add to a new
+#if ANKI_CFG_OCTREE_THREAD_SAFE
+		mtx.lock();
+#endif
 		sceneNodes.push_back(sn);
+#if ANKI_CFG_OCTREE_THREAD_SAFE
+		mtx.unlock();
+#endif
 		sp->octreeNode = this;
 	}
 }
@@ -72,11 +78,21 @@ void OctreeNode::addSceneNode(SceneNode* sn)
 void OctreeNode::removeSceneNode(SceneNode* sn)
 {
 	ANKI_ASSERT(sn != nullptr);
+
+#if ANKI_CFG_OCTREE_THREAD_SAFE
+	mtx.lock();
+#endif
+
 	SceneVector<SceneNode*>::iterator it =
 		std::find(sceneNodes.begin(), sceneNodes.end(), sn);
 	ANKI_ASSERT(it != sceneNodes.end());
 
 	sceneNodes.erase(it);
+
+#if ANKI_CFG_OCTREE_THREAD_SAFE
+	mtx.unlock();
+#endif
+
 	sn->getSpatial()->octreeNode = nullptr;
 }
 
@@ -128,7 +144,7 @@ Octree& OctreeNode::getOctree()
 
 //==============================================================================
 Octree::Octree(Sector* sector_, const Aabb& aabb, U8 maxDepth_, F32 looseness_)
-	: sector(sector_),
+	:	sector(sector_),
 		maxDepth(maxDepth_ < 1 ? 1 : maxDepth_), 
 		looseness(looseness_),
 		root(aabb, sector->getSectorGroup().getScene().getAllocator(), this)
@@ -185,7 +201,8 @@ OctreeNode* Octree::placeInternal(const Aabb& aabb, U depth, OctreeNode& node)
 				// Get the node's AABB. If there is no node then calculate the
 				// AABB
 				Aabb childAabb;
-				if(node.children[id] != nullptr)
+				Bool child = node.children[id] != nullptr;
+				if(child)
 				{
 					// There is a child
 					childAabb = node.children[id]->aabb;
@@ -196,24 +213,37 @@ OctreeNode* Octree::placeInternal(const Aabb& aabb, U depth, OctreeNode& node)
 					calcAabb(i, j, k, node.aabb, childAabb);
 				}
 
-				// If aabb its completely inside the target
-				if(aabb.getMax() <= childAabb.getMax() &&
-					aabb.getMin() >= childAabb.getMin())
+				// If aabb its completely inside the target => go deeper
+				if(aabb.getMax() <= childAabb.getMax() 
+					&& aabb.getMin() >= childAabb.getMin())
 				{
-					// Go deeper
-					if(node.children[id] == nullptr)
+					if(!child)
 					{
-						SceneAllocator<U8> alloc =
-							sector->getSectorGroup().getScene().getAllocator();
-
-						// Create new node if needed
-						OctreeNode* newNode =
-							ANKI_NEW(OctreeNode, alloc,
-							childAabb, &node, alloc);
-
-						node.addChild(id, newNode);
+#if ANKI_CFG_OCTREE_THREAD_SAFE
+						node.mtx.lock();
+					
+						// Check again now that the mutex is locked
+						volatile Bool checkAgain = node.children[id] == nullptr;
+						if(checkAgain)
+#endif
+						{
+							SceneAllocator<U8> alloc =
+								sector->getSectorGroup().getScene().
+								getAllocator();
+
+							// Create new node if needed
+							OctreeNode* newNode =
+								ANKI_NEW(OctreeNode, alloc,
+								childAabb, &node, alloc);
+
+							node.addChild(id, newNode);
+						}
+#if ANKI_CFG_OCTREE_THREAD_SAFE
+						node.mtx.unlock();
+#endif
 					}
 
+					ANKI_ASSERT(node.children[id]);
 					return placeInternal(aabb, depth + 1, *node.children[id]);
 				}
 			} // k

+ 55 - 56
src/scene/Scene.cpp

@@ -33,11 +33,45 @@ struct UpdateMovablesJob: ThreadJob
 };
 
 //==============================================================================
-#if 0
+static void updateSceneNode(SceneNode& sn, F32 prevUpdateTime,
+	F32 crntTime, SectorGroup& sectorGroup)
+{
+	sn.frameUpdate(prevUpdateTime, crntTime, Timestamp::getTimestamp());
+
+	// Do some spatial stuff
+	Spatial* sp = sn.getSpatial();
+	if(sp)
+	{
+		if(sp->getSpatialTimestamp() == Timestamp::getTimestamp())
+		{
+			sectorGroup.placeSceneNode(&sn);
+		}
+		sp->disableFlags(Spatial::SF_VISIBLE_ANY);
+	}
+
+	// Do some frustumable stuff
+	Frustumable* fr = sn.getFrustumable();
+	if(fr)
+	{
+		fr->setVisibilityTestResults(nullptr);
+	}
+
+	// Do some renderable stuff
+	Renderable* r = sn.getRenderable();
+	if(r)
+	{
+		r->resetFrame();
+	}
+}
+
+//==============================================================================
 struct UpdateSceneNodesJob: ThreadJob
 {
 	Scene::Types<SceneNode>::Iterator sceneNodesBegin;
 	U32 sceneNodesCount;
+	F32 prevUpdateTime;
+	F32 crntTime;
+	SectorGroup* sectorGroup;
 
 	void operator()(U threadId, U threadsCount)
 	{
@@ -47,37 +81,10 @@ struct UpdateSceneNodesJob: ThreadJob
 		for(U64 i = start; i < end; i++)
 		{
 			SceneNode* n = *(sceneNodesBegin + i);
-
-			n->frameUpdate(prevUpdateTime, crntTime, Timestamp::getTimestamp());
-
-			// Do some spatial stuff
-			Spatial* sp = n->getSpatial();
-			if(sp)
-			{
-				if(sp->getSpatialTimestamp() == Timestamp::getTimestamp())
-				{
-					sectorGroup.placeSceneNode(n);
-				}
-				sp->disableFlags(Spatial::SF_VISIBLE_ANY);
-			}
-
-			// Do some frustumable stuff
-			Frustumable* fr = n->getFrustumable();
-			if(fr)
-			{
-				fr->setVisibilityTestResults(nullptr);
-			}
-
-			// Do some renderable stuff
-			Renderable* r = n->getRenderable();
-			if(r)
-			{
-				r->resetFrame();
-			}
+			updateSceneNode(*n, prevUpdateTime, crntTime, *sectorGroup);
 		}
 	}
-}
-#endif
+};
 
 //==============================================================================
 // Scene                                                                       =
@@ -85,12 +92,12 @@ struct UpdateSceneNodesJob: ThreadJob
 
 //==============================================================================
 Scene::Scene()
-	:	alloc(SceneConfig::SCENE_ALLOCATOR_SIZE),
-		frameAlloc(SceneConfig::SCENE_FRAME_ALLOCATOR_SIZE),
+	:	alloc(ANKI_CFG_SCENE_ALLOCATOR_SIZE),
+		frameAlloc(ANKI_CFG_SCENE_FRAME_ALLOCATOR_SIZE),
 		nodes(alloc),
 		sectorGroup(this)
 {
-	nodes.reserve(SceneConfig::SCENE_NODES_AVERAGE_COUNT);
+	nodes.reserve(ANKI_CFG_SCENE_NODES_AVERAGE_COUNT);
 
 	ambientCol = Vec3(0.1, 0.05, 0.05) * 2;
 }
@@ -147,35 +154,27 @@ void Scene::update(F32 prevUpdateTime, F32 crntTime, Renderer& renderer)
 #endif
 
 	// Then the rest
+#if 0
 	for(SceneNode* n : nodes)
 	{
-		n->frameUpdate(prevUpdateTime, crntTime, Timestamp::getTimestamp());
+		updateSceneNode(*n, prevUpdateTime, crntTime, *sectorGroup);
+	}
+#else
+	UpdateSceneNodesJob jobs2[ThreadPool::MAX_THREADS];
 
-		// Do some spatial stuff
-		Spatial* sp = n->getSpatial();
-		if(sp)
-		{
-			if(sp->getSpatialTimestamp() == Timestamp::getTimestamp())
-			{
-				sectorGroup.placeSceneNode(n);
-			}
-			sp->disableFlags(Spatial::SF_VISIBLE_ANY);
-		}
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	{
+		UpdateSceneNodesJob& job = jobs2[i];
 
-		// Do some frustumable stuff
-		Frustumable* fr = n->getFrustumable();
-		if(fr)
-		{
-			fr->setVisibilityTestResults(nullptr);
-		}
+		job.sceneNodesBegin = nodes.begin();
+		job.sceneNodesCount = nodes.size();
+		job.prevUpdateTime = prevUpdateTime;
+		job.crntTime = crntTime;
+		job.sectorGroup = &sectorGroup;
 
-		// Do some renderable stuff
-		Renderable* rb = n->getRenderable();
-		if(rb)
-		{
-			rb->resetFrame();
-		}
+		threadPool.assignNewJob(i, &job);
 	}
+#endif
 
 	doVisibilityTests(*mainCam, *this, renderer);
 

+ 49 - 5
src/scene/StaticGeometryNode.cpp

@@ -8,9 +8,9 @@ namespace anki {
 //==============================================================================
 
 //==============================================================================
-StaticGeometrySpatialNode::StaticGeometrySpatialNode(const Obb& obb_,
+StaticGeometrySpatialNode::StaticGeometrySpatialNode(const Obb& obb,
 	const char* name, Scene* scene)
-	: SceneNode(name, scene), Spatial(&obb), obb(obb_)
+	: SceneNode(name, scene), Spatial(&obb)
 {
 	sceneNodeProtected.spatial = this;
 }
@@ -23,7 +23,7 @@ StaticGeometrySpatialNode::StaticGeometrySpatialNode(const Obb& obb_,
 StaticGeometryPatchNode::StaticGeometryPatchNode(
 	const ModelPatchBase* modelPatch_, const char* name, Scene* scene)
 	:	SceneNode(name, scene),
-		Spatial(&obb),
+		Spatial(&modelPatch->getBoundingShape()),
 		Renderable(getSceneAllocator()),
 		modelPatch(modelPatch_)
 {
@@ -33,8 +33,7 @@ StaticGeometryPatchNode::StaticGeometryPatchNode(
 	ANKI_ASSERT(modelPatch);
 	Renderable::init(*this);
 
-	obb = modelPatch->getBoundingShape();
-
+	// For all submeshes create a StaticGeometrySp[atialNode
 	if(modelPatch->getSubMeshesCount() > 1)
 	{
 		spatials.reserve(modelPatch->getSubMeshesCount());
@@ -52,4 +51,49 @@ StaticGeometryPatchNode::StaticGeometryPatchNode(
 	}
 }
 
+//==============================================================================
+StaticGeometryPatchNode::~StaticGeometryPatchNode()
+{
+	for(StaticGeometrySpatialNode* node : spatials)
+	{
+		ANKI_DELETE(node, getSceneAllocator());
+	}
+}
+
+//==============================================================================
+// StaticGeometryNode                                                          =
+//==============================================================================
+
+//==============================================================================
+StaticGeometryNode::StaticGeometryNode(const char* filename,
+	const char* name, Scene* scene)
+	: SceneNode(name, scene)
+{
+	model.load(filename);
+
+	patches.reserve(model->getModelPatches().size());
+
+	U i = 0;
+	for(const ModelPatchBase* patch : model->getModelPatches())
+	{
+		std::string name_ = name + std::to_string(i);
+
+		StaticGeometryPatchNode* node = 
+			ANKI_NEW(StaticGeometryPatchNode, getSceneAllocator(),
+			patch, name_.c_str(), scene);
+
+		patches.push_back(node);
+		++i;
+	}
+}
+
+//==============================================================================
+StaticGeometryNode::~StaticGeometryNode()
+{
+	for(StaticGeometryPatchNode* patch : patches)
+	{
+		ANKI_DELETE(patch, getSceneAllocator());
+	}
+}
+
 } // end namespace anki

+ 18 - 9
src/scene/Visibility.cpp

@@ -166,18 +166,27 @@ void doVisibilityTests(SceneNode& fsn, Scene& scene,
 		renderablesSize, 
 		lightsSize);
 
+	visible->renderables.resize(renderablesSize);
+	visible->lights.resize(lightsSize);
+
 	// Append thread results
+	renderablesSize = 0;
+	lightsSize = 0;
 	for(U i = 0; i < threadPool.getThreadsCount(); i++)
 	{
-		visible->renderables.insert(
-			visible->renderables.end(),
-			jobs[i].visible->renderables.begin(), 
-			jobs[i].visible->renderables.end());
-
-		visible->lights.insert(
-			visible->lights.end(),
-			jobs[i].visible->lights.begin(), 
-			jobs[i].visible->lights.end());
+		const VisibilityTestResults& from = *jobs[i].visible;
+
+		memcpy(&visible->renderables[renderablesSize],
+			&from.renderables[0],
+			sizeof(SceneNode*) * from.renderables.size());
+
+		renderablesSize += from.renderables.size();
+
+		memcpy(&visible->lights[lightsSize],
+			&from.lights[0],
+			sizeof(SceneNode*) * from.lights.size());
+
+		lightsSize += from.lights.size();
 	}
 
 	// Set the frustumable