aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/texture_cache/texture_cache.h
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 17:08:43 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 17:08:43 -0400
commit55d315c644fdf36405daaf71f75942553a267a6e (patch)
tree5c68632583f18db89e15352ee825a8dc73a57e2a /src/video_core/texture_cache/texture_cache.h
parent104381f025772909bb0032d58c47aeeab17a6ef6 (diff)
ASTC: re-compress to BC3 except huge arrays, which stay RGBA8 and trimmed
Title 1 has ~700 ASTC 8x8 textures (~280 MB as stored); decoded to RGBA8 they took ~4.5 GB, pushing Eden past the 8 GB of an RTX 4070 Laptop (8.4-9.8 GB in use) and the texture cache into permanent aggressive collection. With astc_recompression=Bc3 (CPU, asynchronous) they fit (4.6-5.6 GB). The two 8192x8192x68 terrain arrays would still not fit as BC3, so ASTC 2D arrays over 1 GiB as RGBA8 always use the GPU decoder with trimmed levels (RGBA8 host image, UNORM/sRGB views), and the decoder pass now exists whenever the GPU lacks native ASTC. Also logs every 600 frames: device memory against the collector thresholds, the largest images, totals by format, and buffer cache totals. Title screen 65-150 s: 10.1 -> 16.0 fps.
Diffstat (limited to 'src/video_core/texture_cache/texture_cache.h')
-rw-r--r--src/video_core/texture_cache/texture_cache.h40
1 files changed, 40 insertions, 0 deletions
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index e1cb493697..11de37eb6b 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -6,7 +6,10 @@
#pragma once
+#include <algorithm>
#include <limits>
+#include <map>
+#include <vector>
#include <optional>
#include <bit>
#include "common/container/unordered_map.h"
@@ -173,6 +176,43 @@ void TextureCache<P>::TickFrame() {
if (runtime.CanReportMemoryUsage()) {
total_used_memory = runtime.GetDeviceMemoryUsage();
}
+ // Every 600 frames: device memory in use against the collector's thresholds, and what the
+ // images themselves account for (VRAM pressure makes the collector thrash).
+ if (static u64 frames{}; ++frames % 600 == 0) {
+ u64 image_bytes{};
+ size_t images{};
+ std::vector<std::pair<u64, const Image*>> largest;
+ std::map<u32, std::pair<u64, u32>> by_format;
+ for (const auto [image_id, image] : slot_images) {
+ const u64 bytes = GetScaledImageSizeBytes(*image);
+ image_bytes += bytes;
+ ++images;
+ largest.emplace_back(bytes, image);
+ auto& format = by_format[static_cast<u32>(image->info.format)];
+ format.first += bytes;
+ ++format.second;
+ }
+ LOG_INFO(HW_GPU, "Texture cache: device memory {} MiB (expected {} MiB, critical {} MiB), {} images {} MiB",
+ total_used_memory >> 20, expected_memory >> 20, critical_memory >> 20, images,
+ image_bytes >> 20);
+ std::partial_sort(largest.begin(), largest.begin() + std::min<size_t>(12, largest.size()),
+ largest.end(), [](const auto& a, const auto& b) { return a.first > b.first; });
+ for (size_t i = 0; i < std::min<size_t>(12, largest.size()); ++i) {
+ const Image& image = *largest[i].second;
+ LOG_INFO(HW_GPU, " {} MiB: {:#x} {}x{}x{} layers {} levels {} format {} type {} flags {:#x}",
+ largest[i].first >> 20, image.gpu_addr, image.info.size.width, image.info.size.height,
+ image.info.size.depth, image.info.resources.layers, image.info.resources.levels,
+ static_cast<u32>(image.info.format), static_cast<u32>(image.info.type),
+ static_cast<u32>(image.flags));
+ }
+ std::vector<std::pair<u64, u32>> formats;
+ for (const auto& [format, total] : by_format) formats.emplace_back(total.first, format);
+ std::sort(formats.rbegin(), formats.rend());
+ for (size_t i = 0; i < std::min<size_t>(8, formats.size()); ++i) {
+ LOG_INFO(HW_GPU, " format {}: {} MiB in {} images", formats[i].second, formats[i].first >> 20,
+ by_format[formats[i].second].second);
+ }
+ }
if (total_used_memory > minimum_memory) {
RunGarbageCollector();
}