SegregatedListsGpuMemoryPool.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright (C) 2009-2023, 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/SegregatedListsGpuMemoryPool.h>
  6. #include <AnKi/Gr/GrManager.h>
  7. #include <AnKi/Gr/CommandBuffer.h>
  8. namespace anki {
  9. class SegregatedListsGpuMemoryPool::Chunk : public SegregatedListsAllocatorBuilderChunkBase<SingletonMemoryPoolWrapper<GrMemoryPool>>
  10. {
  11. public:
  12. PtrSize m_offsetInGpuBuffer;
  13. };
  14. class SegregatedListsGpuMemoryPool::BuilderInterface
  15. {
  16. public:
  17. SegregatedListsGpuMemoryPool* m_parent = nullptr;
  18. /// @name Interface methods
  19. /// @{
  20. U32 getClassCount() const
  21. {
  22. return m_parent->m_classes.getSize();
  23. }
  24. void getClassInfo(U32 idx, PtrSize& size) const
  25. {
  26. size = m_parent->m_classes[idx];
  27. }
  28. Error allocateChunk(Chunk*& newChunk, PtrSize& chunkSize)
  29. {
  30. return m_parent->allocateChunk(newChunk, chunkSize);
  31. }
  32. void deleteChunk(Chunk* chunk)
  33. {
  34. m_parent->deleteChunk(chunk);
  35. }
  36. static constexpr PtrSize getMinSizeAlignment()
  37. {
  38. return 4;
  39. }
  40. /// @}
  41. };
  42. void SegregatedListsGpuMemoryPool::init(BufferUsageBit gpuBufferUsage, ConstWeakArray<PtrSize> classUpperSizes, PtrSize initialGpuBufferSize,
  43. CString bufferName, Bool allowCoWs, BufferMapAccessBit map)
  44. {
  45. ANKI_ASSERT(!isInitialized());
  46. ANKI_ASSERT(gpuBufferUsage != BufferUsageBit::kNone);
  47. m_bufferUsage = gpuBufferUsage;
  48. ANKI_ASSERT(classUpperSizes.getSize() > 0);
  49. m_classes.resize(classUpperSizes.getSize());
  50. for(U32 i = 0; i < m_classes.getSize(); ++i)
  51. {
  52. m_classes[i] = classUpperSizes[i];
  53. }
  54. ANKI_ASSERT(initialGpuBufferSize > 0);
  55. m_initialBufferSize = initialGpuBufferSize;
  56. m_bufferName = bufferName;
  57. m_builder = newInstance<Builder>(GrMemoryPool::getSingleton());
  58. m_builder->getInterface().m_parent = this;
  59. m_frame = 0;
  60. m_allocatedSize = 0;
  61. m_allowCoWs = allowCoWs;
  62. m_mapAccess = map;
  63. }
  64. void SegregatedListsGpuMemoryPool::destroy()
  65. {
  66. if(!isInitialized())
  67. {
  68. return;
  69. }
  70. GrManager::getSingleton().finish();
  71. if(m_mappedGpuBufferMemory)
  72. {
  73. m_gpuBuffer->unmap();
  74. }
  75. for(GrDynamicArray<SegregatedListsGpuMemoryPoolToken>& arr : m_garbage)
  76. {
  77. for(const SegregatedListsGpuMemoryPoolToken& token : arr)
  78. {
  79. m_builder->free(static_cast<Chunk*>(token.m_chunk), token.m_chunkOffset, token.m_size);
  80. }
  81. }
  82. deleteInstance(GrMemoryPool::getSingleton(), m_builder);
  83. m_gpuBuffer.reset(nullptr);
  84. for(Chunk* chunk : m_deletedChunks)
  85. {
  86. deleteInstance(GrMemoryPool::getSingleton(), chunk);
  87. }
  88. }
  89. Error SegregatedListsGpuMemoryPool::allocateChunk(Chunk*& newChunk, PtrSize& chunkSize)
  90. {
  91. ANKI_ASSERT(isInitialized());
  92. if(!m_gpuBuffer.isCreated())
  93. {
  94. // First chunk, our job is easy, create the buffer
  95. BufferInitInfo buffInit(m_bufferName);
  96. buffInit.m_size = m_initialBufferSize;
  97. buffInit.m_usage = m_bufferUsage | BufferUsageBit::kAllTransfer;
  98. buffInit.m_mapAccess = m_mapAccess;
  99. m_gpuBuffer = GrManager::getSingleton().newBuffer(buffInit);
  100. if(!!m_mapAccess)
  101. {
  102. m_mappedGpuBufferMemory = m_gpuBuffer->map(0, kMaxPtrSize, m_mapAccess);
  103. }
  104. newChunk = newInstance<Chunk>(GrMemoryPool::getSingleton());
  105. newChunk->m_offsetInGpuBuffer = 0;
  106. }
  107. else if(m_deletedChunks.getSize() > 0)
  108. {
  109. // We already have a deleted chunk, use that
  110. newChunk = m_deletedChunks.getBack();
  111. m_deletedChunks.popBack();
  112. }
  113. else if(m_allowCoWs)
  114. {
  115. // Current buffer is not enough. Need to grow it which we can't grow GPU buffers do a CoW
  116. ANKI_GR_LOGV("Will grow the %s buffer and perform a copy-on-write", m_bufferName.cstr());
  117. // Create the new buffer
  118. BufferInitInfo buffInit(m_bufferName);
  119. buffInit.m_size = m_gpuBuffer->getSize() * 2;
  120. buffInit.m_usage = m_bufferUsage | BufferUsageBit::kAllTransfer;
  121. buffInit.m_mapAccess = m_mapAccess;
  122. BufferPtr newBuffer = GrManager::getSingleton().newBuffer(buffInit);
  123. // Do the copy
  124. CommandBufferInitInfo cmdbInit("SegregatedListsGpuMemoryPool CoW");
  125. cmdbInit.m_flags = CommandBufferFlag::kSmallBatch;
  126. CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(cmdbInit);
  127. Array<BufferBarrierInfo, 2> barriers;
  128. barriers[0].m_buffer = m_gpuBuffer.get();
  129. barriers[0].m_previousUsage = m_bufferUsage;
  130. barriers[0].m_nextUsage = BufferUsageBit::kTransferSource;
  131. barriers[1].m_buffer = newBuffer.get();
  132. barriers[1].m_previousUsage = BufferUsageBit::kNone;
  133. barriers[1].m_nextUsage = BufferUsageBit::kTransferDestination;
  134. cmdb->setPipelineBarrier({}, barriers, {});
  135. cmdb->copyBufferToBuffer(m_gpuBuffer.get(), 0, newBuffer.get(), 0, m_gpuBuffer->getSize());
  136. barriers[1].m_previousUsage = BufferUsageBit::kTransferDestination;
  137. barriers[1].m_nextUsage = m_bufferUsage;
  138. cmdb->setPipelineBarrier({}, ConstWeakArray<BufferBarrierInfo>{&barriers[1], 1}, {});
  139. cmdb->endRecording();
  140. GrManager::getSingleton().submit(cmdb.get());
  141. // Create the new chunk
  142. newChunk = newInstance<Chunk>(GrMemoryPool::getSingleton());
  143. newChunk->m_offsetInGpuBuffer = m_gpuBuffer->getSize();
  144. // Switch the buffers
  145. m_gpuBuffer = newBuffer;
  146. if(!!m_mapAccess)
  147. {
  148. m_mappedGpuBufferMemory = m_gpuBuffer->map(0, kMaxPtrSize, m_mapAccess);
  149. }
  150. }
  151. else
  152. {
  153. ANKI_GR_LOGE("Out of memory and can't copy-on-write");
  154. return Error::kOutOfMemory;
  155. }
  156. chunkSize = m_initialBufferSize;
  157. return Error::kNone;
  158. }
  159. void SegregatedListsGpuMemoryPool::deleteChunk(Chunk* chunk)
  160. {
  161. m_deletedChunks.emplaceBack(chunk);
  162. }
  163. void SegregatedListsGpuMemoryPool::allocate(PtrSize size, U32 alignment, SegregatedListsGpuMemoryPoolToken& token)
  164. {
  165. ANKI_ASSERT(isInitialized());
  166. ANKI_ASSERT(size > 0 && alignment > 0);
  167. ANKI_ASSERT(token == SegregatedListsGpuMemoryPoolToken());
  168. LockGuard lock(m_lock);
  169. Chunk* chunk;
  170. PtrSize offset;
  171. const Error err = m_builder->allocate(size, alignment, chunk, offset);
  172. if(err)
  173. {
  174. ANKI_GR_LOGF("Failed to allocate memory");
  175. }
  176. token.m_chunk = chunk;
  177. token.m_chunkOffset = offset;
  178. token.m_offset = offset + chunk->m_offsetInGpuBuffer;
  179. token.m_size = size;
  180. m_allocatedSize += size;
  181. }
  182. void SegregatedListsGpuMemoryPool::deferredFree(SegregatedListsGpuMemoryPoolToken& token)
  183. {
  184. ANKI_ASSERT(isInitialized());
  185. if(token.m_chunk == nullptr)
  186. {
  187. return;
  188. }
  189. {
  190. LockGuard lock(m_lock);
  191. m_garbage[m_frame].emplaceBack(token);
  192. }
  193. token = {};
  194. }
  195. void SegregatedListsGpuMemoryPool::endFrame()
  196. {
  197. ANKI_ASSERT(isInitialized());
  198. LockGuard lock(m_lock);
  199. m_frame = (m_frame + 1) % kMaxFramesInFlight;
  200. // Throw out the garbage
  201. for(SegregatedListsGpuMemoryPoolToken& token : m_garbage[m_frame])
  202. {
  203. m_builder->free(static_cast<Chunk*>(token.m_chunk), token.m_chunkOffset, token.m_size);
  204. ANKI_ASSERT(m_allocatedSize >= token.m_size);
  205. m_allocatedSize -= token.m_size;
  206. }
  207. m_garbage[m_frame].destroy();
  208. }
  209. void SegregatedListsGpuMemoryPool::getStats(F32& externalFragmentation, PtrSize& userAllocatedSize, PtrSize& totalSize) const
  210. {
  211. ANKI_ASSERT(isInitialized());
  212. LockGuard lock(m_lock);
  213. externalFragmentation = m_builder->computeExternalFragmentation();
  214. userAllocatedSize = m_allocatedSize;
  215. totalSize = (m_gpuBuffer) ? m_gpuBuffer->getSize() : 0;
  216. }
  217. } // end namespace anki