texture_cache: Add sampler garbage collection (#4700)

This commit is contained in:
squidbus
2026-07-13 11:42:40 -07:00
committed by GitHub
parent ec33490460
commit 373cbb3745
4 changed files with 85 additions and 4 deletions

View File

@@ -400,6 +400,17 @@ public:
return properties.limits.maxFramebufferHeight;
}
/// Returns the maximum number of samplers that can be allocated at once.
u32 GetMaxSamplerAllocationCount() const {
if (driver_id == vk::DriverId::eMesaKosmickrisp) {
// FIXME: KosmicKrisp has an internal 1024 unique sampler limit before
// vkCreateSampler starts returning VK_ERROR_OUT_OF_HOST_MEMORY. Work
// around this for now by reducing the value to 1024.
return 1024;
}
return properties.limits.maxSamplerAllocationCount;
}
/// Returns the sample count flags supported by color buffers.
vk::SampleCountFlags GetColorSampleCounts() const {
return properties.limits.framebufferColorSampleCounts;

View File

@@ -29,6 +29,8 @@ public:
return *handle;
}
size_t lru_id{};
private:
vk::UniqueSampler handle;
};

View File

@@ -33,6 +33,11 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler&
const auto null_id = GetNullImage(vk::Format::eR8G8B8A8Unorm);
ASSERT(null_id.index == NULL_IMAGE_ID.index);
u32 max_samplers = instance.GetMaxSamplerAllocationCount();
trigger_gc_samplers = max_samplers * 3 / 4;
pressure_gc_samplers = max_samplers * 7 / 8;
critical_gc_samplers = max_samplers * 15 / 16;
// Set up garbage collection parameters.
if (!instance.CanReportMemoryUsage()) {
trigger_gc_memory = 0;
@@ -820,7 +825,15 @@ void TextureCache::RefreshImage(Image& image) {
vk::Sampler TextureCache::GetSampler(const AmdGpu::Sampler& sampler,
AmdGpu::BorderColorBuffer border_color_base) {
const u64 hash = XXH3_64bits(&sampler, sizeof(sampler));
std::scoped_lock lock{samplers_mutex};
const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler, border_color_base);
if (new_sampler) {
samplers.at(hash).lru_id = sampler_lru_cache.Insert(hash, gc_tick);
} else {
sampler_lru_cache.Touch(it->second.lru_id, gc_tick);
}
return it->second.Handle();
}
@@ -966,10 +979,7 @@ void TextureCache::UntrackImageTail(ImageId image_id) {
tracker.UpdatePageWatchers<false>(addr, size);
}
void TextureCache::RunGarbageCollector() {
SCOPE_EXIT {
++gc_tick;
};
void TextureCache::GarbageCollectImages() {
if (instance.CanReportMemoryUsage()) {
total_used_memory = instance.GetDeviceMemoryUsage();
}
@@ -1033,6 +1043,55 @@ void TextureCache::RunGarbageCollector() {
}
}
void TextureCache::GarbageCollectSamplers() {
total_used_samplers = samplers.size();
if (total_used_samplers < trigger_gc_samplers) {
return;
}
std::scoped_lock lock{samplers_mutex};
bool pressured = false;
bool aggresive = false;
u64 ticks_to_destroy = 0;
size_t num_deletions = 0;
const auto configure = [&](bool allow_aggressive) {
pressured = total_used_samplers >= pressure_gc_samplers;
aggresive = allow_aggressive && total_used_samplers >= critical_gc_samplers;
ticks_to_destroy = aggresive ? 160 : pressured ? 80 : 16;
ticks_to_destroy = std::min(ticks_to_destroy, gc_tick);
num_deletions = aggresive ? 40 : pressured ? 20 : 10;
};
const auto clean_up = [&](u64 hash) {
if (num_deletions == 0) {
return true;
}
--num_deletions;
const size_t lru_id = samplers.at(hash).lru_id;
samplers.erase(hash);
sampler_lru_cache.Free(lru_id);
return false;
};
// Try to remove anything old enough and not high priority.
configure(false);
sampler_lru_cache.ForEachItemBelow(gc_tick - ticks_to_destroy, clean_up);
if (total_used_samplers >= critical_gc_samplers) {
// If we are still over the critical limit, run an aggressive GC
configure(true);
sampler_lru_cache.ForEachItemBelow(gc_tick - ticks_to_destroy, clean_up);
}
}
void TextureCache::RunGarbageCollector() {
SCOPE_EXIT {
++gc_tick;
};
GarbageCollectImages();
GarbageCollectSamplers();
}
void TextureCache::TouchImage(const Image& image) {
lru_cache.Touch(image.lru_id, gc_tick);
}

View File

@@ -310,6 +310,9 @@ private:
DeleteImage(image_id);
}
void GarbageCollectImages();
void GarbageCollectSamplers();
private:
const Vulkan::Instance& instance;
Vulkan::Scheduler& scheduler;
@@ -327,11 +330,17 @@ private:
u64 trigger_gc_memory = 0;
u64 pressure_gc_memory = 0;
u64 critical_gc_memory = 0;
u64 total_used_samplers = 0;
u64 trigger_gc_samplers = 0;
u64 pressure_gc_samplers = 0;
u64 critical_gc_samplers = 0;
u64 gc_tick = 0;
Common::LeastRecentlyUsedCache<ImageId, u64> lru_cache;
Common::LeastRecentlyUsedCache<u64, u64> sampler_lru_cache;
bool readback_linear_images;
PageTable page_table;
std::mutex mutex;
std::mutex samplers_mutex;
struct MetaDataInfo {
enum class Type {
CMask,