From 60fc5bb401d27e01af005e1fec19d162401a0e3a Mon Sep 17 00:00:00 2001 From: Ribbit Date: Sun, 12 Oct 2025 01:39:07 -0700 Subject: [PATCH] [vk] Fix user reported issue --- .../renderer_vulkan/vk_texture_cache.cpp | 111 ++++++++++++++++-- .../renderer_vulkan/vk_texture_cache.h | 23 +++- 2 files changed, 125 insertions(+), 9 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 50a73ea76d..615e528e1c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -917,14 +917,29 @@ bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { } VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { - const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL); - if (buffers[level]) { - return *buffers[level]; + const size_t safe_size = needed_size == 0 ? 1 : needed_size; + size_t level = 0; + if (safe_size > 1) { + level = + (8 * sizeof(size_t)) - std::countl_zero(static_cast(safe_size - 1)); } - const auto new_size = Common::NextPow2(needed_size); + level = std::min(level, indexing_slots - 1); + + auto& entry = temporary_buffers[level]; + const size_t new_size = Common::NextPow2(safe_size); + auto& master_semaphore = scheduler.GetMasterSemaphore(); + const u64 current_tick = master_semaphore.CurrentTick(); + + if (entry.buffer && entry.size >= new_size) { + entry.last_frame_used = temp_buffer_frame_index; + entry.last_tick_used = current_tick; + return *entry.buffer; + } + static constexpr VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; + const VkBufferCreateInfo temp_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -935,8 +950,14 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, }; - buffers[level] = memory_allocator.CreateBuffer(temp_ci, MemoryUsage::DeviceLocal); - return *buffers[level]; + + entry.buffer = memory_allocator.CreateBuffer(temp_ci, MemoryUsage::DeviceLocal); + entry.size = new_size; + entry.last_frame_used = temp_buffer_frame_index; + entry.last_tick_used = current_tick; + + TrimTemporaryBuffers(); + return *entry.buffer; } void TextureCacheRuntime::BarrierFeedbackLoop() { @@ -1501,11 +1522,85 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } -void TextureCacheRuntime::TickFrame() {} +void TextureCacheRuntime::TickFrame() { + ++temp_buffer_frame_index; + + auto& master_semaphore = scheduler.GetMasterSemaphore(); + master_semaphore.Refresh(); + const u64 gpu_tick = master_semaphore.KnownGpuTick(); + + for (auto& entry : temporary_buffers) { + if (!entry.buffer) { + continue; + } + if (gpu_tick < entry.last_tick_used) { + continue; + } + const u64 age = temp_buffer_frame_index >= entry.last_frame_used + ? temp_buffer_frame_index - entry.last_frame_used + : 0; + if (age > temp_buffer_retirement_frames) { + entry.buffer = {}; + entry.size = 0; + entry.last_frame_used = 0; + entry.last_tick_used = 0; + } + } + + TrimTemporaryBuffers(); +} + +void TextureCacheRuntime::TrimTemporaryBuffers() { + auto& master_semaphore = scheduler.GetMasterSemaphore(); + master_semaphore.Refresh(); + const u64 gpu_tick = master_semaphore.KnownGpuTick(); + + u64 total_size = 0; + for (const auto& entry : temporary_buffers) { + total_size += entry.size; + } + if (total_size <= temp_buffer_budget_bytes) { + return; + } + + while (total_size > temp_buffer_budget_bytes) { + TemporaryBufferEntry* oldest = nullptr; + for (auto& entry : temporary_buffers) { + if (!entry.buffer) { + continue; + } + if (entry.last_frame_used == temp_buffer_frame_index) { + continue; + } + if (gpu_tick < entry.last_tick_used) { + continue; + } + const u64 age = temp_buffer_frame_index >= entry.last_frame_used + ? temp_buffer_frame_index - entry.last_frame_used + : 0; + if (age < temp_buffer_min_lifetime_frames) { + continue; + } + if (!oldest || entry.last_frame_used < oldest->last_frame_used) { + oldest = &entry; + } + } + + if (oldest == nullptr) { + break; + } + + total_size -= static_cast(oldest->size); + oldest->buffer = {}; + oldest->size = 0; + oldest->last_frame_used = 0; + oldest->last_tick_used = 0; + } +} Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_) - : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.scheduler}, + : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), scheduler{&runtime_.GetScheduler()}, runtime{&runtime_}, original_image(MakeImage(runtime_.device, runtime_.memory_allocator, info, runtime->ViewFormats(info.format))), aspect_mask(ImageAspectMask(info.format)) { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index cd11cc8fc7..fe3fcfcca4 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -102,6 +102,10 @@ public: return true; } + [[nodiscard]] Scheduler& GetScheduler() const noexcept { + return scheduler; + } + [[nodiscard]] VkBuffer GetTemporaryBuffer(size_t needed_size); std::span ViewFormats(PixelFormat format) { @@ -115,6 +119,10 @@ public: VkFormat GetSupportedFormat(VkFormat requested_format, VkFormatFeatureFlags required_features) const; +private: + void TrimTemporaryBuffers(); + +public: const Device& device; Scheduler& scheduler; MemoryAllocator& memory_allocator; @@ -126,8 +134,21 @@ public: const Settings::ResolutionScalingInfo& resolution; std::array, VideoCore::Surface::MaxPixelFormat> view_formats; +private: + struct TemporaryBufferEntry { + vk::Buffer buffer{}; + size_t size{}; + u64 last_frame_used{}; + u64 last_tick_used{}; + }; + static constexpr size_t indexing_slots = 8 * sizeof(size_t); - std::array buffers{}; + static constexpr u64 temp_buffer_retirement_frames = 120; + static constexpr u64 temp_buffer_budget_bytes = 256ULL * 1024ULL * 1024ULL; + static constexpr u64 temp_buffer_min_lifetime_frames = 2; + + std::array temporary_buffers{}; + u64 temp_buffer_frame_index{}; }; class Image : public VideoCommon::ImageBase {