From f9beffe3dc2aef83c12ad700a549d9858a18110f Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 25 Sep 2025 13:39:25 -0700 Subject: [PATCH] vk: add pool for VkSemaphore for re-use and lifetime tracking (#9254) - Add a VulkanSemaphore ref-counted class to track the references of a semamphore - i.e. in a command buffer or in a present. - Add a VulkanSemaphoreManager class to keep a pool of VkSempahores for better re-use. This fixes a validation error where we were re-using a semaphore that is associated with a command buffer while its being used in a present (as a wait signal). Error ris VUID-vkQueueSubmit-pSignalSemaphores-00067 --- filament/backend/CMakeLists.txt | 4 ++ .../backend/src/vulkan/VulkanCommands.cpp | 52 ++++++++------ filament/backend/src/vulkan/VulkanCommands.h | 27 +++---- filament/backend/src/vulkan/VulkanDriver.cpp | 6 +- filament/backend/src/vulkan/VulkanDriver.h | 2 + .../backend/src/vulkan/VulkanSemaphore.cpp | 29 ++++++++ filament/backend/src/vulkan/VulkanSemaphore.h | 42 +++++++++++ .../src/vulkan/VulkanSemaphoreManager.cpp | 70 +++++++++++++++++++ .../src/vulkan/VulkanSemaphoreManager.h | 51 ++++++++++++++ .../backend/src/vulkan/VulkanSwapChain.cpp | 20 +++++- filament/backend/src/vulkan/VulkanSwapChain.h | 1 + .../backend/src/vulkan/memory/Resource.cpp | 6 ++ filament/backend/src/vulkan/memory/Resource.h | 3 +- .../src/vulkan/memory/ResourceManager.cpp | 3 + 14 files changed, 276 insertions(+), 40 deletions(-) create mode 100644 filament/backend/src/vulkan/VulkanSemaphore.cpp create mode 100644 filament/backend/src/vulkan/VulkanSemaphore.h create mode 100644 filament/backend/src/vulkan/VulkanSemaphoreManager.cpp create mode 100644 filament/backend/src/vulkan/VulkanSemaphoreManager.h diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index f6c499d387..977739d3eb 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -217,6 +217,10 @@ if (FILAMENT_SUPPORTS_VULKAN) src/vulkan/VulkanReadPixels.h src/vulkan/VulkanSamplerCache.cpp src/vulkan/VulkanSamplerCache.h + src/vulkan/VulkanSemaphore.cpp + src/vulkan/VulkanSemaphore.h + src/vulkan/VulkanSemaphoreManager.cpp + src/vulkan/VulkanSemaphoreManager.h src/vulkan/VulkanStagePool.cpp src/vulkan/VulkanStagePool.h src/vulkan/VulkanSwapChain.cpp diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index b01baed8a2..3fd41fb282 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -90,18 +90,18 @@ bool VulkanGroupMarkers::empty() const noexcept { uint32_t VulkanCommandBuffer::sAgeCounter = 0; VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice device, - VkQueue queue, VkCommandPool pool, bool isProtected) + VkQueue queue, VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager, + bool isProtected) : mContext(context), mMarkerCount(0), isProtected(isProtected), mDevice(device), mQueue(queue), + mSemaphoreManager(semaphoreManager), mBuffer(createCommandBuffer(device, pool)), + mSubmission(semaphoreManager->acquire()), mFenceStatus(std::make_shared(VK_INCOMPLETE)), mAge(++sAgeCounter) { - VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; - vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission); - VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; VkExportFenceCreateInfo exportFenceCreateInfo{ .sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO, @@ -116,7 +116,6 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice } VulkanCommandBuffer::~VulkanCommandBuffer() { - vkDestroySemaphore(mDevice, mSubmission, VKALLOC); vkDestroyFence(mDevice, mFence, VKALLOC); } @@ -126,6 +125,7 @@ void VulkanCommandBuffer::reset() noexcept { mWaitSemaphores.clear(); mWaitSemaphoreStages.clear(); mAge = ++sAgeCounter; + mSubmission = mSemaphoreManager->acquire(); // Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence // gets, gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually @@ -190,13 +190,14 @@ void VulkanCommandBuffer::begin() noexcept { vkBeginCommandBuffer(mBuffer, &binfo); } -VkSemaphore VulkanCommandBuffer::submit() { +fvkmemory::resource_ptr VulkanCommandBuffer::submit() { while (mMarkerCount > 0) { popMarker(); } vkEndCommandBuffer(mBuffer); + VkSemaphore submissionSemaphore = mSubmission->getVkSemaphore(); VkSubmitInfo submitInfo{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = mWaitSemaphores.size(), @@ -205,7 +206,7 @@ VkSemaphore VulkanCommandBuffer::submit() { .commandBufferCount = 1u, .pCommandBuffers = &mBuffer, .signalSemaphoreCount = 1u, - .pSignalSemaphores = &mSubmission, + .pSignalSemaphores = &submissionSemaphore, }; // add submit protection if needed VkProtectedSubmitInfo protectedSubmitInfo{ @@ -226,7 +227,7 @@ VkSemaphore VulkanCommandBuffer::submit() { << (s < mWaitSemaphores.size() - 1 ? "\n" : ""); } FVK_LOGI << ") " - << " signal=" << mSubmission + << " signal=" << submissionSemaphore << " fence=" << mFence; #endif @@ -245,7 +246,7 @@ VkSemaphore VulkanCommandBuffer::submit() { } CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice device, VkQueue queue, - uint8_t queueFamilyIndex, bool isProtected) + uint8_t queueFamilyIndex, VulkanSemaphoreManager* semaphoreManager, bool isProtected) : mDevice(device), mRecording(INVALID) { VkCommandPoolCreateInfo createInfo = { @@ -258,8 +259,8 @@ CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice devi vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool); for (size_t i = 0; i < CAPACITY; ++i) { - mBuffers.emplace_back( - std::make_unique(context, device, queue, mPool, isProtected)); + mBuffers.emplace_back(std::make_unique( + context, device, queue, mPool, semaphoreManager, isProtected)); } } @@ -331,10 +332,10 @@ void CommandBufferPool::update() { }); } -VkSemaphore CommandBufferPool::flush() { +fvkmemory::resource_ptr CommandBufferPool::flush() { // We're not recording right now. if (!isRecording()) { - return VK_NULL_HANDLE; + return {}; } auto submitSemaphore = mBuffers[mRecording]->submit(); mSubmitted.set(mRecording, true); @@ -396,16 +397,21 @@ void CommandBufferPool::insertEvent(char const* marker) { #endif // FVK_DEBUG_GROUP_MARKERS VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex, - VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext const& context) + VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext const& context, + VulkanSemaphoreManager* semaphoreManager) : mDevice(device), mProtectedQueue(protectedQueue), mProtectedQueueFamilyIndex(protectedQueueFamilyIndex), mContext(context), - mPool(std::make_unique(context, device, queue, queueFamilyIndex, false)) {} + mSemaphoreManager(semaphoreManager), + mPool(std::make_unique( + context, device, queue, queueFamilyIndex, semaphoreManager, false)) {} void VulkanCommands::terminate() { mPool.reset(); mProtectedPool.reset(); + mLastSubmit = {}; + mLastFenceStatus = {}; } VulkanCommandBuffer& VulkanCommands::get() { @@ -418,7 +424,7 @@ VulkanCommandBuffer& VulkanCommands::getProtected() { if (!mProtectedPool) { mProtectedPool = std::make_unique(mContext, mDevice, mProtectedQueue, - mProtectedQueueFamilyIndex, true); + mProtectedQueueFamilyIndex, mSemaphoreManager, true); } auto& ret = mProtectedPool->getRecording(); return ret; @@ -430,8 +436,8 @@ bool VulkanCommands::flush() { return false; } - VkSemaphore dependency = mInjectedDependency; - VkSemaphore lastSubmit = mLastSubmit; + VkSemaphore injectedDependency = mInjectedDependency; + fvkmemory::resource_ptr dependency; bool hasFlushed = false; VkFence flushedFence = VK_NULL_HANDLE; @@ -444,19 +450,19 @@ bool VulkanCommands::flush() { if (!pool || !pool->isRecording()) { continue; } - if (dependency != VK_NULL_HANDLE) { - pool->waitFor(dependency, mInjectedDependencyWaitStage); + if (injectedDependency != VK_NULL_HANDLE) { + pool->waitFor(injectedDependency, mInjectedDependencyWaitStage); } - if (lastSubmit != VK_NULL_HANDLE) { + if (mLastSubmit) { // Note that the stage we're waiting on is the fragment shader stage. This assumes // that the subsequent command buffer will only depend on // 1) fragment output of the previous command buffer // 2) reading/writing of buffers (i.e. UBO) of the previous command buffer // Restricting the wait stages will allow for vertex work to proceed (more overlapping // vertex/fragment work). - pool->waitFor(lastSubmit, + pool->waitFor(mLastSubmit->getVkSemaphore(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT); - lastSubmit = VK_NULL_HANDLE; + mLastSubmit = {}; } flushedFence = pool->getRecording().getVkFence(); flushedFenceStatus = pool->getRecording().getFenceStatus(); diff --git a/filament/backend/src/vulkan/VulkanCommands.h b/filament/backend/src/vulkan/VulkanCommands.h index acfa81f3d5..d24f2e7154 100644 --- a/filament/backend/src/vulkan/VulkanCommands.h +++ b/filament/backend/src/vulkan/VulkanCommands.h @@ -24,6 +24,7 @@ #include "VulkanAsyncHandles.h" #include "VulkanConstants.h" #include "VulkanContext.h" +#include "VulkanSemaphoreManager.h" #include "vulkan/memory/ResourcePointer.h" #include "vulkan/utils/StaticVector.h" @@ -63,8 +64,8 @@ private: // DriverApi fence object and should not be destroyed until both the DriverApi object is freed and // we're done waiting on the most recent submission of the given command buffer. struct VulkanCommandBuffer { - VulkanCommandBuffer(VulkanContext const& mContext, - VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected); + VulkanCommandBuffer(VulkanContext const& mContext, VkDevice device, VkQueue queue, + VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager, bool isProtected); VulkanCommandBuffer(VulkanCommandBuffer const&) = delete; VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete; @@ -87,7 +88,7 @@ struct VulkanCommandBuffer { void insertEvent(char const* marker) noexcept; void begin() noexcept; - VkSemaphore submit(); + fvkmemory::resource_ptr submit(); inline void setComplete() { mFenceStatus->setStatus(VK_SUCCESS); @@ -121,10 +122,11 @@ private: bool const isProtected; VkDevice mDevice; VkQueue mQueue; + VulkanSemaphoreManager* mSemaphoreManager; fvkutils::StaticVector mWaitSemaphores; fvkutils::StaticVector mWaitSemaphoreStages; VkCommandBuffer mBuffer; - VkSemaphore mSubmission; + fvkmemory::resource_ptr mSubmission; VkFence mFence; std::shared_ptr mFenceStatus; std::vector> mResources; @@ -136,14 +138,14 @@ struct CommandBufferPool { static constexpr int8_t INVALID = -1; CommandBufferPool(VulkanContext const& context, VkDevice device, VkQueue queue, - uint8_t queueFamilyIndex, bool isProtected); + uint8_t queueFamilyIndex, VulkanSemaphoreManager* semaphoreManager, bool isProtected); ~CommandBufferPool(); VulkanCommandBuffer& getRecording(); void gc(); void update(); - VkSemaphore flush(); + fvkmemory::resource_ptr flush(); void wait(); void waitFor(VkSemaphore previousAction, VkPipelineStageFlags waitStage); @@ -204,7 +206,7 @@ class VulkanCommands { public: VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex, VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, - VulkanContext const& context); + VulkanContext const& context, VulkanSemaphoreManager* semaphoreManager); void terminate(); @@ -222,10 +224,10 @@ public: // Returns the "rendering finished" semaphore for the most recent flush and removes // it from the existing dependency chain. This is especially useful for setting up // vkQueuePresentKHR. - VkSemaphore acquireFinishedSignal() { - VkSemaphore ret = mLastSubmit; - mLastSubmit = VK_NULL_HANDLE; - return ret; + fvkmemory::resource_ptr acquireFinishedSignal() { + fvkmemory::resource_ptr sem = mLastSubmit; + mLastSubmit = {}; + return sem; } VkFence getMostRecentFence() { @@ -266,12 +268,13 @@ private: // For defered initialization if/when we need protected content uint32_t const mProtectedQueueFamilyIndex; VulkanContext const& mContext; + VulkanSemaphoreManager* mSemaphoreManager; std::unique_ptr mPool; std::unique_ptr mProtectedPool; VkSemaphore mInjectedDependency = VK_NULL_HANDLE; - VkSemaphore mLastSubmit = VK_NULL_HANDLE; + fvkmemory::resource_ptr mLastSubmit; VkFence mLastFence = VK_NULL_HANDLE; std::shared_ptr mLastFenceStatus; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index e702c9f31e..b5dd36069a 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -222,9 +222,10 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context, mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(), mPlatform->getDevice())), mContext(context), + mSemaphoreManager(mPlatform->getDevice(), &mResourceManager), mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(), - mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext), + mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager), mPipelineLayoutCache(mPlatform->getDevice()), mPipelineCache(mPlatform->getDevice()), mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()), @@ -366,6 +367,9 @@ void VulkanDriver::terminate() { // reclaimed, as they perform cleanup within the stage pool. mStagePool.terminate(); + // By this point, all of the VkSemaphores should have been returned to the pool. + mSemaphoreManager.terminate(); + #if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) mResourceManager.print(); #endif diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index 0141401c49..adcde65c0b 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -28,6 +28,7 @@ #include "VulkanQueryManager.h" #include "VulkanReadPixels.h" #include "VulkanSamplerCache.h" +#include "VulkanSemaphoreManager.h" #include "VulkanStagePool.h" #include "VulkanYcbcrConversionCache.h" #include "vulkan/VulkanDescriptorSetCache.h" @@ -141,6 +142,7 @@ private: VulkanContext& mContext; + VulkanSemaphoreManager mSemaphoreManager; VulkanCommands mCommands; VulkanPipelineLayoutCache mPipelineLayoutCache; VulkanPipelineCache mPipelineCache; diff --git a/filament/backend/src/vulkan/VulkanSemaphore.cpp b/filament/backend/src/vulkan/VulkanSemaphore.cpp new file mode 100644 index 0000000000..1fcdd244fb --- /dev/null +++ b/filament/backend/src/vulkan/VulkanSemaphore.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vulkan/VulkanSemaphore.h" +#include "vulkan/VulkanSemaphoreManager.h" + +namespace filament::backend { + +VulkanSemaphore::VulkanSemaphore(VulkanSemaphoreManager* manager, VkSemaphore semaphore) + : mManager(manager), mSemaphore(semaphore) {} + +VulkanSemaphore::~VulkanSemaphore() { + mManager->recycle(mSemaphore); +} + +} // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanSemaphore.h b/filament/backend/src/vulkan/VulkanSemaphore.h new file mode 100644 index 0000000000..86d884926a --- /dev/null +++ b/filament/backend/src/vulkan/VulkanSemaphore.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHORE_H +#define TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHORE_H + +#include "memory/Resource.h" + +#include + +namespace filament::backend { + +class VulkanSemaphoreManager; + +struct VulkanSemaphore : public fvkmemory::Resource { +public: + VulkanSemaphore(VulkanSemaphoreManager* manager, VkSemaphore semaphore); + ~VulkanSemaphore(); + + VkSemaphore getVkSemaphore() const { return mSemaphore; } + +private: + VulkanSemaphoreManager* mManager; + VkSemaphore mSemaphore; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHORE_H diff --git a/filament/backend/src/vulkan/VulkanSemaphoreManager.cpp b/filament/backend/src/vulkan/VulkanSemaphoreManager.cpp new file mode 100644 index 0000000000..e15bb31119 --- /dev/null +++ b/filament/backend/src/vulkan/VulkanSemaphoreManager.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VulkanSemaphoreManager.h" + +#include "VulkanConstants.h" + +using namespace bluevk; + +namespace { +constexpr size_t INITIAL_POOL_SIZE = FVK_MAX_COMMAND_BUFFERS; + +VkSemaphore createSemaphore(VkDevice device) { + VkSemaphore semaphore; + VkSemaphoreCreateInfo semaphoreInfo = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + vkCreateSemaphore(device, &semaphoreInfo, VKALLOC, &semaphore); + return semaphore; +} + +} // namespace + +namespace filament::backend { + +VulkanSemaphoreManager::VulkanSemaphoreManager(VkDevice device, + fvkmemory::ResourceManager* resourceManager) + : mDevice(device), + mResourceManager(resourceManager) { + for (size_t i= 0; i < INITIAL_POOL_SIZE; ++i) { + mPool.push_back(createSemaphore(mDevice)); + } +} + +void VulkanSemaphoreManager::terminate() { + for (VkSemaphore semaphore : mPool) { + vkDestroySemaphore(mDevice, semaphore, VKALLOC); + } + mPool.clear(); +} + +VulkanSemaphoreManager::Semaphore VulkanSemaphoreManager::acquire() { + VkSemaphore semaphore; + if (!mPool.empty()) { + semaphore = mPool.back(); + mPool.pop_back(); + } else { + semaphore = createSemaphore(mDevice); + } + return Semaphore::construct(mResourceManager, this, semaphore); +} + +void VulkanSemaphoreManager::recycle(VkSemaphore semaphore) { + mPool.push_back(semaphore); +} + +} // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanSemaphoreManager.h b/filament/backend/src/vulkan/VulkanSemaphoreManager.h new file mode 100644 index 0000000000..1184f01df7 --- /dev/null +++ b/filament/backend/src/vulkan/VulkanSemaphoreManager.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHOREMANAGER_H +#define TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHOREMANAGER_H + +#include "vulkan/VulkanSemaphore.h" +#include "vulkan/memory/ResourceManager.h" +#include "vulkan/memory/ResourcePointer.h" + +#include + +#include + +namespace filament::backend { + +class VulkanSemaphoreManager { +public: + using Semaphore = fvkmemory::resource_ptr; + + VulkanSemaphoreManager(VkDevice device, fvkmemory::ResourceManager* resourceManager); + ~VulkanSemaphoreManager() = default; + + void terminate(); + Semaphore acquire(); + +private: + friend struct VulkanSemaphore; + void recycle(VkSemaphore semaphore); + + VkDevice mDevice; + fvkmemory::ResourceManager* mResourceManager; + std::vector mPool; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHOREMANAGER_H diff --git a/filament/backend/src/vulkan/VulkanSwapChain.cpp b/filament/backend/src/vulkan/VulkanSwapChain.cpp index 0d098c157c..3fa4cf6ea7 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.cpp +++ b/filament/backend/src/vulkan/VulkanSwapChain.cpp @@ -59,7 +59,10 @@ VulkanSwapChain::~VulkanSwapChain() { mColors = {}; mDepth = {}; - + for (auto& semaphore : mFinishedDrawing) { + semaphore = {}; + } + mFinishedDrawing.clear(); mPlatform->destroy(swapChain); } @@ -67,9 +70,17 @@ void VulkanSwapChain::update() { mColors.clear(); auto const bundle = mPlatform->getSwapChainBundle(swapChain); + size_t const swapChainCount = bundle.colors.size(); mColors.reserve(bundle.colors.size()); VkDevice const device = mPlatform->getDevice(); + mFinishedDrawing.clear(); + mFinishedDrawing.reserve(swapChainCount); + mFinishedDrawing.resize(swapChainCount); + for (size_t i = 0; i < swapChainCount; ++i) { + mFinishedDrawing[i] = {}; + } + TextureUsage depthUsage = TextureUsage::DEPTH_ATTACHMENT; TextureUsage colorUsage = TextureUsage::COLOR_ATTACHMENT; if (bundle.isProtected) { @@ -110,8 +121,10 @@ void VulkanSwapChain::present(DriverBase& driver) { // We only present if it is not headless. No-op for headless. if (!mHeadless) { - VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal(); - VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing); + auto finishedDrawing = mCommands->acquireFinishedSignal(); + mFinishedDrawing[mCurrentSwapIndex] = finishedDrawing; + VkResult const result = + mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing->getVkSemaphore()); FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) << "Cannot present in swapchain. error=" << static_cast(result); @@ -149,6 +162,7 @@ void VulkanSwapChain::acquire(bool& resized) { VulkanPlatform::ImageSyncData imageSyncData; VkResult const result = mPlatform->acquire(swapChain, &imageSyncData); mCurrentSwapIndex = imageSyncData.imageIndex; + mFinishedDrawing[mCurrentSwapIndex] = {}; FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) << "Cannot acquire in swapchain. error=" << static_cast(result); if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) { diff --git a/filament/backend/src/vulkan/VulkanSwapChain.h b/filament/backend/src/vulkan/VulkanSwapChain.h index dc7352e2dc..8a081a607a 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.h +++ b/filament/backend/src/vulkan/VulkanSwapChain.h @@ -116,6 +116,7 @@ private: // We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout // transitions, which are useful here. utils::FixedCapacityVector> mColors; + utils::FixedCapacityVector> mFinishedDrawing; fvkmemory::resource_ptr mDepth; VkExtent2D mExtent; uint32_t mLayerCount; diff --git a/filament/backend/src/vulkan/memory/Resource.cpp b/filament/backend/src/vulkan/memory/Resource.cpp index 271a19738c..72a2dbdc32 100644 --- a/filament/backend/src/vulkan/memory/Resource.cpp +++ b/filament/backend/src/vulkan/memory/Resource.cpp @@ -40,6 +40,7 @@ template ResourceType getTypeEnum() noexcept; template ResourceType getTypeEnum() noexcept; template ResourceType getTypeEnum() noexcept; template ResourceType getTypeEnum() noexcept; +template ResourceType getTypeEnum() noexcept; template ResourceType getTypeEnum() noexcept { @@ -100,6 +101,9 @@ ResourceType getTypeEnum() noexcept { if constexpr (std::is_same_v) { return ResourceType::MEMORY_MAPPED_BUFFER; } + if constexpr (std::is_same_v) { + return ResourceType::SEMAPHORE; + } return ResourceType::UNDEFINED_TYPE; } @@ -143,6 +147,8 @@ std::string_view getTypeStr(ResourceType type) { return "Sync"; case ResourceType::MEMORY_MAPPED_BUFFER: return "VulkanMemoryMappedBuffer"; + case ResourceType::SEMAPHORE: + return "Semaphore"; case ResourceType::UNDEFINED_TYPE: return ""; } diff --git a/filament/backend/src/vulkan/memory/Resource.h b/filament/backend/src/vulkan/memory/Resource.h index e25f12c05b..20d0302260 100644 --- a/filament/backend/src/vulkan/memory/Resource.h +++ b/filament/backend/src/vulkan/memory/Resource.h @@ -54,7 +54,8 @@ enum class ResourceType : uint8_t { STAGE_IMAGE = 16, SYNC = 17, MEMORY_MAPPED_BUFFER = 18, - UNDEFINED_TYPE = 19, // Must be the last enum because we use it for iterating over the enums. + SEMAPHORE = 19, + UNDEFINED_TYPE = 20, // Must be the last enum because we use it for iterating over the enums. }; template diff --git a/filament/backend/src/vulkan/memory/ResourceManager.cpp b/filament/backend/src/vulkan/memory/ResourceManager.cpp index aa3a8a97db..96f586dcef 100644 --- a/filament/backend/src/vulkan/memory/ResourceManager.cpp +++ b/filament/backend/src/vulkan/memory/ResourceManager.cpp @@ -120,6 +120,9 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) { case ResourceType::MEMORY_MAPPED_BUFFER: destruct(Handle(id)); break; + case ResourceType::SEMAPHORE: + destruct(Handle(id)); + break; case ResourceType::UNDEFINED_TYPE: break; }