Browse Source

Memory refactoring

Panagiotis Christopoulos Charitos 11 years ago
parent
commit
3871e5e9c0

+ 3 - 0
include/anki/Util.h

@@ -30,6 +30,9 @@
 /// @defgroup util_thread Threading
 /// @ingroup util
 
+/// @defgroup util_logging Logging
+/// @ingroup util
+
 /// @defgroup util_other Other interfaces
 /// @ingroup util
 

+ 12 - 8
include/anki/util/Logger.h

@@ -15,7 +15,7 @@ namespace anki {
 // Forward
 class File;
 
-/// @addtogroup util_other
+/// @addtogroup util_private
 /// @{
 
 /// The logger singleton class. The logger cannot print errors or throw
@@ -94,13 +94,6 @@ private:
 };
 
 typedef Singleton<Logger> LoggerSingleton;
-/// @}
-
-} // end namespace
-
-//==============================================================================
-// Macros                                                                      =
-//==============================================================================
 
 #define ANKI_LOGGER_MESSAGE(t, ...) \
 	do \
@@ -108,17 +101,28 @@ typedef Singleton<Logger> LoggerSingleton;
 		LoggerSingleton::get().writeFormated(ANKI_FILE, __LINE__, ANKI_FUNC, \
 			t, __VA_ARGS__); \
 	} while(false);
+/// @}
 
+/// @addtogroup util_logging
+/// @{
+
+/// Log information message.
 #define ANKI_LOGI(...) ANKI_LOGGER_MESSAGE(Logger::MessageType::NORMAL, \
 	__VA_ARGS__)
 
+/// Log warning message.
 #define ANKI_LOGW(...) ANKI_LOGGER_MESSAGE(Logger::MessageType::WARNING, \
 	__VA_ARGS__)
 
+/// Log error message.
 #define ANKI_LOGE(...) ANKI_LOGGER_MESSAGE(Logger::MessageType::ERROR, \
 	__VA_ARGS__)
 
+/// Log fatal message. It will will abort.
 #define ANKI_LOGF(...) ANKI_LOGGER_MESSAGE(Logger::MessageType::FATAL, \
 	__VA_ARGS__)
+/// @}
+
+} // end namespace anki
 
 #endif

+ 104 - 130
include/anki/util/Memory.h

@@ -47,31 +47,36 @@ using AllocationSignature = U32;
 void* allocAligned(
 	void* userData, void* ptr, PtrSize size, PtrSize alignment);
 
-/// A dummy interface to match the StackMemoryPool and ChainMemoryPool 
-/// interfaces in order to be used by the same allocator template
-class HeapMemoryPool
+/// Generic memory pool. It can be HeapMemoryPool or StackMemoryPool or 
+/// ChainMemoryPool.
+class BaseMemoryPool: public NonCopyable
 {
 public:
-	/// Default constructor
-	/// @note It does _nothing_ and it should stay that way. First of all,
-	///       this class should have the same interface with the other pools
-	///       and secondly, the default constructors of the allocators that use
-	///       that pool will call that constructor and that happens a lot.
-	///       If that constructor does some actual job then we have a problem.
-	HeapMemoryPool();
+	/// Pool type.
+	enum class Type: U8
+	{
+		NONE,
+		HEAP,
+		STACK,
+		CHAIN
+	};
 
-	/// Destroy
-	~HeapMemoryPool();
+	BaseMemoryPool(Type type)
+	:	m_type(type)
+	{}
 
-	/// The real constructor.
-	/// @param allocCb The allocation function callback
-	/// @param allocCbUserData The user data to pass to the allocation function
-	Error create(AllocAlignedCallback allocCb, void* allocCbUserData);
+	virtual ~BaseMemoryPool();
 
-	/// Allocate memory
-	void* allocate(PtrSize size, PtrSize alignment);
+	/// Allocate memory. This operation MAY be thread safe
+	/// @param size The size to allocate
+	/// @param alignmentBytes The alignment of the returned address
+	/// @return The allocated memory or nullptr on failure
+	void* allocate(PtrSize size, PtrSize alignmentBytes);
 
-	/// Free memory
+	/// Free memory. If the ptr is not the last allocation of the chunk
+	/// then nothing happens and the method returns false
+	/// @param[in, out] ptr Memory block to deallocate
+	/// @return True if the deallocation actually happened and false otherwise
 	Bool free(void* ptr);
 
 	/// Get refcount.
@@ -80,18 +85,12 @@ public:
 		return m_refcount;
 	}
 
-	/// Get the number of users for this pool.
+	/// Get number of users.
 	U32 getUsersCount() const
 	{
 		return m_refcount.load();
 	}
 
-	/// Return number of allocations
-	U32 getAllocationsCount() const
-	{
-		return m_allocationsCount.load();
-	}
-
 	/// Get allocation callback.
 	AllocAlignedCallback getAllocationCallback() const
 	{
@@ -104,11 +103,61 @@ public:
 		return m_allocCbUserData;
 	}
 
-private:
-	AtomicU32 m_refcount = {0};
+	/// Return number of allocations
+	U32 getAllocationsCount() const
+	{
+		return m_allocationsCount.load();
+	}
+
+protected:
+	/// User allocation function.
 	AllocAlignedCallback m_allocCb = nullptr;
+
+	/// User allocation function data.
 	void* m_allocCbUserData = nullptr;
+
+	/// Allocations count.
 	AtomicU32 m_allocationsCount = {0};
+
+	/// Check if already created.
+	Bool isCreated() const;
+
+private:
+	/// Type.
+	Type m_type = Type::NONE;
+
+	/// Refcount.
+	AtomicU32 m_refcount = {0};
+};
+
+/// A dummy interface to match the StackMemoryPool and ChainMemoryPool 
+/// interfaces in order to be used by the same allocator template
+class HeapMemoryPool: public BaseMemoryPool
+{
+public:
+	/// Default constructor
+	/// @note It does _nothing_ and it should stay that way. First of all,
+	///       this class should have the same interface with the other pools
+	///       and secondly, the default constructors of the allocators that use
+	///       that pool will call that constructor and that happens a lot.
+	///       If that constructor does some actual job then we have a problem.
+	HeapMemoryPool();
+
+	/// Destroy
+	~HeapMemoryPool();
+
+	/// The real constructor.
+	/// @param allocCb The allocation function callback
+	/// @param allocCbUserData The user data to pass to the allocation function
+	Error create(AllocAlignedCallback allocCb, void* allocCbUserData);
+
+	/// Allocate memory
+	void* allocate(PtrSize size, PtrSize alignment);
+
+	/// Free memory
+	Bool free(void* ptr);
+
+private:
 #if ANKI_MEM_USE_SIGNATURES
 	AllocationSignature m_signature = 0;
 	static const U32 MAX_ALIGNMENT = 16;
@@ -119,10 +168,8 @@ private:
 /// 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: public BaseMemoryPool
 {
-	friend class ChainMemoryPool;
-
 public:
 	/// The type of the pool's snapshot
 	using Snapshot = void*;
@@ -156,36 +203,6 @@ public:
 	/// @return True if the deallocation actually happened and false otherwise
 	Bool free(void* ptr);
 
-	/// Get the refcount.
-	AtomicU32& getRefcount()
-	{
-		return m_refcount;
-	}
-
-	/// Get the number of users for this pool.
-	U32 getUsersCount() const
-	{
-		return m_refcount.load();
-	}
-
-	/// Get allocation callback.
-	AllocAlignedCallback getAllocationCallback() const
-	{
-		return m_allocCb;
-	}
-
-	/// Get allocation callback user data.
-	void* getAllocationCallbackUserData() const
-	{
-		return m_allocCbUserData;
-	}
-
-	/// Return number of allocations
-	U32 getAllocationsCount() const
-	{
-		return m_allocationsCount.load();
-	}
-
 	/// Get the total size.
 	PtrSize getTotalSize() const
 	{
@@ -221,22 +238,13 @@ private:
 	static_assert(alignof(MemoryBlockHeader) == 1, "Alignment error");
 	static_assert(sizeof(MemoryBlockHeader) == sizeof(U32), "Size error");
 
-	/// Refcount
-	AtomicU32 m_refcount = {0};
-
-	/// User allocation function
-	AllocAlignedCallback m_allocCb = nullptr;
-
-	/// User allocation function data
-	void* m_allocCbUserData = nullptr;
-
 	/// Alignment of allocations
 	PtrSize m_alignmentBytes = 0;
 
 	/// Aligned size of MemoryBlockHeader
 	PtrSize m_headerSize = 0;
 
-	/// Pre-allocated memory chunk
+	/// Pre-allocated memory chunk.
 	U8* m_memory = nullptr;
 
 	/// Size of the pre-allocated memory chunk
@@ -244,16 +252,11 @@ private:
 
 	/// Points to the memory and more specifically to the top of the stack
 	Atomic<U8*> m_top = {nullptr};
-
-	AtomicU32 m_allocationsCount = {0};
-
-	/// The ChainMemoryPool will set that to true.
-	Bool8 m_ignoreAllocationErrors = false;
 };
 
 /// 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 BaseMemoryPool
 {
 public:
 	/// Chunk allocation method. Defines the size a newely created chunk should
@@ -304,36 +307,6 @@ public:
 	/// @return True if the deallocation actually happened and false otherwise
 	Bool free(void* ptr);
 
-	/// Get refcount.
-	AtomicU32& getRefcount()
-	{
-		return m_refcount;
-	}
-
-	/// Get the number of users for this pool
-	U32 getUsersCount() const
-	{
-		return m_refcount.load();
-	}
-
-	/// Get allocation callback.
-	AllocAlignedCallback getAllocationCallback() const
-	{
-		return m_allocCb;
-	}
-
-	/// Get allocation callback user data.
-	void* getAllocationCallbackUserData() const
-	{
-		return m_allocCbUserData;
-	}
-
-	/// Return number of allocations
-	U32 getAllocationsCount() const
-	{
-		return m_allocationsCount;
-	}
-
 	/// @name Methods used for optimizing future chains.
 	/// @{
 	PtrSize getChunksCount() const;
@@ -345,7 +318,14 @@ private:
 	/// A chunk of memory
 	struct Chunk
 	{
-		StackMemoryPool m_pool;
+		/// Pre-allocated memory chunk.
+		U8* m_memory = nullptr;
+
+		/// Size of the pre-allocated memory chunk
+		PtrSize m_memsize = 0;
+
+		/// Points to the memory and more specifically to the top of the stack
+		U8* m_top = nullptr;
 
 		/// Used to identify if the chunk can be deleted
 		U32 m_allocationsCount = 0;
@@ -354,49 +334,43 @@ private:
 		Chunk* m_next = nullptr;
 	};
 
-	/// Refcount
-	AtomicU32 m_refcount = {0};
-
-	/// User allocation function
-	AllocAlignedCallback m_allocCb = nullptr;
-
-	/// User allocation function data
-	void* m_allocCbUserData = nullptr;
-
-	/// Alignment of allocations
+	/// Alignment of allocations.
 	PtrSize m_alignmentBytes = 0;
 
-	/// The first chunk
+	/// The first chunk.
 	Chunk* m_headChunk = nullptr;
 
-	/// Current chunk to allocate from
+	/// Current chunk to allocate from.
 	Chunk* m_tailChunk = nullptr;
 
-	/// Fast thread locking
+	/// Fast thread locking.
 	SpinLock* m_lock = nullptr;
 
-	/// Chunk first chunk size
+	/// Size of the first chunk.
 	PtrSize m_initSize = 0;
 
-	/// Chunk max size
+	/// Chunk max size.
 	PtrSize m_maxSize = 0;
 
-	/// Chunk allocation method value
+	/// Chunk allocation method value.
 	U32 m_step = 0;
 
-	/// Chunk allocation method
+	/// Chunk allocation method.
 	ChunkGrowMethod m_method = ChunkGrowMethod::NONE;
 
-	/// Allocations number.
-	U32 m_allocationsCount = 0;
+	/// Compute the size for the next chunk.
+	/// @param size The current allocation size.
+	PtrSize computeNewChunkSize(PtrSize size);
 
-	/// Create a new chunk
+	/// Create a new chunk.
 	Chunk* createNewChunk(PtrSize size);
 
-	/// Allocate from chunk
+	/// Allocate from chunk.
 	void* allocateFromChunk(Chunk* ch, PtrSize size, PtrSize alignment);
-};
 
+	/// Destroy a chunk.
+	void destroyChunk(Chunk* ch);
+};
 /// @}
 
 } // end namespace anki

+ 159 - 56
src/util/Memory.cpp

@@ -133,19 +133,81 @@ void* allocAligned(
 	return out;
 }
 
+//==============================================================================
+// BaseMemoryPool                                                              =
+//==============================================================================
+
+//==============================================================================
+BaseMemoryPool::~BaseMemoryPool()
+{
+	ANKI_ASSERT(m_refcount == 0 && "Refcount should be zero");
+}
+
+//==============================================================================
+Bool BaseMemoryPool::isCreated() const
+{
+	return m_allocCb != nullptr;
+}
+
+//==============================================================================
+void* BaseMemoryPool::allocate(PtrSize size, PtrSize alignmentBytes)
+{
+	void* out = nullptr;
+	switch(m_type)
+	{
+	case Type::HEAP:
+		out = static_cast<HeapMemoryPool*>(this)->allocate(
+			size, alignmentBytes);
+		break;
+	case Type::STACK:
+		out = static_cast<StackMemoryPool*>(this)->allocate(
+			size, alignmentBytes);
+		break;
+	case Type::CHAIN:
+		out = static_cast<ChainMemoryPool*>(this)->allocate(
+			size, alignmentBytes);
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	return out;
+}
+
+//==============================================================================
+Bool BaseMemoryPool::free(void* ptr)
+{
+	Bool out = false;
+	switch(m_type)
+	{
+	case Type::HEAP:
+		out = static_cast<HeapMemoryPool*>(this)->free(ptr);
+		break;
+	case Type::STACK:
+		out = static_cast<StackMemoryPool*>(this)->free(ptr);
+		break;
+	case Type::CHAIN:
+		out = static_cast<ChainMemoryPool*>(this)->free(ptr);
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	return out;
+}
+
 //==============================================================================
 // HeapMemoryPool                                                              =
 //==============================================================================
 
 //==============================================================================
 HeapMemoryPool::HeapMemoryPool()
+:	BaseMemoryPool(Type::HEAP)
 {}
 
 //==============================================================================
 HeapMemoryPool::~HeapMemoryPool()
 {
-	ANKI_ASSERT(m_refcount == 0 && "Refcount should be zero");
-
 	if(m_allocationsCount != 0)
 	{
 		ANKI_LOGW("Memory pool destroyed before all memory being released");
@@ -156,11 +218,10 @@ HeapMemoryPool::~HeapMemoryPool()
 Error HeapMemoryPool::create(
 	AllocAlignedCallback allocCb, void* allocCbUserData)
 {
+	ANKI_ASSERT(!isCreated());
 	ANKI_ASSERT(m_allocCb == nullptr);
 	ANKI_ASSERT(allocCb != nullptr);
 
-	m_refcount = 0;
-	m_allocationsCount = 0;
 	m_allocCb = allocCb;
 	m_allocCbUserData = allocCbUserData;
 #if ANKI_MEM_SIGNATURES
@@ -173,7 +234,8 @@ Error HeapMemoryPool::create(
 
 //==============================================================================
 void* HeapMemoryPool::allocate(PtrSize size, PtrSize alignment)
-{	
+{
+	ANKI_ASSERT(isCreated());
 #if ANKI_MEM_SIGNATURES
 	ANKI_ASSERT(alignment <= MAX_ALIGNMENT && "Wrong assumption");
 	size += m_headerSize;
@@ -204,6 +266,8 @@ void* HeapMemoryPool::allocate(PtrSize size, PtrSize alignment)
 //==============================================================================
 Bool HeapMemoryPool::free(void* ptr)
 {
+	ANKI_ASSERT(isCreated());
+
 #if ANKI_MEM_SIGNATURES
 	U8* memU8 = static_cast<U8*>(ptr);
 	memU8 -= m_headerSize;
@@ -226,6 +290,7 @@ Bool HeapMemoryPool::free(void* ptr)
 
 //==============================================================================
 StackMemoryPool::StackMemoryPool()
+:	BaseMemoryPool(Type::STACK)
 {}
 
 //==============================================================================
@@ -246,18 +311,17 @@ Error StackMemoryPool::create(
 	AllocAlignedCallback allocCb, void* allocCbUserData,
 	PtrSize size, PtrSize alignmentBytes)
 {
+	ANKI_ASSERT(!isCreated());
 	ANKI_ASSERT(allocCb);
 	ANKI_ASSERT(size > 0);
 	ANKI_ASSERT(alignmentBytes > 0);
 
 	Error error = ErrorCode::NONE;
 
-	m_refcount = 0;
 	m_allocCb = allocCb;
 	m_allocCbUserData = allocCbUserData;
 	m_alignmentBytes = alignmentBytes;
 	m_memsize = getAlignedRoundUp(alignmentBytes, size);
-	m_allocationsCount = 0;
 
 	m_memory = reinterpret_cast<U8*>(m_allocCb(
 		m_allocCbUserData, nullptr, m_memsize, m_alignmentBytes));
@@ -288,7 +352,7 @@ Error StackMemoryPool::create(
 //==============================================================================
 void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment)
 {
-	ANKI_ASSERT(m_memory != nullptr);
+	ANKI_ASSERT(isCreated());
 	ANKI_ASSERT(alignment <= m_alignmentBytes);
 	(void)alignment;
 
@@ -322,12 +386,7 @@ void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment)
 	}
 	else
 	{
-		// Not always an error
-		if(!m_ignoreAllocationErrors)
-		{
-			ANKI_LOGE("Out of memory");
-		}
-
+		ANKI_LOGE("Out of memory");
 		out = nullptr;
 	}
 
@@ -337,6 +396,8 @@ void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment)
 //==============================================================================
 Bool StackMemoryPool::free(void* ptr)
 {
+	ANKI_ASSERT(isCreated());
+
 	// ptr shouldn't be null or not aligned. If not aligned it was not 
 	// allocated by this class
 	ANKI_ASSERT(ptr != nullptr && isAligned(m_alignmentBytes, ptr));
@@ -380,6 +441,8 @@ Bool StackMemoryPool::free(void* ptr)
 //==============================================================================
 void StackMemoryPool::reset()
 {
+	ANKI_ASSERT(isCreated());
+
 	// memory is nullptr if moved
 	ANKI_ASSERT(m_memory != nullptr);
 
@@ -395,14 +458,14 @@ void StackMemoryPool::reset()
 //==============================================================================
 StackMemoryPool::Snapshot StackMemoryPool::getShapshot() const
 {
-	ANKI_ASSERT(m_memory != nullptr);
+	ANKI_ASSERT(isCreated());
 	return m_top.load();
 }
 
 //==============================================================================
 void StackMemoryPool::resetUsingSnapshot(Snapshot s)
 {
-	ANKI_ASSERT(m_memory != nullptr);
+	ANKI_ASSERT(isCreated());
 	ANKI_ASSERT(static_cast<U8*>(s) >= m_memory);
 	ANKI_ASSERT(static_cast<U8*>(s) < m_memory + m_memsize);
 
@@ -415,19 +478,22 @@ void StackMemoryPool::resetUsingSnapshot(Snapshot s)
 
 //==============================================================================
 ChainMemoryPool::ChainMemoryPool()
+:	BaseMemoryPool(Type::CHAIN)
 {}
 
 //==============================================================================
 ChainMemoryPool::~ChainMemoryPool()
 {
+	if(m_allocationsCount != 0)
+	{
+		ANKI_LOGW("Memory pool destroyed before all memory being released");
+	}
+
 	Chunk* ch = m_headChunk;
 	while(ch)
 	{
 		Chunk* next = ch->m_next;
-
-		ch->~Chunk();
-		m_allocCb(m_allocCbUserData, ch, 0, 0);
-
+		destroyChunk(ch);
 		ch = next;
 	}
 
@@ -449,6 +515,7 @@ Error ChainMemoryPool::create(
 	PtrSize chunkAllocStep, 
 	PtrSize alignmentBytes)
 {
+	ANKI_ASSERT(!isCreated());
 	ANKI_ASSERT(m_allocCb == nullptr);
 
 	// Set all values
@@ -464,6 +531,7 @@ Error ChainMemoryPool::create(
 		m_allocCbUserData, nullptr, sizeof(SpinLock), alignof(SpinLock)));
 	if(!m_lock)
 	{
+		ANKI_LOGE("Out of memory");
 		return ErrorCode::OUT_OF_MEMORY;
 	}
 	construct(m_lock);
@@ -472,20 +540,20 @@ Error ChainMemoryPool::create(
 	ANKI_ASSERT(m_initSize > 0 && "Wrong arg");
 
 	// On fixed step should be 0
-	if(m_method == ChainMemoryPool::ChunkGrowMethod::FIXED)
+	if(m_method == ChunkGrowMethod::FIXED)
 	{
 		ANKI_ASSERT(m_step == 0 && "Wrong arg");
 	}
 
 	// On fixed initial size is the same as the max
-	if(m_method == ChainMemoryPool::ChunkGrowMethod::FIXED)
+	if(m_method == ChunkGrowMethod::FIXED)
 	{
 		ANKI_ASSERT(m_initSize == m_maxSize && "Wrong arg");
 	}
 
 	// On add and mul the max size should be greater than initial
-	if(m_method == ChainMemoryPool::ChunkGrowMethod::ADD 
-		|| m_method == ChainMemoryPool::ChunkGrowMethod::MULTIPLY)
+	if(m_method == ChunkGrowMethod::ADD 
+		|| m_method == ChunkGrowMethod::MULTIPLY)
 	{
 		ANKI_ASSERT(m_initSize < m_maxSize && "Wrong arg");
 	}
@@ -496,7 +564,7 @@ Error ChainMemoryPool::create(
 //==============================================================================
 void* ChainMemoryPool::allocate(PtrSize size, PtrSize alignment)
 {
-	ANKI_ASSERT(m_allocCb);
+	ANKI_ASSERT(isCreated());
 	ANKI_ASSERT(size <= m_maxSize);
 
 	Chunk* ch;
@@ -512,7 +580,8 @@ void* ChainMemoryPool::allocate(PtrSize size, PtrSize alignment)
 		|| (mem = allocateFromChunk(ch, size, alignment)) == nullptr)
 	{
 		// Create new chunk
-		ch = createNewChunk(size);
+		PtrSize chunkSize = computeNewChunkSize(size);
+		ch = createNewChunk(chunkSize);
 
 		// Chunk creation failed
 		if(ch == nullptr)
@@ -535,7 +604,7 @@ void* ChainMemoryPool::allocate(PtrSize size, PtrSize alignment)
 //==============================================================================
 Bool ChainMemoryPool::free(void* ptr)
 {
-	ANKI_ASSERT(m_allocCb);
+	ANKI_ASSERT(isCreated());
 	if(ptr == nullptr)
 	{
 		return true;
@@ -543,13 +612,13 @@ Bool ChainMemoryPool::free(void* ptr)
 
 	LockGuard<SpinLock> lock(*m_lock);
 
-	// Get the chunk that ptr belongs to
+	// Find the chunk the ptr belongs to
 	Chunk* chunk = m_headChunk;
 	Chunk* prevChunk = nullptr;
 	while(chunk)
 	{
-		const U8* from = chunk->m_pool.m_memory;
-		const U8* to = from + chunk->m_pool.m_memsize;
+		const U8* from = chunk->m_memory;
+		const U8* to = from + chunk->m_memsize;
 		const U8* cptr = reinterpret_cast<const U8*>(ptr);
 		if(cptr >= from && cptr < to)
 		{
@@ -587,8 +656,7 @@ Bool ChainMemoryPool::free(void* ptr)
 		}
 
 		// Finaly delete it
-		chunk->~Chunk();
-		m_allocCb(m_allocCbUserData, chunk, 0, 0);
+		destroyChunk(chunk);
 	}
 
 	--m_allocationsCount;
@@ -599,7 +667,7 @@ Bool ChainMemoryPool::free(void* ptr)
 //==============================================================================
 PtrSize ChainMemoryPool::getChunksCount() const
 {
-	ANKI_ASSERT(m_allocCb);
+	ANKI_ASSERT(isCreated());
 
 	PtrSize count = 0;
 	Chunk* ch = m_headChunk;
@@ -615,13 +683,13 @@ PtrSize ChainMemoryPool::getChunksCount() const
 //==============================================================================
 PtrSize ChainMemoryPool::getAllocatedSize() const
 {
-	ANKI_ASSERT(m_allocCb);
+	ANKI_ASSERT(isCreated());
 
 	PtrSize sum = 0;
 	Chunk* ch = m_headChunk;
 	while(ch)
 	{
-		sum += ch->m_pool.getAllocatedSize();
+		sum += ch->m_top - ch->m_memory;
 		ch = ch->m_next;
 	}
 
@@ -629,15 +697,13 @@ PtrSize ChainMemoryPool::getAllocatedSize() const
 }
 
 //==============================================================================
-ChainMemoryPool::Chunk* ChainMemoryPool::createNewChunk(PtrSize size)
+PtrSize ChainMemoryPool::computeNewChunkSize(PtrSize size)
 {
-	//
-	// Calculate preferred size
-	//
-	
+	ANKI_ASSERT(size <= m_maxSize);
+
 	// Get the size of the next chunk
 	PtrSize crntMaxSize;
-	if(m_method == ChainMemoryPool::ChunkGrowMethod::FIXED)
+	if(m_method == ChunkGrowMethod::FIXED)
 	{
 		crntMaxSize = m_initSize;
 	}
@@ -647,7 +713,8 @@ ChainMemoryPool::Chunk* ChainMemoryPool::createNewChunk(PtrSize size)
 		if(m_tailChunk != nullptr)
 		{
 			// Get the size of previous
-			crntMaxSize = m_tailChunk->m_pool.getTotalSize();
+			crntMaxSize = m_tailChunk->m_memsize;
+			ANKI_ASSERT(crntMaxSize > 0);
 
 			// Increase it
 			if(m_method == ChainMemoryPool::ChunkGrowMethod::MULTIPLY)
@@ -672,27 +739,38 @@ ChainMemoryPool::Chunk* ChainMemoryPool::createNewChunk(PtrSize size)
 		ANKI_ASSERT(crntMaxSize > 0);
 
 		// Fix the size
-		crntMaxSize = min(crntMaxSize, (PtrSize)m_maxSize);
+		crntMaxSize = min(crntMaxSize, m_maxSize);
 	}
 
-	size = max(crntMaxSize, size) + 16;
+	size = max(crntMaxSize, size) + m_alignmentBytes;
+
+	return size;
+}
+
+//==============================================================================
+ChainMemoryPool::Chunk* ChainMemoryPool::createNewChunk(PtrSize size)
+{
+	ANKI_ASSERT(size > 0);
 
-	//
-	// Create the chunk
-	//
-	Chunk* chunk = (Chunk*)m_allocCb(
-		m_allocCbUserData, nullptr, sizeof(Chunk), alignof(Chunk));
+	Chunk* chunk = reinterpret_cast<Chunk*>(m_allocCb(
+		m_allocCbUserData, nullptr, sizeof(Chunk), alignof(Chunk)));
 
 	if(chunk)
 	{
 		// Construct it
 		construct(chunk);
-		Error error = chunk->m_pool.create(
-			m_allocCb, m_allocCbUserData, size, m_alignmentBytes);
 
-		if(!error)
+		// Initialize it
+		chunk->m_memory = reinterpret_cast<U8*>(
+			m_allocCb(m_allocCbUserData, nullptr, size, m_alignmentBytes));
+		if(chunk->m_memory)
 		{
-			chunk->m_pool.m_ignoreAllocationErrors = true;
+			chunk->m_memsize = size;
+			chunk->m_top = chunk->m_memory;
+
+#if ANKI_DEBUG
+			memset(chunk->m_memory, 0xCC, chunk->m_memsize);
+#endif
 
 			// Register it
 			if(m_tailChunk)
@@ -703,12 +781,12 @@ ChainMemoryPool::Chunk* ChainMemoryPool::createNewChunk(PtrSize size)
 			else
 			{
 				ANKI_ASSERT(m_headChunk == nullptr);
-
 				m_headChunk = m_tailChunk = chunk;
 			}
 		}
 		else
 		{
+			ANKI_LOGE("Out of memory");
 			destruct(chunk);
 			m_allocCb(m_allocCbUserData, chunk, 0, 0);
 			chunk = nullptr;
@@ -728,18 +806,43 @@ void* ChainMemoryPool::allocateFromChunk(
 {
 	ANKI_ASSERT(ch);
 	ANKI_ASSERT(size <= m_maxSize);
-	void* mem = ch->m_pool.allocate(size, alignment);
+	ANKI_ASSERT(ch->m_top <= ch->m_memory + ch->m_memsize);
+
+	U8* mem = ch->m_top;
+	alignRoundUp(m_alignmentBytes, mem);
+	U8* newTop = mem + size;
 
-	if(mem)
+	if(newTop <= ch->m_memory + ch->m_memsize)
 	{
+		ch->m_top = newTop;
 		++ch->m_allocationsCount;
 	}
 	else
 	{
 		// Chunk is full. Need a new one
+		mem = nullptr;
 	}
 
 	return mem;
 }
 
+//==============================================================================
+void ChainMemoryPool::destroyChunk(Chunk* ch)
+{
+	ANKI_ASSERT(ch);
+
+	if(ch->m_memory)
+	{
+#if ANKI_DEBUG
+		memset(ch->m_memory, 0xCC, ch->m_memsize);
+#endif
+		m_allocCb(m_allocCbUserData, ch->m_memory, 0, 0);
+	}
+
+#if ANKI_DEBUG
+	memset(ch, 0xCC, sizeof(*ch));
+#endif
+	m_allocCb(m_allocCbUserData, ch, 0, 0);
+}
+
 } // end namespace anki

+ 1 - 1
testapp/Main.cpp

@@ -229,7 +229,7 @@ Error init()
 	{
 		ScriptResourcePointer script;
 
-		err = script.load("maps/adis/scene.lua", &resources);
+		err = script.load("maps/sponza/scene.lua", &resources);
 		if(err) return err;
 
 		err = app->getScriptManager().evalString(script->getSource());

+ 1 - 1
tests/util/Allocator.cpp

@@ -8,7 +8,7 @@
 #include "anki/util/Allocator.h"
 #include <string>
 
-ANKI_TEST(Allocators, StackAllocator)
+ANKI_TEST(Util, StackAllocator)
 {
 	Foo::reset();
 

+ 4 - 4
tests/util/HighRezTimer.cpp

@@ -7,22 +7,22 @@
 #include "anki/util/HighRezTimer.h"
 #include <unistd.h>
 
-ANKI_TEST(HighRezTimer, Test)
+ANKI_TEST(Util, Test)
 {
 	HighRezTimer t;
 	t.start();
 	
 	sleep(2);
 	
-	ANKI_TEST_EXPECT_EQ(t.getElapsedTime(), 2.0);
+	ANKI_TEST_EXPECT_NEAR(t.getElapsedTime(), 2.0, 0.2);
 	
 	sleep(1);
 	
-	ANKI_TEST_EXPECT_EQ(t.getElapsedTime(), 3.0);
+	ANKI_TEST_EXPECT_NEAR(t.getElapsedTime(), 3.0, 0.2);
 	
 	sleep(1);
 	t.stop();
 	sleep(1);
 	
-	ANKI_TEST_EXPECT_EQ(t.getElapsedTime(), 4.0);
+	ANKI_TEST_EXPECT_NEAR(t.getElapsedTime(), 4.0, 0.2);
 }

+ 2 - 2
tests/util/Memory.cpp

@@ -8,7 +8,7 @@
 #include "anki/util/Memory.h"
 #include <type_traits>
 
-ANKI_TEST(Memory, StackMemoryPool)
+ANKI_TEST(Util, StackMemoryPool)
 {
 	// Create/destroy test
 	{
@@ -64,7 +64,7 @@ ANKI_TEST(Memory, StackMemoryPool)
 	}
 }
 
-ANKI_TEST(Memory, ChainMemoryPool)
+ANKI_TEST(Util, ChainMemoryPool)
 {
 	// Basic test
 	{