[texture_cache] Reduce garbage collection logic by simplifying conditions and thresholds

This commit is contained in:
CamilleLaVey 2026-04-03 12:52:22 -04:00 committed by crueter
parent 29e77b5e2d
commit 90fd089e58

View file

@ -118,17 +118,10 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
template <class P> template <class P>
void TextureCache<P>::RunGarbageCollector() { void TextureCache<P>::RunGarbageCollector() {
bool high_priority_mode = false; bool high_priority_mode = total_used_memory >= expected_memory;
bool aggressive_mode = false; bool aggressive_mode = false;
u64 ticks_to_destroy = 0; u64 ticks_to_destroy = high_priority_mode ? 25ULL : 50ULL;
size_t num_iterations = 0; size_t num_iterations = high_priority_mode ? 20 : 10;
const auto Configure = [&](bool allow_aggressive) {
high_priority_mode = total_used_memory >= expected_memory;
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
};
const auto Cleanup = [this, &num_iterations, &high_priority_mode, const auto Cleanup = [this, &num_iterations, &high_priority_mode,
&aggressive_mode](ImageId image_id) { &aggressive_mode](ImageId image_id) {
@ -145,12 +138,9 @@ void TextureCache<P>::RunGarbageCollector() {
} }
if (True(image.flags & ImageFlagBits::IsDecoding)) { if (True(image.flags & ImageFlagBits::IsDecoding)) {
// This image is still being decoded, deleting it will invalidate the slot
// used by the async decoder thread.
return false; return false;
} }
// Prioritize large sparse textures for cleanup
const bool is_large_sparse = lowmemorydevice && const bool is_large_sparse = lowmemorydevice &&
image.info.is_sparse && image.info.is_sparse &&
image.guest_size_bytes >= 256_MiB; image.guest_size_bytes >= 256_MiB;
@ -183,7 +173,6 @@ void TextureCache<P>::RunGarbageCollector() {
if (total_used_memory < critical_memory) { if (total_used_memory < critical_memory) {
if (aggressive_mode) { if (aggressive_mode) {
// Sink the aggresiveness.
num_iterations >>= 2; num_iterations >>= 2;
aggressive_mode = false; aggressive_mode = false;
return false; return false;
@ -196,59 +185,57 @@ void TextureCache<P>::RunGarbageCollector() {
return false; return false;
}; };
const auto CollectBelow = [this](u64 threshold) { // Single pass: collect all candidates, classified by tier
const u64 normal_threshold = frame_tick > ticks_to_destroy ? frame_tick - ticks_to_destroy : 0;
const u64 aggressive_threshold = frame_tick > 10 ? frame_tick - 10 : 0;
boost::container::small_vector<ImageId, 64> sparse_candidates;
boost::container::small_vector<ImageId, 64> expired; boost::container::small_vector<ImageId, 64> expired;
boost::container::small_vector<ImageId, 64> aggressive_expired;
for (auto [id, image] : slot_images) { for (auto [id, image] : slot_images) {
if (True(image->flags & ImageFlagBits::Registered) && if (False(image->flags & ImageFlagBits::Registered)) {
image->last_use_tick < threshold) { continue;
}
const u64 tick = image->last_use_tick;
if (tick < normal_threshold) {
expired.push_back(id); expired.push_back(id);
} else if (tick < aggressive_threshold) {
aggressive_expired.push_back(id);
} else if (high_priority_mode && tick < frame_tick &&
lowmemorydevice && image->info.is_sparse &&
image->guest_size_bytes >= 256_MiB) {
sparse_candidates.push_back(id);
} }
} }
return expired;
};
// Aggressively clear massive sparse textures // Tier 1: large sparse textures under memory pressure
if (total_used_memory >= expected_memory) { for (const auto image_id : sparse_candidates) {
auto candidates = CollectBelow(frame_tick);
for (const auto image_id : candidates) {
auto& image = slot_images[image_id]; auto& image = slot_images[image_id];
if (lowmemorydevice && if (image.allocation_tick < frame_tick - 3) {
image.info.is_sparse &&
image.guest_size_bytes >= 256_MiB &&
image.allocation_tick < frame_tick - 3) {
LOG_DEBUG(HW_GPU, "GC targeting old sparse texture at 0x{:X} ({} MiB, age: {} frames)",
image.gpu_addr, image.guest_size_bytes / (1024 * 1024),
frame_tick - image.allocation_tick);
if (Cleanup(image_id)) { if (Cleanup(image_id)) {
break; break;
} }
} }
} }
}
Configure(false); // Tier 2: normal expiration
if (frame_tick > ticks_to_destroy) {
auto expired = CollectBelow(frame_tick - ticks_to_destroy);
for (const auto image_id : expired) { for (const auto image_id : expired) {
if (Cleanup(image_id)) { if (Cleanup(image_id)) {
break; break;
} }
} }
}
// If pressure is still too high, prune aggressively. // Tier 3: if still critical, use aggressive threshold with more iterations
if (total_used_memory >= critical_memory) { if (total_used_memory >= critical_memory) {
Configure(true); aggressive_mode = true;
if (frame_tick > ticks_to_destroy) { num_iterations = 40;
auto expired = CollectBelow(frame_tick - ticks_to_destroy); for (const auto image_id : aggressive_expired) {
for (const auto image_id : expired) {
if (Cleanup(image_id)) { if (Cleanup(image_id)) {
break; break;
} }
} }
} }
} }
}
template <class P> template <class P>
void TextureCache<P>::TickFrame() { void TextureCache<P>::TickFrame() {