| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
- #pragma once
- #include "Prerequisites/BsPrerequisitesUtil.h"
- #include <climits>
- namespace bs
- {
- /** @addtogroup Internal-Utility
- * @{
- */
- /** @addtogroup Memory-Internal
- * @{
- */
- /**
- * A memory allocator that allocates elements of the same size. Allows for fairly quick allocations and deallocations.
- *
- * @tparam ElemSize Size of a single element in the pool. This will be the exact allocation size. 4 byte minimum.
- * @tparam ElemsPerBlock Determines how much space to reserve for elements. This determines the initial size of the
- * pool, and the additional size the pool will be expanded by every time the number of elements
- * goes over the available storage limit.
- * @tparam Alignment Memory alignment of each allocated element. Note that alignments that are larger than
- * element size, or aren't a multiplier of element size will introduce additionally padding
- * for each element, and therefore require more internal memory.
- */
- template <int ElemSize, int ElemsPerBlock = 512, int Alignment = 4>
- class PoolAlloc
- {
- private:
- /** A single block able to hold ElemsPerBlock elements. */
- class MemBlock
- {
- public:
- MemBlock(UINT8* blockData)
- :blockData(blockData), freePtr(0), freeElems(ElemsPerBlock), nextBlock(nullptr)
- {
- UINT32 offset = 0;
- for(UINT32 i = 0; i < ElemsPerBlock; i++)
- {
- UINT32* entryPtr = (UINT32*)&blockData[offset];
- offset += ActualElemSize;
- *entryPtr = offset;
- }
- }
- ~MemBlock()
- {
- assert(freeElems == ElemsPerBlock && "Not all elements were deallocated from a block.");
- }
- /**
- * Returns the first free address and increments the free pointer. Caller needs to ensure the remaining block
- * size is adequate before calling.
- */
- UINT8* alloc()
- {
- UINT8* freeEntry = &blockData[freePtr];
- freePtr = *(UINT32*)freeEntry;
- --freeElems;
- return freeEntry;
- }
- /** Deallocates the provided pointer. */
- void dealloc(void* data)
- {
- UINT32* entryPtr = (UINT32*)data;
- *entryPtr = freePtr;
- ++freeElems;
- freePtr = (UINT32)(((UINT8*)data) - blockData);
- }
- UINT8* blockData;
- UINT32 freePtr;
- UINT32 freeElems;
- MemBlock* nextBlock;
- };
- public:
- PoolAlloc()
- : mFreeBlock(nullptr), mTotalNumElems(0), mNumBlocks(0)
- {
- static_assert(ElemSize >= 4, "Pool allocator minimum allowed element size is 4 bytes.");
- static_assert(ElemsPerBlock > 0, "Number of elements per block must be at least 1.");
- static_assert(ElemsPerBlock * ActualElemSize <= UINT_MAX, "Pool allocator block size too large.");
- }
- ~PoolAlloc()
- {
- MemBlock* curBlock = mFreeBlock;
- while (curBlock != nullptr)
- {
- MemBlock* nextBlock = curBlock->nextBlock;
- deallocBlock(curBlock);
- curBlock = nextBlock;
- }
- }
- /** Allocates enough memory for a single element in the pool. */
- UINT8* alloc()
- {
- if(mFreeBlock == nullptr || mFreeBlock->freeElems == 0)
- allocBlock();
- mTotalNumElems++;
- return mFreeBlock->alloc();
- }
- /** Deallocates an element from the pool. */
- void free(void* data)
- {
- MemBlock* curBlock = mFreeBlock;
- while(curBlock)
- {
- constexpr UINT32 blockDataSize = ActualElemSize * ElemsPerBlock;
- if(data >= curBlock->blockData && data < (curBlock->blockData + blockDataSize))
- {
- curBlock->dealloc(data);
- mTotalNumElems--;
- if(curBlock->freeElems == 0 && curBlock->nextBlock)
- {
- // Free the block, but only if there is some extra free space in other blocks
- const UINT32 totalSpace = (mNumBlocks - 1) * ElemsPerBlock;
- const UINT32 freeSpace = totalSpace - mTotalNumElems;
- if(freeSpace > ElemsPerBlock / 2)
- {
- mFreeBlock = curBlock->nextBlock;
- deallocBlock(curBlock);
- }
- }
- return;
- }
- curBlock = curBlock->nextBlock;
- }
- assert(false);
- }
- /** Allocates and constructs a single pool element. */
- template<class T, class... Args>
- T* construct(Args &&...args)
- {
- T* data = (T*)alloc();
- new ((void*)data) T(std::forward<Args>(args)...);
- return data;
- }
- /** Destructs and deallocates a single pool element. */
- template<class T>
- void destruct(T* data)
- {
- data->~T();
- free(data);
- }
- private:
- /** Allocates a new block of memory using a heap allocator. */
- MemBlock* allocBlock()
- {
- MemBlock* newBlock = nullptr;
- MemBlock* curBlock = mFreeBlock;
- while (curBlock != nullptr)
- {
- MemBlock* nextBlock = curBlock->nextBlock;
- if (nextBlock != nullptr && nextBlock->freeElems > 0)
- {
- // Found an existing block with free space
- newBlock = nextBlock;
- curBlock->nextBlock = newBlock->nextBlock;
- newBlock->nextBlock = mFreeBlock;
- break;
- }
- curBlock = nextBlock;
- }
- if (newBlock == nullptr)
- {
- constexpr UINT32 blockDataSize = ActualElemSize * ElemsPerBlock;
- size_t paddedBlockDataSize = blockDataSize + (Alignment - 1); // Padding for potential alignment correction
- UINT8* data = (UINT8*)bs_alloc(sizeof(MemBlock) + (UINT32)paddedBlockDataSize);
- void* blockData = data + sizeof(MemBlock);
- blockData = std::align(Alignment, blockDataSize, blockData, paddedBlockDataSize);
- newBlock = new (data) MemBlock((UINT8*)blockData);
- mNumBlocks++;
- newBlock->nextBlock = mFreeBlock;
- }
- mFreeBlock = newBlock;
- return newBlock;
- }
- /** Deallocates a block of memory. */
- void deallocBlock(MemBlock* block)
- {
- block->~MemBlock();
- bs_free(block);
- mNumBlocks--;
- }
- static constexpr int ActualElemSize = ((ElemSize + Alignment - 1) / Alignment) * Alignment;
- MemBlock* mFreeBlock;
- UINT32 mTotalNumElems;
- UINT32 mNumBlocks;
- };
- /** @} */
- /** @} */
- }
|