Bläddra i källkod

Finalizing the chain allocator & some GL renaming

Panagiotis Christopoulos Charitos 12 år sedan
förälder
incheckning
43ffa8ad16

+ 2 - 2
include/anki/gl/Ogl.h → include/anki/gl/Common.h

@@ -1,5 +1,5 @@
-#ifndef ANKI_GL_OGL_H
-#define ANKI_GL_OGL_H
+#ifndef ANKI_GL_COMMON_H
+#define ANKI_GL_COMMON_H
 
 #include "anki/Config.h"
 

+ 1 - 1
include/anki/gl/Drawcall.h

@@ -1,7 +1,7 @@
 #ifndef ANKI_GL_DRAWCALL_H
 #define ANKI_GL_DRAWCALL_H
 
-#include "anki/gl/Ogl.h"
+#include "anki/gl/Common.h"
 #include "anki/util/StdTypes.h"
 #include "anki/util/Array.h"
 

+ 2 - 2
include/anki/gl/Gl.h

@@ -4,7 +4,7 @@
 #ifndef ANKI_GL_GL_H
 #define ANKI_GL_GL_H
 
-#include "anki/gl/BufferObject.h"
+#include "anki/gl/GlBuffer.h"
 #include "anki/gl/Fbo.h"
 #include "anki/gl/GlException.h"
 #include "anki/gl/GlState.h"
@@ -14,6 +14,6 @@
 #include "anki/gl/Vao.h"
 #include "anki/gl/Drawcall.h"
 
-#include "anki/gl/Ogl.h"
+#include "anki/gl/Common.h"
 
 #endif

+ 7 - 7
include/anki/gl/BufferObject.h → include/anki/gl/GlBuffer.h

@@ -1,5 +1,5 @@
-#ifndef ANKI_GL_BUFFER_OBJECT_H
-#define ANKI_GL_BUFFER_OBJECT_H
+#ifndef ANKI_GL_GL_BUFFER_H
+#define ANKI_GL_GL_BUFFER_H
 
 #include "anki/gl/GlObject.h"
 
@@ -10,7 +10,7 @@ namespace anki {
 	
 /// A wrapper for OpenGL buffer objects (vertex arrays, texture buffers etc)
 /// to prevent us from making idiotic errors
-class BufferObject: public GlObject
+class GlBuffer: public GlObject
 {
 public:
 	typedef GlObject Base;
@@ -19,24 +19,24 @@ public:
 	/// @{
 
 	/// Default
-	BufferObject()
+	GlBuffer()
 	{}
 
 	/// Move
-	BufferObject(BufferObject&& b)	
+	GlBuffer(GlBuffer&& b)	
 	{
 		*this = std::move(b);
 	}
 
 	/// It deletes the BO
-	~BufferObject()
+	~GlBuffer()
 	{
 		destroy();
 	}
 	/// @}
 
 	/// Move
-	BufferObject& operator=(BufferObject&& b);
+	GlBuffer& operator=(GlBuffer&& b);
 
 	/// @name Accessors
 	/// @{

+ 1 - 1
include/anki/gl/GlObject.h

@@ -1,7 +1,7 @@
 #ifndef ANKI_GL_GL_OBJECT_H
 #define ANKI_GL_GL_OBJECT_H
 
-#include "anki/gl/Ogl.h"
+#include "anki/gl/Common.h"
 #include "anki/gl/GlException.h"
 #include "anki/Config.h"
 #include "anki/util/NonCopyable.h"

+ 1 - 1
include/anki/gl/GlState.h

@@ -2,7 +2,7 @@
 #define ANKI_GL_GL_STATE_H
 
 #include "anki/util/Singleton.h"
-#include "anki/gl/Ogl.h"
+#include "anki/gl/Common.h"
 #include "anki/util/Assert.h"
 #include "anki/util/StdTypes.h"
 #include "anki/util/Array.h"

+ 1 - 1
include/anki/gl/Texture.h

@@ -7,7 +7,7 @@
 #include "anki/util/Vector.h"
 #include "anki/util/StdTypes.h"
 #include "anki/util/NonCopyable.h"
-#include "anki/gl/Ogl.h"
+#include "anki/gl/Common.h"
 #include <cstdlib>
 #include <cstring>
 #include <limits>

+ 4 - 4
include/anki/gl/Vao.h

@@ -7,7 +7,7 @@
 namespace anki {
 
 class ShaderProgramAttributeVariable;
-class BufferObject;
+class GlBuffer;
 
 /// @addtogroup OpenGL
 /// @{
@@ -81,7 +81,7 @@ public:
 	/// @param offset Specifies a offset of the first component of the
 	///        first generic vertex attribute in the array
 	void attachArrayBufferVbo(
-	    const BufferObject* vbo,
+	    const GlBuffer* vbo,
 	    const ShaderProgramAttributeVariable& attribVar,
 	    const PtrSize size,
 	    const GLenum type,
@@ -104,7 +104,7 @@ public:
 	/// @param pointer Specifies a offset of the first component of the
 	///        first generic vertex attribute in the array
 	void attachArrayBufferVbo(
-	    const BufferObject* vbo,
+	    const GlBuffer* vbo,
 	    const GLint attribVarLocation,
 	    const PtrSize size,
 	    const GLenum type,
@@ -119,7 +119,7 @@ public:
 	}
 
 	/// Attach an element array buffer VBO
-	void attachElementArrayBufferVbo(const BufferObject* vbo);
+	void attachElementArrayBufferVbo(const GlBuffer* vbo);
 
 	/// Bind it
 	void bind() const

+ 2 - 2
include/anki/renderer/DebugDrawer.h

@@ -2,7 +2,7 @@
 #define ANKI_RENDERER_DEBUG_DRAWER_H
 
 #include "anki/Math.h"
-#include "anki/gl/BufferObject.h"
+#include "anki/gl/GlBuffer.h"
 #include "anki/gl/Vao.h"
 #include "anki/resource/Resource.h"
 #include "anki/collision/CollisionShape.h"
@@ -64,7 +64,7 @@ private:
 
 	Array<Vertex, MAX_POINTS_PER_DRAW> clientVerts;
 
-	BufferObject vbo;
+	GlBuffer vbo;
 	Vao vao;
 
 	/// This is a container of some precalculated spheres. Its a map that

+ 2 - 2
include/anki/renderer/Hdr.h

@@ -3,7 +3,7 @@
 
 #include "anki/renderer/RenderingPass.h"
 #include "anki/gl/Fbo.h"
-#include "anki/gl/BufferObject.h"
+#include "anki/gl/GlBuffer.h"
 #include "anki/resource/TextureResource.h"
 #include "anki/resource/ShaderProgramResource.h"
 #include "anki/resource/Resource.h"
@@ -69,7 +69,7 @@ private:
 	Timestamp parameterUpdateTimestamp = getGlobTimestamp();
 	/// When the commonUbo got updated
 	Timestamp commonUboUpdateTimestamp = getGlobTimestamp();
-	BufferObject commonUbo;
+	GlBuffer commonUbo;
 
 	void initFbo(Fbo& fbo, Texture& fai);
 	void initInternal(const RendererInitializer& initializer);

+ 6 - 6
include/anki/renderer/Is.h

@@ -66,19 +66,19 @@ private:
 	PtrSize uboAlignment = MAX_PTR_SIZE; ///< Cache the value here
 
 	/// Contains common data for all shader programs
-	BufferObject commonUbo;
+	GlBuffer commonUbo;
 
 	/// Track the updates of commonUbo
 	Timestamp commonUboUpdateTimestamp = getGlobTimestamp();
 
 	/// Contains all the lights
-	BufferObject lightsUbo;
+	GlBuffer lightsUbo;
 
 	/// Contains the number of lights per tile
-	BufferObject tilesBuffer;
+	GlBuffer tilesBuffer;
 
-	BufferObject pointLightIndicesBuffer;
-	BufferObject spotLightIndicesBuffer;
+	GlBuffer pointLightIndicesBuffer;
+	GlBuffer spotLightIndicesBuffer;
 	/// @}
 
 	// Light shaders
@@ -98,7 +98,7 @@ private:
 
 	/// @name For drawing a quad into the active framebuffer
 	/// @{
-	BufferObject quadPositionsVbo; ///< The VBO for quad positions
+	GlBuffer quadPositionsVbo; ///< The VBO for quad positions
 	Vao quadVao; ///< This VAO is used everywhere except material stage
 	/// @}
 

+ 1 - 1
include/anki/renderer/Lf.h

@@ -39,7 +39,7 @@ private:
 	TextureResourcePointer lensDirtTex;
 	U8 maxFlaresPerLight;
 	U8 maxLightsWithFlares;
-	BufferObject flareDataUbo;
+	GlBuffer flareDataUbo;
 	const ShaderProgramUniformBlock* ublock;
 	
 

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

@@ -266,7 +266,7 @@ private:
 
 	/// @name For drawing a quad into the active framebuffer
 	/// @{
-	BufferObject quadPositionsVbo; ///< The VBO for quad positions
+	GlBuffer quadPositionsVbo; ///< The VBO for quad positions
 	Vao quadVao; ///< This VAO is used everywhere except material stage
 	/// @}
 

+ 2 - 2
include/anki/renderer/Ssao.h

@@ -7,7 +7,7 @@
 #include "anki/resource/Resource.h"
 #include "anki/gl/Fbo.h"
 #include "anki/gl/Texture.h"
-#include "anki/gl/BufferObject.h"
+#include "anki/gl/GlBuffer.h"
 #include "anki/core/Timestamp.h"
 
 namespace anki {
@@ -49,7 +49,7 @@ private:
 	ShaderProgramResourcePointer hblurSProg;
 	ShaderProgramResourcePointer vblurSProg;
 	Timestamp commonUboUpdateTimestamp = getGlobTimestamp();
-	BufferObject commonUbo;
+	GlBuffer commonUbo;
 
 	static void createFbo(Fbo& fbo, Texture& fai, U width, U height);
 	void initInternal(const RendererInitializer& initializer);

+ 1 - 1
include/anki/renderer/Tiler.h

@@ -64,7 +64,7 @@ private:
 	Fbo fbo;
 
 	/// PBO buffer that is used to read the data of fai asynchronously
-	BufferObject pbo;
+	GlBuffer pbo;
 
 	/// Main shader program
 	ShaderProgramResourcePointer prog;

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

@@ -8,7 +8,6 @@
 #include "anki/util/Visitor.h"
 #include "anki/util/Dictionary.h"
 #include "anki/util/NonCopyable.h"
-#include "anki/gl/Ogl.h"
 #include <memory>
 
 namespace anki {

+ 4 - 4
include/anki/resource/Mesh.h

@@ -2,7 +2,7 @@
 #define ANKI_RESOURCE_MESH_H
 
 #include "anki/Math.h"
-#include "anki/gl/BufferObject.h"
+#include "anki/gl/GlBuffer.h"
 #include "anki/collision/Obb.h"
 #include "anki/util/Vector.h"
 
@@ -85,7 +85,7 @@ public:
 
 	/// Get info on how to attach a VBO to a VAO
 	void getVboInfo(
-		const VertexAttribute attrib, const BufferObject*& vbo,
+		const VertexAttribute attrib, const GlBuffer*& vbo,
 		U32& size, GLenum& type, U32& stride, U32& offset) const;
 
 	/// Helper function for correct loading
@@ -110,8 +110,8 @@ protected:
 	U8 texChannelsCount;
 	Bool8 weights;
 
-	BufferObject vbo;
-	BufferObject indicesVbo;
+	GlBuffer vbo;
+	GlBuffer indicesVbo;
 
 	/// Create the VBOs using the mesh data
 	void createVbos(const MeshLoader& loader);

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

@@ -220,7 +220,7 @@ private:
 	U32 aliveParticlesCountDraw = 0;
 
 	Vao vao; ///< Hold the VBO
-	BufferObject vbo; ///< Hold the vertex data
+	GlBuffer vbo; ///< Hold the vertex data
 	SceneVector<F32> clientBuffer;
 
 	U8 simulationType = UNDEFINED_SIMULATION;

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

@@ -4,7 +4,7 @@
 #include "anki/scene/Property.h"
 #include "anki/scene/Common.h"
 #include "anki/scene/SceneComponent.h"
-#include "anki/gl/BufferObject.h"
+#include "anki/gl/GlBuffer.h"
 #include "anki/resource/Material.h"
 #include "anki/resource/Model.h"
 

+ 60 - 51
include/anki/util/Allocator.h

@@ -6,7 +6,6 @@
 #include "anki/util/Memory.h"
 #include <cstddef> // For ptrdiff_t
 #include <utility> // For forward
-#include <memory> // For shared_ptr
 
 #define ANKI_DEBUG_ALLOCATORS ANKI_DEBUG
 #define ANKI_PRINT_ALLOCATOR_MESSAGES 1
@@ -230,26 +229,25 @@ inline bool operator!=(const HeapAllocator<T1>&, const AnotherAllocator&)
 	return true;
 }
 
-/// Stack based allocator
+/// Pool based allocator
+///
+/// This is a template that accepts memory pools with a specific interface
 ///
 /// @tparam T The type
 /// @tparam deallocationFlag If true then the allocator will try to deallocate
-///                          the memory. This is extremely important to
+///                          the memory. It is extremely important to
 ///                          understand when it should be true. See notes
-/// @tparam alignmentBytes Set the alighment in bytes
 ///
-/// @note The deallocationFlag can brake the allocator when the deallocations
-///       are not in the correct order. For example when deallocationFlag==true
-///       and the allocator is used in vector it is likely to fail.
+/// @note The deallocationFlag can brake the allocator when used with stack 
+///       pools and the deallocations are not in the correct order.
 ///
 /// @note Don't ever EVER remove the double copy constructor and the double
 ///       operator=. The compiler will create defaults
-template<typename T, Bool deallocationFlag = false,
-	U32 alignmentBytes = ANKI_SAFE_ALIGNMENT>
-class StackAllocator
+template<typename T, typename TPool, Bool deallocationFlag = false>
+class GenericPoolAllocator
 {
-	template<typename U, Bool deallocationFlag_, U32 alignmentBytes_>
-	friend class StackAllocator;
+	template<typename U, typename TPool_, Bool deallocationFlag_>
+	friend class GenericPoolAllocator;
 
 public:
 	// Typedefs
@@ -262,40 +260,43 @@ public:
 	typedef T value_type;
 
 	/// Default constructor
-	StackAllocator() throw()
+	GenericPoolAllocator() throw()
 	{}
+
 	/// Copy constructor
-	StackAllocator(const StackAllocator& b) throw()
+	GenericPoolAllocator(const GenericPoolAllocator& b) throw()
 	{
 		*this = b;
 	}
+
 	/// Copy constructor
 	template<typename U>
-	StackAllocator(
-		const StackAllocator<U, deallocationFlag, alignmentBytes>& b) throw()
+	GenericPoolAllocator(const GenericPoolAllocator<
+		U, TPool, deallocationFlag>& b) throw()
 	{
 		*this = b;
 	}
-	/// Constuctor with size
-	StackAllocator(size_type size) throw()
-	{
-		mpool.reset(new StackMemoryPool(size, alignmentBytes));
-	}
+
+	/// Constuctor that accepts a pool
+	GenericPoolAllocator(const TPool& pool) throw()
+		: mpool(pool)
+	{}
 
 	/// Destructor
-	~StackAllocator()
+	~GenericPoolAllocator()
 	{}
 
 	/// Copy
-	StackAllocator& operator=(const StackAllocator& b)
+	GenericPoolAllocator& operator=(const GenericPoolAllocator& b)
 	{
 		mpool = b.mpool;
 		return *this;
 	}
+
 	/// Copy
 	template<typename U>
-	StackAllocator& operator=(
-		const StackAllocator<U, deallocationFlag, alignmentBytes>& b)
+	GenericPoolAllocator& operator=(const GenericPoolAllocator<
+		U, TPool, deallocationFlag>& b)
 	{
 		mpool = b.mpool;
 		return *this;
@@ -306,6 +307,7 @@ public:
 	{
 		return &x;
 	}
+
 	/// Get the const address of a const reference
 	const_pointer address(const_reference x) const
 	{
@@ -315,11 +317,10 @@ public:
 	/// Allocate memory
 	pointer allocate(size_type n, const void* hint = 0)
 	{
-		ANKI_ASSERT(mpool != nullptr);
 		(void)hint;
 		size_type size = n * sizeof(value_type);
 		
-		void* out = mpool->allocate(size);
+		void* out = mpool.allocate(size);
 
 		if(out != nullptr)
 		{
@@ -336,18 +337,17 @@ public:
 	/// Deallocate memory
 	void deallocate(void* p, size_type n)
 	{
-		ANKI_ASSERT(mpool != nullptr);
 		(void)p;
 		(void)n;
 
 		if(deallocationFlag)
 		{
-			Bool ok = mpool->free(p);
+			Bool ok = mpool.free(p);
 
 			if(!ok)
 			{
 				throw ANKI_EXCEPTION("Freeing wrong pointer. "
-					"The deallocations on StackAllocator should be in order");
+					"Pool's free returned false");
 			}
 		}
 	}
@@ -358,6 +358,7 @@ public:
 		// Placement new
 		new ((T*)p) T(val);
 	}
+
 	/// Call constructor with many arguments
 	template<typename U, typename... Args>
 	void construct(U* p, Args&&... args)
@@ -371,6 +372,7 @@ public:
 	{
 		p->~T();
 	}
+
 	/// Call destructor
 	template<typename U>
 	void destroy(U* p)
@@ -381,28 +383,25 @@ public:
 	/// Get the max allocation size
 	size_type max_size() const
 	{
-		ANKI_ASSERT(mpool != nullptr);
-		return mpool->getSize();
+		return mpool.getTotalSize();
 	}
 
 	/// A struct to rebind the allocator to another allocator of type U
 	template<typename U>
 	struct rebind
 	{
-		typedef StackAllocator<U, deallocationFlag, alignmentBytes> other;
+		typedef GenericPoolAllocator<U, TPool, deallocationFlag> other;
 	};
 
 	/// Reinit the allocator. All existing allocated memory will be lost
 	void reset()
 	{
-		ANKI_ASSERT(mpool != nullptr);
-		mpool->reset();
+		mpool.reset();
 	}
 
-	const StackMemoryPool& getMemoryPool() const
+	const TPool& getMemoryPool() const
 	{
-		ANKI_ASSERT(mpool != nullptr);
-		return *mpool;
+		return mpool;
 	}
 
 	/// Allocate a new object and call it's constructor
@@ -460,47 +459,57 @@ public:
 	}
 
 private:
-	std::shared_ptr<StackMemoryPool> mpool;
+	TPool mpool;
 };
 
 /// Another allocator of the same type can deallocate from this one
-template<typename T1, typename T2, Bool deallocationFlag, U32 alignmentBytes>
+template<typename T1, typename T2, typename TPool, Bool deallocationFlag>
 inline bool operator==(
-	const StackAllocator<T1, deallocationFlag, alignmentBytes>&,
-	const StackAllocator<T2, deallocationFlag, alignmentBytes>&)
+	const GenericPoolAllocator<T1, TPool, deallocationFlag>&,
+	const GenericPoolAllocator<T2, TPool, deallocationFlag>&)
 {
 	return true;
 }
 
 /// Another allocator of the another type cannot deallocate from this one
-template<typename T1, typename AnotherAllocator, Bool deallocationFlag,
-	U32 alignmentBytes>
+template<typename T1, typename AnotherAllocator, typename TPool, 
+	Bool deallocationFlag>
 inline bool operator==(
-	const StackAllocator<T1, deallocationFlag, alignmentBytes>&,
+	const GenericPoolAllocator<T1, TPool, deallocationFlag>&,
 	const AnotherAllocator&)
 {
 	return false;
 }
 
 /// Another allocator of the same type can deallocate from this one
-template<typename T1, typename T2, Bool deallocationFlag, U32 alignmentBytes>
+template<typename T1, typename T2, typename TPool, Bool deallocationFlag>
 inline bool operator!=(
-	const StackAllocator<T1, deallocationFlag, alignmentBytes>&,
-	const StackAllocator<T2, deallocationFlag, alignmentBytes>&)
+	const GenericPoolAllocator<T1, TPool, deallocationFlag>&,
+	const GenericPoolAllocator<T2, TPool, deallocationFlag>&)
 {
 	return false;
 }
 
 /// Another allocator of the another type cannot deallocate from this one
-template<typename T1, typename AnotherAllocator, Bool deallocationFlag,
-	U32 alignmentBytes>
+template<typename T1, typename AnotherAllocator, typename TPool, 
+	Bool deallocationFlag>
 inline bool operator!=(
-	const StackAllocator<T1, deallocationFlag, alignmentBytes>&,
+	const GenericPoolAllocator<T1, TPool, deallocationFlag>&,
 	const AnotherAllocator&)
 {
 	return true;
 }
 
+/// Allocator that uses a StackMemoryPool
+template<typename T, Bool deallocationFlag = false>
+using StackAllocator = 
+	GenericPoolAllocator<T, StackMemoryPool, deallocationFlag>;
+
+/// Allocator that uses a ChainMemoryPool
+template<typename T, Bool deallocationFlag = true>
+using ChainAllocator = 
+	GenericPoolAllocator<T, ChainMemoryPool, deallocationFlag>;
+
 /// @}
 /// @}
 

+ 65 - 71
include/anki/util/Memory.h

@@ -2,13 +2,7 @@
 #define ANKI_UTIL_MEMORY_H
 
 #include "anki/util/StdTypes.h"
-#include "anki/util/Assert.h"
-#include "anki/util/NonCopyable.h"
-#include "anki/util/Thread.h"
-#include <atomic>
-#include <vector>
 #include <memory>
-#include <algorithm> // For the std::move
 
 namespace anki {
 
@@ -26,30 +20,38 @@ void freeAligned(void* ptr);
 /// Thread safe memory pool. It's a preallocated memory pool that is used for 
 /// memory allocations on top of that preallocated memory. It is mainly used by 
 /// fast stack allocators
-class StackMemoryPool: public NonCopyable
+class StackMemoryPool
 {
+	friend class ChainMemoryPool;
+
 public:
 	/// Default constructor
-	StackMemoryPool(PtrSize size, U32 alignmentBytes = ANKI_SAFE_ALIGNMENT);
+	StackMemoryPool()
+	{}
 
-	/// Move
-	StackMemoryPool(StackMemoryPool&& other)
+	/// Copy constructor. It's not copying any data
+	StackMemoryPool(const StackMemoryPool& other)
 	{
-		*this = std::move(other);
+		*this = other;
 	}
 
+	/// Constructor with parameters
+	StackMemoryPool(PtrSize size, U32 alignmentBytes = ANKI_SAFE_ALIGNMENT);
+
 	/// Destroy
 	~StackMemoryPool();
 
-	/// Move
-	StackMemoryPool& operator=(StackMemoryPool&& other);
-
-	/// Access the total size
-	PtrSize getSize() const
+	/// Copy. It will not copy any data, what it will do is add visibility of
+	/// other's pool to this instance as well
+	StackMemoryPool& operator=(const StackMemoryPool& other)
 	{
-		return memsize;
+		impl = other.impl;
+		return *this;
 	}
 
+	/// Access the total size
+	PtrSize getTotalSize() const;
+
 	/// Get the allocated size
 	PtrSize getAllocatedSize() const;
 
@@ -60,7 +62,7 @@ public:
 	/// Free memory in StackMemoryPool. If the ptr is not the last allocation
 	/// then nothing happens and the method returns false
 	///
-	/// @param[in] ptr Memory block to deallocate
+	/// @param[in, out] ptr Memory block to deallocate
 	/// @return True if the deallocation actually happened and false otherwise
 	Bool free(void* ptr) throw();
 
@@ -68,41 +70,58 @@ public:
 	void reset();
 
 private:
-	/// Alignment of allocations
-	U32 alignmentBytes;
-
-	/// Pre-allocated memory chunk
-	U8* memory = nullptr;
+	// Forward
+	class Implementation;
 
-	/// Size of the pre-allocated memory chunk
-	PtrSize memsize = 0;
-
-	/// Points to the memory and more specifically to the top of the stack
-	std::atomic<U8*> top = {nullptr};
+	/// The actual implementation
+	std::shared_ptr<Implementation> impl;
 };
 
 /// Chain memory pool. Almost similar to StackMemoryPool but more flexible and 
 /// at the same time a bit slower.
-class ChainMemoryPool: public NonCopyable
+class ChainMemoryPool
 {
 public:
-	/// Chunk allocation method
-	enum NextChunkAllocationMethod
+	/// Chunk allocation method. Defines the size a newely created has chunk 
+	/// compared to the last created
+	enum ChunkAllocationStepMethod
 	{
-		FIXED,
-		MULTIPLY,
-		ADD
+		FIXED, ///< All chunks have the same size
+		MULTIPLY, ///< Next chuck's size will be old_chuck_size * a_value
+		ADD ///< Next chuck's size will be old_chuck_size + a_value
 	};
 
-	/// Default constructor
+	/// Copy constructor. It's not copying any data
+	ChainMemoryPool(const ChainMemoryPool& other)
+	{
+		*this = other;
+	}
+
+	/// Constructor with parameters
+	/// @param initialChunkSize The size of the first chunk
+	/// @param maxChunkSize The size of the chunks cannot exceed that number
+	/// @param chunkAllocStepMethod How new chunks grow compared to the old ones
+	/// @param chunkAllocStep Used along with chunkAllocStepMethod and defines
+	///                       the ammount of chunk size increase 
+	/// @param alignmentBytes The standard alignment of the allocations
 	ChainMemoryPool(
-		NextChunkAllocationMethod allocMethod, 
-		U32 allocMethodValue, 
+		U32 initialChunkSize,
+		U32 maxChunkSize,
+		ChunkAllocationStepMethod chunkAllocStepMethod = MULTIPLY, 
+		U32 chunkAllocStep = 2, 
 		U32 alignmentBytes = ANKI_SAFE_ALIGNMENT);
 
 	/// Destroy
 	~ChainMemoryPool();
 
+	/// Copy. It will not copy any data, what it will do is add visibility of
+	/// other's pool to this instance as well
+	ChainMemoryPool& operator=(const ChainMemoryPool& other)
+	{
+		impl = other.impl;
+		return *this;
+	}
+
 	/// Allocate memory. This operation is thread safe
 	/// @return The allocated memory or nullptr on failure
 	void* allocate(PtrSize size) throw();
@@ -114,42 +133,17 @@ public:
 	/// @return True if the deallocation actually happened and false otherwise
 	Bool free(void* ptr) throw();
 
-private:
-	/// A chunk of memory
-	class Chunk
-	{
-	public:
-		StackMemoryPool pool;
-		std::atomic<U32> allocationsCount;
-
-		Chunk(PtrSize size, U32 alignmentBytes)
-			: pool(size, alignmentBytes), allocationsCount{0}
-		{}
-	};
-
-	/// Alignment of allocations
-	U32 alignmentBytes;
-
-	/// A list of chunks
-	std::vector<Chunk*> chunks;
-
-	/// Current chunk to allocate from
-	Chunk* crntChunk = nullptr;
-
-	/// Fast thread locking
-	SpinLock lock;
+	/// @name Methods used for debugging
+	/// @{
+	PtrSize getChunksCount() const;
+	/// @}
 
-	/// Chunk allocation method value
-	U32 chAllocMethodValue;
-
-	/// Chunk allocation method
-	U8 chAllocMethod;
-
-	/// Allocate memory from a chunk
-	void* allocateFromChunk(Chunk* ch, PtrSize size) throw();
+private:
+	// Forward
+	class Implementation;
 
-	/// Create a new chunk
-	Chunk* createNewChunk(PtrSize minSize) throw();
+	/// The actual implementation
+	std::shared_ptr<Implementation> impl;
 };
 
 /// @}

+ 0 - 2
src/gl/CMakeLists.txt

@@ -1,6 +1,4 @@
 FILE(GLOB_RECURSE ANKI_GL_SOURCES *.cpp)
 FILE(GLOB_RECURSE ANKI_GL_HEADERS *.h)
 
-#INCLUDE_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/BufferObjects")
-
 ADD_LIBRARY(ankigl ${ANKI_GL_SOURCES} ${ANKI_GL_HEADERS})

+ 11 - 11
src/gl/BufferObject.cpp → src/gl/GlBuffer.cpp

@@ -1,5 +1,5 @@
 #include <cstring>
-#include "anki/gl/BufferObject.h"
+#include "anki/gl/GlBuffer.h"
 #include "anki/gl/GlException.h"
 #include "anki/util/Exception.h"
 #include "anki/core/Logger.h"
@@ -16,11 +16,11 @@ namespace anki {
 #define USE_BUFFER_DATA_ON_WRITE 1
 
 //==============================================================================
-// BufferObject                                                                =
+// GlBuffer                                                                    =
 //==============================================================================
 
 //==============================================================================
-BufferObject& BufferObject::operator=(BufferObject&& b)
+GlBuffer& GlBuffer::operator=(GlBuffer&& b)
 {
 	destroy();
 	Base::operator=(std::forward<Base>(b));
@@ -34,7 +34,7 @@ BufferObject& BufferObject::operator=(BufferObject&& b)
 }
 
 //==============================================================================
-void BufferObject::destroy()
+void GlBuffer::destroy()
 {
 	if(isCreated())
 	{
@@ -45,7 +45,7 @@ void BufferObject::destroy()
 }
 
 //==============================================================================
-void BufferObject::create(GLenum target_, U32 sizeInBytes_,
+void GlBuffer::create(GLenum target_, U32 sizeInBytes_,
 	const void* dataPtr, GLenum usage_)
 {
 	ANKI_ASSERT(!isCreated());
@@ -94,7 +94,7 @@ void BufferObject::create(GLenum target_, U32 sizeInBytes_,
 }
 
 //==============================================================================
-void* BufferObject::map(U32 offset, U32 length, GLuint flags)
+void* GlBuffer::map(U32 offset, U32 length, GLuint flags)
 {
 	// XXX Remove this workaround
 #if ANKI_GL == ANKI_GL_ES
@@ -120,7 +120,7 @@ void* BufferObject::map(U32 offset, U32 length, GLuint flags)
 }
 
 //==============================================================================
-void BufferObject::unmap()
+void GlBuffer::unmap()
 {
 	ANKI_ASSERT(isCreated());
 #if ANKI_DEBUG
@@ -134,7 +134,7 @@ void BufferObject::unmap()
 }
 
 //==============================================================================
-void BufferObject::write(void* buff, U32 offset, U32 size)
+void GlBuffer::write(void* buff, U32 offset, U32 size)
 {
 	ANKI_ASSERT(isCreated());
 	ANKI_ASSERT(usage != GL_STATIC_DRAW);
@@ -154,7 +154,7 @@ void BufferObject::write(void* buff, U32 offset, U32 size)
 }
 
 //==============================================================================
-void BufferObject::read(void* outBuff, U32 offset, U32 size)
+void GlBuffer::read(void* outBuff, U32 offset, U32 size)
 {
 	ANKI_ASSERT(isCreated());
 	ANKI_ASSERT(usage != GL_STATIC_DRAW);
@@ -167,7 +167,7 @@ void BufferObject::read(void* outBuff, U32 offset, U32 size)
 }
 
 //==============================================================================
-void BufferObject::setBinding(GLuint binding) const
+void GlBuffer::setBinding(GLuint binding) const
 {
 	ANKI_ASSERT(isCreated());
 	glBindBufferBase(target, binding, getGlId());
@@ -175,7 +175,7 @@ void BufferObject::setBinding(GLuint binding) const
 }
 
 //==============================================================================
-void BufferObject::setBindingRange(
+void GlBuffer::setBindingRange(
 	GLuint binding, PtrSize offset, PtrSize size) const
 {
 	ANKI_ASSERT(isCreated());

+ 4 - 4
src/gl/Vao.cpp

@@ -1,5 +1,5 @@
 #include "anki/gl/Vao.h"
-#include "anki/gl/BufferObject.h"
+#include "anki/gl/GlBuffer.h"
 #include "anki/util/Exception.h"
 #include "anki/gl/ShaderProgram.h"
 #include "anki/core/Logger.h"
@@ -25,7 +25,7 @@ void Vao::destroy()
 }
 
 //==============================================================================
-void Vao::attachArrayBufferVbo(const BufferObject* vbo, 
+void Vao::attachArrayBufferVbo(const GlBuffer* vbo, 
 	const GLint attribVarLocation,
 	const PtrSize size, const GLenum type, const Bool normalized, 
 	const PtrSize stride, const PtrSize offset)
@@ -50,7 +50,7 @@ void Vao::attachArrayBufferVbo(const BufferObject* vbo,
 }
 
 //==============================================================================
-void Vao::attachArrayBufferVbo(const BufferObject* vbo,
+void Vao::attachArrayBufferVbo(const GlBuffer* vbo,
 	const ShaderProgramAttributeVariable& attribVar,
 	const PtrSize size, const GLenum type, const Bool normalized, 
 	const PtrSize stride, const PtrSize offset)
@@ -60,7 +60,7 @@ void Vao::attachArrayBufferVbo(const BufferObject* vbo,
 }
 
 //==============================================================================
-void Vao::attachElementArrayBufferVbo(const BufferObject* vbo)
+void Vao::attachElementArrayBufferVbo(const GlBuffer* vbo)
 {
 	ANKI_ASSERT(isCreated());
 	checkNonSharable();

+ 1 - 1
src/renderer/Is.cpp

@@ -516,7 +516,7 @@ void Is::initInternal(const RendererInitializer& initializer)
 	// Create UBOs
 	//
 
-	uboAlignment = BufferObject::getUniformBufferOffsetAlignment();
+	uboAlignment = GlBuffer::getUniformBufferOffsetAlignment();
 
 	commonUbo.create(GL_UNIFORM_BUFFER, sizeof(shader::CommonUniforms), 
 		nullptr, GL_DYNAMIC_DRAW);

+ 1 - 1
src/resource/Mesh.cpp

@@ -120,7 +120,7 @@ void Mesh::createVbos(const MeshLoader& loader)
 }
 
 //==============================================================================
-void Mesh::getVboInfo(const VertexAttribute attrib, const BufferObject*& v, 
+void Mesh::getVboInfo(const VertexAttribute attrib, const GlBuffer*& v, 
 	U32& size, GLenum& type, U32& stride, U32& offset) const
 {
 	stride = calcVertexSize();

+ 1 - 1
src/resource/Model.cpp

@@ -36,7 +36,7 @@ void ModelPatchBase::createVao(const ShaderProgram& prog,
 {
 	vao.create();
 
-	const BufferObject* vbo;
+	const GlBuffer* vbo;
 	U32 size;
 	GLenum type;
 	U32 stride;

+ 409 - 188
src/util/Memory.cpp

@@ -1,8 +1,10 @@
 #include "anki/util/Memory.h"
-#include "anki/util/Assert.h"
 #include "anki/util/Exception.h"
 #include "anki/util/Functions.h"
-#include <limits>
+#include "anki/util/Assert.h"
+#include "anki/util/NonCopyable.h"
+#include "anki/util/Thread.h"
+#include "anki/util/Vector.h"
 #include <cstdlib>
 #include <cstring>
 
@@ -68,279 +70,498 @@ void freeAligned(void* ptr)
 //==============================================================================
 
 //==============================================================================
-struct MemoryBlockHeader
+class StackMemoryPool::Implementation: public NonCopyable
 {
-	U32 size;
-};
+public:
+	/// The header of each allocation
+	struct MemoryBlockHeader
+	{
+		U32 size;
+	};
 
-//==============================================================================
-StackMemoryPool::StackMemoryPool(PtrSize size, U32 alignmentBytes_)
-	:	alignmentBytes(alignmentBytes_), 
-		memsize(getAlignedRoundUp(alignmentBytes, size))
-{
-	ANKI_ASSERT(memsize > 0);
-	memory = (U8*)mallocAligned(memsize, alignmentBytes);
+	/// Alignment of allocations
+	U32 alignmentBytes;
 
-	if(memory != nullptr)
+	/// Pre-allocated memory chunk
+	U8* memory = nullptr;
+
+	/// Size of the pre-allocated memory chunk
+	PtrSize memsize = 0;
+
+	/// Points to the memory and more specifically to the top of the stack
+	std::atomic<U8*> top = {nullptr};
+
+	// Construct
+	Implementation(PtrSize size, U32 alignmentBytes_)
+		:	alignmentBytes(alignmentBytes_), 
+			memsize(getAlignedRoundUp(alignmentBytes, size))
 	{
-		// Align allocated memory
-		top = memory;
+		ANKI_ASSERT(memsize > 0);
+		memory = (U8*)mallocAligned(memsize, alignmentBytes);
+
+		if(memory != nullptr)
+		{
+			// Align allocated memory
+			top = memory;
+		}
+		else
+		{
+			throw ANKI_EXCEPTION("Failed to allocate memory");
+		}
 	}
-	else
+
+	// Destroy
+	~Implementation()
 	{
-		throw ANKI_EXCEPTION("Failed to allocate memory");
+		if(memory != nullptr)
+		{
+			freeAligned(memory);
+		}
 	}
-}
 
-//==============================================================================
-StackMemoryPool::~StackMemoryPool()
-{
-	if(memory != nullptr)
+	PtrSize getTotalSize() const
 	{
-		freeAligned(memory);
+		return memsize;
 	}
-}
 
-//==============================================================================
-StackMemoryPool& StackMemoryPool::operator=(StackMemoryPool&& other)
-{
-	if(memory != nullptr)
+	PtrSize getAllocatedSize() const
 	{
-		freeAligned(memory);
+		ANKI_ASSERT(memory != nullptr);
+		return top.load() - memory;
 	}
 
-	memory = other.memory;
-	memsize = other.memsize;
-	alignmentBytes = other.alignmentBytes;
-	top.store(other.top.load());
-
-	other.memory = nullptr;
-	other.memsize = 0;
-	other.top = nullptr;
-
-	return *this;
-}
+	const void* getBaseAddress() const
+	{
+		ANKI_ASSERT(memory != nullptr);
+		return memory;
+	}
 
-//==============================================================================
-PtrSize StackMemoryPool::getAllocatedSize() const
-{
-	return top.load() - memory;
-}
+	/// Allocate
+	void* allocate(PtrSize size_) throw()
+	{
+		// memory is nullptr if moved
+		ANKI_ASSERT(memory != nullptr);
 
-//==============================================================================
-void* StackMemoryPool::allocate(PtrSize size_) throw()
-{
-	// memory is nullptr if moved
-	ANKI_ASSERT(memory != nullptr);
+		PtrSize headerSize = 
+			getAlignedRoundUp(alignmentBytes, sizeof(MemoryBlockHeader));
+		PtrSize size = 
+			getAlignedRoundUp(alignmentBytes, size_ + headerSize);
 
-	PtrSize headerSize = 
-		getAlignedRoundUp(alignmentBytes, sizeof(MemoryBlockHeader));
-	PtrSize size = 
-		getAlignedRoundUp(alignmentBytes, size_ + headerSize);
+		ANKI_ASSERT(size < MAX_U32 && "Too big allocation");
 
-	ANKI_ASSERT(size < MAX_U32 && "Too big allocation");
+		U8* out = top.fetch_add(size);
 
-	U8* out = top.fetch_add(size);
+		if(out + size <= memory + memsize)
+		{
+			// Write the block header
+			((MemoryBlockHeader*)out)->size = size;
 
-	if(out + size <= memory + memsize)
-	{
-		// Write the block header
-		((MemoryBlockHeader*)out)->size = size;
+			// Set the correct output
+			out += headerSize;
 
-		// Set the correct output
-		out += headerSize;
+			// Check alignment
+			ANKI_ASSERT(isAligned(alignmentBytes, out));
+		}
+		else
+		{
+			// Error
+			out = nullptr;
+		}
 
-		// Check alignment
-		ANKI_ASSERT(isAligned(alignmentBytes, out));
-	}
-	else
-	{
-		// Error
-		out = nullptr;
+		return out;
 	}
 
-	return out;
-}
-
-//==============================================================================
-Bool StackMemoryPool::free(void* ptr) throw()
-{
-	// ptr shouldn't be null or not aligned. If not aligned it was not 
-	// allocated by this class
-	ANKI_ASSERT(ptr != nullptr && isAligned(alignmentBytes, ptr));
+	/// Free
+	Bool free(void* ptr) throw()
+	{
+		// ptr shouldn't be null or not aligned. If not aligned it was not 
+		// allocated by this class
+		ANKI_ASSERT(ptr != nullptr && isAligned(alignmentBytes, ptr));
 
-	// memory is nullptr if moved
-	ANKI_ASSERT(memory != nullptr);
+		// memory is nullptr if moved
+		ANKI_ASSERT(memory != nullptr);
 
-	// Correct the p
-	PtrSize headerSize = 
-		getAlignedRoundUp(alignmentBytes, sizeof(MemoryBlockHeader));
-	U8* realptr = (U8*)ptr - headerSize;
+		// Correct the p
+		PtrSize headerSize = 
+			getAlignedRoundUp(alignmentBytes, sizeof(MemoryBlockHeader));
+		U8* realptr = (U8*)ptr - headerSize;
 
-	// realptr should be inside the pool's preallocated memory
-	ANKI_ASSERT(realptr >= memory);
+		// realptr should be inside the pool's preallocated memory
+		ANKI_ASSERT(realptr >= memory);
 
-	// Get block size
-	U32 size = ((MemoryBlockHeader*)realptr)->size;
+		// Get block size
+		U32 size = ((MemoryBlockHeader*)realptr)->size;
 
-	// Check if the size is ok
-	ANKI_ASSERT(realptr + size <= memory + memsize);
+		// Check if the size is ok
+		ANKI_ASSERT(realptr + size <= memory + memsize);
 
-	// Atomic stuff
-	U8* expected = realptr + size;
-	U8* desired = realptr;
+		// Atomic stuff
+		U8* expected = realptr + size;
+		U8* desired = realptr;
 
-	// if(top == expected) {
-	//     top = desired;
-	//     exchange = true;
-	// } else {
-	//     expected = top;
-	//     exchange = false;
-	// }
-	Bool exchange = top.compare_exchange_strong(expected, desired);
+		// if(top == expected) {
+		//     top = desired;
+		//     exchange = true;
+		// } else {
+		//     expected = top;
+		//     exchange = false;
+		// }
+		Bool exchange = top.compare_exchange_strong(expected, desired);
 
-	return exchange;
-}
+		return exchange;
+	}
 
-//==============================================================================
-void StackMemoryPool::reset()
-{
-	// memory is nullptr if moved
-	ANKI_ASSERT(memory != nullptr);
+	/// Reset
+	void reset()
+	{
+		// memory is nullptr if moved
+		ANKI_ASSERT(memory != nullptr);
 
 #if ANKI_DEBUG
-	// Invalidate the memory
-	memset(memory, 0xCC, memsize);
+		// Invalidate the memory
+		memset(memory, 0xCC, memsize);
 #endif
 
-	top = memory;
-}
+		top = memory;
+	}
+};
 
 //==============================================================================
-// ChainMemoryPool                                                             =
-//==============================================================================
+StackMemoryPool::StackMemoryPool(PtrSize size, U32 alignmentBytes)
+{
+	impl.reset(new Implementation(size, alignmentBytes));
+}
 
 //==============================================================================
-ChainMemoryPool::ChainMemoryPool(
-	NextChunkAllocationMethod allocMethod, 
-	U32 allocMethodValue, 
-	U32 alignmentBytes_)
-	:	alignmentBytes(alignmentBytes_), 
-		chAllocMethodValue(allocMethodValue),
-		chAllocMethod(allocMethod)
+StackMemoryPool::~StackMemoryPool()
 {}
 
 //==============================================================================
-ChainMemoryPool::~ChainMemoryPool()
+PtrSize StackMemoryPool::getTotalSize() const
 {
-	// TODO
+	ANKI_ASSERT(impl.get() != nullptr);
+	return impl->getTotalSize();
 }
 
 //==============================================================================
-void* ChainMemoryPool::allocateFromChunk(Chunk* ch, PtrSize size) throw()
+PtrSize StackMemoryPool::getAllocatedSize() const
 {
-	ANKI_ASSERT(ch);
-	void* mem = ch->pool.allocate(size);
+	ANKI_ASSERT(impl.get() != nullptr);
+	return impl->getAllocatedSize();
+}
 
-	if(mem)
-	{
-		++ch->allocationsCount;
-	}
+//==============================================================================
+void* StackMemoryPool::allocate(PtrSize size) throw()
+{
+	ANKI_ASSERT(impl.get() != nullptr);
+	return impl->allocate(size);
+}
 
-	return mem;
+//==============================================================================
+Bool StackMemoryPool::free(void* ptr) throw()
+{
+	ANKI_ASSERT(impl.get() != nullptr);
+	return impl->free(ptr);
 }
 
 //==============================================================================
-ChainMemoryPool::Chunk* ChainMemoryPool::createNewChunk(PtrSize size) throw()
+void StackMemoryPool::reset()
 {
-	//
-	// Calculate preferred size
-	//
-	
-	// Get the size of the next chunk
-	PtrSize crntMaxSize;
-	if(chAllocMethod == FIXED)
+	ANKI_ASSERT(impl.get() != nullptr);
+	impl->reset();
+}
+
+//==============================================================================
+// ChainMemoryPool                                                             =
+//==============================================================================
+
+//==============================================================================
+class ChainMemoryPool::Implementation: public NonCopyable
+{
+public:
+	/// A chunk of memory
+	class Chunk
 	{
-		crntMaxSize = chAllocMethodValue;
+	public:
+		StackMemoryPool::Implementation pool;
+		U32 allocationsCount;
+
+		Chunk(PtrSize size, U32 alignmentBytes)
+			: pool(size, alignmentBytes), allocationsCount(0)
+		{}
+	};
+
+	/// Alignment of allocations
+	U32 alignmentBytes;
+
+	/// A list of chunks
+	Vector<Chunk*> chunks;
+
+	/// Current chunk to allocate from
+	Chunk* crntChunk = nullptr;
+
+	/// Fast thread locking
+	SpinLock lock;
+
+	/// Chunk first chunk size
+	U32 initSize;
+
+	/// Chunk max size
+	U32 maxSize;
+
+	/// Chunk allocation method value
+	U32 step;
+
+	/// Chunk allocation method
+	U8 method;
+
+	/// Construct
+	Implementation(
+		U32 initialChunkSize,
+		U32 maxChunkSize,
+		ChunkAllocationStepMethod chunkAllocStepMethod, 
+		U32 chunkAllocStep, 
+		U32 alignmentBytes_)
+		:	alignmentBytes(alignmentBytes_), 
+			initSize(initialChunkSize),
+			maxSize(maxChunkSize),
+			step(chunkAllocStep),
+			method(chunkAllocStepMethod)
+	{
+		// Initial size should be > 0
+		ANKI_ASSERT(initSize > 0);
+
+		// On fixed step should be 0
+		ANKI_ASSERT(!(method == FIXED && step != 0));
+
+		// On fixed initial size is the same as the max
+		ANKI_ASSERT(!(method == FIXED && initSize != maxSize));
+
+		// On add and mul the max size should be greater than initial
+		ANKI_ASSERT(!(
+			(method == ADD || method == MULTIPLY) && initSize < maxSize));
 	}
-	else
+
+	/// Destroy
+	~Implementation()
+	{
+		for(Chunk* ch : chunks)
+		{
+			ANKI_ASSERT(ch);
+			delete ch;
+		}
+	}
+
+	/// Create a new chunk
+	Chunk* createNewChunk(PtrSize size) throw()
 	{
-		// Get the size of the previous max chunk
-		crntMaxSize = 0;
-		for(Chunk* c : chunks)
+		ANKI_ASSERT(size <= maxSize && "To big chunk");
+		//
+		// Calculate preferred size
+		//
+		
+		// Get the size of the next chunk
+		PtrSize crntMaxSize;
+		if(method == FIXED)
 		{
-			if(c)
+			crntMaxSize = initSize;
+		}
+		else
+		{
+			// Get the size of the previous max chunk
+			if(chunks.size() > 0)
 			{
-				PtrSize poolSize = c->pool.getSize();
-				crntMaxSize = std::max(crntMaxSize, poolSize);
+				// Get the size of previous
+				crntMaxSize = chunks.back()->pool.getTotalSize();
+
+				// Increase it
+				if(method == MULTIPLY)
+				{
+					crntMaxSize *= step;
+				}
+				else
+				{
+					ANKI_ASSERT(method == ADD);
+					crntMaxSize += step;
+				}
 			}
+			else
+			{
+				// No chunks. Choose initial size
+
+				ANKI_ASSERT(crntChunk == nullptr);
+				crntMaxSize = initSize;
+			}
+
+			ANKI_ASSERT(crntMaxSize > 0);
+
+			// Fix the size
+			crntMaxSize = std::min(crntMaxSize, (PtrSize)maxSize);
 		}
 
-		// Increase it
-		if(chAllocMethod == MULTIPLY)
+		size = crntMaxSize;
+
+		//
+		// Create the chunk
+		//
+		Chunk* chunk = new Chunk(size, alignmentBytes);
+
+		if(chunk)
 		{
-			crntMaxSize *= chAllocMethodValue;
+			chunks.push_back(chunk);
 		}
 		else
 		{
-			ANKI_ASSERT(chAllocMethod == ADD);
-			crntMaxSize += chAllocMethodValue;
+			throw ANKI_EXCEPTION("Chunk creation failed");
 		}
+		
+		return chunk;
 	}
 
-	// Fix the size
-	size = std::max(crntMaxSize, size * 2);
-
-	//
-	// Create the chunk
-	//
-	Chunk* chunk = new Chunk(size, alignmentBytes);
-
-	if(chunk)
+	/// Allocate from chunk
+	void* allocateFromChunk(Chunk* ch, PtrSize size) throw()
 	{
-		chunks.push_back(chunk);
-	}
-	
-	return chunk;
-}
+		ANKI_ASSERT(ch);
+		ANKI_ASSERT(size <= maxSize);
+		void* mem = ch->pool.allocate(size);
 
-//==============================================================================
-void* ChainMemoryPool::allocate(PtrSize size) throw()
-{
-	Chunk* ch;
-	void* mem = nullptr;
+		if(mem)
+		{
+			++ch->allocationsCount;
+		}
 
-	// Get chunk
-	lock.lock();
-	ch = crntChunk;
-	lock.unlock();
+		return mem;
+	}
 
-	// Create new chunk if needed
-	if(ch == nullptr || (mem = allocateFromChunk(ch, size)) == nullptr)
+	/// Allocate memory
+	void* allocate(PtrSize size) throw()
 	{
-		// Create new chunk
+		Chunk* ch;
+		void* mem = nullptr;
+
 		lock.lock();
 
-		crntChunk = createNewChunk(size);
+		// Get chunk
 		ch = crntChunk;
 
-		lock.unlock();
+		// Create new chunk if needed
+		if(ch == nullptr || (mem = allocateFromChunk(ch, size)) == nullptr)
+		{
+			// Create new chunk
+			crntChunk = createNewChunk(size);
+			ch = crntChunk;
 
-		// Chunk creation failed
-		if(ch == nullptr)
+			// Chunk creation failed
+			if(ch == nullptr)
+			{
+				lock.unlock();
+				return mem;
+			}
+		}
+
+		if(mem == nullptr)
 		{
-			return mem;
+			mem = allocateFromChunk(ch, size);
+			ANKI_ASSERT(mem != nullptr && "The chunk should have space");
 		}
+
+		lock.unlock();
+		return mem;
 	}
 
-	if(mem == nullptr)
+	/// Free memory
+	Bool free(void* ptr) throw()
 	{
-		ANKI_ASSERT(ch != nullptr);
-		mem = allocateFromChunk(ch, size);
-		ANKI_ASSERT(mem != nullptr && "The chunk should have space");
+		lock.lock();
+
+		// Get the chunk that ptr belongs to
+		Vector<Chunk*>::iterator it;
+		for(it = chunks.begin(); it != chunks.end(); it++)
+		{
+			Chunk* ch = *it;
+			ANKI_ASSERT(ch);
+
+			const U8* from = (const U8*)ch->pool.getBaseAddress();
+			const U8* to = from + ch->pool.getTotalSize();
+			const U8* cptr = (const U8*)ptr;
+			if(cptr >= from && cptr < to)
+			{
+				break;
+			}
+		}
+
+		ANKI_ASSERT(it != chunks.end() 
+			&& "Not initialized or ptr is incorrect");
+
+		// Decrease the deallocation refcount and if it's zero delete the chunk
+		ANKI_ASSERT((*it)->allocationsCount > 0);
+		if(--(*it)->allocationsCount == 0)
+		{
+			// Chunk is empty. Delete it
+
+			Chunk* ch = *it;
+			ANKI_ASSERT(ch);
+			
+			delete ch;
+			chunks.erase(it);
+
+			// Change the crntChunk
+			ANKI_ASSERT(crntChunk);
+			if(ch == crntChunk)
+			{
+				// If there are chunks chose the last. It's probably full but
+				// let the allocate() create a new one
+				if(chunks.size() > 0)
+				{
+					crntChunk = chunks.back();
+					ANKI_ASSERT(crntChunk);
+				}
+				else
+				{
+					crntChunk = nullptr;
+				}
+			}
+		}
+
+		lock.unlock();
+
+		return true;
 	}
+};
+
+//==============================================================================
+ChainMemoryPool::ChainMemoryPool(
+	U32 initialChunkSize,
+	U32 maxChunkSize,
+	ChunkAllocationStepMethod chunkAllocStepMethod, 
+	U32 chunkAllocStep, 
+	U32 alignmentBytes)
+{
+	impl.reset(new Implementation(
+		initialChunkSize, maxChunkSize, chunkAllocStepMethod, chunkAllocStep,
+		alignmentBytes));
+}
 
-	return mem;
+//==============================================================================
+ChainMemoryPool::~ChainMemoryPool()
+{}
+
+//==============================================================================
+void* ChainMemoryPool::allocate(PtrSize size) throw()
+{
+	return impl->allocate(size);
+}
+
+//==============================================================================
+Bool ChainMemoryPool::free(void* ptr) throw()
+{
+	return impl->free(ptr);
+}
+
+//==============================================================================
+PtrSize ChainMemoryPool::getChunksCount() const
+{
+	return impl->chunks.size();
 }
 
 } // end namespace anki

+ 1 - 1
testapp/Main.cpp

@@ -516,7 +516,7 @@ void mainLoop()
 
 		// Sleep
 		//
-#if 1
+#if 0
 		timer.stop();
 		if(timer.getElapsedTime() < AppSingleton::get().getTimerTick())
 		{

+ 1 - 39
tests/Main.cpp

@@ -3,49 +3,11 @@
 
 using namespace anki;
 
-struct LoggerMessageHandler
-{
-	ANKI_HAS_SLOTS(LoggerMessageHandler)
-
-	void handleLoggerMessages(const Logger::Info& info)
-	{
-		std::ostream* out = NULL;
-		const char* x = NULL;
-
-		switch(info.type)
-		{
-		case Logger::LMT_NORMAL:
-			out = &std::cout;
-			x = "Info";
-			break;
-
-		case Logger::LMT_ERROR:
-			out = &std::cerr;
-			x = "Error";
-			break;
-
-		case Logger::LMT_WARNING:
-			out = &std::cerr;
-			x = "Warn";
-			break;
-		}
-
-		(*out) << "(" << info.file << ":" << info.line << " "<< info.func 
-			<< ") " << x << ": " << info.msg << std::endl;
-	}
-
-	ANKI_SLOT(handleLoggerMessages, const Logger::Info&)
-};
-
-static LoggerMessageHandler msgh;
-
 int main(int argc, char** argv)
 {
 	// Call a few singletons to avoid memory leak confusion
 	LoggerSingleton::get();
-
-	ANKI_CONNECT(&LoggerSingleton::get(), messageRecieved, 
-		&msgh, handleLoggerMessages);
+	LoggerSingleton::get().init(Logger::INIT_SYSTEM_MESSAGE_HANDLER);
 
 	int exitcode = getTesterSingleton().run(argc, argv);
 

+ 0 - 48
tests/core/Async.cpp

@@ -1,48 +0,0 @@
-#include "tests/framework/Framework.h"
-#include "anki/core/Async.h"
-#include "anki/core/Logger.h"
-#include "anki/util/HighRezTimer.h"
-#include "anki/util/StdTypes.h"
-
-namespace anki {
-
-/// Struct for our tests
-struct TestJob: AsyncJob
-{
-	U32 result = 0;
-
-	void operator()()
-	{
-		ANKI_LOGI("-- Job " << this << " starting");
-		HighRezTimer::sleep(1.0);
-		++result;
-		ANKI_LOGI("-- Job " << this << " done");
-	}
-
-	void post()
-	{
-		ANKI_LOGI("-- Job " << this << " starting post");
-		HighRezTimer::sleep(1.0);
-		++result;
-		ANKI_LOGI("-- Job " << this << " done post");
-	}
-};
-
-} // end namespace anki
-
-ANKI_TEST(Core, Async)
-{
-	Async a;
-	TestJob pjob;
-
-	a.start();
-	a.assignNewJob(&pjob);
-
-	HighRezTimer::sleep(2.0);
-
-	a.cleanupFinishedJobs(123.0);
-	a.stop();
-
-	ANKI_TEST_EXPECT_EQ(pjob.result, 2);
-}
-

+ 23 - 4
tests/framework/Framework.cpp

@@ -5,6 +5,15 @@
 
 namespace anki {
 
+//==============================================================================
+TestSuite::~TestSuite()
+{
+	for(Test* t : tests)
+	{
+		delete t;
+	}
+}
+
 //==============================================================================
 void Test::run()
 {
@@ -28,7 +37,7 @@ void Test::run()
 void Tester::addTest(const char* name, const char* suiteName,
 	TestCallback callback)
 {
-	PtrVector<TestSuite>::iterator it;
+	Vector<TestSuite*>::iterator it;
 	for(it = suites.begin(); it != suites.end(); it++)
 	{
 		if((*it)->name == suiteName)
@@ -51,7 +60,7 @@ void Tester::addTest(const char* name, const char* suiteName,
 	}
 
 	// Sanity check
-	PtrVector<Test>::iterator it1;
+	Vector<Test*>::iterator it1;
 	for(it1 = suite->tests.begin(); it1 != suite->tests.end(); it1++)
 	{
 		if((*it)->name == name)
@@ -178,8 +187,18 @@ Options:
 		}
 	}
 
-	std::cout << "========\nRun " << run << " tests, failed " << (run - passed)
-		<< "\n" << std::endl;
+	int failed = run - passed;
+	std::cout << "========\nRun " << run << " tests, failed " << failed
+		<< std::endl;
+
+	if(failed == 0)
+	{
+		std::cout << "SUCCESS!" << std::endl;
+	}
+	else
+	{
+		std::cout << "FAILURE" << std::endl;
+	}
 
 	return run - passed;
 }

+ 12 - 2
tests/framework/Framework.h

@@ -23,7 +23,9 @@ typedef void (*TestCallback)(Test&);
 struct TestSuite
 {
 	std::string name;
-	PtrVector<Test> tests;
+	Vector<Test*> tests;
+
+	~TestSuite();
 };
 
 /// Test
@@ -39,7 +41,7 @@ struct Test
 /// A container of test suites
 struct Tester
 {
-	PtrVector<TestSuite> suites;
+	Vector<TestSuite*> suites;
 	std::string programName;
 
 	void addTest(const char* name, const char* suite, TestCallback callback);
@@ -47,6 +49,14 @@ struct Tester
 	int run(int argc, char** argv);
 
 	int listTests();
+
+	~Tester()
+	{
+		for(TestSuite* s : suites)
+		{
+			delete s;
+		}
+	}
 };
 
 /// Singleton so we can do the ANKI_TEST trick

+ 6 - 3
tests/util/Allocator.cpp

@@ -3,7 +3,7 @@
 #include "anki/util/Allocator.h"
 #include <string>
 
-ANKI_TEST(Allocator, StackAllocator)
+ANKI_TEST(Allocators, StackAllocator)
 {
 	Foo::reset();
 
@@ -21,8 +21,11 @@ ANKI_TEST(Allocator, StackAllocator)
 
 	// With vector
 	{
-		StackAllocator<Foo> alloc(128);
-		std::vector<Foo, StackAllocator<Foo>> vec(alloc);
+		typedef StackAllocator<Foo, false> All;
+		All alloc(StackMemoryPool((sizeof(Foo) + 1) * 10, 1));
+		std::vector<Foo, All> vec(alloc);
+
+		vec.reserve(10);
 
 		U sumi = 0;
 		for(U i = 0; i < 10; i++)

+ 0 - 42
tests/util/DynamicArray.cpp

@@ -1,42 +0,0 @@
-#include "tests/framework/Framework.h"
-#include "tests/util/Foo.h"
-#include "anki/util/DynamicArray.h"
-
-ANKI_TEST(DynamicArray, Test)
-{
-	constexpr U n = 4;
-	StackAllocator<U8, true> alloc(sizeof(Foo) * n + sizeof(PtrSize));
-
-	DynamicArray<Foo, StackAllocator<Foo, true>> arr(alloc);
-
-	arr.resize(n, 1);
-
-	U i = 0;
-	for(const Foo& f : arr)
-	{
-		i += f.x;
-	}
-
-	ANKI_TEST_EXPECT_EQ(i, n);
-
-	arr.clear();
-
-	ANKI_TEST_EXPECT_EQ(arr.getSize(), 0);
-	ANKI_TEST_EXPECT_EQ(alloc.getMemoryPool().getAllocatedSize(), 0);
-
-	// Again
-	arr.resize(n);
-
-	i = 0;
-	for(const Foo& f : arr)
-	{
-		i += f.x;
-	}
-
-	ANKI_TEST_EXPECT_EQ(i, n * 666);
-
-	arr.clear();
-
-	ANKI_TEST_EXPECT_EQ(arr.getSize(), 0);
-	ANKI_TEST_EXPECT_EQ(alloc.getMemoryPool().getAllocatedSize(), 0);
-}

+ 43 - 3
tests/util/Memory.cpp

@@ -3,9 +3,9 @@
 #include "anki/util/Memory.h"
 #include <type_traits>
 
-ANKI_TEST(Memory, Test)
+ANKI_TEST(Memory, StackMemoryPool)
 {
-	constexpr U n = 4;
+	/*constexpr U n = 4;
 	StackAllocator<U8, true> alloc(sizeof(Foo) * n + sizeof(PtrSize));
 
 	Foo* f = ANKI_NEW(Foo, alloc, 123);
@@ -29,6 +29,46 @@ ANKI_TEST(Memory, Test)
 
 	ANKI_DELETE_ARRAY(f, alloc);
 
-	ANKI_TEST_EXPECT_EQ(alloc.getMemoryPool().getAllocatedSize(), 0);
+	ANKI_TEST_EXPECT_EQ(alloc.getMemoryPool().getAllocatedSize(), 0);*/
 }
 
+ANKI_TEST(Memory, ChainMemoryPool)
+{
+	// Basic test
+	{
+		const U size = sizeof(PtrSize) + 10;
+		ChainMemoryPool pool(
+			size, size, ChainMemoryPool::MULTIPLY, 2, 1);
+
+		void* mem = pool.allocate(1);
+		ANKI_TEST_EXPECT_NEQ(mem, nullptr);
+		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 1);
+
+		void* mem1 = pool.allocate(10);
+		ANKI_TEST_EXPECT_NEQ(mem1, nullptr);
+		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 2);
+
+		pool.free(mem1);
+		pool.free(mem);
+		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 0);
+	}
+
+	// Basic test 2
+	{
+		const U size = sizeof(PtrSize) + 10;
+		ChainMemoryPool pool(
+			size, size, ChainMemoryPool::MULTIPLY, 2, 1);
+
+		void* mem = pool.allocate(1);
+		ANKI_TEST_EXPECT_NEQ(mem, nullptr);
+		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 1);
+
+		void* mem1 = pool.allocate(10);
+		ANKI_TEST_EXPECT_NEQ(mem1, nullptr);
+		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 2);
+
+		pool.free(mem);
+		pool.free(mem1);
+		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 0);
+	}
+}

+ 3 - 0
tests/util/Object.cpp

@@ -3,6 +3,8 @@
 
 using namespace anki;
 
+#if 0
+
 template<typename T, typename Alloc>
 struct Deleter
 {
@@ -79,4 +81,5 @@ ANKI_TEST(Object, Test)
 	delete a;
 }
 
+#endif
 

+ 9 - 8
tests/core/ThreadPool.cpp → tests/util/Thread.cpp

@@ -1,17 +1,16 @@
 #include "tests/framework/Framework.h"
-#include "anki/core/ThreadPool.h"
-#include "anki/core/Logger.h"
+#include "anki/util/Thread.h"
 #include "anki/util/StdTypes.h"
 
 namespace anki {
 
 /// Struct for our tests
-struct TestJobTP: ThreadJob
+struct TestJobTP: ThreadpoolTask
 {
 	U32 in = 0;
 	U32 iterations = 0;
 
-	void operator()(U /*threadId*/, U /*threadsCount*/)
+	void operator()(ThreadId /*threadId*/, U /*threadsCount*/)
 	{
 		for(U32 i = 0; i < iterations; i++)	
 		{
@@ -22,11 +21,11 @@ struct TestJobTP: ThreadJob
 
 } // end namespace anki
 
-ANKI_TEST(Core, ThreadPool)
+ANKI_TEST(Core, Threadpool)
 {
 	const U32 threadsCount = 4;
 	const U32 repeat = 500;
-	ThreadPool* tp = new ThreadPool;
+	Threadpool* tp = new Threadpool;
 
 	tp->init(threadsCount);
 	TestJobTP jobs[threadsCount];
@@ -40,15 +39,17 @@ ANKI_TEST(Core, ThreadPool)
 			jobs[j].in = i;
 			jobs[j].iterations = iterations;
 
-			tp->assignNewJob(j, &jobs[j]);
+			tp->assignNewTask(j, &jobs[j]);
 		}
 
-		tp->waitForAllJobsToFinish();
+		tp->waitForAllThreadsToFinish();
 
 		for(U32 j = 0; j < threadsCount; j++)
 		{
 			ANKI_TEST_EXPECT_EQ(jobs[j].in, i + iterations);
 		}
 	}
+
+	delete tp;
 }