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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<VulkanCmdFence>(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<VulkanSemaphore> 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<VulkanCommandBuffer>(context, device, queue, mPool, isProtected));
|
||||
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(
|
||||
context, device, queue, mPool, semaphoreManager, isProtected));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,10 +332,10 @@ void CommandBufferPool::update() {
|
||||
});
|
||||
}
|
||||
|
||||
VkSemaphore CommandBufferPool::flush() {
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> 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<CommandBufferPool>(context, device, queue, queueFamilyIndex, false)) {}
|
||||
mSemaphoreManager(semaphoreManager),
|
||||
mPool(std::make_unique<CommandBufferPool>(
|
||||
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<CommandBufferPool>(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<VulkanSemaphore> 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();
|
||||
|
||||
@@ -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<VulkanSemaphore> submit();
|
||||
|
||||
inline void setComplete() {
|
||||
mFenceStatus->setStatus(VK_SUCCESS);
|
||||
@@ -121,10 +122,11 @@ private:
|
||||
bool const isProtected;
|
||||
VkDevice mDevice;
|
||||
VkQueue mQueue;
|
||||
VulkanSemaphoreManager* mSemaphoreManager;
|
||||
fvkutils::StaticVector<VkSemaphore, 2> mWaitSemaphores;
|
||||
fvkutils::StaticVector<VkPipelineStageFlags, 2> mWaitSemaphoreStages;
|
||||
VkCommandBuffer mBuffer;
|
||||
VkSemaphore mSubmission;
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> mSubmission;
|
||||
VkFence mFence;
|
||||
std::shared_ptr<VulkanCmdFence> mFenceStatus;
|
||||
std::vector<fvkmemory::resource_ptr<Resource>> 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<VulkanSemaphore> 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<VulkanSemaphore> acquireFinishedSignal() {
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> 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<CommandBufferPool> mPool;
|
||||
std::unique_ptr<CommandBufferPool> mProtectedPool;
|
||||
|
||||
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
|
||||
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
|
||||
fvkmemory::resource_ptr<VulkanSemaphore> mLastSubmit;
|
||||
|
||||
VkFence mLastFence = VK_NULL_HANDLE;
|
||||
std::shared_ptr<VulkanCmdFence> mLastFenceStatus;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
29
filament/backend/src/vulkan/VulkanSemaphore.cpp
Normal file
29
filament/backend/src/vulkan/VulkanSemaphore.cpp
Normal file
@@ -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
|
||||
42
filament/backend/src/vulkan/VulkanSemaphore.h
Normal file
42
filament/backend/src/vulkan/VulkanSemaphore.h
Normal file
@@ -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 <bluevk/BlueVK.h>
|
||||
|
||||
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
|
||||
70
filament/backend/src/vulkan/VulkanSemaphoreManager.cpp
Normal file
70
filament/backend/src/vulkan/VulkanSemaphoreManager.cpp
Normal file
@@ -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
|
||||
51
filament/backend/src/vulkan/VulkanSemaphoreManager.h
Normal file
51
filament/backend/src/vulkan/VulkanSemaphoreManager.h
Normal file
@@ -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 <bluevk/BlueVK.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanSemaphoreManager {
|
||||
public:
|
||||
using Semaphore = fvkmemory::resource_ptr<VulkanSemaphore>;
|
||||
|
||||
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<VkSemaphore> mPool;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHOREMANAGER_H
|
||||
@@ -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<int32_t>(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<int32_t>(result);
|
||||
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
|
||||
|
||||
@@ -116,6 +116,7 @@ private:
|
||||
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
|
||||
// transitions, which are useful here.
|
||||
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanTexture>> mColors;
|
||||
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanSemaphore>> mFinishedDrawing;
|
||||
fvkmemory::resource_ptr<VulkanTexture> mDepth;
|
||||
VkExtent2D mExtent;
|
||||
uint32_t mLayerCount;
|
||||
|
||||
@@ -40,6 +40,7 @@ template ResourceType getTypeEnum<VulkanFence>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanBuffer>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanSync>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
|
||||
|
||||
template<typename D>
|
||||
ResourceType getTypeEnum() noexcept {
|
||||
@@ -100,6 +101,9 @@ ResourceType getTypeEnum() noexcept {
|
||||
if constexpr (std::is_same_v<D, VulkanMemoryMappedBuffer>) {
|
||||
return ResourceType::MEMORY_MAPPED_BUFFER;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanSemaphore>) {
|
||||
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 "";
|
||||
}
|
||||
|
||||
@@ -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<typename D>
|
||||
|
||||
@@ -120,6 +120,9 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
|
||||
case ResourceType::MEMORY_MAPPED_BUFFER:
|
||||
destruct<VulkanMemoryMappedBuffer>(Handle<VulkanMemoryMappedBuffer>(id));
|
||||
break;
|
||||
case ResourceType::SEMAPHORE:
|
||||
destruct<VulkanSemaphore>(Handle<VulkanSemaphore>(id));
|
||||
break;
|
||||
case ResourceType::UNDEFINED_TYPE:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user