ClassGpuAllocator.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Gr/Utils/ClassGpuAllocator.h>
  6. #include <AnKi/Util/List.h>
  7. #include <AnKi/Util/BitSet.h>
  8. namespace anki {
  9. /// Max number of sub allocations (aka slots) per chunk.
  10. const U32 MAX_SLOTS_PER_CHUNK = 128;
  11. class ClassGpuAllocatorChunk : public IntrusiveListEnabled<ClassGpuAllocatorChunk>
  12. {
  13. public:
  14. /// mem.
  15. ClassGpuAllocatorMemory* m_mem;
  16. /// The in use slots mask.
  17. BitSet<MAX_SLOTS_PER_CHUNK, U64> m_inUseSlots = {false};
  18. /// The number of in-use slots.
  19. U32 m_inUseSlotCount = 0;
  20. /// The owner.
  21. ClassGpuAllocatorClass* m_class = nullptr;
  22. };
  23. class ClassGpuAllocatorClass
  24. {
  25. public:
  26. /// The active chunks.
  27. IntrusiveList<ClassGpuAllocatorChunk> m_inUseChunks;
  28. /// The size of each chunk.
  29. PtrSize m_chunkSize = 0;
  30. /// The max slot size for this class.
  31. PtrSize m_maxSlotSize = 0;
  32. /// The number of slots for a single chunk.
  33. U32 m_slotsPerChunkCount = 0;
  34. Mutex m_mtx;
  35. };
  36. ClassGpuAllocator::~ClassGpuAllocator()
  37. {
  38. for(Class& c : m_classes)
  39. {
  40. (void)c;
  41. ANKI_ASSERT(c.m_inUseChunks.isEmpty() && "Forgot to deallocate");
  42. }
  43. m_classes.destroy(m_alloc);
  44. }
  45. void ClassGpuAllocator::init(GenericMemoryPoolAllocator<U8> alloc, ClassGpuAllocatorInterface* iface)
  46. {
  47. ANKI_ASSERT(iface);
  48. m_alloc = alloc;
  49. m_iface = iface;
  50. //
  51. // Initialize the classes
  52. //
  53. U32 classCount = iface->getClassCount();
  54. ANKI_ASSERT(classCount > 0);
  55. m_classes.create(m_alloc, classCount);
  56. for(U32 i = 0; i < classCount; ++i)
  57. {
  58. PtrSize slotSize = 0, chunkSize = 0;
  59. m_iface->getClassInfo(i, slotSize, chunkSize);
  60. ANKI_ASSERT(slotSize > 0 && chunkSize > 0);
  61. ANKI_ASSERT((chunkSize % slotSize) == 0);
  62. ANKI_ASSERT((chunkSize / slotSize) <= MAX_SLOTS_PER_CHUNK);
  63. Class& c = m_classes[i];
  64. c.m_chunkSize = chunkSize;
  65. c.m_maxSlotSize = slotSize;
  66. c.m_slotsPerChunkCount = U32(chunkSize / slotSize);
  67. }
  68. }
  69. ClassGpuAllocator::Class* ClassGpuAllocator::findClass(PtrSize size, U alignment)
  70. {
  71. ANKI_ASSERT(size > 0 && alignment > 0);
  72. PtrSize lowLimit = 0;
  73. Class* it = m_classes.getBegin();
  74. const Class* end = m_classes.getEnd();
  75. while(it != end)
  76. {
  77. PtrSize highLimit = it->m_maxSlotSize;
  78. if(size > lowLimit && size <= highLimit)
  79. {
  80. if(alignment <= highLimit)
  81. {
  82. // Found the class
  83. return it;
  84. }
  85. else
  86. {
  87. // The class found doesn't have the proper alignment. Need to go higher
  88. while(++it != end)
  89. {
  90. if(alignment <= it->m_maxSlotSize)
  91. {
  92. // Now found something
  93. return it;
  94. }
  95. }
  96. }
  97. }
  98. lowLimit = highLimit;
  99. ++it;
  100. }
  101. ANKI_GR_LOGF("Memory class not found");
  102. return nullptr;
  103. }
  104. ClassGpuAllocator::Chunk* ClassGpuAllocator::findChunkWithUnusedSlot(Class& cl)
  105. {
  106. auto it = cl.m_inUseChunks.getBegin();
  107. const auto end = cl.m_inUseChunks.getEnd();
  108. while(it != end)
  109. {
  110. if(it->m_inUseSlotCount < cl.m_slotsPerChunkCount)
  111. {
  112. return &(*it);
  113. }
  114. ++it;
  115. }
  116. return nullptr;
  117. }
  118. Error ClassGpuAllocator::createChunk(Class& cl, Chunk*& chunk)
  119. {
  120. ClassGpuAllocatorMemory* mem = nullptr;
  121. ANKI_CHECK(m_iface->allocate(U32(&cl - &m_classes[0]), mem));
  122. ANKI_ASSERT(mem);
  123. chunk = m_alloc.newInstance<Chunk>();
  124. chunk->m_mem = mem;
  125. chunk->m_class = &cl;
  126. cl.m_inUseChunks.pushBack(chunk);
  127. // Update stats
  128. m_allocatedMem += cl.m_chunkSize;
  129. return Error::NONE;
  130. }
  131. void ClassGpuAllocator::destroyChunk(Class& cl, Chunk& chunk)
  132. {
  133. cl.m_inUseChunks.erase(&chunk);
  134. m_iface->free(chunk.m_mem);
  135. m_alloc.deleteInstance(&chunk);
  136. // Update stats
  137. ANKI_ASSERT(m_allocatedMem >= cl.m_chunkSize);
  138. m_allocatedMem -= cl.m_chunkSize;
  139. }
  140. Error ClassGpuAllocator::allocate(PtrSize size, U alignment, ClassGpuAllocatorHandle& handle)
  141. {
  142. ANKI_ASSERT(!handle);
  143. ANKI_ASSERT(handle.valid());
  144. // Find the class for the given size
  145. Class* cl = findClass(size, alignment);
  146. LockGuard<Mutex> lock(cl->m_mtx);
  147. Chunk* chunk = findChunkWithUnusedSlot(*cl);
  148. // Create a new chunk if needed
  149. if(chunk == nullptr)
  150. {
  151. ANKI_CHECK(createChunk(*cl, chunk));
  152. }
  153. // Allocate from chunk
  154. U32 bitCount = cl->m_slotsPerChunkCount;
  155. for(U32 i = 0; i < bitCount; ++i)
  156. {
  157. if(!chunk->m_inUseSlots.get(i))
  158. {
  159. // Found an empty slot, allocate from it
  160. chunk->m_inUseSlots.set(i);
  161. ++chunk->m_inUseSlotCount;
  162. handle.m_memory = chunk->m_mem;
  163. handle.m_offset = i * cl->m_maxSlotSize;
  164. handle.m_chunk = chunk;
  165. break;
  166. }
  167. }
  168. ANKI_ASSERT(handle.m_memory && handle.m_chunk);
  169. ANKI_ASSERT(isAligned(alignment, handle.m_offset));
  170. return Error::NONE;
  171. }
  172. void ClassGpuAllocator::free(ClassGpuAllocatorHandle& handle)
  173. {
  174. ANKI_ASSERT(handle);
  175. ANKI_ASSERT(handle.valid());
  176. Chunk& chunk = *handle.m_chunk;
  177. Class& cl = *chunk.m_class;
  178. LockGuard<Mutex> lock(cl.m_mtx);
  179. const U32 slotIdx = U32(handle.m_offset / cl.m_maxSlotSize);
  180. ANKI_ASSERT(chunk.m_inUseSlots.get(slotIdx));
  181. ANKI_ASSERT(chunk.m_inUseSlotCount > 0);
  182. chunk.m_inUseSlots.unset(slotIdx);
  183. --chunk.m_inUseSlotCount;
  184. if(chunk.m_inUseSlotCount == 0)
  185. {
  186. destroyChunk(cl, chunk);
  187. }
  188. handle = {};
  189. }
  190. } // end namespace anki