Browse Source

Some CPU memory refactoring

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
4cb12bad44

+ 2 - 6
AnKi/Util/Allocator.h

@@ -93,8 +93,8 @@ public:
 			ANKI_UTIL_LOGF("Out of memory");
 			ANKI_UTIL_LOGF("Out of memory");
 		}
 		}
 
 
-		::new(m_pool) TPool(allocCb, allocCbUserData, std::forward<TArgs>(args)...);
-
+		::new(m_pool) TPool();
+		m_pool->init(allocCb, allocCbUserData, std::forward<TArgs>(args)...);
 		m_pool->retain();
 		m_pool->retain();
 	}
 	}
 
 
@@ -420,10 +420,6 @@ using HeapAllocator = GenericPoolAllocator<T, HeapMemoryPool>;
 template<typename T>
 template<typename T>
 using StackAllocator = GenericPoolAllocator<T, StackMemoryPool>;
 using StackAllocator = GenericPoolAllocator<T, StackMemoryPool>;
 
 
-/// Allocator that uses a ChainMemoryPool
-template<typename T>
-using ChainAllocator = GenericPoolAllocator<T, ChainMemoryPool>;
-
 #define ANKI_FRIEND_ALLOCATOR \
 #define ANKI_FRIEND_ALLOCATOR \
 	template<typename, typename> \
 	template<typename, typename> \
 	friend class GenericPoolAllocator;
 	friend class GenericPoolAllocator;

+ 53 - 297
AnKi/Util/CpuMemoryPools.cpp

@@ -35,58 +35,53 @@ public:
 };
 };
 
 
 constexpr U32 kExtraChecksMaxAlignment = 64;
 constexpr U32 kExtraChecksMaxAlignment = 64;
-constexpr U32 kAllocationHeaderSize = getAlignedRoundUp(kExtraChecksMaxAlignment, sizeof(AllocationHeader));
-#endif
-
-#define ANKI_CREATION_OOM_ACTION() ANKI_UTIL_LOGF("Out of memory")
-#define ANKI_OOM_ACTION() ANKI_UTIL_LOGE("Out of memory. Expect segfault")
+constexpr U32 kAllocationHeaderSize = getAlignedRoundUp(kExtraChecksMaxAlignment, U32(sizeof(AllocationHeader)));
 
 
 template<typename TPtr, typename TSize>
 template<typename TPtr, typename TSize>
 static void invalidateMemory([[maybe_unused]] TPtr ptr, [[maybe_unused]] TSize size)
 static void invalidateMemory([[maybe_unused]] TPtr ptr, [[maybe_unused]] TSize size)
 {
 {
-#if ANKI_MEM_EXTRA_CHECKS
 	memset(static_cast<void*>(ptr), 0xCC, size);
 	memset(static_cast<void*>(ptr), 0xCC, size);
-#endif
 }
 }
+#endif
+
+#define ANKI_OOM_ACTION() ANKI_UTIL_LOGE("Out of memory. Expect segfault")
 
 
 void* mallocAligned(PtrSize size, PtrSize alignmentBytes)
 void* mallocAligned(PtrSize size, PtrSize alignmentBytes)
 {
 {
 	ANKI_ASSERT(size > 0);
 	ANKI_ASSERT(size > 0);
 	ANKI_ASSERT(alignmentBytes > 0);
 	ANKI_ASSERT(alignmentBytes > 0);
 
 
-#if ANKI_POSIX
-#	if !ANKI_OS_ANDROID
-	void* out = nullptr;
-	PtrSize alignment = getAlignedRoundUp(alignmentBytes, sizeof(void*));
-	int err = posix_memalign(&out, alignment, size);
+#if ANKI_OS_ANDROID
+	void* out = memalign(getAlignedRoundUp(alignmentBytes, sizeof(void*)), size);
 
 
-	if(ANKI_LIKELY(!err))
+	if(out)
 	{
 	{
-		ANKI_ASSERT(out != nullptr);
 		// Make sure it's aligned
 		// Make sure it's aligned
 		ANKI_ASSERT(isAligned(alignmentBytes, out));
 		ANKI_ASSERT(isAligned(alignmentBytes, out));
 	}
 	}
 	else
 	else
 	{
 	{
-		ANKI_UTIL_LOGE("mallocAligned() failed");
+		ANKI_UTIL_LOGE("memalign() failed, Size %zu, alignment %zu", size, alignmentBytes);
 	}
 	}
 
 
 	return out;
 	return out;
-#	else
-	void* out = memalign(getAlignedRoundUp(alignmentBytes, sizeof(void*)), size);
+#elif ANKI_POSIX
+	void* out = nullptr;
+	PtrSize alignment = getAlignedRoundUp(alignmentBytes, sizeof(void*));
+	int err = posix_memalign(&out, alignment, size);
 
 
-	if(out)
+	if(ANKI_LIKELY(!err))
 	{
 	{
+		ANKI_ASSERT(out != nullptr);
 		// Make sure it's aligned
 		// Make sure it's aligned
 		ANKI_ASSERT(isAligned(alignmentBytes, out));
 		ANKI_ASSERT(isAligned(alignmentBytes, out));
 	}
 	}
 	else
 	else
 	{
 	{
-		ANKI_UTIL_LOGE("memalign() failed");
+		ANKI_UTIL_LOGE("posix_memalign() failed, Size %zu, alignment %zu", size, alignmentBytes);
 	}
 	}
 
 
 	return out;
 	return out;
-#	endif
 #elif ANKI_OS_WINDOWS
 #elif ANKI_OS_WINDOWS
 	void* out = _aligned_malloc(size, alignmentBytes);
 	void* out = _aligned_malloc(size, alignmentBytes);
 
 
@@ -140,35 +135,48 @@ void* allocAligned([[maybe_unused]] void* userData, void* ptr, PtrSize size, Ptr
 	return out;
 	return out;
 }
 }
 
 
-BaseMemoryPool::BaseMemoryPool(Type type, AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name)
-	: m_allocCb(allocCb)
-	, m_allocCbUserData(allocCbUserData)
-	, m_type(type)
+void BaseMemoryPool::init(AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name)
 {
 {
+	ANKI_ASSERT(m_allocCb == nullptr && m_name == nullptr);
+
 	ANKI_ASSERT(allocCb != nullptr);
 	ANKI_ASSERT(allocCb != nullptr);
+	m_allocCb = allocCb;
+	m_allocCbUserData = allocCbUserData;
 
 
-	I64 len;
+	PtrSize len;
 	if(name && (len = strlen(name)) > 0)
 	if(name && (len = strlen(name)) > 0)
 	{
 	{
-		m_name = static_cast<char*>(malloc(len + 1));
+		m_name = static_cast<char*>(m_allocCb(m_allocCbUserData, nullptr, len + 1, 1));
 		memcpy(m_name, name, len + 1);
 		memcpy(m_name, name, len + 1);
 	}
 	}
 }
 }
 
 
-BaseMemoryPool::~BaseMemoryPool()
+void BaseMemoryPool::destroy()
 {
 {
 	ANKI_ASSERT(m_refcount.load() == 0 && "Refcount should be zero");
 	ANKI_ASSERT(m_refcount.load() == 0 && "Refcount should be zero");
+	if(m_name != nullptr)
+	{
+		m_allocCb(m_allocCbUserData, m_name, 0, 0);
+		m_name = nullptr;
+	}
+	m_allocCb = nullptr;
+	m_allocationCount.setNonAtomically(0);
 }
 }
 
 
-HeapMemoryPool::HeapMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserDataconst, const Char* name)
-	: BaseMemoryPool(Type::kHeap, allocCb, allocCbUserDataconst, name)
+HeapMemoryPool::HeapMemoryPool()
+	: BaseMemoryPool(Type::kHeap)
 {
 {
 #if ANKI_MEM_EXTRA_CHECKS
 #if ANKI_MEM_EXTRA_CHECKS
 	m_signature = computePoolSignature(this);
 	m_signature = computePoolSignature(this);
 #endif
 #endif
 }
 }
 
 
-HeapMemoryPool::~HeapMemoryPool()
+void HeapMemoryPool::init(AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name)
+{
+	BaseMemoryPool::init(allocCb, allocCbUserData, name);
+}
+
+void HeapMemoryPool::destroy()
 {
 {
 	const U32 count = m_allocationCount.load();
 	const U32 count = m_allocationCount.load();
 	if(count != 0)
 	if(count != 0)
@@ -176,6 +184,7 @@ HeapMemoryPool::~HeapMemoryPool()
 		ANKI_UTIL_LOGE("Memory pool destroyed before all memory being released (%u deallocations missed): %s", count,
 		ANKI_UTIL_LOGE("Memory pool destroyed before all memory being released (%u deallocations missed): %s", count,
 					   getName());
 					   getName());
 	}
 	}
+	BaseMemoryPool::destroy();
 }
 }
 
 
 void* HeapMemoryPool::allocate(PtrSize size, PtrSize alignment)
 void* HeapMemoryPool::allocate(PtrSize size, PtrSize alignment)
@@ -234,6 +243,7 @@ void HeapMemoryPool::free(void* ptr)
 Error StackMemoryPool::StackAllocatorBuilderInterface::allocateChunk(PtrSize size, Chunk*& out)
 Error StackMemoryPool::StackAllocatorBuilderInterface::allocateChunk(PtrSize size, Chunk*& out)
 {
 {
 	ANKI_ASSERT(size > 0);
 	ANKI_ASSERT(size > 0);
+	ANKI_ASSERT(m_parent && m_parent->m_allocCb);
 
 
 	const PtrSize fullChunkSize = offsetof(Chunk, m_memoryStart) + size;
 	const PtrSize fullChunkSize = offsetof(Chunk, m_memoryStart) + size;
 
 
@@ -242,7 +252,9 @@ Error StackMemoryPool::StackAllocatorBuilderInterface::allocateChunk(PtrSize siz
 	if(ANKI_LIKELY(mem))
 	if(ANKI_LIKELY(mem))
 	{
 	{
 		out = static_cast<Chunk*>(mem);
 		out = static_cast<Chunk*>(mem);
+#if ANKI_MEM_EXTRA_CHECKS
 		invalidateMemory(&out->m_memoryStart[0], size);
 		invalidateMemory(&out->m_memoryStart[0], size);
+#endif
 	}
 	}
 	else
 	else
 	{
 	{
@@ -256,23 +268,26 @@ Error StackMemoryPool::StackAllocatorBuilderInterface::allocateChunk(PtrSize siz
 void StackMemoryPool::StackAllocatorBuilderInterface::freeChunk(Chunk* chunk)
 void StackMemoryPool::StackAllocatorBuilderInterface::freeChunk(Chunk* chunk)
 {
 {
 	ANKI_ASSERT(chunk);
 	ANKI_ASSERT(chunk);
+	ANKI_ASSERT(m_parent && m_parent->m_allocCb);
 	m_parent->m_allocCb(m_parent->m_allocCbUserData, chunk, 0, 0);
 	m_parent->m_allocCb(m_parent->m_allocCbUserData, chunk, 0, 0);
 }
 }
 
 
 void StackMemoryPool::StackAllocatorBuilderInterface::recycleChunk(Chunk& chunk)
 void StackMemoryPool::StackAllocatorBuilderInterface::recycleChunk(Chunk& chunk)
 {
 {
 	ANKI_ASSERT(chunk.m_chunkSize > 0);
 	ANKI_ASSERT(chunk.m_chunkSize > 0);
+#if ANKI_MEM_EXTRA_CHECKS
 	invalidateMemory(&chunk.m_memoryStart[0], chunk.m_chunkSize);
 	invalidateMemory(&chunk.m_memoryStart[0], chunk.m_chunkSize);
+#endif
 }
 }
 
 
-StackMemoryPool::StackMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize,
-								 F64 nextChunkScale, PtrSize nextChunkBias, Bool ignoreDeallocationErrors,
-								 U32 alignmentBytes, const Char* name)
-	: BaseMemoryPool(Type::kStack, allocCb, allocCbUserData, name)
+void StackMemoryPool::init(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize,
+						   F64 nextChunkScale, PtrSize nextChunkBias, Bool ignoreDeallocationErrors, U32 alignmentBytes,
+						   const Char* name)
 {
 {
 	ANKI_ASSERT(initialChunkSize > 0);
 	ANKI_ASSERT(initialChunkSize > 0);
 	ANKI_ASSERT(nextChunkScale >= 1.0);
 	ANKI_ASSERT(nextChunkScale >= 1.0);
 	ANKI_ASSERT(alignmentBytes > 0 && alignmentBytes <= kMaxAlignment);
 	ANKI_ASSERT(alignmentBytes > 0 && alignmentBytes <= kMaxAlignment);
+	BaseMemoryPool::init(allocCb, allocCbUserData, name);
 
 
 	m_builder.getInterface().m_parent = this;
 	m_builder.getInterface().m_parent = this;
 	m_builder.getInterface().m_alignmentBytes = alignmentBytes;
 	m_builder.getInterface().m_alignmentBytes = alignmentBytes;
@@ -282,8 +297,11 @@ StackMemoryPool::StackMemoryPool(AllocAlignedCallback allocCb, void* allocCbUser
 	m_builder.getInterface().m_nextChunkBias = nextChunkBias;
 	m_builder.getInterface().m_nextChunkBias = nextChunkBias;
 }
 }
 
 
-StackMemoryPool::~StackMemoryPool()
+void StackMemoryPool::destroy()
 {
 {
+	m_builder.destroy();
+	m_builder.getInterface() = {};
+	BaseMemoryPool::destroy();
 }
 }
 
 
 void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment)
 void* StackMemoryPool::allocate(PtrSize size, PtrSize alignment)
@@ -320,266 +338,4 @@ void StackMemoryPool::reset()
 	m_allocationCount.store(0);
 	m_allocationCount.store(0);
 }
 }
 
 
-ChainMemoryPool::ChainMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize,
-								 F32 nextChunkScale, PtrSize nextChunkBias, PtrSize alignmentBytes, const Char* name)
-	: BaseMemoryPool(Type::kChain, allocCb, allocCbUserData, name)
-{
-	ANKI_ASSERT(initialChunkSize > 0);
-	ANKI_ASSERT(nextChunkScale >= 1.0);
-	ANKI_ASSERT(alignmentBytes > 0);
-
-	// Set all values
-	m_alignmentBytes = alignmentBytes;
-	m_initSize = initialChunkSize;
-	m_scale = nextChunkScale;
-	m_bias = nextChunkBias;
-	m_headerSize = max<PtrSize>(m_alignmentBytes, sizeof(Chunk*));
-
-	// Initial size should be > 0
-	ANKI_ASSERT(m_initSize > 0 && "Wrong arg");
-
-	// On fixed initial size is the same as the max
-	if(m_scale == 0.0 && m_bias == 0)
-	{
-		ANKI_ASSERT(0 && "Wrong arg");
-	}
-}
-
-ChainMemoryPool::~ChainMemoryPool()
-{
-	if(m_allocationCount.load() != 0)
-	{
-		ANKI_UTIL_LOGE("Memory pool destroyed before all memory being released");
-	}
-
-	Chunk* ch = m_headChunk;
-	while(ch)
-	{
-		Chunk* next = ch->m_next;
-		destroyChunk(ch);
-		ch = next;
-	}
-}
-
-void* ChainMemoryPool::allocate(PtrSize size, PtrSize alignment)
-{
-	ANKI_ASSERT(size > 0);
-
-	Chunk* ch;
-	void* mem = nullptr;
-
-	LockGuard<SpinLock> lock(m_lock);
-
-	// Get chunk
-	ch = m_tailChunk;
-
-	// Create new chunk if needed
-	if(ch == nullptr || (mem = allocateFromChunk(ch, size, alignment)) == nullptr)
-	{
-		// Create new chunk
-		PtrSize chunkSize = computeNewChunkSize(size);
-		ch = createNewChunk(chunkSize);
-
-		// Chunk creation failed
-		if(ch == nullptr)
-		{
-			return mem;
-		}
-	}
-
-	if(mem == nullptr)
-	{
-		mem = allocateFromChunk(ch, size, alignment);
-		ANKI_ASSERT(mem != nullptr && "The chunk should have space");
-	}
-
-	m_allocationCount.fetchAdd(1);
-
-	return mem;
-}
-
-void ChainMemoryPool::free(void* ptr)
-{
-	if(ANKI_UNLIKELY(ptr == nullptr))
-	{
-		return;
-	}
-
-	// Get the chunk
-	U8* mem = static_cast<U8*>(ptr);
-	mem -= m_headerSize;
-	Chunk* chunk = *reinterpret_cast<Chunk**>(mem);
-
-	ANKI_ASSERT(chunk != nullptr);
-	ANKI_ASSERT((mem >= chunk->m_memory && mem < (chunk->m_memory + chunk->m_memsize)) && "Wrong chunk");
-
-	LockGuard<SpinLock> lock(m_lock);
-
-	// Decrease the deallocation refcount and if it's zero delete the chunk
-	ANKI_ASSERT(chunk->m_allocationCount > 0);
-	if(--chunk->m_allocationCount == 0)
-	{
-		// Chunk is empty. Delete it
-		destroyChunk(chunk);
-	}
-
-	m_allocationCount.fetchSub(1);
-}
-
-PtrSize ChainMemoryPool::getChunksCount() const
-{
-	PtrSize count = 0;
-	Chunk* ch = m_headChunk;
-	while(ch)
-	{
-		++count;
-		ch = ch->m_next;
-	}
-
-	return count;
-}
-
-PtrSize ChainMemoryPool::getAllocatedSize() const
-{
-	PtrSize sum = 0;
-	Chunk* ch = m_headChunk;
-	while(ch)
-	{
-		sum += ch->m_top - ch->m_memory;
-		ch = ch->m_next;
-	}
-
-	return sum;
-}
-
-PtrSize ChainMemoryPool::computeNewChunkSize(PtrSize size) const
-{
-	size += m_headerSize;
-
-	PtrSize crntMaxSize;
-	if(m_tailChunk != nullptr)
-	{
-		// Get the size of previous
-		crntMaxSize = m_tailChunk->m_memsize;
-
-		// Compute new size
-		crntMaxSize = PtrSize(F32(crntMaxSize) * m_scale) + m_bias;
-	}
-	else
-	{
-		// No chunks. Choose initial size
-		ANKI_ASSERT(m_headChunk == nullptr);
-		crntMaxSize = m_initSize;
-	}
-
-	crntMaxSize = max(crntMaxSize, size);
-
-	ANKI_ASSERT(crntMaxSize > 0);
-
-	return crntMaxSize;
-}
-
-ChainMemoryPool::Chunk* ChainMemoryPool::createNewChunk(PtrSize size)
-{
-	ANKI_ASSERT(size > 0);
-
-	// Allocate memory and chunk in one go
-	PtrSize chunkAllocSize = getAlignedRoundUp(m_alignmentBytes, sizeof(Chunk));
-	PtrSize memAllocSize = getAlignedRoundUp(m_alignmentBytes, size);
-	PtrSize allocationSize = chunkAllocSize + memAllocSize;
-
-	Chunk* chunk = reinterpret_cast<Chunk*>(m_allocCb(m_allocCbUserData, nullptr, allocationSize, m_alignmentBytes));
-
-	if(chunk)
-	{
-		invalidateMemory(chunk, allocationSize);
-
-		// Construct it
-		memset(chunk, 0, sizeof(Chunk));
-
-		// Initialize it
-		chunk->m_memory = reinterpret_cast<U8*>(chunk) + chunkAllocSize;
-
-		chunk->m_memsize = memAllocSize;
-		chunk->m_top = chunk->m_memory;
-
-		// Register it
-		if(m_tailChunk)
-		{
-			m_tailChunk->m_next = chunk;
-			chunk->m_prev = m_tailChunk;
-			m_tailChunk = chunk;
-		}
-		else
-		{
-			ANKI_ASSERT(m_headChunk == nullptr);
-			m_headChunk = m_tailChunk = chunk;
-		}
-	}
-	else
-	{
-		ANKI_OOM_ACTION();
-	}
-
-	return chunk;
-}
-
-void* ChainMemoryPool::allocateFromChunk(Chunk* ch, PtrSize size, [[maybe_unused]] PtrSize alignment)
-{
-	ANKI_ASSERT(ch);
-	ANKI_ASSERT(ch->m_top <= ch->m_memory + ch->m_memsize);
-
-	U8* mem = ch->m_top;
-	PtrSize memV = ptrToNumber(mem);
-	alignRoundUp(m_alignmentBytes, memV);
-	mem = numberToPtr<U8*>(memV);
-	U8* newTop = mem + m_headerSize + size;
-
-	if(newTop <= ch->m_memory + ch->m_memsize)
-	{
-		*reinterpret_cast<Chunk**>(mem) = ch;
-		mem += m_headerSize;
-
-		ch->m_top = newTop;
-		++ch->m_allocationCount;
-	}
-	else
-	{
-		// Chunk is full. Need a new one
-		mem = nullptr;
-	}
-
-	return mem;
-}
-
-void ChainMemoryPool::destroyChunk(Chunk* ch)
-{
-	ANKI_ASSERT(ch);
-
-	if(ch == m_tailChunk)
-	{
-		m_tailChunk = ch->m_prev;
-	}
-
-	if(ch == m_headChunk)
-	{
-		m_headChunk = ch->m_next;
-	}
-
-	if(ch->m_prev)
-	{
-		ANKI_ASSERT(ch->m_prev->m_next == ch);
-		ch->m_prev->m_next = ch->m_next;
-	}
-
-	if(ch->m_next)
-	{
-		ANKI_ASSERT(ch->m_next->m_prev == ch);
-		ch->m_next->m_prev = ch->m_prev;
-	}
-
-	invalidateMemory(ch, getAlignedRoundUp(m_alignmentBytes, sizeof(Chunk)) + ch->m_memsize);
-	m_allocCb(m_allocCbUserData, ch, 0, 0);
-}
-
 } // end namespace anki
 } // end namespace anki

+ 61 - 124
AnKi/Util/CpuMemoryPools.h

@@ -45,13 +45,15 @@ using PoolSignature = U32;
 ///         returns nullptr
 ///         returns nullptr
 void* allocAligned(void* userData, void* ptr, PtrSize size, PtrSize alignment);
 void* allocAligned(void* userData, void* ptr, PtrSize size, PtrSize alignment);
 
 
-/// Generic memory pool. The base of HeapMemoryPool or StackMemoryPool or ChainMemoryPool.
+/// Generic memory pool. The base of HeapMemoryPool or StackMemoryPool.
 class BaseMemoryPool
 class BaseMemoryPool
 {
 {
 public:
 public:
 	BaseMemoryPool(const BaseMemoryPool&) = delete; // Non-copyable
 	BaseMemoryPool(const BaseMemoryPool&) = delete; // Non-copyable
 
 
-	virtual ~BaseMemoryPool();
+	virtual ~BaseMemoryPool()
+	{
+	}
 
 
 	BaseMemoryPool& operator=(const BaseMemoryPool&) = delete; // Non-copyable
 	BaseMemoryPool& operator=(const BaseMemoryPool&) = delete; // Non-copyable
 
 
@@ -112,7 +114,6 @@ protected:
 		kNone,
 		kNone,
 		kHeap,
 		kHeap,
 		kStack,
 		kStack,
-		kChain
 	};
 	};
 
 
 	/// User allocation function.
 	/// User allocation function.
@@ -124,8 +125,14 @@ protected:
 	/// Allocations count.
 	/// Allocations count.
 	Atomic<U32> m_allocationCount = {0};
 	Atomic<U32> m_allocationCount = {0};
 
 
-	/// Construct it.
-	BaseMemoryPool(Type type, AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name);
+	BaseMemoryPool(Type type)
+		: m_type(type)
+	{
+	}
+
+	void init(AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name);
+
+	void destroy();
 
 
 private:
 private:
 	/// Refcount.
 	/// Refcount.
@@ -138,24 +145,34 @@ private:
 	Type m_type = Type::kNone;
 	Type m_type = Type::kNone;
 };
 };
 
 
-/// A dummy interface to match the StackMemoryPool and ChainMemoryPool interfaces in order to be used by the same
-/// allocator template.
+/// A dummy interface to match the StackMemoryPool interfaces in order to be used by the same allocator template.
 class HeapMemoryPool final : public BaseMemoryPool
 class HeapMemoryPool final : public BaseMemoryPool
 {
 {
 public:
 public:
 	/// Construct it.
 	/// Construct it.
+	HeapMemoryPool();
+
+	/// @see init
+	HeapMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name = nullptr)
+		: HeapMemoryPool()
+	{
+		init(allocCb, allocCbUserData, name);
+	}
+
+	/// Destroy
+	~HeapMemoryPool()
+	{
+		destroy();
+	}
+
+	/// Init.
 	/// @param allocCb The allocation function callback.
 	/// @param allocCb The allocation function callback.
 	/// @param allocCbUserData The user data to pass to the allocation function.
 	/// @param allocCbUserData The user data to pass to the allocation function.
 	/// @param name An optional name.
 	/// @param name An optional name.
-	HeapMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserDataconst, const Char* name = nullptr);
+	void init(AllocAlignedCallback allocCb, void* allocCbUserData, const Char* name = nullptr);
 
 
-	/// Destroy
-	~HeapMemoryPool();
-
-	/// The real constructor.
-	/// @param allocCb The allocation function callback
-	/// @param allocCbUserData The user data to pass to the allocation function
-	void init(AllocAlignedCallback allocCb, void* allocCbUserData);
+	/// Manual destroy. The destructor calls that as well.
+	void destroy();
 
 
 	/// Allocate memory
 	/// Allocate memory
 	void* allocate(PtrSize size, PtrSize alignment);
 	void* allocate(PtrSize size, PtrSize alignment);
@@ -175,6 +192,27 @@ private:
 class StackMemoryPool final : public BaseMemoryPool
 class StackMemoryPool final : public BaseMemoryPool
 {
 {
 public:
 public:
+	StackMemoryPool()
+		: BaseMemoryPool(Type::kStack)
+	{
+	}
+
+	/// @see init
+	StackMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize,
+					F64 nextChunkScale = 2.0, PtrSize nextChunkBias = 0, Bool ignoreDeallocationErrors = true,
+					U32 alignmentBytes = ANKI_SAFE_ALIGNMENT, const Char* name = nullptr)
+		: StackMemoryPool()
+	{
+		init(allocCb, allocCbUserData, initialChunkSize, nextChunkScale, nextChunkBias, ignoreDeallocationErrors,
+			 alignmentBytes, name);
+	}
+
+	/// Destroy
+	~StackMemoryPool()
+	{
+		destroy();
+	}
+
 	/// Init with parameters.
 	/// Init with parameters.
 	/// @param allocCb The allocation function callback.
 	/// @param allocCb The allocation function callback.
 	/// @param allocCbUserData The user data to pass to the allocation function.
 	/// @param allocCbUserData The user data to pass to the allocation function.
@@ -185,12 +223,12 @@ public:
 	///        true to suppress such errors.
 	///        true to suppress such errors.
 	/// @param alignmentBytes The maximum supported alignment for returned memory.
 	/// @param alignmentBytes The maximum supported alignment for returned memory.
 	/// @param name An optional name.
 	/// @param name An optional name.
-	StackMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize,
-					F64 nextChunkScale = 2.0, PtrSize nextChunkBias = 0, Bool ignoreDeallocationErrors = true,
-					U32 alignmentBytes = ANKI_SAFE_ALIGNMENT, const Char* name = nullptr);
+	void init(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize, F64 nextChunkScale = 2.0,
+			  PtrSize nextChunkBias = 0, Bool ignoreDeallocationErrors = true, U32 alignmentBytes = ANKI_SAFE_ALIGNMENT,
+			  const Char* name = nullptr);
 
 
-	/// Destroy
-	~StackMemoryPool();
+	/// Manual destroy. The destructor calls that as well.
+	void destroy();
 
 
 	/// Allocate aligned memory.
 	/// Allocate aligned memory.
 	/// @param size The size to allocate.
 	/// @param size The size to allocate.
@@ -290,7 +328,7 @@ private:
 
 
 		Atomic<U32>* getAllocationCount()
 		Atomic<U32>* getAllocationCount()
 		{
 		{
-			return &m_parent->m_allocationCount;
+			return (m_parent) ? &m_parent->m_allocationCount : nullptr;
 		}
 		}
 	};
 	};
 
 
@@ -298,105 +336,6 @@ private:
 	StackAllocatorBuilder<Chunk, StackAllocatorBuilderInterface, Mutex> m_builder;
 	StackAllocatorBuilder<Chunk, StackAllocatorBuilderInterface, Mutex> m_builder;
 };
 };
 
 
-/// Chain memory pool. Almost similar to StackMemoryPool but more flexible and at the same time a bit slower.
-class ChainMemoryPool final : public BaseMemoryPool
-{
-public:
-	/// Init the pool.
-	/// @param allocCb The allocation function callback.
-	/// @param allocCbUserData The user data to pass to the allocation function.
-	/// @param initialChunkSize The size of the first chunk.
-	/// @param nextChunkScale Value that controls the next chunk.
-	/// @param nextChunkBias Value that controls the next chunk.
-	/// @param alignmentBytes The maximum supported alignment for returned memory.
-	/// @param name An optional name.
-	ChainMemoryPool(AllocAlignedCallback allocCb, void* allocCbUserData, PtrSize initialChunkSize,
-					F32 nextChunkScale = 2.0, PtrSize nextChunkBias = 0, PtrSize alignmentBytes = ANKI_SAFE_ALIGNMENT,
-					const Char* name = nullptr);
-
-	/// Destroy
-	~ChainMemoryPool();
-
-	/// Allocate memory. This operation is 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. 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
-	void free(void* ptr);
-
-	/// @name Methods used for optimizing future chains.
-	/// @{
-	PtrSize getChunksCount() const;
-
-	PtrSize getAllocatedSize() const;
-	/// @}
-
-private:
-	/// A chunk of memory
-	class Chunk
-	{
-	public:
-		/// 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
-		PtrSize m_allocationCount = 0;
-
-		/// Previous chunk in the list
-		Chunk* m_prev = nullptr;
-
-		/// Next chunk in the list
-		Chunk* m_next = nullptr;
-	};
-
-	/// Alignment of allocations.
-	PtrSize m_alignmentBytes = 0;
-
-	/// The first chunk.
-	Chunk* m_headChunk = nullptr;
-
-	/// Current chunk to allocate from.
-	Chunk* m_tailChunk = nullptr;
-
-	/// Size of the first chunk.
-	PtrSize m_initSize = 0;
-
-	/// Fast thread locking.
-	SpinLock m_lock;
-
-	/// Chunk scale.
-	F32 m_scale = 2.0;
-
-	/// Chunk bias.
-	PtrSize m_bias = 0;
-
-	/// Cache a value.
-	PtrSize m_headerSize = 0;
-
-	/// Compute the size for the next chunk.
-	/// @param size The current allocation size.
-	PtrSize computeNewChunkSize(PtrSize size) const;
-
-	/// Create a new chunk.
-	Chunk* createNewChunk(PtrSize size);
-
-	/// Allocate from chunk.
-	void* allocateFromChunk(Chunk* ch, PtrSize size, PtrSize alignment);
-
-	/// Destroy a chunk.
-	void destroyChunk(Chunk* ch);
-};
-
 inline void* BaseMemoryPool::allocate(PtrSize size, PtrSize alignmentBytes)
 inline void* BaseMemoryPool::allocate(PtrSize size, PtrSize alignmentBytes)
 {
 {
 	void* out = nullptr;
 	void* out = nullptr;
@@ -409,8 +348,7 @@ inline void* BaseMemoryPool::allocate(PtrSize size, PtrSize alignmentBytes)
 		out = static_cast<StackMemoryPool*>(this)->allocate(size, alignmentBytes);
 		out = static_cast<StackMemoryPool*>(this)->allocate(size, alignmentBytes);
 		break;
 		break;
 	default:
 	default:
-		ANKI_ASSERT(m_type == Type::kChain);
-		out = static_cast<ChainMemoryPool*>(this)->allocate(size, alignmentBytes);
+		ANKI_ASSERT(0);
 	}
 	}
 
 
 	return out;
 	return out;
@@ -427,8 +365,7 @@ inline void BaseMemoryPool::free(void* ptr)
 		static_cast<StackMemoryPool*>(this)->free(ptr);
 		static_cast<StackMemoryPool*>(this)->free(ptr);
 		break;
 		break;
 	default:
 	default:
-		ANKI_ASSERT(m_type == Type::kChain);
-		static_cast<ChainMemoryPool*>(this)->free(ptr);
+		ANKI_ASSERT(0);
 	}
 	}
 }
 }
 /// @}
 /// @}

+ 8 - 1
AnKi/Util/StackAllocatorBuilder.h

@@ -41,7 +41,14 @@ public:
 	StackAllocatorBuilder() = default;
 	StackAllocatorBuilder() = default;
 
 
 	/// Destroy.
 	/// Destroy.
-	~StackAllocatorBuilder();
+	~StackAllocatorBuilder()
+	{
+		destroy();
+	}
+
+	/// Manual destroy. The destructor calls that as well.
+	/// @note It's not thread safe.
+	void destroy();
 
 
 	/// Allocate memory.
 	/// Allocate memory.
 	/// @param size The size to allocate.
 	/// @param size The size to allocate.

+ 7 - 3
AnKi/Util/StackAllocatorBuilder.inl.h

@@ -10,7 +10,7 @@
 namespace anki {
 namespace anki {
 
 
 template<typename TChunk, typename TInterface, typename TLock>
 template<typename TChunk, typename TInterface, typename TLock>
-StackAllocatorBuilder<TChunk, TInterface, TLock>::~StackAllocatorBuilder()
+void StackAllocatorBuilder<TChunk, TInterface, TLock>::destroy()
 {
 {
 	// Free chunks
 	// Free chunks
 	TChunk* chunk = m_chunksListHead;
 	TChunk* chunk = m_chunksListHead;
@@ -25,12 +25,16 @@ StackAllocatorBuilder<TChunk, TInterface, TLock>::~StackAllocatorBuilder()
 	Atomic<U32>* allocationCount = m_interface.getAllocationCount();
 	Atomic<U32>* allocationCount = m_interface.getAllocationCount();
 	if(allocationCount)
 	if(allocationCount)
 	{
 	{
-		const U32 allocCount = allocationCount->load();
-		if(!m_interface.ignoreDeallocationErrors() && allocCount != 0)
+		if(!m_interface.ignoreDeallocationErrors() && allocationCount->load() != 0)
 		{
 		{
 			ANKI_UTIL_LOGW("Forgot to deallocate");
 			ANKI_UTIL_LOGW("Forgot to deallocate");
 		}
 		}
 	}
 	}
+
+	m_crntChunk.setNonAtomically(nullptr);
+	m_chunksListHead = nullptr;
+	m_memoryCapacity = 0;
+	m_chunkCount = 0;
 }
 }
 
 
 template<typename TChunk, typename TInterface, typename TLock>
 template<typename TChunk, typename TInterface, typename TLock>

+ 0 - 45
Tests/Util/Memory.cpp

@@ -157,48 +157,3 @@ ANKI_TEST(Util, StackMemoryPool)
 		}
 		}
 	}
 	}
 }
 }
-
-ANKI_TEST(Util, ChainMemoryPool)
-{
-	// Basic test
-	{
-		const U size = 8;
-		ChainMemoryPool pool(allocAligned, nullptr, size, 2.0, 0, 1);
-
-		void* mem = pool.allocate(5, 1);
-		ANKI_TEST_EXPECT_NEQ(mem, nullptr);
-
-		void* mem1 = pool.allocate(5, 1);
-		ANKI_TEST_EXPECT_NEQ(mem1, nullptr);
-
-		pool.free(mem1);
-		pool.free(mem);
-		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 0);
-	}
-
-	// Basic test 2
-	{
-		const U size = sizeof(PtrSize) + 10;
-		ChainMemoryPool pool(allocAligned, nullptr, size, 2.0, 0, 1);
-
-		void* mem = pool.allocate(size, 1);
-		ANKI_TEST_EXPECT_NEQ(mem, nullptr);
-
-		void* mem1 = pool.allocate(size, 1);
-		ANKI_TEST_EXPECT_NEQ(mem1, nullptr);
-
-		void* mem3 = pool.allocate(size, 1);
-		ANKI_TEST_EXPECT_NEQ(mem1, nullptr);
-
-		pool.free(mem1);
-
-		mem1 = pool.allocate(size, 1);
-		ANKI_TEST_EXPECT_NEQ(mem1, nullptr);
-
-		pool.free(mem3);
-		pool.free(mem1);
-		pool.free(mem);
-
-		ANKI_TEST_EXPECT_EQ(pool.getChunksCount(), 0);
-	}
-}