ClassGpuAllocator.cpp 5.0 KB

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