CommandBufferFactory.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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/Vulkan/CommandBufferFactory.h>
  6. #include <AnKi/Util/Tracer.h>
  7. namespace anki
  8. {
  9. static VulkanQueueType getQueueTypeFromCommandBufferFlags(CommandBufferFlag flags,
  10. const VulkanQueueFamilies& queueFamilies)
  11. {
  12. ANKI_ASSERT(!!(flags & CommandBufferFlag::GENERAL_WORK) ^ !!(flags & CommandBufferFlag::COMPUTE_WORK));
  13. if(!(flags & CommandBufferFlag::GENERAL_WORK) && queueFamilies[VulkanQueueType::COMPUTE] != MAX_U32)
  14. {
  15. return VulkanQueueType::COMPUTE;
  16. }
  17. else
  18. {
  19. ANKI_ASSERT(queueFamilies[VulkanQueueType::GENERAL] != MAX_U32);
  20. return VulkanQueueType::GENERAL;
  21. }
  22. }
  23. void MicroCommandBuffer::destroy()
  24. {
  25. reset();
  26. if(m_handle)
  27. {
  28. vkFreeCommandBuffers(m_threadAlloc->m_factory->m_dev, m_threadAlloc->m_pools[m_queue], 1, &m_handle);
  29. m_handle = {};
  30. }
  31. }
  32. void MicroCommandBuffer::reset()
  33. {
  34. ANKI_TRACE_SCOPED_EVENT(VK_COMMAND_BUFFER_RESET);
  35. ANKI_ASSERT(m_refcount.load() == 0);
  36. ANKI_ASSERT(!m_fence.isCreated() || m_fence->done());
  37. for(GrObjectType type : EnumIterable<GrObjectType>())
  38. {
  39. m_objectRefs[type].destroy(m_fastAlloc);
  40. }
  41. m_fastAlloc.getMemoryPool().reset();
  42. m_fence = {};
  43. }
  44. Error CommandBufferThreadAllocator::init()
  45. {
  46. for(VulkanQueueType qtype : EnumIterable<VulkanQueueType>())
  47. {
  48. if(m_factory->m_queueFamilies[qtype] == MAX_U32)
  49. {
  50. continue;
  51. }
  52. VkCommandPoolCreateInfo ci = {};
  53. ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  54. ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
  55. ci.queueFamilyIndex = m_factory->m_queueFamilies[qtype];
  56. ANKI_VK_CHECK(vkCreateCommandPool(m_factory->m_dev, &ci, nullptr, &m_pools[qtype]));
  57. }
  58. return Error::NONE;
  59. }
  60. void CommandBufferThreadAllocator::destroyList(IntrusiveList<MicroCommandBuffer>& list)
  61. {
  62. while(!list.isEmpty())
  63. {
  64. MicroCommandBuffer* ptr = list.popFront();
  65. ptr->destroy();
  66. getAllocator().deleteInstance(ptr);
  67. #if ANKI_EXTRA_CHECKS
  68. m_createdCmdbs.fetchSub(1);
  69. #endif
  70. }
  71. }
  72. void CommandBufferThreadAllocator::destroyLists()
  73. {
  74. for(U i = 0; i < 2; ++i)
  75. {
  76. for(U j = 0; j < 2; ++j)
  77. {
  78. for(VulkanQueueType qtype : EnumIterable<VulkanQueueType>())
  79. {
  80. CmdbType& type = m_types[i][j][qtype];
  81. destroyList(type.m_deletedCmdbs);
  82. destroyList(type.m_readyCmdbs);
  83. destroyList(type.m_inUseCmdbs);
  84. }
  85. }
  86. }
  87. }
  88. void CommandBufferThreadAllocator::destroy()
  89. {
  90. for(VkCommandPool& pool : m_pools)
  91. {
  92. if(pool)
  93. {
  94. vkDestroyCommandPool(m_factory->m_dev, pool, nullptr);
  95. pool = VK_NULL_HANDLE;
  96. }
  97. }
  98. ANKI_ASSERT(m_createdCmdbs.load() == 0 && "Someone still holds references to command buffers");
  99. }
  100. Error CommandBufferThreadAllocator::newCommandBuffer(CommandBufferFlag cmdbFlags, MicroCommandBufferPtr& outPtr,
  101. Bool& createdNew)
  102. {
  103. ANKI_ASSERT(!!(cmdbFlags & CommandBufferFlag::COMPUTE_WORK) ^ !!(cmdbFlags & CommandBufferFlag::GENERAL_WORK));
  104. createdNew = false;
  105. const Bool secondLevel = !!(cmdbFlags & CommandBufferFlag::SECOND_LEVEL);
  106. const Bool smallBatch = !!(cmdbFlags & CommandBufferFlag::SMALL_BATCH);
  107. const VulkanQueueType queue = getQueueTypeFromCommandBufferFlags(cmdbFlags, m_factory->m_queueFamilies);
  108. CmdbType& type = m_types[secondLevel][smallBatch][queue];
  109. // Move the deleted to (possibly) in-use or ready
  110. {
  111. LockGuard<Mutex> lock(type.m_deletedMtx);
  112. while(!type.m_deletedCmdbs.isEmpty())
  113. {
  114. MicroCommandBuffer* ptr = type.m_deletedCmdbs.popFront();
  115. if(secondLevel)
  116. {
  117. type.m_readyCmdbs.pushFront(ptr);
  118. ptr->reset();
  119. }
  120. else
  121. {
  122. type.m_inUseCmdbs.pushFront(ptr);
  123. }
  124. }
  125. }
  126. // Reset the in-use command buffers and try to get one available
  127. MicroCommandBuffer* out = nullptr;
  128. if(!secondLevel)
  129. {
  130. // Primary
  131. // Try to reuse a ready buffer
  132. if(!type.m_readyCmdbs.isEmpty())
  133. {
  134. out = type.m_readyCmdbs.popFront();
  135. }
  136. // Do a sweep and move in-use buffers to ready
  137. IntrusiveList<MicroCommandBuffer> inUseCmdbs; // Push to temporary because we are iterating
  138. while(!type.m_inUseCmdbs.isEmpty())
  139. {
  140. MicroCommandBuffer* inUseCmdb = type.m_inUseCmdbs.popFront();
  141. if(!inUseCmdb->m_fence.isCreated() || inUseCmdb->m_fence->done())
  142. {
  143. // It's ready
  144. if(out)
  145. {
  146. type.m_readyCmdbs.pushFront(inUseCmdb);
  147. inUseCmdb->reset();
  148. }
  149. else
  150. {
  151. out = inUseCmdb;
  152. }
  153. }
  154. else
  155. {
  156. inUseCmdbs.pushBack(inUseCmdb);
  157. }
  158. }
  159. ANKI_ASSERT(type.m_inUseCmdbs.isEmpty());
  160. type.m_inUseCmdbs = std::move(inUseCmdbs);
  161. }
  162. else
  163. {
  164. // Secondary
  165. ANKI_ASSERT(type.m_inUseCmdbs.isEmpty());
  166. if(!type.m_readyCmdbs.isEmpty())
  167. {
  168. out = type.m_readyCmdbs.popFront();
  169. }
  170. }
  171. if(ANKI_UNLIKELY(out == nullptr))
  172. {
  173. // Create a new one
  174. VkCommandBufferAllocateInfo ci = {};
  175. ci.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  176. ci.commandPool = m_pools[queue];
  177. ci.level = (secondLevel) ? VK_COMMAND_BUFFER_LEVEL_SECONDARY : VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  178. ci.commandBufferCount = 1;
  179. ANKI_TRACE_INC_COUNTER(VK_COMMAND_BUFFER_CREATE, 1);
  180. VkCommandBuffer cmdb;
  181. ANKI_VK_CHECK(vkAllocateCommandBuffers(m_factory->m_dev, &ci, &cmdb));
  182. MicroCommandBuffer* newCmdb = getAllocator().newInstance<MicroCommandBuffer>(this);
  183. #if ANKI_EXTRA_CHECKS
  184. m_createdCmdbs.fetchAdd(1);
  185. #endif
  186. newCmdb->m_fastAlloc =
  187. StackAllocator<U8>(m_factory->m_alloc.getMemoryPool().getAllocationCallback(),
  188. m_factory->m_alloc.getMemoryPool().getAllocationCallbackUserData(), 256_KB, 2.0f);
  189. newCmdb->m_handle = cmdb;
  190. newCmdb->m_flags = cmdbFlags;
  191. newCmdb->m_queue = queue;
  192. out = newCmdb;
  193. createdNew = true;
  194. }
  195. else
  196. {
  197. out->reset();
  198. }
  199. ANKI_ASSERT(out && out->m_refcount.load() == 0);
  200. ANKI_ASSERT(out->m_flags == cmdbFlags);
  201. outPtr.reset(out);
  202. return Error::NONE;
  203. }
  204. void CommandBufferThreadAllocator::deleteCommandBuffer(MicroCommandBuffer* ptr)
  205. {
  206. ANKI_ASSERT(ptr);
  207. const Bool secondLevel = !!(ptr->m_flags & CommandBufferFlag::SECOND_LEVEL);
  208. const Bool smallBatch = !!(ptr->m_flags & CommandBufferFlag::SMALL_BATCH);
  209. CmdbType& type = m_types[secondLevel][smallBatch][ptr->m_queue];
  210. LockGuard<Mutex> lock(type.m_deletedMtx);
  211. type.m_deletedCmdbs.pushBack(ptr);
  212. }
  213. Error CommandBufferFactory::init(GrAllocator<U8> alloc, VkDevice dev, const VulkanQueueFamilies& queueFamilies)
  214. {
  215. ANKI_ASSERT(dev);
  216. m_alloc = alloc;
  217. m_dev = dev;
  218. m_queueFamilies = queueFamilies;
  219. return Error::NONE;
  220. }
  221. void CommandBufferFactory::destroy()
  222. {
  223. // Run 2 times because destroyLists() populates other allocators' lists
  224. for(U i = 0; i < 2; ++i)
  225. {
  226. for(CommandBufferThreadAllocator* alloc : m_threadAllocs)
  227. {
  228. alloc->destroyLists();
  229. }
  230. }
  231. for(CommandBufferThreadAllocator* talloc : m_threadAllocs)
  232. {
  233. talloc->destroy();
  234. m_alloc.deleteInstance(talloc);
  235. }
  236. m_threadAllocs.destroy(m_alloc);
  237. }
  238. Error CommandBufferFactory::newCommandBuffer(ThreadId tid, CommandBufferFlag cmdbFlags, MicroCommandBufferPtr& ptr)
  239. {
  240. CommandBufferThreadAllocator* alloc = nullptr;
  241. // Get the thread allocator
  242. {
  243. class Comp
  244. {
  245. public:
  246. Bool operator()(const CommandBufferThreadAllocator* a, ThreadId tid) const
  247. {
  248. return a->m_tid < tid;
  249. }
  250. Bool operator()(ThreadId tid, const CommandBufferThreadAllocator* a) const
  251. {
  252. return tid < a->m_tid;
  253. }
  254. };
  255. // Find using binary search
  256. {
  257. RLockGuard<RWMutex> lock(m_threadAllocMtx);
  258. auto it = binarySearch(m_threadAllocs.getBegin(), m_threadAllocs.getEnd(), tid, Comp());
  259. alloc = (it != m_threadAllocs.getEnd()) ? (*it) : nullptr;
  260. }
  261. if(ANKI_UNLIKELY(alloc == nullptr))
  262. {
  263. WLockGuard<RWMutex> lock(m_threadAllocMtx);
  264. // Check again
  265. auto it = binarySearch(m_threadAllocs.getBegin(), m_threadAllocs.getEnd(), tid, Comp());
  266. alloc = (it != m_threadAllocs.getEnd()) ? (*it) : nullptr;
  267. if(alloc == nullptr)
  268. {
  269. alloc = m_alloc.newInstance<CommandBufferThreadAllocator>(this, tid);
  270. m_threadAllocs.resize(m_alloc, m_threadAllocs.getSize() + 1);
  271. m_threadAllocs[m_threadAllocs.getSize() - 1] = alloc;
  272. // Sort for fast find
  273. std::sort(m_threadAllocs.getBegin(), m_threadAllocs.getEnd(),
  274. [](const CommandBufferThreadAllocator* a, const CommandBufferThreadAllocator* b) {
  275. return a->m_tid < b->m_tid;
  276. });
  277. ANKI_CHECK(alloc->init());
  278. }
  279. }
  280. }
  281. ANKI_ASSERT(alloc);
  282. ANKI_ASSERT(alloc->m_tid == tid);
  283. Bool createdNew;
  284. ANKI_CHECK(alloc->newCommandBuffer(cmdbFlags, ptr, createdNew));
  285. if(createdNew)
  286. {
  287. m_createdCmdBufferCount.fetchAdd(1);
  288. }
  289. return Error::NONE;
  290. }
  291. } // end namespace anki