BsVulkanCommandBuffer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsVulkanCommandBuffer.h"
  4. #include "BsVulkanCommandBufferManager.h"
  5. #include "BsVulkanUtility.h"
  6. #include "BsVulkanDevice.h"
  7. #include "BsVulkanGpuParams.h"
  8. #include "BsVulkanQueue.h"
  9. namespace BansheeEngine
  10. {
  11. VulkanCmdBufferPool::VulkanCmdBufferPool(VulkanDevice& device)
  12. :mDevice(device), mNextId(1)
  13. {
  14. for (UINT32 i = 0; i < GQT_COUNT; i++)
  15. {
  16. UINT32 familyIdx = device.getQueueFamily((GpuQueueType)i);
  17. if (familyIdx == (UINT32)-1)
  18. continue;
  19. VkCommandPoolCreateInfo poolCI;
  20. poolCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  21. poolCI.pNext = nullptr;
  22. poolCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
  23. poolCI.queueFamilyIndex = familyIdx;
  24. PoolInfo& poolInfo = mPools[familyIdx];
  25. poolInfo.queueFamily = familyIdx;
  26. memset(poolInfo.buffers, 0, sizeof(poolInfo.buffers));
  27. vkCreateCommandPool(device.getLogical(), &poolCI, gVulkanAllocator, &poolInfo.pool);
  28. }
  29. }
  30. VulkanCmdBufferPool::~VulkanCmdBufferPool()
  31. {
  32. // Note: Shutdown should be the only place command buffers are destroyed at, as the system relies on the fact that
  33. // they won't be destroyed during normal operation.
  34. for(auto& entry : mPools)
  35. {
  36. PoolInfo& poolInfo = entry.second;
  37. for (UINT32 i = 0; i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY; i++)
  38. {
  39. VulkanCmdBuffer* buffer = poolInfo.buffers[i];
  40. if (buffer == nullptr)
  41. break;
  42. bs_delete(buffer);
  43. }
  44. vkDestroyCommandPool(mDevice.getLogical(), poolInfo.pool, gVulkanAllocator);
  45. }
  46. }
  47. VulkanCmdBuffer* VulkanCmdBufferPool::getBuffer(UINT32 queueFamily, bool secondary)
  48. {
  49. auto iterFind = mPools.find(queueFamily);
  50. if (iterFind != mPools.end())
  51. return nullptr;
  52. VulkanCmdBuffer** buffers = iterFind->second.buffers;
  53. UINT32 i = 0;
  54. for(; i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY; i++)
  55. {
  56. if (buffers[i] == nullptr)
  57. break;
  58. if(buffers[i]->mState == VulkanCmdBuffer::State::Ready)
  59. {
  60. buffers[i]->begin();
  61. return buffers[i];
  62. }
  63. }
  64. assert(i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY &&
  65. "Too many command buffers allocated. Increment BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY to a higher value. ");
  66. buffers[i] = createBuffer(queueFamily, secondary);
  67. buffers[i]->begin();
  68. return buffers[i];
  69. }
  70. VulkanCmdBuffer* VulkanCmdBufferPool::createBuffer(UINT32 queueFamily, bool secondary)
  71. {
  72. auto iterFind = mPools.find(queueFamily);
  73. if (iterFind != mPools.end())
  74. return nullptr;
  75. const PoolInfo& poolInfo = iterFind->second;
  76. return bs_new<VulkanCmdBuffer>(mDevice, mNextId++, poolInfo.pool, poolInfo.queueFamily, secondary);
  77. }
  78. VulkanCmdBuffer::VulkanCmdBuffer(VulkanDevice& device, UINT32 id, VkCommandPool pool, UINT32 queueFamily, bool secondary)
  79. : mId(id), mQueueFamily(queueFamily), mState(State::Ready), mDevice(device), mPool(pool), mFenceCounter(0)
  80. {
  81. VkCommandBufferAllocateInfo cmdBufferAllocInfo;
  82. cmdBufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  83. cmdBufferAllocInfo.pNext = nullptr;
  84. cmdBufferAllocInfo.commandPool = pool;
  85. cmdBufferAllocInfo.level = secondary ? VK_COMMAND_BUFFER_LEVEL_SECONDARY : VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  86. cmdBufferAllocInfo.commandBufferCount = 1;
  87. VkResult result = vkAllocateCommandBuffers(mDevice.getLogical(), &cmdBufferAllocInfo, &mCmdBuffer);
  88. assert(result == VK_SUCCESS);
  89. VkFenceCreateInfo fenceCI;
  90. fenceCI.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
  91. fenceCI.pNext = nullptr;
  92. fenceCI.flags = 0;
  93. result = vkCreateFence(mDevice.getLogical(), &fenceCI, gVulkanAllocator, &mFence);
  94. assert(result == VK_SUCCESS);
  95. VkSemaphoreCreateInfo semaphoreCI;
  96. semaphoreCI.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
  97. semaphoreCI.pNext = nullptr;
  98. semaphoreCI.flags = 0;
  99. result = vkCreateSemaphore(mDevice.getLogical(), &semaphoreCI, gVulkanAllocator, &mSemaphore);
  100. assert(result == VK_SUCCESS);
  101. }
  102. VulkanCmdBuffer::~VulkanCmdBuffer()
  103. {
  104. VkDevice device = mDevice.getLogical();
  105. if(mState == State::Submitted)
  106. {
  107. // Wait 1s
  108. UINT64 waitTime = 1000 * 1000 * 1000;
  109. VkResult result = vkWaitForFences(device, 1, &mFence, true, waitTime);
  110. assert(result == VK_SUCCESS || result == VK_TIMEOUT);
  111. if (result == VK_TIMEOUT)
  112. LOGWRN("Freeing a command buffer before done executing because fence wait expired!");
  113. }
  114. vkDestroyFence(device, mFence, gVulkanAllocator);
  115. vkDestroySemaphore(device, mSemaphore, gVulkanAllocator);
  116. vkFreeCommandBuffers(device, mPool, 1, &mCmdBuffer);
  117. }
  118. UINT32 VulkanCmdBuffer::getDeviceIdx() const
  119. {
  120. return mDevice.getIndex();
  121. }
  122. void VulkanCmdBuffer::begin()
  123. {
  124. assert(mState == State::Ready);
  125. VkCommandBufferBeginInfo beginInfo;
  126. beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  127. beginInfo.pNext = nullptr;
  128. beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
  129. beginInfo.pInheritanceInfo = nullptr;
  130. VkResult result = vkBeginCommandBuffer(mCmdBuffer, &beginInfo);
  131. assert(result == VK_SUCCESS);
  132. mState = State::Recording;
  133. }
  134. void VulkanCmdBuffer::end()
  135. {
  136. assert(mState == State::Recording);
  137. VkResult result = vkEndCommandBuffer(mCmdBuffer);
  138. assert(result == VK_SUCCESS);
  139. mState = State::RecordingDone;
  140. }
  141. void VulkanCmdBuffer::beginRenderPass()
  142. {
  143. assert(mState == State::Recording);
  144. // TODO
  145. BS_EXCEPT(NotImplementedException, "Not implemented");
  146. mState = State::RecordingRenderPass;
  147. }
  148. void VulkanCmdBuffer::endRenderPass()
  149. {
  150. assert(mState == State::RecordingRenderPass);
  151. vkCmdEndRenderPass(mCmdBuffer);
  152. mState = State::Recording;
  153. }
  154. void VulkanCmdBuffer::refreshFenceStatus()
  155. {
  156. VkResult result = vkGetFenceStatus(mDevice.getLogical(), mFence);
  157. assert(result == VK_SUCCESS || result == VK_NOT_READY);
  158. bool signaled = result == VK_SUCCESS;
  159. if (mState == State::Submitted)
  160. {
  161. if(signaled)
  162. {
  163. mState = State::Ready;
  164. vkResetCommandBuffer(mCmdBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); // Note: Maybe better not to release resources?
  165. result = vkResetFences(mDevice.getLogical(), 1, &mFence);
  166. assert(result == VK_SUCCESS);
  167. mFenceCounter++;
  168. for (auto& entry : mResources)
  169. entry.first->notifyDone(this);
  170. mResources.clear();
  171. mBoundParams.clear();
  172. }
  173. }
  174. else
  175. assert(!signaled); // We reset the fence along with mState so this shouldn't be possible
  176. }
  177. void VulkanCmdBuffer::registerResource(VulkanResource* res, VulkanUseFlags flags)
  178. {
  179. mResources[res].flags |= flags;
  180. }
  181. void VulkanCmdBuffer::registerGpuParams(const SPtr<VulkanGpuParams>& params)
  182. {
  183. mBoundParams.insert(params);
  184. }
  185. VulkanCommandBuffer::VulkanCommandBuffer(VulkanDevice& device, GpuQueueType type, UINT32 deviceIdx,
  186. UINT32 queueIdx, bool secondary)
  187. : CommandBuffer(type, deviceIdx, queueIdx, secondary), mBuffer(nullptr)
  188. , mDevice(device), mQueue(nullptr), mIdMask(0)
  189. {
  190. UINT32 numQueues = device.getNumQueues(mType);
  191. if (numQueues == 0) // Fall back to graphics queue
  192. {
  193. mType = GQT_GRAPHICS;
  194. numQueues = device.getNumQueues(GQT_GRAPHICS);
  195. }
  196. mQueue = device.getQueue(mType, mQueueIdx % numQueues);
  197. // If multiple command buffer IDs map to the same queue, mark them in the mask
  198. UINT32 curIdx = mQueueIdx;
  199. while (curIdx < BS_MAX_QUEUES_PER_TYPE)
  200. {
  201. mIdMask |= CommandSyncMask::getGlobalQueueIdx(mType, curIdx);
  202. curIdx += numQueues;
  203. }
  204. acquireNewBuffer();
  205. }
  206. void VulkanCommandBuffer::acquireNewBuffer()
  207. {
  208. VulkanCmdBufferPool& pool = mDevice.getCmdBufferPool();
  209. if (mBuffer != nullptr)
  210. assert(mBuffer->isSubmitted());
  211. UINT32 queueFamily = mDevice.getQueueFamily(mType);
  212. mBuffer = pool.getBuffer(queueFamily, mIsSecondary);
  213. }
  214. void VulkanCmdBuffer::submit(VulkanQueue* queue, UINT32 queueIdx, UINT32 syncMask)
  215. {
  216. // Issue pipeline barriers for queue transitions (need to happen on original queue first, then on new queue)
  217. for (auto& entry : mBoundParams)
  218. entry->prepareForSubmit(this, mTransitionInfoTemp);
  219. VulkanDevice& device = queue->getDevice();
  220. for (auto& entry : mTransitionInfoTemp)
  221. {
  222. UINT32 entryQueueFamily = entry.first;
  223. // No queue transition needed for entries on this queue (this entry is most likely an in image layout transition)
  224. if (entryQueueFamily == mQueueFamily)
  225. continue;
  226. VkCommandBuffer cmdBuffer; // TODO - Get the command buffer on entryQueueFamily
  227. TransitionInfo& barriers = entry.second;
  228. UINT32 numImgBarriers = (UINT32)barriers.imageBarriers.size();
  229. UINT32 numBufferBarriers = (UINT32)barriers.bufferBarriers.size();
  230. vkCmdPipelineBarrier(cmdBuffer,
  231. VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
  232. VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
  233. 0, 0, nullptr,
  234. numBufferBarriers, barriers.bufferBarriers.data(),
  235. numImgBarriers, barriers.imageBarriers.data());
  236. // TODO - Submit the command buffer
  237. // TODO - Register the command buffer in the sync mask so we wait on it
  238. // If there are any layout transitions, reset them as we don't need them for the second pipeline barrier
  239. for (auto& barrierEntry : barriers.imageBarriers)
  240. barrierEntry.oldLayout = barrierEntry.newLayout;
  241. }
  242. // Issue second part of transition pipeline barriers (on this queue)
  243. for (auto& entry : mTransitionInfoTemp)
  244. {
  245. VkCommandBuffer cmdBuffer; // TODO - Get the command buffer on queueFamily AND this exact queue
  246. // - Probably best to just append it to current submitInfo as it is executed in order
  247. TransitionInfo& barriers = entry.second;
  248. UINT32 numImgBarriers = (UINT32)barriers.imageBarriers.size();
  249. UINT32 numBufferBarriers = (UINT32)barriers.bufferBarriers.size();
  250. vkCmdPipelineBarrier(cmdBuffer,
  251. VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
  252. VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
  253. 0, 0, nullptr,
  254. numBufferBarriers, barriers.bufferBarriers.data(),
  255. numImgBarriers, barriers.imageBarriers.data());
  256. }
  257. UINT32 deviceIdx = device.getIndex();
  258. VulkanCommandBufferManager& cbm = static_cast<VulkanCommandBufferManager&>(CommandBufferManager::instance());
  259. UINT32 numSemaphores;
  260. cbm.getSyncSemaphores(deviceIdx, syncMask, mSemaphoresTemp, numSemaphores);
  261. queue->submit(this, mSemaphoresTemp, numSemaphores);
  262. for (auto& entry : mResources)
  263. entry.first->notifyUsed(this, entry.second.flags);
  264. cbm.refreshStates(deviceIdx);
  265. // Note: Uncommented for debugging only, prevents any device concurrency issues.
  266. // vkQueueWaitIdle(queue->getHandle());
  267. mState = State::Submitted;
  268. cbm.setActiveBuffer(queue->getType(), deviceIdx, queueIdx, this);
  269. // Clear vectors but don't clear the actual map, as we want to re-use the memory since we expect queue family
  270. // indices to be the same
  271. for (auto& entry : mTransitionInfoTemp)
  272. {
  273. entry.second.imageBarriers.clear();
  274. entry.second.bufferBarriers.clear();
  275. }
  276. }
  277. void VulkanCommandBuffer::submit(UINT32 syncMask)
  278. {
  279. assert(mBuffer != nullptr && mBuffer->isReadyForSubmit());
  280. // Ignore myself
  281. syncMask &= ~mIdMask;
  282. mBuffer->submit(mQueue, mQueueIdx, syncMask);
  283. acquireNewBuffer();
  284. }
  285. }