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