diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index b80af2b480..fe5f37548d 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -71,10 +71,14 @@ TextureCache
::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_MEMORY));
minimum_memory = static_cast ::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
}
}
-template ::RunAllocationGarbageCollector(size_t requested_bytes) {
- if (requested_bytes == 0) {
- return;
- }
-
- if (allocation_gc_frame != frame_tick) {
- allocation_gc_frame = frame_tick;
- allocation_gc_passes = 0;
- }
- if (allocation_gc_passes >= MAX_ALLOCATION_GC_PASSES_PER_FRAME) {
- return;
- }
-
- if (runtime.CanReportMemoryUsage()) {
- total_used_memory = runtime.GetDeviceMemoryUsage();
- }
-
- const u64 request = static_cast ::RunGarbageCollector() {
bool high_priority_mode = total_used_memory >= expected_memory;
@@ -186,19 +142,24 @@ void TextureCache ::RunGarbageCollector() {
return false;
}
- if (!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) {
+ const bool is_large_sparse = lowmemorydevice &&
+ image.info.is_sparse &&
+ image.guest_size_bytes >= 256_MiB;
+
+ if (!aggressive_mode && !is_large_sparse &&
+ True(image.flags & ImageFlagBits::CostlyLoad)) {
return false;
}
const bool must_download =
image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
- if (!high_priority_mode && must_download) {
+ if (!high_priority_mode && !is_large_sparse && must_download) {
return false;
}
--num_iterations;
- if (must_download) {
+ if (must_download && !is_large_sparse) {
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
image.DownloadMemory(map, copies);
@@ -236,6 +197,7 @@ void TextureCache ::RunGarbageCollector() {
// 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 ::RunGarbageCollector() {
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);
}
}
SortByAge(expired);
SortByAge(aggressive_expired);
- // Tier 1: normal expiration
+ // Tier 1: large sparse textures under memory pressure
+ for (const auto image_id : sparse_candidates) {
+ auto& image = slot_images[image_id];
+ if (image.allocation_tick < frame_tick - 3) {
+ if (Cleanup(image_id)) {
+ break;
+ }
+ }
+ }
+
+ // Tier 2: normal expiration
for (const auto image_id : expired) {
if (Cleanup(image_id)) {
break;
}
}
- // Tier 2: if still critical, use aggressive threshold with more iterations
+ // Tier 3: if still critical, use aggressive threshold with more iterations
if (total_used_memory >= critical_memory) {
aggressive_mode = true;
num_iterations = 40;
@@ -1245,6 +1221,9 @@ void TextureCache ::RefreshContents(Image& image, ImageId image_id) {
}
image.flags &= ~ImageFlagBits::CpuModified;
+ if( lowmemorydevice && image.info.format == PixelFormat::BC1_RGBA_UNORM && MapSizeBytes(image) >= 256_MiB ) {
+ return;
+ }
TrackImage(image, image_id);
@@ -1654,19 +1633,49 @@ bool TextureCache ::ScaleDown(Image& image) {
template ::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
RelaxedOptions options) {
- const size_t requested_size = CalculateGuestSizeInBytes(info);
std::optional ::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA
ImageInfo new_info = info;
const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
- RunAllocationGarbageCollector(size_bytes);
+ // Proactive cleanup for large sparse texture allocations
+ if (lowmemorydevice && new_info.is_sparse && size_bytes >= 256_MiB) {
+ const u64 estimated_alloc_size = size_bytes;
+
+ if (total_used_memory + estimated_alloc_size >= critical_memory) {
+ LOG_DEBUG(HW_GPU, "Large sparse texture allocation ({} MiB) - running aggressive GC. "
+ "Current memory: {} MiB, Critical: {} MiB",
+ size_bytes / (1024 * 1024),
+ total_used_memory / (1024 * 1024),
+ critical_memory / (1024 * 1024));
+ RunGarbageCollector();
+
+ // If still over threshold after GC, try one more aggressive pass
+ if (total_used_memory + estimated_alloc_size >= critical_memory) {
+ LOG_DEBUG(HW_GPU, "Still critically low on memory, running second GC pass");
+ RunGarbageCollector();
+ }
+ }
+ }
const bool broken_views = runtime.HasBrokenTextureViewFormats();
const bool native_bgr = runtime.HasNativeBgr();
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index e2c2c5d7d9..ba2af1bf44 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -120,7 +120,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches