aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp90
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h9
2 files changed, 73 insertions, 26 deletions
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 4b8697734c..34905743db 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -2115,10 +2115,33 @@ void TextureCacheRuntime::ReleaseMsaaScratchImage(VkImage image) {
}
void TextureCacheRuntime::TickFrame() {
+ static constexpr u32 MAX_UNUSED_SCRATCH_FRAMES = 60;
+ // Finished decode images wait for the next texture of their shape, up to this much memory.
+ static constexpr u64 MaxFreeTranscodeBytes = u64{256} << 20;
+ const auto decode_bytes = [](const PendingTranscode& t) {
+ u64 bytes{};
+ for (u32 level = 0; level < t.levels; ++level) {
+ bytes += u64{(std::max)(t.width >> level, 1U)} * (std::max)(t.height >> level, 1U) * 4;
+ }
+ return bytes * t.layers;
+ };
+ u64 free_bytes{};
+ for (const PendingTranscode& t : free_transcodes) {
+ free_bytes += decode_bytes(t);
+ }
while (!pending_transcodes.empty() && scheduler.IsFree(pending_transcodes.front().tick)) {
+ PendingTranscode& done = pending_transcodes.front();
+ const u64 bytes = decode_bytes(done);
+ if (free_bytes + bytes <= MaxFreeTranscodeBytes) {
+ done.unused_frames = 0;
+ free_bytes += bytes;
+ free_transcodes.push_back(std::move(done));
+ }
pending_transcodes.pop_front();
}
- static constexpr u32 MAX_UNUSED_SCRATCH_FRAMES = 60;
+ std::erase_if(free_transcodes, [](PendingTranscode& t) {
+ return ++t.unused_frames > MAX_UNUSED_SCRATCH_FRAMES;
+ });
std::erase_if(msaa_scratch_images, [this](MsaaScratchImage& scratch) {
if (!scheduler.IsFree(scratch.tick)) {
scratch.unused_frames = 0;
@@ -4095,30 +4118,47 @@ void TextureCacheRuntime::TranscodeAstcToBcn(
const u32 levels = static_cast<u32>(info.resources.levels);
const u32 layers = static_cast<u32>(info.resources.layers);
constexpr VkFormat DecodeFormat = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
- PendingTranscode pending{
- .tick = 0,
- .image = memory_allocator.CreateImage(VkImageCreateInfo{
- .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- .imageType = VK_IMAGE_TYPE_2D,
- .format = DecodeFormat,
- .extent = {width, height, 1},
- .mipLevels = levels,
- .arrayLayers = layers,
- .samples = VK_SAMPLE_COUNT_1_BIT,
- .tiling = VK_IMAGE_TILING_OPTIMAL,
- .usage = VK_IMAGE_USAGE_STORAGE_BIT,
- .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
- .queueFamilyIndexCount = 0,
- .pQueueFamilyIndices = nullptr,
- .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
- }, true),
- .views = {},
- };
- for (u32 level = 0; level < levels; ++level) {
- pending.views.push_back(
- MakeStorageView(device.GetLogical(), level, *pending.image, DecodeFormat));
+ // A decode image of the same shape the GPU is done with: title 1's title screen decodes
+ // dozens of textures a frame, mostly of a few shapes. Making one took 263 us; from VMA's
+ // shared blocks 90 us, but freeing them there left ~1 GB of those blocks unused.
+ const auto reusable = std::ranges::find_if(free_transcodes, [&](const PendingTranscode& t) {
+ return t.width == width && t.height == height && t.levels == levels && t.layers == layers;
+ });
+ PendingTranscode pending;
+ if (reusable != free_transcodes.end()) {
+ pending = std::move(*reusable);
+ free_transcodes.erase(reusable);
+ } else {
+ pending = PendingTranscode{
+ .tick = 0,
+ .image = memory_allocator.CreateImage(VkImageCreateInfo{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .imageType = VK_IMAGE_TYPE_2D,
+ .format = DecodeFormat,
+ .extent = {width, height, 1},
+ .mipLevels = levels,
+ .arrayLayers = layers,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = VK_IMAGE_USAGE_STORAGE_BIT,
+ .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .queueFamilyIndexCount = 0,
+ .pQueueFamilyIndices = nullptr,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ }, true),
+ .views = {},
+ .width = width,
+ .height = height,
+ .levels = levels,
+ .layers = layers,
+ .unused_frames = 0,
+ };
+ for (u32 level = 0; level < levels; ++level) {
+ pending.views.push_back(
+ MakeStorageView(device.GetLogical(), level, *pending.image, DecodeFormat));
+ }
}
astc_decoder_pass->AssembleInto(
info, 0, image.guest_size_bytes,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index ee7f520053..efdb705e93 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -182,13 +182,20 @@ public:
void TranscodeAstcToBcn(Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles);
- /// Temporary decode images, freed once the GPU is past `tick`.
+ /// Temporary decode images, reusable once the GPU is past `tick`.
struct PendingTranscode {
u64 tick;
vk::Image image;
std::vector<vk::ImageView> views;
+ u32 width;
+ u32 height;
+ u32 levels;
+ u32 layers;
+ u32 unused_frames;
};
std::deque<PendingTranscode> pending_transcodes;
+ /// Decode images the GPU is done with, for the next texture of the same shape.
+ std::vector<PendingTranscode> free_transcodes;
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
const Settings::ResolutionScalingInfo& resolution;