2
0

SegregatedListsGpuMemoryPool.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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)
  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. }
  63. void SegregatedListsGpuMemoryPool::destroy()
  64. {
  65. if(!isInitialized())
  66. {
  67. return;
  68. }
  69. GrManager::getSingleton().finish();
  70. for(GrDynamicArray<SegregatedListsGpuMemoryPoolToken>& arr : m_garbage)
  71. {
  72. for(const SegregatedListsGpuMemoryPoolToken& token : arr)
  73. {
  74. m_builder->free(static_cast<Chunk*>(token.m_chunk), token.m_chunkOffset, token.m_size);
  75. }
  76. }
  77. deleteInstance(GrMemoryPool::getSingleton(), m_builder);
  78. m_gpuBuffer.reset(nullptr);
  79. for(Chunk* chunk : m_deletedChunks)
  80. {
  81. deleteInstance(GrMemoryPool::getSingleton(), chunk);
  82. }
  83. }
  84. Error SegregatedListsGpuMemoryPool::allocateChunk(Chunk*& newChunk, PtrSize& chunkSize)
  85. {
  86. ANKI_ASSERT(isInitialized());
  87. if(!m_gpuBuffer.isCreated())
  88. {
  89. // First chunk, our job is easy, create the buffer
  90. BufferInitInfo buffInit(m_bufferName);
  91. buffInit.m_size = m_initialBufferSize;
  92. buffInit.m_usage = m_bufferUsage | BufferUsageBit::kAllTransfer;
  93. m_gpuBuffer = GrManager::getSingleton().newBuffer(buffInit);
  94. newChunk = newInstance<Chunk>(GrMemoryPool::getSingleton());
  95. newChunk->m_offsetInGpuBuffer = 0;
  96. }
  97. else if(m_deletedChunks.getSize() > 0)
  98. {
  99. // We already have a deleted chunk, use that
  100. newChunk = m_deletedChunks.getBack();
  101. m_deletedChunks.popBack();
  102. }
  103. else if(m_allowCoWs)
  104. {
  105. // Current buffer is not enough. Need to grow it which we can't grow GPU buffers do a CoW
  106. ANKI_GR_LOGV("Will grow the %s buffer and perform a copy-on-write", m_bufferName.cstr());
  107. // Create the new buffer
  108. BufferInitInfo buffInit(m_bufferName);
  109. buffInit.m_size = m_gpuBuffer->getSize() * 2;
  110. buffInit.m_usage = m_bufferUsage | BufferUsageBit::kAllTransfer;
  111. BufferPtr newBuffer = GrManager::getSingleton().newBuffer(buffInit);
  112. // Do the copy
  113. CommandBufferInitInfo cmdbInit("SegregatedListsGpuMemoryPool CoW");
  114. cmdbInit.m_flags = CommandBufferFlag::kSmallBatch;
  115. CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(cmdbInit);
  116. Array<BufferBarrierInfo, 2> barriers;
  117. barriers[0].m_buffer = m_gpuBuffer.get();
  118. barriers[0].m_previousUsage = m_bufferUsage;
  119. barriers[0].m_nextUsage = BufferUsageBit::kTransferSource;
  120. barriers[1].m_buffer = newBuffer.get();
  121. barriers[1].m_previousUsage = BufferUsageBit::kNone;
  122. barriers[1].m_nextUsage = BufferUsageBit::kTransferDestination;
  123. cmdb->setPipelineBarrier({}, barriers, {});
  124. cmdb->copyBufferToBuffer(m_gpuBuffer, 0, newBuffer, 0, m_gpuBuffer->getSize());
  125. barriers[1].m_previousUsage = BufferUsageBit::kTransferDestination;
  126. barriers[1].m_nextUsage = m_bufferUsage;
  127. cmdb->setPipelineBarrier({}, ConstWeakArray<BufferBarrierInfo>{&barriers[1], 1}, {});
  128. cmdb->flush();
  129. // Create the new chunk
  130. newChunk = newInstance<Chunk>(GrMemoryPool::getSingleton());
  131. newChunk->m_offsetInGpuBuffer = m_gpuBuffer->getSize();
  132. // Switch the buffers
  133. m_gpuBuffer = newBuffer;
  134. }
  135. else
  136. {
  137. ANKI_GR_LOGE("Out of memory and can't copy-on-write");
  138. return Error::kOutOfMemory;
  139. }
  140. chunkSize = m_initialBufferSize;
  141. return Error::kNone;
  142. }
  143. void SegregatedListsGpuMemoryPool::deleteChunk(Chunk* chunk)
  144. {
  145. m_deletedChunks.emplaceBack(chunk);
  146. }
  147. void SegregatedListsGpuMemoryPool::allocate(PtrSize size, U32 alignment, SegregatedListsGpuMemoryPoolToken& token)
  148. {
  149. ANKI_ASSERT(isInitialized());
  150. ANKI_ASSERT(size > 0 && alignment > 0 && isPowerOfTwo(alignment));
  151. ANKI_ASSERT(token == SegregatedListsGpuMemoryPoolToken());
  152. LockGuard lock(m_lock);
  153. Chunk* chunk;
  154. PtrSize offset;
  155. const Error err = m_builder->allocate(size, alignment, chunk, offset);
  156. if(err)
  157. {
  158. ANKI_GR_LOGF("Failed to allocate memory");
  159. }
  160. token.m_chunk = chunk;
  161. token.m_chunkOffset = offset;
  162. token.m_offset = offset + chunk->m_offsetInGpuBuffer;
  163. token.m_size = size;
  164. m_allocatedSize += size;
  165. }
  166. void SegregatedListsGpuMemoryPool::deferredFree(SegregatedListsGpuMemoryPoolToken& token)
  167. {
  168. ANKI_ASSERT(isInitialized());
  169. if(token.m_chunk == nullptr)
  170. {
  171. return;
  172. }
  173. {
  174. LockGuard lock(m_lock);
  175. m_garbage[m_frame].emplaceBack(token);
  176. }
  177. token = {};
  178. }
  179. void SegregatedListsGpuMemoryPool::endFrame()
  180. {
  181. ANKI_ASSERT(isInitialized());
  182. LockGuard lock(m_lock);
  183. m_frame = (m_frame + 1) % kMaxFramesInFlight;
  184. // Throw out the garbage
  185. for(SegregatedListsGpuMemoryPoolToken& token : m_garbage[m_frame])
  186. {
  187. m_builder->free(static_cast<Chunk*>(token.m_chunk), token.m_chunkOffset, token.m_size);
  188. ANKI_ASSERT(m_allocatedSize >= token.m_size);
  189. m_allocatedSize -= token.m_size;
  190. }
  191. m_garbage[m_frame].destroy();
  192. }
  193. void SegregatedListsGpuMemoryPool::getStats(F32& externalFragmentation, PtrSize& userAllocatedSize, PtrSize& totalSize) const
  194. {
  195. ANKI_ASSERT(isInitialized());
  196. LockGuard lock(m_lock);
  197. externalFragmentation = m_builder->computeExternalFragmentation();
  198. userAllocatedSize = m_allocatedSize;
  199. totalSize = (m_gpuBuffer) ? m_gpuBuffer->getSize() : 0;
  200. }
  201. } // end namespace anki