diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 6f23f53e91..209d5bb959 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -544,12 +544,17 @@ ComputePipeline* PipelineCache::CurrentComputePipeline() { .shared_memory_size = qmd.shared_alloc, .workgroup_size{qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z}, }; - const auto [pair, is_new]{compute_cache.try_emplace(key)}; + const auto [pair, inserted]{compute_cache.try_emplace(key)}; auto& pipeline{pair->second}; - if (!is_new) { - return pipeline.get(); + if (!pipeline) { + auto [slot, should_build] = AcquireComputeBuildSlot(key); + if (!should_build) { + WaitForBuildCompletion(slot); + } else { + pipeline = CreateComputePipeline(key, shader); + ReleaseComputeBuildSlot(key, slot); + } } - pipeline = CreateComputePipeline(key, shader); return pipeline.get(); } @@ -665,13 +670,20 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading } GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { - const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)}; + const auto [pair, inserted]{graphics_cache.try_emplace(graphics_key)}; auto& pipeline{pair->second}; - if (is_new) { - pipeline = CreateGraphicsPipeline(); - } if (!pipeline) { - return nullptr; + const auto key = pair->first; + auto [slot, should_build] = AcquireGraphicsBuildSlot(key); + if (!should_build) { + WaitForBuildCompletion(slot); + } else { + pipeline = CreateGraphicsPipeline(); + ReleaseGraphicsBuildSlot(key, slot); + } + if (!pipeline) { + return nullptr; + } } if (current_pipeline) { current_pipeline->AddTransition(pipeline.get()); @@ -985,4 +997,68 @@ vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem:: } } +auto PipelineCache::AcquireGraphicsBuildSlot(const GraphicsPipelineCacheKey& key) + -> std::pair { + std::scoped_lock lock(graphics_inflight_mutex); + auto [it, inserted] = graphics_inflight_builds.try_emplace(key); + if (inserted || !it->second) { + it->second = std::make_shared(); + return {it->second, true}; + } + return {it->second, false}; +} + +auto PipelineCache::AcquireComputeBuildSlot(const ComputePipelineCacheKey& key) + -> std::pair { + std::scoped_lock lock(compute_inflight_mutex); + auto [it, inserted] = compute_inflight_builds.try_emplace(key); + if (inserted || !it->second) { + it->second = std::make_shared(); + return {it->second, true}; + } + return {it->second, false}; +} + +void PipelineCache::ReleaseGraphicsBuildSlot(const GraphicsPipelineCacheKey& key, + const InFlightPipelinePtr& slot) { + if (!slot) { + return; + } + { + std::scoped_lock slot_lock(slot->mutex); + slot->building = false; + } + slot->cv.notify_all(); + std::scoped_lock map_lock(graphics_inflight_mutex); + auto it = graphics_inflight_builds.find(key); + if (it != graphics_inflight_builds.end() && it->second == slot) { + graphics_inflight_builds.erase(it); + } +} + +void PipelineCache::ReleaseComputeBuildSlot(const ComputePipelineCacheKey& key, + const InFlightPipelinePtr& slot) { + if (!slot) { + return; + } + { + std::scoped_lock slot_lock(slot->mutex); + slot->building = false; + } + slot->cv.notify_all(); + std::scoped_lock map_lock(compute_inflight_mutex); + auto it = compute_inflight_builds.find(key); + if (it != compute_inflight_builds.end() && it->second == slot) { + compute_inflight_builds.erase(it); + } +} + +void PipelineCache::WaitForBuildCompletion(const InFlightPipelinePtr& slot) const { + if (!slot) { + return; + } + std::unique_lock lock(slot->mutex); + slot->cv.wait(lock, [&] { return !slot->building; }); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 7909bd8cf0..d4a30b026a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -5,8 +5,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -114,6 +116,14 @@ public: const VideoCore::DiskResourceLoadCallback& callback); private: + struct InFlightPipelineBuild { + std::mutex mutex; + std::condition_variable cv; + bool building{true}; + }; + + using InFlightPipelinePtr = std::shared_ptr; + [[nodiscard]] GraphicsPipeline* CurrentGraphicsPipelineSlowPath(); [[nodiscard]] GraphicsPipeline* BuiltPipeline(GraphicsPipeline* pipeline) const noexcept; @@ -140,6 +150,14 @@ private: vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename, u32 expected_cache_version); + std::pair AcquireGraphicsBuildSlot( + const GraphicsPipelineCacheKey& key); + std::pair AcquireComputeBuildSlot( + const ComputePipelineCacheKey& key); + void ReleaseGraphicsBuildSlot(const GraphicsPipelineCacheKey& key, const InFlightPipelinePtr& slot); + void ReleaseComputeBuildSlot(const ComputePipelineCacheKey& key, const InFlightPipelinePtr& slot); + void WaitForBuildCompletion(const InFlightPipelinePtr& slot) const; + const Device& device; Scheduler& scheduler; DescriptorPool& descriptor_pool; @@ -158,6 +176,11 @@ private: std::unordered_map> compute_cache; std::unordered_map> graphics_cache; + std::mutex graphics_inflight_mutex; + std::unordered_map graphics_inflight_builds; + std::mutex compute_inflight_mutex; + std::unordered_map compute_inflight_builds; + ShaderPools main_pools; Shader::Profile profile;