yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
f65d756bf
master
1 2#include "slang-memory-arena.h" 3 4namespace Slang 5{ 6 7MemoryArena ::MemoryArena () 8{ 9// Mark as invalid so any alloc call will fail 10m_blockAlignment = 0 ; 11m_blockAllocSize = 0 ; 12 13// Set up as empty 14m_usedBlocks = nullptr ; 15m_availableBlocks = nullptr ; 16 17_resetCurrentBlock (); 18 19m_blockFreeList .init (sizeof (Block ),sizeof (void * ),16 ); 20} 21 22MemoryArena ::~MemoryArena () 23{ 24reset (); 25} 26 27 28MemoryArena ::MemoryArena (size_t blockPayloadSize ,size_t blockAlignment ) 29{ 30_initialize (blockPayloadSize ,blockAlignment ); 31} 32 33void MemoryArena ::init (size_t blockPayloadSize ,size_t blockAlignment ) 34{ 35reset (); 36_initialize (blockPayloadSize ,blockAlignment ); 37} 38 39void MemoryArena ::_initialize (size_t blockPayloadSize ,size_t alignment ) 40{ 41// Alignment must be a power of 2 42assert (((alignment - 1 )& alignment )== 0 ); 43 44// Ensure it's alignment is at least kMinAlignment 45alignment = (alignment < kMinAlignment ) ?kMinAlignment :alignment ; 46 47const size_t alignMask = alignment - 1 ; 48 49// Make sure the payload is rounded up to the alignment 50blockPayloadSize = (blockPayloadSize + alignMask )& ~alignMask ; 51 52m_blockPayloadSize = blockPayloadSize ; 53 54// If alignment required is larger then the backing allocators then 55// make larger to ensure when alignment correction takes place it will be aligned 56const size_t blockAllocSize = 57 (alignment > kMinAlignment ) ? (blockPayloadSize + alignment ) :blockPayloadSize ; 58 59m_blockAllocSize = blockAllocSize ; 60m_blockAlignment = alignment ; 61m_availableBlocks = nullptr ; 62 63m_blockFreeList .init (sizeof (Block ),sizeof (void * ),16 ); 64 65_resetCurrentBlock (); 66} 67 68void MemoryArena ::swapWith (ThisType & rhs ) 69{ 70Swap (m_start ,rhs .m_start ); 71Swap (m_end ,rhs .m_end ); 72Swap (m_current ,rhs .m_current ); 73 74Swap (m_blockPayloadSize ,rhs .m_blockPayloadSize ); 75Swap (m_blockAllocSize ,rhs .m_blockAllocSize ); 76Swap (m_blockAlignment ,rhs .m_blockAlignment ); 77 78Swap (m_availableBlocks ,rhs .m_availableBlocks ); 79Swap (m_usedBlocks ,rhs .m_usedBlocks ); 80 81m_blockFreeList .swapWith (rhs .m_blockFreeList ); 82} 83 84void MemoryArena ::_resetCurrentBlock () 85{ 86m_start = nullptr ; 87m_end = nullptr ; 88m_current = nullptr ; 89 90m_usedBlocks = nullptr ; 91} 92 93void MemoryArena ::_addCurrentBlock (Block * block ) 94{ 95// Set up for allocation from 96m_end = block -> m_end ; 97m_start = block -> m_start ; 98m_current = m_start ; 99 100// Add to linked list of used block, making it the top used block 101block -> m_next = m_usedBlocks ; 102m_usedBlocks = block ; 103} 104 105void MemoryArena ::_setCurrentBlock (Block * block ) 106{ 107// Set up for allocation from 108m_end = block -> m_end ; 109m_start = block -> m_start ; 110m_current = m_start ; 111 112assert (m_usedBlocks == block ); 113} 114 115void MemoryArena ::_deallocateBlocksPayload (Block * start ) 116{ 117Block * cur = start ; 118while (cur ) 119 { 120// Deallocate the block 121 ::free (cur -> m_alloc ); 122cur = cur -> m_next ; 123 } 124} 125 126void MemoryArena ::_deallocateBlocks (Block * start ) 127{ 128Block * cur = start ; 129while (cur ) 130 { 131Block * next = cur -> m_next ; 132// Deallocate the block 133 ::free (cur -> m_alloc ); 134 135m_blockFreeList .deallocate (cur ); 136cur = next ; 137 } 138} 139 140bool MemoryArena ::_isNormalBlock (Block * block ) 141{ 142// The size of the block in total is from m_alloc to the m_end (ie the size that is passed into 143// _newBlock) 144const size_t blockSize = size_t (block -> m_end - block -> m_alloc ); 145return (blockSize == m_blockAllocSize )&& 146 ((size_t (block -> m_start )& (m_blockAlignment - 1 ))== 0 ); 147} 148 149void MemoryArena ::_deallocateBlock (Block * block ) 150{ 151// If it's a normal block then make it available 152if (_isNormalBlock (block )) 153 { 154block -> m_next = m_availableBlocks ; 155m_availableBlocks = block ; 156 } 157else 158 { 159// Must be odd sized so free it 160 ::free (block -> m_alloc ); 161// Free it in the block list 162m_blockFreeList .deallocate (block ); 163 } 164} 165 166void MemoryArena ::deallocateAll () 167{ 168// we need to rewind through m_usedBlocks -> seeing it the are normal sized or not 169Block * block = m_usedBlocks ; 170while (block ) 171 { 172Block * next = block -> m_next ; 173_deallocateBlock (block ); 174block = next ; 175 } 176 177// Reset current block 178_resetCurrentBlock (); 179} 180 181void MemoryArena ::reset () 182{ 183_deallocateBlocksPayload (m_usedBlocks ); 184_deallocateBlocksPayload (m_availableBlocks ); 185 186m_blockFreeList .reset (); 187 188m_availableBlocks = nullptr ; 189 190_resetCurrentBlock (); 191} 192 193MemoryArena ::Block * MemoryArena ::_findNonCurrent (const void * data ,size_t size )const 194{ 195return m_usedBlocks ?_findInBlocks (m_usedBlocks -> m_next ,data ,size ) :nullptr ; 196} 197 198MemoryArena ::Block * MemoryArena ::_findNonCurrent (const void * data )const 199{ 200return m_usedBlocks ?_findInBlocks (m_usedBlocks -> m_next ,data ) :nullptr ; 201} 202 203MemoryArena ::Block * MemoryArena ::_findInBlocks (Block * block ,const void * data ,size_t size )const 204{ 205const uint8_t * ptr = (const uint8_t * )data ; 206while (block ) 207 { 208if (ptr >=block -> m_start && ptr + size <=block -> m_end ) 209 { 210return block ; 211 } 212block = block -> m_next ; 213 } 214return nullptr ; 215} 216 217MemoryArena ::Block * MemoryArena ::_findInBlocks (Block * block ,const void * data )const 218{ 219const uint8_t * ptr = (const uint8_t * )data ; 220while (block ) 221 { 222if (ptr >=block -> m_start && ptr <=block -> m_end ) 223 { 224return block ; 225 } 226block = block -> m_next ; 227 } 228return nullptr ; 229} 230 231MemoryArena ::Block * MemoryArena ::_newNormalBlock () 232{ 233if (m_availableBlocks ) 234 { 235// We have an available block.. 236Block * block = m_availableBlocks ; 237m_availableBlocks = block -> m_next ; 238return block ; 239 } 240 241Block * block = _newBlock (m_blockAllocSize ,m_blockAlignment ); 242// Check that every normal block has m_blockPayloadSize space 243assert (size_t (block -> m_end - block -> m_start ) >=m_blockPayloadSize ); 244return block ; 245} 246 247MemoryArena ::Block * MemoryArena ::_newBlock (size_t allocSize ,size_t alignment ) 248{ 249assert (alignment >=m_blockAlignment ); 250// Alignment must be a power of 2 251assert (((alignment - 1 )& alignment )== 0 ); 252 253// Allocate block 254Block * block = (Block * )m_blockFreeList .allocate (); 255if (!block ) 256 { 257return nullptr ; 258 } 259 260// Allocate the memory 261uint8_t * alloc = (uint8_t * )::malloc (allocSize ); 262if (!alloc ) 263 { 264m_blockFreeList .deallocate (block ); 265return nullptr ; 266 } 267 268const size_t alignMask = alignment - 1 ; 269 270// Do the alignment on the allocation 271uint8_t * const start = (uint8_t * )((size_t (alloc )+ alignMask )& ~alignMask ); 272 273// Setup the block 274block -> m_alloc = alloc ; 275block -> m_start = start ; 276block -> m_end = alloc + allocSize ; 277block -> m_next = nullptr ; 278 279return block ; 280} 281 282void MemoryArena ::addExternalBlock (void * inData ,size_t size ) 283{ 284// Allocate block 285Block * block = (Block * )m_blockFreeList .allocate (); 286if (!block ) 287 { 288return ; 289 } 290 291uint8_t * alloc = (uint8_t * )inData ; 292 293const size_t alignMask = m_blockAlignment - 1 ; 294 295// Do the alignment on the allocation 296uint8_t * const start = (uint8_t * )((size_t (alloc )+ alignMask )& ~alignMask ); 297 298// Setup the block 299block -> m_alloc = alloc ; 300block -> m_start = start ; 301block -> m_end = alloc + size ; 302block -> m_next = nullptr ; 303 304// We don't want to place at start, if there is any used blocks - as that is the one 305// that is being split from and can be rewound. So we place just behind in that case 306if (m_usedBlocks ) 307 { 308block -> m_next = m_usedBlocks -> m_next ; 309m_usedBlocks -> m_next = block ; 310 } 311else 312 { 313// There aren't any blocks, so just place at the front 314m_usedBlocks = block ; 315 } 316} 317 318void * MemoryArena ::_allocateAlignedFromNewBlockAndZero (size_t sizeInBytes ,size_t alignment ) 319{ 320void * mem = _allocateAlignedFromNewBlock (sizeInBytes ,alignment ); 321if (mem ) 322 { 323 ::memset (mem ,0 ,sizeInBytes ); 324 } 325return mem ; 326} 327 328void * MemoryArena ::_allocateAlignedFromNewBlock (size_t size ,size_t alignment ) 329{ 330// Make sure init has been called (or has been set up in parameterized constructor) 331assert (m_blockAllocSize > 0 ); 332// Alignment must be a power of 2 333assert (((alignment - 1 )& alignment )== 0 ); 334 335// Alignment must at a minimum be block alignment (such if reused the constraints hold) 336alignment = (alignment < m_blockAlignment ) ?m_blockAlignment :alignment ; 337 338const size_t alignMask = alignment - 1 ; 339 340// The size of the block must be at least large enough to take into account alignment 341size_t allocSize = (alignment <=kMinAlignment ) ?size : (size + alignment ); 342 343Block * block ; 344 345// There are two scenarios 346// a) Allocate a new normal block and make current 347// b) Allocate a new 'odd-sized' block and make current 348// 349// That by always allocating a new block if odd-sized, we lose more efficiency in terms of 350// storage (the previous block may not have been used much). BUT doing so makes it easy to 351// rewind - as the blocks are always in order of allocation. 352// 353// An improvement might be to have some abstraction that sits on top that can do this tracking 354// (or have the blocks themselves record if they alias over a previously used block - but we 355// don't bother with this here. If the alignment is greater than regular alignment we need to 356// handle specially 357if (allocSize > m_blockPayloadSize || 358 (alignment > m_blockAlignment && allocSize + alignment > m_blockPayloadSize )) 359 { 360// This is an odd-sized block so just allocate the whole thing. 361block = _newBlock (allocSize ,alignment ); 362 } 363else 364 { 365// Must be allocatable within a normal block 366assert (allocSize <=m_blockAllocSize ); 367block = _newNormalBlock (); 368 } 369 370// If not allocated we are done 371if (!block ) 372 { 373return nullptr ; 374 } 375 376// Make the current block 377_addCurrentBlock (block ); 378 379// Align the memory 380uint8_t * memory = (uint8_t * )((size_t (m_current )+ alignMask )& ~alignMask ); 381 382// It must be aligned 383assert ((size_t (memory )& alignMask )== 0 ); 384 385// Do the aligned allocation (which must fit) by aligning the pointer 386// It must fit if the previous code is correct... 387assert (memory + size <=m_end ); 388// Move the current pointer 389m_current = memory + size ; 390return memory ; 391} 392 393size_t MemoryArena ::_calcBlocksUsedMemory (const Block * block )const 394{ 395size_t total = 0 ; 396while (block ) 397 { 398total += size_t (block -> m_end - block -> m_start ); 399block = block -> m_next ; 400 } 401return total ; 402} 403 404size_t MemoryArena ::_calcBlocksAllocatedMemory (const Block * block )const 405{ 406size_t total = 0 ; 407while (block ) 408 { 409total += size_t (block -> m_end - block -> m_alloc ); 410block = block -> m_next ; 411 } 412return total ; 413} 414 415void MemoryArena ::_rewindToCursor (const void * cursorIn ) 416{ 417// If it's nullptr, then there are no allocation so free all 418if (cursorIn == nullptr ) 419 { 420deallocateAll (); 421return ; 422 } 423 424// Find the block that contains the allocation 425Block * cursorBlock = _findNonCurrent (cursorIn ); 426assert (cursorBlock ); 427if (!cursorBlock ) 428 { 429// If not found it means this address is NOT part any of the active used heap! 430// Probably an invalid cursor 431return ; 432 } 433 434// Deallocate all of the blocks up to the cursor block 435 { 436Block * block = m_usedBlocks ; 437while (block != cursorBlock ) 438 { 439Block * next = block -> m_next ; 440_deallocateBlock (block ); 441block = next ; 442 } 443 } 444 445// The cursor block is now the current block 446m_usedBlocks = cursorBlock ; 447_setCurrentBlock (cursorBlock ); 448 449const uint8_t * cursor = (const uint8_t * )cursorIn ; 450// Must be in the range of the currently set block 451assert (cursor >=m_start && cursor <=m_end ); 452 453// Set the current position where the cursor is 454m_current = const_cast < uint8_t *> (cursor ); 455} 456 457size_t MemoryArena ::calcTotalMemoryUsed ()const 458{ 459return (m_usedBlocks ?_calcBlocksUsedMemory (m_usedBlocks -> m_next ) :0 )+ 460size_t (m_current - m_start ); 461} 462 463size_t MemoryArena ::calcTotalMemoryAllocated ()const 464{ 465return _calcBlocksAllocatedMemory (m_usedBlocks )+ _calcBlocksAllocatedMemory (m_availableBlocks ); 466} 467 468 469}// namespace Slang