// // Copyright 2023 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // SecondaryCommandPool: // A class for allocating Command Buffers for VulkanSecondaryCommandBuffer. // #include "libANGLE/renderer/vulkan/SecondaryCommandPool.h" #include "common/debug.h" #include "libANGLE/renderer/vulkan/vk_renderer.h" #include "libANGLE/renderer/vulkan/vk_utils.h" namespace rx { namespace vk { SecondaryCommandPool::SecondaryCommandPool() : mCollectedBuffers(kFixedQueueLimit) {} SecondaryCommandPool::~SecondaryCommandPool() { ASSERT(mCollectedBuffers.empty()); ASSERT(mCollectedBuffersOverflow.empty()); } angle::Result SecondaryCommandPool::init(Context *context, uint32_t queueFamilyIndex, ProtectionType protectionType) { VkCommandPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; poolInfo.queueFamilyIndex = queueFamilyIndex; if (context->getFeatures().useResetCommandBufferBitForSecondaryPools.enabled) { poolInfo.flags |= VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; } ASSERT(protectionType == ProtectionType::Unprotected || protectionType == ProtectionType::Protected); if (protectionType == ProtectionType::Protected) { poolInfo.flags |= VK_COMMAND_POOL_CREATE_PROTECTED_BIT; } ANGLE_VK_TRY(context, mCommandPool.init(context->getDevice(), poolInfo)); return angle::Result::Continue; } void SecondaryCommandPool::destroy(VkDevice device) { // Command buffers will be destroyed with the Pool. Avoid possible slowdown during cleanup. mCollectedBuffers.clear(); mCollectedBuffersOverflow.clear(); mCommandPool.destroy(device); } angle::Result SecondaryCommandPool::allocate(Context *context, VulkanSecondaryCommandBuffer *buffer) { ASSERT(valid()); ASSERT(!buffer->valid()); VkDevice device = context->getDevice(); freeCollectedBuffers(device); VkCommandBufferAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY; allocInfo.commandBufferCount = 1; allocInfo.commandPool = mCommandPool.getHandle(); ANGLE_VK_TRY(context, buffer->init(device, allocInfo)); return angle::Result::Continue; } void SecondaryCommandPool::collect(VulkanSecondaryCommandBuffer *buffer) { ASSERT(valid()); ASSERT(buffer->valid()); VkCommandBuffer bufferHandle = buffer->releaseHandle(); if (!mCollectedBuffers.full()) { mCollectedBuffers.push(bufferHandle); } else { std::lock_guard lock(mOverflowMutex); mCollectedBuffersOverflow.emplace_back(bufferHandle); mHasOverflow.store(true, std::memory_order_relaxed); } } void SecondaryCommandPool::freeCollectedBuffers(VkDevice device) { // Free Command Buffer for now. May later add recycling or reset/free pool at once. ANGLE_TRACE_EVENT0("gpu.angle", "SecondaryCommandPool::freeCollectedBuffers"); while (!mCollectedBuffers.empty()) { VkCommandBuffer bufferHandle = mCollectedBuffers.front(); mCommandPool.freeCommandBuffers(device, 1, &bufferHandle); mCollectedBuffers.pop(); } if (ANGLE_UNLIKELY(mHasOverflow.load(std::memory_order_relaxed))) { std::vector buffers; { std::lock_guard lock(mOverflowMutex); buffers = std::move(mCollectedBuffersOverflow); mHasOverflow.store(false, std::memory_order_relaxed); } for (VkCommandBuffer bufferHandle : buffers) { mCommandPool.freeCommandBuffers(device, 1, &bufferHandle); } } } } // namespace vk } // namespace rx