diff options
| author | Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-02 11:44:14 -0500 |
|---|---|---|
| committer | Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-02 14:53:47 -0500 |
| commit | b45f4c6d2c79cbb60eeda4d3ad887f9964a297e3 (patch) | |
| tree | f3ca29dbe3718b56ca82c057c0f8712e583e7f7d | |
| parent | 46d1554186cdfd2255cc3745e3e2cfe9130c5a84 (diff) | |
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)
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 21 |
1 files changed, 17 insertions, 4 deletions
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<u64, u8> LoadUsedPipelines(const std::filesystem::path& path) { +u8 MaxUsedPipelineAge(const Device& device) { + return device.IsMoltenVK() ? UsedPipelinesMaxAgeMoltenVK : UsedPipelinesMaxAge; +} + +std::unordered_map<u64, u8> LoadUsedPipelines(const std::filesystem::path& path, u8 max_age) { std::unordered_map<u64, u8> used; std::ifstream file{path, std::ios::binary}; u32 magic{}, count{}; @@ -618,7 +626,7 @@ std::unordered_map<u64, u8> LoadUsedPipelines(const std::filesystem::path& path) !file.read(reinterpret_cast<char*>(&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<u8>(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. |
