7#if PLATFORM_HAS_FPlatformVirtualMemoryBlock
9
10
11#define UE4_TMEMORY_POOL_DO_SANITY_CHECKS (UE_BUILD_DEBUG || (UE_BUILD_DEVELOPMENT && !UE_EDITOR))
14
15
16
17
18
19
21 SIZE_T RequiredAlignment
31 SIZE_T AlignedPoolStart;
34 SIZE_T AlignedPoolEnd;
43 SIZE_T BitmaskSizeInBytes;
49 SIZE_T UsefulMemorySize;
51 FPlatformMemory::FPlatformVirtualMemoryBlock VMBlock;
53#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
54 FThreadSafeCounter NoConcurrentAccess;
59 TMemoryPool(SIZE_T InBlockSize, SIZE_T InAlignedPoolStart, SIZE_T InNumBlocks, uint8* InBitmask, FPlatformMemory::FPlatformVirtualMemoryBlock& InVMBlock)
60 : BlockSize(InBlockSize)
61 , AlignedPoolStart(InAlignedPoolStart)
62 , AlignedPoolEnd(InAlignedPoolStart + InBlockSize * (SIZE_T)InNumBlocks)
63 , NumBlocks(InNumBlocks)
65 , BitmaskSizeInBytes(BitmaskMemorySize(NumBlocks))
66 , NumFreeBlocks(InNumBlocks)
70#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
71 checkf((AlignedPoolStart % RequiredAlignment) == 0, TEXT(
"Non-aligned pool address passed to a TMemoryPool"));
74 FMemory::Memset(Bitmask, 0xFF, BitmaskSizeInBytes);
78 VMBlock.DecommitByPtr(
reinterpret_cast<
void *>(AlignedPoolStart), Align(NumBlocks * BlockSize, FPlatformMemory::FPlatformVirtualMemoryBlock::GetCommitAlignment()));
82 void* Allocate(SIZE_T Size)
84#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
85 checkf(Size <= BlockSize, TEXT(
"Attempting to allocate %llu bytes from a memory pool of %llu byte blocks"), (uint64)Size, (uint64)BlockSize);
87 checkf(NoConcurrentAccess.Increment() == 1, TEXT(
"TMemoryPool is being accessed on multiple threads. The class is not thread safe, add locking!."));
90 void* Address =
nullptr;
91 if (LIKELY(NumFreeBlocks > 0))
94 Address = FindFirstFreeAndMarkUsed();
96 UsefulMemorySize += Size;
99#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
100 checkf(Address !=
nullptr, TEXT(
"NumFreeBlocks and bitmask of the free blocks are not in sync - bug in TMemoryPool"));
104 VMBlock.CommitByPtr(Address, Align(Size, FPlatformMemory::FPlatformVirtualMemoryBlock::GetCommitAlignment()));
108#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
109 checkf(NoConcurrentAccess.Decrement() == 0, TEXT(
"TMemoryPool is being accessed on multiple threads. The class is not thread safe, add locking!."));
116 void Free(
void *Ptr, SIZE_T Size)
119#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
120 checkf(WasAllocatedFromThisPool(Ptr, BlockSize), TEXT(
"Address passed to Free() of a pool of block size %llu was not allocated in it (address: %p, boundaries: %p - %p"),
123 reinterpret_cast<
void *>(AlignedPoolStart),
124 reinterpret_cast<
void *>(AlignedPoolEnd)
127 checkf((
reinterpret_cast<SIZE_T>(Ptr) % RequiredAlignment == 0), TEXT(
"Address passed to Free() of a pool of block size %llu was not aligned to %llu bytes (address: %p)"),
129 (uint64)RequiredAlignment,
133 checkf(NoConcurrentAccess.Increment() == 1, TEXT(
"TMemoryPool is being accessed on multiple threads. The class is not thread safe, add locking!."));
140 UsefulMemorySize -= Size;
144 VMBlock.DecommitByPtr(Ptr, BlockSize);
146#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
148 checkf(NumFreeBlocks <= NumBlocks, TEXT(
"Too many frees!"));
150 checkf(NoConcurrentAccess.Decrement() == 0, TEXT(
"TMemoryPool is being accessed on multiple threads. The class is not thread safe, add locking!."));
154 static SIZE_T BitmaskMemorySize(SIZE_T NumBlocks)
156 return (NumBlocks / 8) + ((NumBlocks & 7) ? 1 : 0);
159 void MarkFree(
void *Ptr)
162 SIZE_T PtrOffset =
reinterpret_cast<SIZE_T>(Ptr);
163 SIZE_T BitIndex = (PtrOffset - AlignedPoolStart) / BlockSize;
165#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
166 checkf(BitIndex < NumBlocks, TEXT(
"Incorrect pointer %p passed to MarkFree()"), Ptr);
169 SIZE_T ByteIndex = BitIndex / 8;
170 uint8 Byte = Bitmask[ByteIndex];
172 int32 IndexInByte =
static_cast<int32>(BitIndex & 0x7);
173#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
174 checkf((Byte & (1 << IndexInByte)) == 0, TEXT(
"MarkFree() - double freeing the pointer %p"), Ptr);
176 Byte |= (1 << IndexInByte);
177 Bitmask[ByteIndex] = Byte;
180 void* FindFirstFreeAndMarkUsed()
182 uint8* CurPtr = Bitmask;
184 SIZE_T BitmaskSizeInQWords = BitmaskSizeInBytes / 8;
186 while(IdxQword < BitmaskSizeInQWords)
188 uint64 Qword = *
reinterpret_cast<uint64*>(CurPtr);
189 if (UNLIKELY(Qword != 0))
193 SIZE_T IndexOfFirstFreeInQword = FMath::CountTrailingZeros64(Qword);
196 Qword &= ~(1ULL << IndexOfFirstFreeInQword);
197 *
reinterpret_cast<uint64*>(CurPtr) = Qword;
200 SIZE_T IndexOfFirstBlock =
static_cast<SIZE_T>(CurPtr - Bitmask) * 8 + IndexOfFirstFreeInQword;
201#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
202 checkf(IndexOfFirstBlock < NumBlocks, TEXT(
"Allocating outside of pool - TMemoryPool error."));
204 return reinterpret_cast<
void *>(AlignedPoolStart + IndexOfFirstBlock * BlockSize);
212 SIZE_T MaxIndexInLastByte = (NumBlocks & 0x7) ? (NumBlocks & 0x7) : 8;
214 uint8* BitmaskEnd = Bitmask + BitmaskSizeInBytes;
215 while(CurPtr < BitmaskEnd)
217 uint8 Byte = *CurPtr;
218 if (UNLIKELY(Byte != 0))
221 SIZE_T IndexOfFirstFreeInByte = FMath::CountTrailingZeros(
static_cast<int32>(Byte));
224 if (LIKELY(CurPtr < BitmaskEnd - 1 || IndexOfFirstFreeInByte < MaxIndexInLastByte))
227 Byte &= ~(1 << IndexOfFirstFreeInByte);
231 SIZE_T IndexOfFirstBlock =
static_cast<SIZE_T>(CurPtr - Bitmask) * 8 + IndexOfFirstFreeInByte;
232#if UE4_TMEMORY_POOL_DO_SANITY_CHECKS
233 checkf(IndexOfFirstBlock < NumBlocks, TEXT(
"Allocating outside of pool - TMemoryPool error."));
235 return reinterpret_cast<
void *>(AlignedPoolStart + IndexOfFirstBlock * BlockSize);
246 SIZE_T CalculateFreeBlocksInBitmap()
250 uint8* CurPtr = Bitmask;
251 uint8* BitmaskEnd = Bitmask + BitmaskSizeInBytes;
252 while(CurPtr < BitmaskEnd -
sizeof(uint64))
254 NumFree += FMath::CountBits(*
reinterpret_cast<uint64*>(CurPtr));
255 CurPtr +=
sizeof(uint64);
258 while(CurPtr < BitmaskEnd - 1)
260 NumFree += FMath::CountBits(
static_cast<uint64>(*CurPtr));
265 if ((NumBlocks & 0x7) == 0)
267 NumFree += FMath::CountBits(
static_cast<uint64>(*CurPtr));
271 uint8 LastByte = *CurPtr;
273 SIZE_T MaxIndexInLastByte = (NumBlocks & 0x7);
274 for(SIZE_T Idx = 0; Idx < MaxIndexInLastByte; ++Idx)
276 NumFree += (LastByte & (1 << Idx)) ? 1 : 0;
284 bool CanAllocateFromThisPool(SIZE_T Size)
286 return BlockSize >= Size;
290 bool WasAllocatedFromThisPool(
void* Ptr, SIZE_T Size)
293 return reinterpret_cast<SIZE_T>(Ptr) >= AlignedPoolStart &&
reinterpret_cast<SIZE_T>(Ptr) < AlignedPoolEnd;
298 return NumFreeBlocks == NumBlocks;
302 uint64 GetAllocatableMemorySize()
const
304 return NumFreeBlocks * BlockSize;
308 uint64 GetOverheadSize()
const
310 return (NumBlocks - NumFreeBlocks) * BlockSize - UsefulMemorySize;
313 void PrintDebugInfo()
315 printf(
"BlockSize: %llu NumAllocated/TotalBlocks = %llu/%llu\n", (uint64)BlockSize, (uint64)(NumBlocks - NumFreeBlocks), (uint64)NumBlocks);