From a20d3f6799594e0a7218ff78a970d8f77301ce0f Mon Sep 17 00:00:00 2001 From: wildcard Date: Fri, 12 Dec 2025 01:13:01 +0100 Subject: [PATCH] [Vulkan] Implement Pipeline Derivatives Applies mostly on android devices, should be tested for regressions on other platforms as well just incase. --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 26 +++++++++++++---- .../renderer_vulkan/vk_graphics_pipeline.h | 9 +++++- .../renderer_vulkan/vk_pipeline_cache.cpp | 29 +++++++++++++++---- .../renderer_vulkan/vk_pipeline_cache.h | 3 +- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index ca58e3fb4c..43d54534a3 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -248,10 +248,12 @@ GraphicsPipeline::GraphicsPipeline( GuestDescriptorQueue& guest_descriptor_queue_, Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, std::array stages, - const std::array& infos) - : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, - pipeline_cache(pipeline_cache_), scheduler{scheduler_}, - guest_descriptor_queue{guest_descriptor_queue_}, spv_modules{std::move(stages)} { + const std::array& infos, + GraphicsPipeline* base_pipeline_) + : key{key_}, base_pipeline{base_pipeline_}, allow_derivatives{base_pipeline_ == nullptr}, + device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, + pipeline_cache(pipeline_cache_), scheduler{scheduler_}, + guest_descriptor_queue{guest_descriptor_queue_}, spv_modules{std::move(stages)} { if (shader_notify) { shader_notify->MarkShaderBuilding(); } @@ -935,6 +937,18 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (device.IsKhrPipelineExecutablePropertiesEnabled() && Settings::values.renderer_debug.GetValue()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } + VkPipeline base_handle = VK_NULL_HANDLE; + + // First pipeline in a "cluster" allows derivatives + if (allow_derivatives) { + flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT; + } + + // Children mark themselves as derivatives when base is already built + if (base_pipeline != nullptr && base_pipeline->IsBuilt()) { + flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT; + base_handle = *base_pipeline->pipeline; + } pipeline = device.GetLogical().CreateGraphicsPipeline( { @@ -955,8 +969,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .layout = *pipeline_layout, .renderPass = render_pass, .subpass = 0, - .basePipelineHandle = nullptr, - .basePipelineIndex = 0, + .basePipelineHandle = base_handle, + .basePipelineIndex = -1, }, *pipeline_cache); } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index c8e89d60a4..55630135f4 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -79,9 +79,14 @@ public: GuestDescriptorQueue& guest_descriptor_queue, Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key, std::array stages, - const std::array& infos); + const std::array& infos, + GraphicsPipeline* base_pipeline = nullptr); + bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; } + [[nodiscard]] const GraphicsPipelineCacheKey& Key() const noexcept { + return key; + } GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; @@ -129,6 +134,8 @@ private: void Validate(); const GraphicsPipelineCacheKey key; + GraphicsPipeline* base_pipeline = nullptr; // non-owning + bool allow_derivatives = false; Tegra::Engines::Maxwell3D* maxwell3d; Tegra::MemoryManager* gpu_memory; const Device& device; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index f75398da65..14d44efe9a 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -575,8 +575,18 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)}; auto& pipeline{pair->second}; if (is_new) { - pipeline = CreateGraphicsPipeline(); + GraphicsPipeline* base = nullptr; + + // Use the current pipeline as a base if shaders match + if (current_pipeline) { + const auto& base_key = current_pipeline->Key(); + if (base_key.unique_hashes == graphics_key.unique_hashes) { + base = current_pipeline; + } } + pipeline = CreateGraphicsPipeline(base); + } + if (!pipeline) { return nullptr; } @@ -607,7 +617,7 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const std::unique_ptr PipelineCache::CreateGraphicsPipeline( ShaderPools& pools, const GraphicsPipelineCacheKey& key, std::span envs, PipelineStatistics* statistics, - bool build_in_parallel) try { + bool build_in_parallel, GraphicsPipeline* base_pipeline) try { auto hash = key.Hash(); LOG_INFO(Render_Vulkan, "0x{:016x}", hash); size_t env_index{0}; @@ -686,7 +696,7 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( return std::make_unique( scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device, descriptor_pool, guest_descriptor_queue, thread_worker, statistics, render_pass_cache, key, - std::move(modules), infos); + std::move(modules), infos, base_pipeline); } catch (const Shader::Exception& exception) { auto hash = key.Hash(); @@ -707,16 +717,23 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( } std::unique_ptr PipelineCache::CreateGraphicsPipeline() { + // Preserve old behaviour, no base pipeline + return CreateGraphicsPipeline(nullptr); +} + +std::unique_ptr PipelineCache::CreateGraphicsPipeline( + GraphicsPipeline* base_pipeline) { GraphicsEnvironments environments; GetGraphicsEnvironments(environments, graphics_key.unique_hashes); main_pools.ReleaseContents(); - auto pipeline{ - CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)}; + auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), + nullptr, true, base_pipeline)}; if (!pipeline || pipeline_cache_filename.empty()) { return pipeline; } - serialization_thread.QueueWork([this, key = graphics_key, envs = std::move(environments.envs)] { + serialization_thread.QueueWork( + [this, key = graphics_key, envs = std::move(environments.envs)] { boost::container::static_vector env_ptrs; for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 7909bd8cf0..42bafbbaf0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -119,11 +119,12 @@ private: [[nodiscard]] GraphicsPipeline* BuiltPipeline(GraphicsPipeline* pipeline) const noexcept; std::unique_ptr CreateGraphicsPipeline(); + std::unique_ptr CreateGraphicsPipeline(GraphicsPipeline* base_pipeline); std::unique_ptr CreateGraphicsPipeline( ShaderPools& pools, const GraphicsPipelineCacheKey& key, std::span envs, PipelineStatistics* statistics, - bool build_in_parallel); + bool build_in_parallel, GraphicsPipeline* base_pipeline = nullptr); std::unique_ptr CreateComputePipeline(const ComputePipelineCacheKey& key, const ShaderInfo* shader);