CommandBufferFactory.cpp 7.0 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/Vulkan/CommandBufferFactory.h>
  6. #include <AnKi/Util/Tracer.h>
  7. namespace anki {
  8. static VulkanQueueType getQueueTypeFromCommandBufferFlags(CommandBufferFlag flags, const VulkanQueueFamilies& queueFamilies)
  9. {
  10. ANKI_ASSERT(!!(flags & CommandBufferFlag::kGeneralWork) ^ !!(flags & CommandBufferFlag::kComputeWork));
  11. if(!(flags & CommandBufferFlag::kGeneralWork) && queueFamilies[VulkanQueueType::kCompute] != kMaxU32)
  12. {
  13. return VulkanQueueType::kCompute;
  14. }
  15. else
  16. {
  17. ANKI_ASSERT(queueFamilies[VulkanQueueType::kGeneral] != kMaxU32);
  18. return VulkanQueueType::kGeneral;
  19. }
  20. }
  21. void MicroCommandBufferPtrDeleter::operator()(MicroCommandBuffer* ptr)
  22. {
  23. ANKI_ASSERT(ptr);
  24. ptr->m_threadAlloc->deleteCommandBuffer(ptr);
  25. }
  26. MicroCommandBuffer::~MicroCommandBuffer()
  27. {
  28. reset();
  29. if(m_handle)
  30. {
  31. vkFreeCommandBuffers(getVkDevice(), m_threadAlloc->m_pools[m_queue], 1, &m_handle);
  32. m_handle = {};
  33. [[maybe_unused]] const U32 count = m_threadAlloc->m_factory->m_createdCmdBufferCount.fetchSub(1);
  34. ANKI_ASSERT(count > 0);
  35. }
  36. }
  37. void MicroCommandBuffer::reset()
  38. {
  39. ANKI_TRACE_SCOPED_EVENT(VkCommandBufferReset);
  40. ANKI_ASSERT(m_refcount.load() == 0);
  41. ANKI_ASSERT(!m_fence.isCreated());
  42. for(GrObjectType type : EnumIterable<GrObjectType>())
  43. {
  44. m_objectRefs[type].destroy();
  45. }
  46. m_fastPool.reset();
  47. }
  48. Error CommandBufferThreadAllocator::init()
  49. {
  50. for(VulkanQueueType qtype : EnumIterable<VulkanQueueType>())
  51. {
  52. if(m_factory->m_queueFamilies[qtype] == kMaxU32)
  53. {
  54. continue;
  55. }
  56. VkCommandPoolCreateInfo ci = {};
  57. ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  58. ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
  59. ci.queueFamilyIndex = m_factory->m_queueFamilies[qtype];
  60. ANKI_VK_CHECK(vkCreateCommandPool(getVkDevice(), &ci, nullptr, &m_pools[qtype]));
  61. }
  62. return Error::kNone;
  63. }
  64. void CommandBufferThreadAllocator::destroy()
  65. {
  66. for(U32 secondLevel = 0; secondLevel < 2; ++secondLevel)
  67. {
  68. for(U32 smallBatch = 0; smallBatch < 2; ++smallBatch)
  69. {
  70. for(VulkanQueueType queue : EnumIterable<VulkanQueueType>())
  71. {
  72. m_recyclers[secondLevel][smallBatch][queue].destroy();
  73. }
  74. }
  75. }
  76. for(VkCommandPool& pool : m_pools)
  77. {
  78. if(pool)
  79. {
  80. vkDestroyCommandPool(getVkDevice(), pool, nullptr);
  81. pool = VK_NULL_HANDLE;
  82. }
  83. }
  84. }
  85. Error CommandBufferThreadAllocator::newCommandBuffer(CommandBufferFlag cmdbFlags, MicroCommandBufferPtr& outPtr)
  86. {
  87. ANKI_ASSERT(!!(cmdbFlags & CommandBufferFlag::kComputeWork) ^ !!(cmdbFlags & CommandBufferFlag::kGeneralWork));
  88. const Bool secondLevel = !!(cmdbFlags & CommandBufferFlag::kSecondLevel);
  89. const Bool smallBatch = !!(cmdbFlags & CommandBufferFlag::kSmallBatch);
  90. const VulkanQueueType queue = getQueueTypeFromCommandBufferFlags(cmdbFlags, m_factory->m_queueFamilies);
  91. MicroObjectRecycler<MicroCommandBuffer>& recycler = m_recyclers[secondLevel][smallBatch][queue];
  92. MicroCommandBuffer* out = recycler.findToReuse();
  93. if(out == nullptr) [[unlikely]]
  94. {
  95. // Create a new one
  96. VkCommandBufferAllocateInfo ci = {};
  97. ci.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  98. ci.commandPool = m_pools[queue];
  99. ci.level = (secondLevel) ? VK_COMMAND_BUFFER_LEVEL_SECONDARY : VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  100. ci.commandBufferCount = 1;
  101. ANKI_TRACE_INC_COUNTER(VkCommandBufferCreate, 1);
  102. VkCommandBuffer cmdb;
  103. ANKI_VK_CHECK(vkAllocateCommandBuffers(getVkDevice(), &ci, &cmdb));
  104. MicroCommandBuffer* newCmdb = newInstance<MicroCommandBuffer>(GrMemoryPool::getSingleton(), this);
  105. newCmdb->m_fastPool.init(GrMemoryPool::getSingleton().getAllocationCallback(), GrMemoryPool::getSingleton().getAllocationCallbackUserData(),
  106. 256_KB, 2.0f);
  107. for(DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>& arr : newCmdb->m_objectRefs)
  108. {
  109. arr = DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>(&newCmdb->m_fastPool);
  110. }
  111. newCmdb->m_handle = cmdb;
  112. newCmdb->m_flags = cmdbFlags;
  113. newCmdb->m_queue = queue;
  114. out = newCmdb;
  115. m_factory->m_createdCmdBufferCount.fetchAdd(1);
  116. }
  117. else
  118. {
  119. for([[maybe_unused]] GrObjectType type : EnumIterable<GrObjectType>())
  120. {
  121. ANKI_ASSERT(out->m_objectRefs[type].getSize() == 0);
  122. }
  123. }
  124. ANKI_ASSERT(out && out->m_refcount.load() == 0);
  125. ANKI_ASSERT(out->m_flags == cmdbFlags);
  126. outPtr.reset(out);
  127. return Error::kNone;
  128. }
  129. void CommandBufferThreadAllocator::deleteCommandBuffer(MicroCommandBuffer* ptr)
  130. {
  131. ANKI_ASSERT(ptr);
  132. const Bool secondLevel = !!(ptr->m_flags & CommandBufferFlag::kSecondLevel);
  133. const Bool smallBatch = !!(ptr->m_flags & CommandBufferFlag::kSmallBatch);
  134. m_recyclers[secondLevel][smallBatch][ptr->m_queue].recycle(ptr);
  135. }
  136. void CommandBufferFactory::destroy()
  137. {
  138. // First trim the caches for all recyclers. This will release the primaries and populate the recyclers of
  139. // secondaries
  140. for(CommandBufferThreadAllocator* talloc : m_threadAllocs)
  141. {
  142. for(U32 secondLevel = 0; secondLevel < 2; ++secondLevel)
  143. {
  144. for(U32 smallBatch = 0; smallBatch < 2; ++smallBatch)
  145. {
  146. for(VulkanQueueType queue : EnumIterable<VulkanQueueType>())
  147. {
  148. talloc->m_recyclers[secondLevel][smallBatch][queue].trimCache();
  149. }
  150. }
  151. }
  152. }
  153. for(CommandBufferThreadAllocator* talloc : m_threadAllocs)
  154. {
  155. talloc->destroy();
  156. deleteInstance(GrMemoryPool::getSingleton(), talloc);
  157. }
  158. m_threadAllocs.destroy();
  159. }
  160. Error CommandBufferFactory::newCommandBuffer(ThreadId tid, CommandBufferFlag cmdbFlags, MicroCommandBufferPtr& ptr)
  161. {
  162. CommandBufferThreadAllocator* alloc = nullptr;
  163. // Get the thread allocator
  164. {
  165. class Comp
  166. {
  167. public:
  168. Bool operator()(const CommandBufferThreadAllocator* a, ThreadId tid) const
  169. {
  170. return a->m_tid < tid;
  171. }
  172. Bool operator()(ThreadId tid, const CommandBufferThreadAllocator* a) const
  173. {
  174. return tid < a->m_tid;
  175. }
  176. };
  177. // Find using binary search
  178. {
  179. RLockGuard<RWMutex> lock(m_threadAllocMtx);
  180. auto it = binarySearch(m_threadAllocs.getBegin(), m_threadAllocs.getEnd(), tid, Comp());
  181. alloc = (it != m_threadAllocs.getEnd()) ? (*it) : nullptr;
  182. }
  183. if(alloc == nullptr) [[unlikely]]
  184. {
  185. WLockGuard<RWMutex> lock(m_threadAllocMtx);
  186. // Check again
  187. auto it = binarySearch(m_threadAllocs.getBegin(), m_threadAllocs.getEnd(), tid, Comp());
  188. alloc = (it != m_threadAllocs.getEnd()) ? (*it) : nullptr;
  189. if(alloc == nullptr)
  190. {
  191. alloc = newInstance<CommandBufferThreadAllocator>(GrMemoryPool::getSingleton(), this, tid);
  192. m_threadAllocs.resize(m_threadAllocs.getSize() + 1);
  193. m_threadAllocs[m_threadAllocs.getSize() - 1] = alloc;
  194. // Sort for fast find
  195. std::sort(m_threadAllocs.getBegin(), m_threadAllocs.getEnd(),
  196. [](const CommandBufferThreadAllocator* a, const CommandBufferThreadAllocator* b) {
  197. return a->m_tid < b->m_tid;
  198. });
  199. ANKI_CHECK(alloc->init());
  200. }
  201. }
  202. }
  203. ANKI_ASSERT(alloc);
  204. ANKI_ASSERT(alloc->m_tid == tid);
  205. ANKI_CHECK(alloc->newCommandBuffer(cmdbFlags, ptr));
  206. return Error::kNone;
  207. }
  208. } // end namespace anki