diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 13:26:31 -0500 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 13:26:31 -0500 |
| commit | 745fab0f0a6902de4808daf86d0628904e123791 (patch) | |
| tree | 3e6dce202ada53ef3ec1aa3b66068f16c410f275 /src | |
| parent | 0d68ea8eba3f3f6a3eb9098c4e586f78997de7e2 (diff) | |
| parent | 6d4c595634f85376e0600f67162347426e74944d (diff) | |
Merge remote-tracking branch 'origin/fix/vk-transcode-reuse-decode-images' into test/linux-all-prs
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_texture_cache.cpp | 90 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_texture_cache.h | 9 |
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; |
