diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index fdbe9bfdf4..297defeba0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -230,6 +230,9 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra if (freed < bytes) { freed += buffer_cache.ReclaimMemory(bytes - freed, false); } + auto& master_semaphore = scheduler.GetMasterSemaphore(); + master_semaphore.Refresh(); + vk::TickDeletionQueue(master_semaphore.KnownGpuTick()); return freed; }); } @@ -892,6 +895,9 @@ void RasterizerVulkan::FlushCommands() { void RasterizerVulkan::TickFrame() { draw_counter = 0; + auto& master_semaphore = scheduler.GetMasterSemaphore(); + master_semaphore.Refresh(); + vk::TickDeletionQueue(master_semaphore.KnownGpuTick()); guest_descriptor_queue.TickFrame(); compute_pass_descriptor_queue.TickFrame(); fence_manager.TickFrame(); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 6234b41978..6a7528e0fa 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -47,6 +47,7 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_) master_semaphore{std::make_unique(device)}, command_pool{std::make_unique(*master_semaphore, device)} { + vk::SetDeletionTimeline(master_semaphore->CurrentTick()); AcquireNewChunk(); AllocateWorkerCommandBuffer(); worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); }); @@ -322,6 +323,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se InvalidateState(); const u64 signal_value = master_semaphore->NextTick(); + vk::SetDeletionTimeline(master_semaphore->CurrentTick()); RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { static constexpr VkMemoryBarrier WRITE_BARRIER{ diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index ca38caa456..b6f1d04fa2 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -969,7 +969,7 @@ void TextureCache

::CommitAsyncFlushes() { template void TextureCache

::PopAsyncFlushes() { - FlushEvictionDownloads(); + TickEvictionDownloads(runtime.CompletedSyncPoint()); if (committed_downloads.empty()) { return; } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index a8d088a994..6de1e4ce3e 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -770,6 +770,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR Device::~Device() { ShutdownGPULogging(); + vk::FlushDeletionQueue(); vmaDestroyAllocator(allocator); } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index b162c953ef..efef01eda0 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -5,7 +5,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include +#include #include +#include #include #include #include @@ -24,6 +27,58 @@ namespace { std::thread::id allocator_owner_thread; +template +struct PendingRelease { + VmaAllocator allocator; + HandleType handle; + VmaAllocation allocation; + u64 timeline; +}; + +std::mutex deletion_mutex; +std::atomic deletion_timeline{1}; +std::vector> pending_images; +std::vector> pending_buffers; + +template +void PushPendingRelease(std::vector>& pending, VmaAllocator allocator, + HandleType handle, VmaAllocation allocation) noexcept { + std::scoped_lock lock{deletion_mutex}; + pending.push_back(PendingRelease{ + .allocator = allocator, + .handle = handle, + .allocation = allocation, + .timeline = deletion_timeline.load(std::memory_order_acquire), + }); +} + +template +void ExtractReleased(std::vector>& pending, + std::vector>& released, u64 completed_value) { + const auto split = std::partition(pending.begin(), pending.end(), + [completed_value](const PendingRelease& entry) { + return entry.timeline > completed_value; + }); + released.assign(split, pending.end()); + pending.erase(split, pending.end()); +} + +void DrainDeletionQueue(u64 completed_value) noexcept { + std::vector> images; + std::vector> buffers; + { + std::scoped_lock lock{deletion_mutex}; + ExtractReleased(pending_images, images, completed_value); + ExtractReleased(pending_buffers, buffers, completed_value); + } + for (const auto& entry : images) { + vmaDestroyImage(entry.allocator, entry.handle, entry.allocation); + } + for (const auto& entry : buffers) { + vmaDestroyBuffer(entry.allocator, entry.handle, entry.allocation); + } +} + template void SortPhysicalDevices(std::vector& devices, const InstanceDispatch& dld, Func&& func) { @@ -515,14 +570,26 @@ bool OnAllocatorOwnerThread() noexcept { allocator_owner_thread == std::this_thread::get_id(); } +void SetDeletionTimeline(u64 value) noexcept { + deletion_timeline.store(value, std::memory_order_release); +} + +void TickDeletionQueue(u64 completed_value) noexcept { + DEBUG_ASSERT(OnAllocatorOwnerThread()); + DrainDeletionQueue(completed_value); +} + +void FlushDeletionQueue() noexcept { + DrainDeletionQueue((std::numeric_limits::max)()); +} + void Image::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name); } void Image::Release() const noexcept { if (handle) { - DEBUG_ASSERT(OnAllocatorOwnerThread()); - vmaDestroyImage(allocator, handle, allocation); + PushPendingRelease(pending_images, allocator, handle, allocation); } } @@ -544,8 +611,7 @@ void Buffer::SetObjectNameEXT(const char* name) const { void Buffer::Release() const noexcept { if (handle) { - DEBUG_ASSERT(OnAllocatorOwnerThread()); - vmaDestroyBuffer(allocator, handle, allocation); + PushPendingRelease(pending_buffers, allocator, handle, allocation); } } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index a93a52f160..eabe6b0ccf 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -135,6 +135,12 @@ void SetAllocatorOwnerThread(); [[nodiscard]] bool OnAllocatorOwnerThread() noexcept; +void SetDeletionTimeline(u64 value) noexcept; + +void TickDeletionQueue(u64 completed_value) noexcept; + +void FlushDeletionQueue() noexcept; + /// Throws a Vulkan exception if result is not success. inline void Check(VkResult result) { if (result != VK_SUCCESS) {