소스 검색

Introduce the ObjectAllocator

Panagiotis Christopoulos Charitos 7 년 전
부모
커밋
0bb82460d7
5개의 변경된 파일286개의 추가작업 그리고 20개의 파일을 삭제
  1. 1 0
      src/anki/Util.h
  2. 67 11
      src/anki/scene/Octree.cpp
  3. 26 9
      src/anki/scene/Octree.h
  4. 58 0
      src/anki/util/ObjectAllocator.h
  5. 134 0
      src/anki/util/ObjectAllocator.inl.h

+ 1 - 0
src/anki/Util.h

@@ -35,6 +35,7 @@
 #include <anki/util/Visitor.h>
 #include <anki/util/Visitor.h>
 #include <anki/util/INotify.h>
 #include <anki/util/INotify.h>
 #include <anki/util/SparseArray.h>
 #include <anki/util/SparseArray.h>
+#include <anki/util/ObjectAllocator.h>
 
 
 /// @defgroup util Utilities (like STL)
 /// @defgroup util Utilities (like STL)
 
 

+ 67 - 11
src/anki/scene/Octree.cpp

@@ -10,31 +10,87 @@
 namespace anki
 namespace anki
 {
 {
 
 
-void Octree::init(const Vec3& sceneMin, const Vec3& sceneMax, U32 maxDepth)
+void Octree::init(const Vec3& sceneAabbMin, const Vec3& sceneAabbMax, U32 maxDepth)
 {
 {
-	ANKI_ASSERT(sceneMin < sceneMax);
+	ANKI_ASSERT(sceneAabbMin < sceneAabbMax);
 	ANKI_ASSERT(maxDepth > 0);
 	ANKI_ASSERT(maxDepth > 0);
 
 
 	m_maxDepth = maxDepth;
 	m_maxDepth = maxDepth;
+	m_sceneAabbMin = sceneAabbMin;
+	m_sceneAabbMax = sceneAabbMax;
 
 
 	OctreeLeaf& root = newLeaf();
 	OctreeLeaf& root = newLeaf();
-	root.m_aabbMin = sceneMin;
-	root.m_aabbMax = sceneMax;
 	m_rootLeaf = &root;
 	m_rootLeaf = &root;
 }
 }
 
 
-void Octree::placeElement(const Aabb& volume, OctreeElement& element)
+void Octree::placeElement(const Aabb& volume, OctreeElement* element)
 {
 {
 	ANKI_ASSERT(m_rootLeaf);
 	ANKI_ASSERT(m_rootLeaf);
-	placeElementRecursive(volume, element, *m_rootLeaf);
+	ANKI_ASSERT(element);
+	placeElementRecursive(volume, element, *m_rootLeaf, m_sceneAabbMin, m_sceneAabbMax);
 }
 }
 
 
-void Octree::placeElementRecursive(const Aabb& volume, OctreeElement& element, OctreeLeaf& parent)
+void Octree::placeElementRecursive(
+	const Aabb& volume, OctreeElement* element, OctreeLeaf& parent, const Vec3& aabbMin, const Vec3& aabbMax)
 {
 {
-	ANKI_ASSERT(testCollisionShapes(volume, Aabb(Vec4(parent.m_aabbMin, 0.0f), Vec4(parent.m_aabbMax, 0.0f)))
-				&& "Should be inside");
+	ANKI_ASSERT(testCollisionShapes(volume, Aabb(Vec4(aabbMin, 0.0f), Vec4(aabbMax, 0.0f))) && "Should be inside");
 
 
-	const Vec3 center = (parent.m_aabbMax + parent.m_aabbMin) / 2.0f;
+	const Vec4& vMin = volume.getMin();
+	const Vec4& vMax = volume.getMax();
+	const Vec3 center = (aabbMax + aabbMin) / 2.0f;
+
+	LeafMask maskX;
+	if(vMin.x() > center.x())
+	{
+		// Only right
+		maskX = LeafMask::PX_PY_PZ | LeafMask::PX_PY_NZ | LeafMask::PX_NY_PZ | LeafMask::PX_NY_NZ;
+	}
+	else if(vMax.x() < center.x())
+	{
+		// Only left
+		maskX = LeafMask::NX_PY_PZ | LeafMask::NX_PY_NZ | LeafMask::NX_NY_PZ | LeafMask::NX_NY_NZ;
+	}
+	else
+	{
+		maskX = LeafMask::ALL;
+	}
+
+	LeafMask maskY;
+	if(vMin.y() > center.y())
+	{
+		// Only top
+		maskY = LeafMask::PX_PY_PZ | LeafMask::PX_PY_NZ | LeafMask::NX_PY_PZ | LeafMask::NX_PY_NZ;
+	}
+	else if(vMax.y() < center.y())
+	{
+		// Only bottom
+		maskY = LeafMask::PX_NY_PZ | LeafMask::PX_NY_NZ | LeafMask::NX_NY_PZ | LeafMask::NX_NY_NZ;
+	}
+	else
+	{
+		maskY = LeafMask::ALL;
+	}
+
+	LeafMask maskZ;
+	if(vMin.z() > center.z())
+	{
+		// Only front
+		maskZ = LeafMask::PX_PY_PZ | LeafMask::PX_NY_PZ | LeafMask::NX_PY_PZ | LeafMask::NX_NY_PZ;
+	}
+	else if(vMax.z() < center.z())
+	{
+		// Only back
+		maskZ = LeafMask::PX_PY_NZ | LeafMask::PX_NY_NZ | LeafMask::NX_PY_NZ | LeafMask::NX_NY_NZ;
+	}
+	else
+	{
+		maskZ = LeafMask::ALL;
+	}
+
+	LeafMask maskUnion = maskX & maskY & maskZ;
+	ANKI_ASSERT(!!maskUnion);
+
+	// TODO
 }
 }
 
 
 OctreeLeaf& Octree::newLeaf()
 OctreeLeaf& Octree::newLeaf()
@@ -101,4 +157,4 @@ void Octree::releaseLeaf(OctreeLeaf* leaf)
 	ANKI_ASSERT(!"Not found");
 	ANKI_ASSERT(!"Not found");
 }
 }
 
 
-} // end namespace anki
+} // end namespace anki

+ 26 - 9
src/anki/scene/Octree.h

@@ -9,6 +9,7 @@
 #include <anki/Math.h>
 #include <anki/Math.h>
 #include <anki/collision/Forward.h>
 #include <anki/collision/Forward.h>
 #include <anki/util/WeakArray.h>
 #include <anki/util/WeakArray.h>
+#include <anki/util/Enum.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -22,10 +23,8 @@ class OctreeLeaf;
 /// XXX
 /// XXX
 class OctreeElement
 class OctreeElement
 {
 {
-protected:
-	OctreeElement* m_prev = nullptr;
-	OctreeElement* m_next = nullptr;
-	OctreeLeaf* m_leaf = nullptr;
+public:
+	Bool8 m_visited = false;
 };
 };
 
 
 /// XXX
 /// XXX
@@ -37,8 +36,6 @@ public:
 	OctreeElement* m_first;
 	OctreeElement* m_first;
 	OctreeElement* m_last;
 	OctreeElement* m_last;
 	Array<OctreeLeaf*, 8> m_leafs;
 	Array<OctreeLeaf*, 8> m_leafs;
-	Vec3 m_aabbMin;
-	Vec3 m_aabbMax;
 };
 };
 
 
 /// Octree for visibility tests.
 /// Octree for visibility tests.
@@ -52,11 +49,11 @@ public:
 
 
 	~Octree();
 	~Octree();
 
 
-	void init(const Vec3& sceneMin, const Vec3& sceneMax, U32 maxDepth);
+	void init(const Vec3& sceneAabbMin, const Vec3& sceneAabbMax, U32 maxDepth);
 
 
 	/// Place or re-place an element in the tree.
 	/// Place or re-place an element in the tree.
 	/// @note It's thread-safe.
 	/// @note It's thread-safe.
-	void placeElement(const Aabb& volume, OctreeElement& element);
+	void placeElement(const Aabb& volume, OctreeElement* element);
 
 
 	/// Remove an element from the tree.
 	/// Remove an element from the tree.
 	/// @note It's thread-safe.
 	/// @note It's thread-safe.
@@ -65,6 +62,8 @@ public:
 private:
 private:
 	SceneAllocator<U8> m_alloc;
 	SceneAllocator<U8> m_alloc;
 	U32 m_maxDepth = 0;
 	U32 m_maxDepth = 0;
+	Vec3 m_sceneAabbMin = Vec3(0.0f);
+	Vec3 m_sceneAabbMax = Vec3(0.0f);
 
 
 	/// Keep the allocations of leafes tight because we want quite alot of them.
 	/// Keep the allocations of leafes tight because we want quite alot of them.
 	class LeafStorage
 	class LeafStorage
@@ -81,10 +80,28 @@ private:
 
 
 	OctreeLeaf* m_rootLeaf = nullptr;
 	OctreeLeaf* m_rootLeaf = nullptr;
 
 
+	/// P: Stands for positive and N: Negative
+	enum class LeafMask : U8
+	{
+		PX_PY_PZ = 1 << 0,
+		PX_PY_NZ = 1 << 1,
+		PX_NY_PZ = 1 << 2,
+		PX_NY_NZ = 1 << 3,
+		NX_PY_PZ = 1 << 4,
+		NX_PY_NZ = 1 << 5,
+		NX_NY_PZ = 1 << 6,
+		NX_NY_NZ = 1 << 7,
+
+		NONE = 0,
+		ALL = PX_PY_PZ | PX_PY_NZ | PX_NY_PZ | PX_NY_NZ | NX_PY_PZ | NX_PY_NZ | NX_NY_PZ | NX_NY_NZ,
+	};
+	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(LeafMask, friend)
+
 	OctreeLeaf& newLeaf();
 	OctreeLeaf& newLeaf();
 	void releaseLeaf(OctreeLeaf* leaf);
 	void releaseLeaf(OctreeLeaf* leaf);
 
 
-	void placeElementRecursive(const Aabb& volume, OctreeElement& element, OctreeLeaf& parent);
+	void placeElementRecursive(
+		const Aabb& volume, OctreeElement* element, OctreeLeaf& parent, const Vec3& aabbMin, const Vec3& aabbMax);
 };
 };
 /// @}
 /// @}
 
 

+ 58 - 0
src/anki/util/ObjectAllocator.h

@@ -0,0 +1,58 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/util/Array.h>
+
+namespace anki
+{
+
+/// @addtogroup util
+/// @{
+
+/// A simple allocator of objects of the same type.
+template<typename T, U32 T_CHUNK_SIZE = 64>
+class ObjectAllocator
+{
+public:
+	using Value = T;
+	static constexpr U32 CHUNK_SIZE = T_CHUNK_SIZE;
+
+	/// Allocate and construct a new object instance.
+	template<typename TAlloc, typename... TArgs>
+	Value* newInstance(TAlloc& alloc, TArgs&&... args);
+
+	/// Delete an object.
+	template<typename TAlloc>
+	void deleteInstance(TAlloc& alloc, Value* obj);
+
+private:
+	/// Storage with equal properties as the Value.
+	struct alignas(alignof(Value)) Object
+	{
+		U8 m_storage[sizeof(Value)];
+	};
+
+	/// A  single allocation.
+	class Chunk
+	{
+	public:
+		Array<Object, CHUNK_SIZE> m_objects;
+		Array<U32, CHUNK_SIZE> m_unusedStack;
+		U32 m_unusedCount;
+
+		Chunk* m_next = nullptr;
+		Chunk* m_prev = nullptr;
+	};
+
+	Chunk* m_chunksHead = nullptr;
+	Chunk* m_chunksTail = nullptr;
+};
+/// @}
+
+} // end namespace anki
+
+#include <anki/util/ObjectAllocator.inl.h>

+ 134 - 0
src/anki/util/ObjectAllocator.inl.h

@@ -0,0 +1,134 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/util/ObjectAllocator.h>
+
+namespace anki
+{
+
+template<typename T, U32 T_CHUNK_SIZE>
+template<typename TAlloc, typename... TArgs>
+T* ObjectAllocator<T, T_CHUNK_SIZE>::newInstance(TAlloc& alloc, TArgs&&... args)
+{
+	Value* out = nullptr;
+
+	// Try find one in the chunks
+	Chunk* chunk = m_chunksHead;
+	while(chunk)
+	{
+		if(chunk->m_unusedCount > 0)
+		{
+			// Pop an element
+			--chunk->m_unusedCount;
+			out = &chunk->m_objects[chunk->m_unusedStack[chunk->m_unusedCount]];
+			break;
+		}
+
+		chunk = chunk->m_next;
+	}
+
+	if(out == nullptr)
+	{
+		// Need to create a new chunk
+
+		// Create the chunk
+		Chunk* newChunk = alloc.template newInstance<Chunk>();
+		newChunk->m_unusedCount = CHUNK_SIZE;
+
+		for(U i = 0; i < CHUNK_SIZE; ++i)
+		{
+			newChunk->m_unusedStack[i] = CHUNK_SIZE - (i + 1);
+		}
+
+		if(m_chunksTail)
+		{
+			ANKI_ASSERT(m_chunksHead);
+			newChunk->m_prev = m_chunksTail;
+			m_chunksTail->m_next = newChunk;
+			m_chunksTail = newChunk;
+		}
+		else
+		{
+			m_chunksTail = m_chunksHead = newChunk;
+		}
+
+		// Allocate one object
+		out = reinterpret_cast<Value*>(&chunk->m_objects[0].m_storage);
+		--chunk->m_unusedCount;
+	}
+
+	ANKI_ASSERT(out);
+
+	// Construct it
+	::new(out) Value(std::forward<TArgs>(args)...);
+
+	return out;
+}
+
+template<typename T, U32 T_CHUNK_SIZE>
+template<typename TAlloc>
+void ObjectAllocator<T, T_CHUNK_SIZE>::deleteInstance(TAlloc& alloc, Value* obj)
+{
+	ANKI_ASSERT(obj);
+
+	// Find the chunk the obj is in
+	const Object* const mem = reinterpret_cast<Object*>(obj);
+	Chunk* chunk = m_chunksHead;
+	while(chunk)
+	{
+		const Object* const begin = chunk->m_objects.getBegin();
+		const Object* const end = chunk->m_objects.getEnd();
+		if(mem >= begin && mem < end)
+		{
+			// Found it
+
+			ANKI_ASSERT(chunk->m_unusedCount < CHUNK_SIZE);
+			const U idx = mem - begin;
+
+			// Destroy the object
+			obj->~Value();
+
+			// Remove from the chunk
+			chunk->m_unusedStack[chunk->m_unusedCount] = idx;
+			++chunk->m_unusedCount;
+
+			// If chunk is empty delete it
+			if(chunk->m_unusedCount == CHUNK_SIZE)
+			{
+				if(chunk == m_chunksTail)
+				{
+					m_chunksTail = chunk->m_prev;
+				}
+
+				if(chunk == m_chunksHead)
+				{
+					m_chunksHead = chunk->m_next;
+				}
+
+				if(chunk->m_prev)
+				{
+					ANKI_ASSERT(chunk->m_prev->m_next == chunk);
+					chunk->m_prev->m_next = chunk->m_next;
+				}
+
+				if(chunk->m_next)
+				{
+					ANKI_ASSERT(chunk->m_next->m_prev == chunk);
+					chunk->m_next->m_prev = chunk->m_prev;
+				}
+
+				alloc.deleteInstance(chunk);
+			}
+
+			break;
+		}
+
+		chunk = chunk->m_next;
+	}
+
+	ANKI_ASSERT(chunk != nullptr);
+}
+
+} // end namespace anki