From b45f4c6d2c79cbb60eeda4d3ad887f9964a297e3 Mon Sep 17 00:00:00 2001 From: Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> Date: Mon, 2 Nov 2026 11:44:14 -0500 Subject: vk_pipeline_cache: keep the pipelines on MoltenVK and preload eight sessions 5de94bdd1d cut the boot preload to the last two sessions and 2f94fb9680 evicts pipelines unused for 9000 frames, both because rebuilding one costs 2-3 ms through the driver's pipeline cache and each holds ~0.45 MB of driver memory. On MoltenVK a pipeline built while playing is translated from SPIR-V to MSL and compiled by Metal, which takes far longer even with a VkPipelineCache and shows as a stutter. On MoltenVK, keep the previous eight sessions' pipelines in vulkan_used.bin (be76402ab5's window) so they are built at boot, and do not evict. The memory is bounded by what a title builds; the stutter is not. Other drivers are unchanged and EDEN_NX2_PRELOAD_ALL_PIPELINES=1 still builds every cached pipeline. Assisted-by: Claude Sonnet 5.5 (implementation and commit message) --- .../renderer_vulkan/vk_pipeline_cache.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index ff8e84b019..4df940bf71 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -600,9 +600,17 @@ constexpr u32 UsedPipelinesMagic = 0x31505545; // "EUP1" // Pipelines the last two sessions used are built at boot. Each holds ~0.45 MB of driver memory: // title 1 built 4,800 from eight sessions, 1.5 GB more than building them on first use (but that // stalled frames 4x as often), while one session of its title screen uses ~1,500. +// MoltenVK keeps the previous eight sessions: a pipeline built while playing goes through +// SPIR-V -> MSL -> Metal, far more than the 2-3 ms the shorter window assumes, so each one +// built at boot is a stutter avoided. constexpr u8 UsedPipelinesMaxAge = 2; +constexpr u8 UsedPipelinesMaxAgeMoltenVK = 8; -std::unordered_map LoadUsedPipelines(const std::filesystem::path& path) { +u8 MaxUsedPipelineAge(const Device& device) { + return device.IsMoltenVK() ? UsedPipelinesMaxAgeMoltenVK : UsedPipelinesMaxAge; +} + +std::unordered_map LoadUsedPipelines(const std::filesystem::path& path, u8 max_age) { std::unordered_map used; std::ifstream file{path, std::ios::binary}; u32 magic{}, count{}; @@ -618,7 +626,7 @@ std::unordered_map LoadUsedPipelines(const std::filesystem::path& path) !file.read(reinterpret_cast(&age), sizeof(age))) { break; } - if (age < UsedPipelinesMaxAge) { // lists saved with a longer window + if (age < max_age) { // lists saved with a longer window used.emplace(hash, age); } } @@ -669,7 +677,7 @@ void PipelineCache::SaveUsedPipelines() { next.emplace(hash, u8{0}); } for (const auto& [hash, age] : used_pipelines) { - if (age + 1 < UsedPipelinesMaxAge) { + if (age + 1 < MaxUsedPipelineAge(device)) { next.try_emplace(hash, static_cast(age + 1)); } } @@ -744,7 +752,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading } pipeline_cache_filename = base_dir / "vulkan.bin"; used_pipelines_filename = base_dir / "vulkan_used.bin"; - used_pipelines = LoadUsedPipelines(used_pipelines_filename); + used_pipelines = LoadUsedPipelines(used_pipelines_filename, MaxUsedPipelineAge(device)); used_pipelines_saved = std::chrono::steady_clock::now(); size_t skipped_pipelines = 0; size_t queued_graphics = 0; @@ -1004,6 +1012,11 @@ void PipelineCache::TickFrame() { } void PipelineCache::EvictUnusedPipelines() { + // Building a pipeline again on MoltenVK means a Metal compile mid-game, far dearer than the + // 2-3 ms below, so none are evicted there. + if (device.IsMoltenVK()) { + return; + } // Each built pipeline holds ~0.45 MB of driver memory and none was ever destroyed, so it grew // with every place a session went (title 1: 1,500 on its title screen alone). One unused for // this long is destroyed; it takes 2-3 ms to build again from the driver's pipeline cache. -- cgit v1.2.3