From 8200e397ef275c51c72eba5ecc50d2ca5ceb3308 Mon Sep 17 00:00:00 2001 From: Ribbit Date: Sun, 5 Oct 2025 20:07:11 -0700 Subject: [PATCH] [vk] Descriptor pools: per-set free + RAII release; add ResourcePool::Drain --- .../renderer_vulkan/present/util.cpp | 2 +- .../renderer_vulkan/vk_command_pool.cpp | 4 +- .../renderer_vulkan/vk_descriptor_pool.cpp | 6 ++- .../renderer_vulkan/vk_descriptor_pool.h | 2 +- .../renderer_vulkan/vk_resource_pool.cpp | 37 +++++++++++++++++++ .../renderer_vulkan/vk_resource_pool.h | 1 + src/video_core/vulkan_common/vulkan_wrapper.h | 35 +++++++++++++++++- 7 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 0b1a89eec0..dbdb06ebe5 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -312,7 +312,7 @@ vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_ return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .pNext = nullptr, - .flags = 0, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, .maxSets = static_cast(max_sets), .poolSizeCount = static_cast(pool_sizes.size()), .pPoolSizes = pool_sizes.data(), diff --git a/src/video_core/renderer_vulkan/vk_command_pool.cpp b/src/video_core/renderer_vulkan/vk_command_pool.cpp index d0dbf7ca54..fa92e0e095 100644 --- a/src/video_core/renderer_vulkan/vk_command_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_command_pool.cpp @@ -19,7 +19,9 @@ struct CommandPool::Pool { CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const Device& device_) : ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {} -CommandPool::~CommandPool() = default; +CommandPool::~CommandPool() { + Drain(); +} void CommandPool::Allocate(size_t begin, size_t end) { // Command buffers are going to be committed, recorded, executed every single usage cycle. diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index 3af9758a31..f627595eec 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -80,7 +80,7 @@ static void AllocatePool(const Device& device, DescriptorBank& bank) { bank.pools.push_back(device.GetLogical().CreateDescriptorPool({ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .pNext = nullptr, - .flags = 0, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, .maxSets = sets_per_pool, .poolSizeCount = static_cast(pool_cursor), .pPoolSizes = std::data(pool_sizes), @@ -92,6 +92,10 @@ DescriptorAllocator::DescriptorAllocator(const Device& device_, MasterSemaphore& : ResourcePool(master_semaphore_, SETS_GROW_RATE), device{&device_}, bank{&bank_}, layout{layout_} {} +DescriptorAllocator::~DescriptorAllocator() { + Drain(); +} + VkDescriptorSet DescriptorAllocator::Commit() { const size_t index = CommitResource(); return sets[index / SETS_GROW_RATE][index % SETS_GROW_RATE]; diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h index 4aada5a006..4c140890af 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h @@ -35,7 +35,7 @@ class DescriptorAllocator final : public ResourcePool { public: explicit DescriptorAllocator() = default; - ~DescriptorAllocator() override = default; + ~DescriptorAllocator() override; DescriptorAllocator& operator=(DescriptorAllocator&&) noexcept = default; DescriptorAllocator(DescriptorAllocator&&) noexcept = default; diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.cpp b/src/video_core/renderer_vulkan/vk_resource_pool.cpp index 6572f82ba9..34f01ecf7f 100644 --- a/src/video_core/renderer_vulkan/vk_resource_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_resource_pool.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "video_core/renderer_vulkan/vk_master_semaphore.h" @@ -42,6 +43,42 @@ size_t ResourcePool::CommitResource() { return *found; } +void ResourcePool::Drain() { + if (!master_semaphore || ticks.empty()) { + return; + } + + master_semaphore->Refresh(); + const auto highest_iter = std::max_element(ticks.begin(), ticks.end()); + if (highest_iter == ticks.end()) { + return; + } + + const u64 highest_tick = *highest_iter; + if (highest_tick == 0) { + return; + } + + const u64 current_tick = master_semaphore->CurrentTick(); + const u64 last_submitted_tick = current_tick > 0 ? current_tick - 1 : 0; + u64 wait_tick = 0; + if (last_submitted_tick != 0) { + wait_tick = std::min(highest_tick, last_submitted_tick); + } + + if (wait_tick != 0 && !master_semaphore->IsFree(wait_tick)) { + // Clamp to the last submitted tick so we never wait for a value that was + // never enqueued on the GPU timeline (CurrentTick() is always one ahead + // of the most recent submission). + master_semaphore->Wait(wait_tick); + } + + master_semaphore->Refresh(); + const u64 completed_tick = master_semaphore->KnownGpuTick(); + std::fill(ticks.begin(), ticks.end(), completed_tick); + hint_iterator = 0; +} + size_t ResourcePool::ManageOverflow() { const size_t old_capacity = ticks.size(); Grow(); diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.h b/src/video_core/renderer_vulkan/vk_resource_pool.h index a39a3b8815..fdee5a4676 100644 --- a/src/video_core/renderer_vulkan/vk_resource_pool.h +++ b/src/video_core/renderer_vulkan/vk_resource_pool.h @@ -30,6 +30,7 @@ public: protected: size_t CommitResource(); + void Drain(); /// Called when a chunk of resources have to be allocated. virtual void Allocate(size_t begin, size_t end) = 0; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 39396b3279..a857ae0a81 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -15,6 +15,7 @@ #include #include +#include "common/assert.h" #include "common/common_types.h" #include "video_core/vulkan_common/vulkan.h" @@ -552,6 +553,10 @@ public: /// Construct an empty allocation. PoolAllocations() = default; + ~PoolAllocations() noexcept { + Release(); + } + /// Construct an allocation. Errors are reported through IsOutOfPoolMemory(). explicit PoolAllocations(std::unique_ptr allocations_, std::size_t num_, VkDevice device_, PoolType pool_, const DeviceDispatch& dld_) noexcept @@ -565,18 +570,38 @@ public: /// Construct an allocation transferring ownership from another allocation. PoolAllocations(PoolAllocations&& rhs) noexcept : allocations{std::move(rhs.allocations)}, num{rhs.num}, device{rhs.device}, pool{rhs.pool}, - dld{rhs.dld} {} + dld{rhs.dld} { + rhs.Reset(); + } /// Assign an allocation transferring ownership from another allocation. PoolAllocations& operator=(PoolAllocations&& rhs) noexcept { + if (this == &rhs) [[unlikely]] { + return *this; + } + Release(); allocations = std::move(rhs.allocations); num = rhs.num; device = rhs.device; pool = rhs.pool; dld = rhs.dld; + rhs.Reset(); return *this; } + /// Releases the underlying allocations back to their pool if owned. + void Release() noexcept { + if (!allocations || num == 0 || !device || !pool || !dld) { + Reset(); + return; + } + + const Span span{allocations.get(), num}; + [[maybe_unused]] const VkResult result = Free(device, pool, span, *dld); + DEBUG_ASSERT(result == VK_SUCCESS); + Reset(); + } + /// Returns the number of allocations. std::size_t size() const noexcept { return num; @@ -604,6 +629,14 @@ private: VkDevice device = nullptr; PoolType pool = nullptr; const DeviceDispatch* dld = nullptr; + + void Reset() noexcept { + allocations.reset(); + num = 0; + device = nullptr; + pool = nullptr; + dld = nullptr; + } }; using DebugUtilsMessenger = Handle;