| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "BsVulkanCommandBuffer.h"
- #include "BsVulkanCommandBufferManager.h"
- #include "BsVulkanUtility.h"
- #include "BsVulkanDevice.h"
- #include "BsVulkanGpuParams.h"
- #include "BsVulkanQueue.h"
- namespace BansheeEngine
- {
- VulkanCmdBufferPool::VulkanCmdBufferPool(VulkanDevice& device)
- :mDevice(device), mNextId(1)
- {
- for (UINT32 i = 0; i < GQT_COUNT; i++)
- {
- UINT32 familyIdx = device.getQueueFamily((GpuQueueType)i);
- if (familyIdx == (UINT32)-1)
- continue;
- VkCommandPoolCreateInfo poolCI;
- poolCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
- poolCI.pNext = nullptr;
- poolCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
- poolCI.queueFamilyIndex = familyIdx;
- PoolInfo& poolInfo = mPools[familyIdx];
- poolInfo.queueFamily = familyIdx;
- memset(poolInfo.buffers, 0, sizeof(poolInfo.buffers));
- vkCreateCommandPool(device.getLogical(), &poolCI, gVulkanAllocator, &poolInfo.pool);
- }
- }
- VulkanCmdBufferPool::~VulkanCmdBufferPool()
- {
- // Note: Shutdown should be the only place command buffers are destroyed at, as the system relies on the fact that
- // they won't be destroyed during normal operation.
- for(auto& entry : mPools)
- {
- PoolInfo& poolInfo = entry.second;
- for (UINT32 i = 0; i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY; i++)
- {
- VulkanCmdBuffer* buffer = poolInfo.buffers[i];
- if (buffer == nullptr)
- break;
- bs_delete(buffer);
- }
- vkDestroyCommandPool(mDevice.getLogical(), poolInfo.pool, gVulkanAllocator);
- }
- }
- VulkanCmdBuffer* VulkanCmdBufferPool::getBuffer(UINT32 queueFamily, bool secondary)
- {
- auto iterFind = mPools.find(queueFamily);
- if (iterFind != mPools.end())
- return nullptr;
- VulkanCmdBuffer** buffers = iterFind->second.buffers;
- UINT32 i = 0;
- for(; i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY; i++)
- {
- if (buffers[i] == nullptr)
- break;
- if(buffers[i]->mState == VulkanCmdBuffer::State::Ready)
- {
- buffers[i]->begin();
- return buffers[i];
- }
- }
- assert(i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY &&
- "Too many command buffers allocated. Increment BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY to a higher value. ");
- buffers[i] = createBuffer(queueFamily, secondary);
- buffers[i]->begin();
- return buffers[i];
- }
- VulkanCmdBuffer* VulkanCmdBufferPool::createBuffer(UINT32 queueFamily, bool secondary)
- {
- auto iterFind = mPools.find(queueFamily);
- if (iterFind != mPools.end())
- return nullptr;
- const PoolInfo& poolInfo = iterFind->second;
- return bs_new<VulkanCmdBuffer>(mDevice, mNextId++, poolInfo.pool, poolInfo.queueFamily, secondary);
- }
- VulkanCmdBuffer::VulkanCmdBuffer(VulkanDevice& device, UINT32 id, VkCommandPool pool, UINT32 queueFamily, bool secondary)
- : mId(id), mQueueFamily(queueFamily), mState(State::Ready), mDevice(device), mPool(pool), mFenceCounter(0)
- {
- VkCommandBufferAllocateInfo cmdBufferAllocInfo;
- cmdBufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
- cmdBufferAllocInfo.pNext = nullptr;
- cmdBufferAllocInfo.commandPool = pool;
- cmdBufferAllocInfo.level = secondary ? VK_COMMAND_BUFFER_LEVEL_SECONDARY : VK_COMMAND_BUFFER_LEVEL_PRIMARY;
- cmdBufferAllocInfo.commandBufferCount = 1;
- VkResult result = vkAllocateCommandBuffers(mDevice.getLogical(), &cmdBufferAllocInfo, &mCmdBuffer);
- assert(result == VK_SUCCESS);
- VkFenceCreateInfo fenceCI;
- fenceCI.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
- fenceCI.pNext = nullptr;
- fenceCI.flags = 0;
- result = vkCreateFence(mDevice.getLogical(), &fenceCI, gVulkanAllocator, &mFence);
- assert(result == VK_SUCCESS);
- VkSemaphoreCreateInfo semaphoreCI;
- semaphoreCI.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
- semaphoreCI.pNext = nullptr;
- semaphoreCI.flags = 0;
- result = vkCreateSemaphore(mDevice.getLogical(), &semaphoreCI, gVulkanAllocator, &mSemaphore);
- assert(result == VK_SUCCESS);
- }
- VulkanCmdBuffer::~VulkanCmdBuffer()
- {
- VkDevice device = mDevice.getLogical();
- if(mState == State::Submitted)
- {
- // Wait 1s
- UINT64 waitTime = 1000 * 1000 * 1000;
- VkResult result = vkWaitForFences(device, 1, &mFence, true, waitTime);
- assert(result == VK_SUCCESS || result == VK_TIMEOUT);
- if (result == VK_TIMEOUT)
- LOGWRN("Freeing a command buffer before done executing because fence wait expired!");
- }
-
- vkDestroyFence(device, mFence, gVulkanAllocator);
- vkDestroySemaphore(device, mSemaphore, gVulkanAllocator);
- vkFreeCommandBuffers(device, mPool, 1, &mCmdBuffer);
- }
- UINT32 VulkanCmdBuffer::getDeviceIdx() const
- {
- return mDevice.getIndex();
- }
- void VulkanCmdBuffer::begin()
- {
- assert(mState == State::Ready);
- VkCommandBufferBeginInfo beginInfo;
- beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
- beginInfo.pNext = nullptr;
- beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
- beginInfo.pInheritanceInfo = nullptr;
- VkResult result = vkBeginCommandBuffer(mCmdBuffer, &beginInfo);
- assert(result == VK_SUCCESS);
- mState = State::Recording;
- }
- void VulkanCmdBuffer::end()
- {
- assert(mState == State::Recording);
- VkResult result = vkEndCommandBuffer(mCmdBuffer);
- assert(result == VK_SUCCESS);
- mState = State::RecordingDone;
- }
- void VulkanCmdBuffer::beginRenderPass()
- {
- assert(mState == State::Recording);
- // TODO
- BS_EXCEPT(NotImplementedException, "Not implemented");
- mState = State::RecordingRenderPass;
- }
- void VulkanCmdBuffer::endRenderPass()
- {
- assert(mState == State::RecordingRenderPass);
- vkCmdEndRenderPass(mCmdBuffer);
- mState = State::Recording;
- }
- void VulkanCmdBuffer::refreshFenceStatus()
- {
- VkResult result = vkGetFenceStatus(mDevice.getLogical(), mFence);
- assert(result == VK_SUCCESS || result == VK_NOT_READY);
- bool signaled = result == VK_SUCCESS;
- if (mState == State::Submitted)
- {
- if(signaled)
- {
- mState = State::Ready;
- vkResetCommandBuffer(mCmdBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); // Note: Maybe better not to release resources?
- result = vkResetFences(mDevice.getLogical(), 1, &mFence);
- assert(result == VK_SUCCESS);
- mFenceCounter++;
- for (auto& entry : mResources)
- entry.first->notifyDone(this);
- mResources.clear();
- mBoundParams.clear();
- }
- }
- else
- assert(!signaled); // We reset the fence along with mState so this shouldn't be possible
- }
- void VulkanCmdBuffer::registerResource(VulkanResource* res, VulkanUseFlags flags)
- {
- mResources[res].flags |= flags;
- }
- void VulkanCmdBuffer::registerGpuParams(const SPtr<VulkanGpuParams>& params)
- {
- mBoundParams.insert(params);
- }
- VulkanCommandBuffer::VulkanCommandBuffer(VulkanDevice& device, GpuQueueType type, UINT32 deviceIdx,
- UINT32 queueIdx, bool secondary)
- : CommandBuffer(type, deviceIdx, queueIdx, secondary), mBuffer(nullptr)
- , mDevice(device), mQueue(nullptr), mIdMask(0)
- {
- UINT32 numQueues = device.getNumQueues(mType);
- if (numQueues == 0) // Fall back to graphics queue
- {
- mType = GQT_GRAPHICS;
- numQueues = device.getNumQueues(GQT_GRAPHICS);
- }
- mQueue = device.getQueue(mType, mQueueIdx % numQueues);
- // If multiple command buffer IDs map to the same queue, mark them in the mask
- UINT32 curIdx = mQueueIdx;
- while (curIdx < BS_MAX_QUEUES_PER_TYPE)
- {
- mIdMask |= CommandSyncMask::getGlobalQueueIdx(mType, curIdx);
- curIdx += numQueues;
- }
- acquireNewBuffer();
- }
- void VulkanCommandBuffer::acquireNewBuffer()
- {
- VulkanCmdBufferPool& pool = mDevice.getCmdBufferPool();
- if (mBuffer != nullptr)
- assert(mBuffer->isSubmitted());
- UINT32 queueFamily = mDevice.getQueueFamily(mType);
- mBuffer = pool.getBuffer(queueFamily, mIsSecondary);
- }
- void VulkanCmdBuffer::submit(VulkanQueue* queue, UINT32 queueIdx, UINT32 syncMask)
- {
- // Issue pipeline barriers for queue transitions (need to happen on original queue first, then on new queue)
- for (auto& entry : mBoundParams)
- entry->prepareForSubmit(this, mTransitionInfoTemp);
- VulkanDevice& device = queue->getDevice();
- for (auto& entry : mTransitionInfoTemp)
- {
- UINT32 entryQueueFamily = entry.first;
- // No queue transition needed for entries on this queue (this entry is most likely an in image layout transition)
- if (entryQueueFamily == mQueueFamily)
- continue;
- VkCommandBuffer cmdBuffer; // TODO - Get the command buffer on entryQueueFamily
- TransitionInfo& barriers = entry.second;
- UINT32 numImgBarriers = (UINT32)barriers.imageBarriers.size();
- UINT32 numBufferBarriers = (UINT32)barriers.bufferBarriers.size();
- vkCmdPipelineBarrier(cmdBuffer,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
- 0, 0, nullptr,
- numBufferBarriers, barriers.bufferBarriers.data(),
- numImgBarriers, barriers.imageBarriers.data());
- // TODO - Submit the command buffer
- // TODO - Register the command buffer in the sync mask so we wait on it
- // If there are any layout transitions, reset them as we don't need them for the second pipeline barrier
- for (auto& barrierEntry : barriers.imageBarriers)
- barrierEntry.oldLayout = barrierEntry.newLayout;
- }
- // Issue second part of transition pipeline barriers (on this queue)
- for (auto& entry : mTransitionInfoTemp)
- {
- VkCommandBuffer cmdBuffer; // TODO - Get the command buffer on queueFamily AND this exact queue
- // - Probably best to just append it to current submitInfo as it is executed in order
- TransitionInfo& barriers = entry.second;
- UINT32 numImgBarriers = (UINT32)barriers.imageBarriers.size();
- UINT32 numBufferBarriers = (UINT32)barriers.bufferBarriers.size();
- vkCmdPipelineBarrier(cmdBuffer,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
- 0, 0, nullptr,
- numBufferBarriers, barriers.bufferBarriers.data(),
- numImgBarriers, barriers.imageBarriers.data());
- }
- UINT32 deviceIdx = device.getIndex();
- VulkanCommandBufferManager& cbm = static_cast<VulkanCommandBufferManager&>(CommandBufferManager::instance());
-
- UINT32 numSemaphores;
- cbm.getSyncSemaphores(deviceIdx, syncMask, mSemaphoresTemp, numSemaphores);
- queue->submit(this, mSemaphoresTemp, numSemaphores);
- for (auto& entry : mResources)
- entry.first->notifyUsed(this, entry.second.flags);
- cbm.refreshStates(deviceIdx);
- // Note: Uncommented for debugging only, prevents any device concurrency issues.
- // vkQueueWaitIdle(queue->getHandle());
- mState = State::Submitted;
- cbm.setActiveBuffer(queue->getType(), deviceIdx, queueIdx, this);
- // Clear vectors but don't clear the actual map, as we want to re-use the memory since we expect queue family
- // indices to be the same
- for (auto& entry : mTransitionInfoTemp)
- {
- entry.second.imageBarriers.clear();
- entry.second.bufferBarriers.clear();
- }
- }
- void VulkanCommandBuffer::submit(UINT32 syncMask)
- {
- assert(mBuffer != nullptr && mBuffer->isReadyForSubmit());
- // Ignore myself
- syncMask &= ~mIdMask;
- mBuffer->submit(mQueue, mQueueIdx, syncMask);
- acquireNewBuffer();
- }
- }
|