diff --git a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp index d61bd0a13..138f2b8d1 100644 --- a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp @@ -51,7 +51,7 @@ struct BufferCache::DownloadCopy { }; void BufferCache::Register(BufferId id) { - const auto& buffer = m_slot_buffers[id]; + auto& buffer = m_slot_buffers[id]; PageTable::PageRange pages {}; EXIT_IF(!PageTable::TryGetPageRange(buffer.CpuAddress(), buffer.Size(), pages)); for (size_t page = pages.first; page < pages.last_exclusive; ++page) { @@ -60,6 +60,7 @@ void BufferCache::Register(BufferId id) { const auto [it, inserted] = m_buffers.emplace(buffer.CpuAddress(), id); (void)it; EXIT_IF(!inserted); + buffer.lru_id = m_lru_cache.Insert(id, m_gc_tick); m_total_used_memory += buffer.Size(); } @@ -83,9 +84,16 @@ void BufferCache::Unregister(BufferId id) { EXIT("BufferCache: allocation accounting underflow\n"); } m_total_used_memory -= buffer.Size(); + m_lru_cache.Free(buffer.lru_id); buffer.is_deleted = true; } +void BufferCache::TouchBuffer(const Buffer& buffer) { + if (!buffer.is_deleted) { + m_lru_cache.Touch(buffer.lru_id, m_gc_tick); + } +} + void BufferCache::DeleteBuffer(BufferId id) { auto* buffer = m_slot_buffers.try_get(id); if (buffer == nullptr || buffer->is_deleted) { @@ -364,7 +372,7 @@ BufferId BufferCache::FindBuffer(uint64_t vaddr, uint64_t size) { if (owner != nullptr && *owner) { auto& buffer = m_slot_buffers[*owner]; if (buffer.IsInBounds(vaddr, size)) { - buffer.tick_accessed_last = m_gc_tick; + TouchBuffer(buffer); return *owner; } } @@ -393,8 +401,7 @@ BufferId BufferCache::CreateBuffer(uint64_t vaddr, uint64_t size) { const auto id = m_slot_buffers.insert(m_graphics, m_scheduler, MemoryUsage::DeviceLocal, begin, AllFlags, end - begin); - auto& buffer = m_slot_buffers[id]; - buffer.tick_accessed_last = m_gc_tick; + auto& buffer = m_slot_buffers[id]; SetVulkanObjectNameF(m_graphics.device, buffer.Handle(), "Kyty.GameBuffer[guest=0x{:016x} size=0x{:x}]", begin, end - begin); for (auto overlap = first; overlap != last;) { @@ -509,6 +516,7 @@ std::pair BufferCache::ObtainBuffer(uint64_t vaddr, uint64_t id = FindBuffer(vaddr, size); buffer = &m_slot_buffers[id]; } + TouchBuffer(*buffer); (void)SynchronizeBuffer(*buffer, vaddr, size, is_written, is_texel_buffer); if (is_written) { m_gpu_modified_ranges.Add(vaddr, size); @@ -545,7 +553,7 @@ std::pair BufferCache::ObtainBufferForImage(uint64_t vaddr, u owner = &m_slot_buffers[FindBuffer(vaddr, size)]; } if (owner != nullptr && !cpu_modified && (!gpu_modified || has_dirty_buffer_source)) { - owner->tick_accessed_last = m_gc_tick; + TouchBuffer(*owner); return {owner, owner->Offset(vaddr)}; } if (has_dirty_buffer_source && owner == nullptr) { @@ -570,7 +578,7 @@ std::pair BufferCache::ObtainBufferForImage(uint64_t vaddr, u return {&m_staging_buffer, stage_offset}; } - owner->tick_accessed_last = m_gc_tick; + TouchBuffer(*owner); std::vector> uploads; m_memory_tracker.ForEachUploadRange( vaddr, size, false, @@ -603,7 +611,7 @@ void BufferCache::WriteHostMemory(uint64_t vaddr, std::span data) continue; } WriteDataBuffer(buffer, begin, data.data() + begin - vaddr, range_end - begin); - buffer.tick_accessed_last = m_gc_tick; + TouchBuffer(buffer); } } @@ -743,30 +751,17 @@ void BufferCache::RunGarbageCollector() { const uint64_t age = std::min(aggressive ? 80 : 160, tick); const size_t limit = aggressive ? 64 : 32; - std::vector candidates; - for (const auto& [address, id]: m_buffers) { - const auto& buffer = m_slot_buffers[id]; - if (tick - std::min(tick, buffer.tick_accessed_last) >= age) { - candidates.push_back(address); - } - } - std::ranges::sort(candidates, [&](uint64_t left, uint64_t right) { - return m_slot_buffers[m_buffers.at(left)].tick_accessed_last < - m_slot_buffers[m_buffers.at(right)].tick_accessed_last; - }); std::vector dirty_buffers; std::vector copies; size_t retire_count = 0; - for (const auto address: candidates) { - auto found = m_buffers.find(address); - EXIT_IF(found == m_buffers.end()); - const auto id = found->second; - auto& buffer = m_slot_buffers[id]; + m_lru_cache.ForEachItemBelow(tick - age, [&](BufferId id) { + auto& buffer = m_slot_buffers[id]; + EXIT_IF(buffer.is_deleted); m_memory_tracker.ValidateGpuDirtyOwnership(m_gpu_modified_ranges, buffer.CpuAddress(), buffer.Size(), "garbage collection"); const bool dirty = m_memory_tracker.IsRegionGpuModified(buffer.CpuAddress(), buffer.Size()); if (dirty && !aggressive) { - continue; + return false; } if (dirty) { m_memory_tracker.ForEachDownloadRange( @@ -787,10 +782,8 @@ void BufferCache::RunGarbageCollector() { m_memory_tracker.UntrackMemory(buffer.CpuAddress(), buffer.Size()); DeleteBuffer(id); } - if (++retire_count == limit) { - break; - } - } + return ++retire_count == limit; + }); if (dirty_buffers.empty()) { return; } diff --git a/src/graphics/host_gpu/renderer/cache/bufferCache.h b/src/graphics/host_gpu/renderer/cache/bufferCache.h index 5a3d11716..9c88a06cb 100644 --- a/src/graphics/host_gpu/renderer/cache/bufferCache.h +++ b/src/graphics/host_gpu/renderer/cache/bufferCache.h @@ -3,6 +3,7 @@ #include "common/abi.h" #include "common/common.h" +#include "common/lruCache.h" #include "common/slotVector.h" #include "graphics/host_gpu/memoryTracker.h" #include "graphics/host_gpu/rangeSet.h" @@ -74,6 +75,7 @@ private: } [[nodiscard]] static std::pair DownloadEnvelope(const DownloadCopy& copy); void WriteDataBuffer(Buffer& buffer, uint64_t address, const void* source, uint64_t size); + void TouchBuffer(const Buffer& buffer); [[nodiscard]] BufferId CreateBuffer(uint64_t vaddr, uint64_t size); void Register(BufferId id); void Unregister(BufferId id); @@ -87,23 +89,24 @@ private: void WriteHostMemory(uint64_t vaddr, std::span data); void ReadMemoryOnGpu(uint64_t vaddr, uint64_t size, bool is_write); - GraphicContext& m_graphics; - CommandScheduler& m_scheduler; - Buffer m_gds_buffer; - Common::SlotVector m_slot_buffers; - std::map m_buffers; - PageTable m_page_table; - RangeSet m_gpu_modified_ranges; - MemoryTracker m_memory_tracker; - StreamBuffer m_staging_buffer; - StreamBuffer m_stream_buffer; - StreamBuffer m_download_buffer; - StreamBuffer m_device_buffer; - TextureCache& m_texture_cache; - uint64_t m_total_used_memory = 0; - uint64_t m_trigger_gc_memory = 1ull * 1024 * 1024 * 1024; - uint64_t m_critical_gc_memory = 2ull * 1024 * 1024 * 1024; - uint64_t m_gc_tick = 0; + GraphicContext& m_graphics; + CommandScheduler& m_scheduler; + Buffer m_gds_buffer; + Common::SlotVector m_slot_buffers; + Common::LeastRecentlyUsedCache m_lru_cache; + std::map m_buffers; + PageTable m_page_table; + RangeSet m_gpu_modified_ranges; + MemoryTracker m_memory_tracker; + StreamBuffer m_staging_buffer; + StreamBuffer m_stream_buffer; + StreamBuffer m_download_buffer; + StreamBuffer m_device_buffer; + TextureCache& m_texture_cache; + uint64_t m_total_used_memory = 0; + uint64_t m_trigger_gc_memory = 1ull * 1024 * 1024 * 1024; + uint64_t m_critical_gc_memory = 2ull * 1024 * 1024 * 1024; + uint64_t m_gc_tick = 0; }; } // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/cache/streamBuffer.h b/src/graphics/host_gpu/renderer/cache/streamBuffer.h index 9b1cf4159..f8072c200 100644 --- a/src/graphics/host_gpu/renderer/cache/streamBuffer.h +++ b/src/graphics/host_gpu/renderer/cache/streamBuffer.h @@ -66,8 +66,8 @@ public: void Fill(uint64_t offset, uint64_t size, uint32_t value); // BufferCache state lives directly on the resource. - bool is_deleted = false; - uint64_t tick_accessed_last = 0; + bool is_deleted = false; + size_t lru_id = 0; protected: [[nodiscard]] GraphicContext& Graphics() const noexcept { return *m_graphics; }