summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/common/settings.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp42
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp51
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h19
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp128
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h10
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp27
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h24
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp10
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui10
-rw-r--r--src/yuzu/main.cpp20
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu_cmd/config.cpp1
17 files changed, 285 insertions, 68 deletions
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 149e621f9..2eaded242 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -200,6 +200,7 @@ void RestoreGlobalState(bool is_powered_on) {
200 values.use_asynchronous_shaders.SetGlobal(true); 200 values.use_asynchronous_shaders.SetGlobal(true);
201 values.use_fast_gpu_time.SetGlobal(true); 201 values.use_fast_gpu_time.SetGlobal(true);
202 values.use_pessimistic_flushes.SetGlobal(true); 202 values.use_pessimistic_flushes.SetGlobal(true);
203 values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
203 values.bg_red.SetGlobal(true); 204 values.bg_red.SetGlobal(true);
204 values.bg_green.SetGlobal(true); 205 values.bg_green.SetGlobal(true);
205 values.bg_blue.SetGlobal(true); 206 values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 5017951c5..d9e82087d 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -451,6 +451,8 @@ struct Values {
451 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 451 SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
452 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 452 SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
453 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"}; 453 SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
454 SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
455 "use_vulkan_driver_pipeline_cache"};
454 456
455 SwitchableSetting<u8> bg_red{0, "bg_red"}; 457 SwitchableSetting<u8> bg_red{0, "bg_red"};
456 SwitchableSetting<u8> bg_green{0, "bg_green"}; 458 SwitchableSetting<u8> bg_green{0, "bg_green"};
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 04a3a861e..2a0f0dbf0 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -24,13 +24,15 @@ using Shader::ImageBufferDescriptor;
24using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; 24using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
25using Tegra::Texture::TexturePair; 25using Tegra::Texture::TexturePair;
26 26
27ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, 27ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_,
28 DescriptorPool& descriptor_pool,
28 UpdateDescriptorQueue& update_descriptor_queue_, 29 UpdateDescriptorQueue& update_descriptor_queue_,
29 Common::ThreadWorker* thread_worker, 30 Common::ThreadWorker* thread_worker,
30 PipelineStatistics* pipeline_statistics, 31 PipelineStatistics* pipeline_statistics,
31 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, 32 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
32 vk::ShaderModule spv_module_) 33 vk::ShaderModule spv_module_)
33 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, 34 : device{device_}, pipeline_cache(pipeline_cache_),
35 update_descriptor_queue{update_descriptor_queue_}, info{info_},
34 spv_module(std::move(spv_module_)) { 36 spv_module(std::move(spv_module_)) {
35 if (shader_notify) { 37 if (shader_notify) {
36 shader_notify->MarkShaderBuilding(); 38 shader_notify->MarkShaderBuilding();
@@ -56,23 +58,27 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
56 if (device.IsKhrPipelineExecutablePropertiesEnabled()) { 58 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
57 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 59 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
58 } 60 }
59 pipeline = device.GetLogical().CreateComputePipeline({ 61 pipeline = device.GetLogical().CreateComputePipeline(
60 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 62 {
61 .pNext = nullptr, 63 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
62 .flags = flags, 64 .pNext = nullptr,
63 .stage{ 65 .flags = flags,
64 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 66 .stage{
65 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, 67 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
66 .flags = 0, 68 .pNext =
67 .stage = VK_SHADER_STAGE_COMPUTE_BIT, 69 device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
68 .module = *spv_module, 70 .flags = 0,
69 .pName = "main", 71 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
70 .pSpecializationInfo = nullptr, 72 .module = *spv_module,
73 .pName = "main",
74 .pSpecializationInfo = nullptr,
75 },
76 .layout = *pipeline_layout,
77 .basePipelineHandle = 0,
78 .basePipelineIndex = 0,
71 }, 79 },
72 .layout = *pipeline_layout, 80 *pipeline_cache);
73 .basePipelineHandle = 0, 81
74 .basePipelineIndex = 0,
75 });
76 if (pipeline_statistics) { 82 if (pipeline_statistics) {
77 pipeline_statistics->Collect(*pipeline); 83 pipeline_statistics->Collect(*pipeline);
78 } 84 }
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index d70837fc5..78d77027f 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -28,7 +28,8 @@ class Scheduler;
28 28
29class ComputePipeline { 29class ComputePipeline {
30public: 30public:
31 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, 31 explicit ComputePipeline(const Device& device, vk::PipelineCache& pipeline_cache,
32 DescriptorPool& descriptor_pool,
32 UpdateDescriptorQueue& update_descriptor_queue, 33 UpdateDescriptorQueue& update_descriptor_queue,
33 Common::ThreadWorker* thread_worker, 34 Common::ThreadWorker* thread_worker,
34 PipelineStatistics* pipeline_statistics, 35 PipelineStatistics* pipeline_statistics,
@@ -46,6 +47,7 @@ public:
46 47
47private: 48private:
48 const Device& device; 49 const Device& device;
50 vk::PipelineCache& pipeline_cache;
49 UpdateDescriptorQueue& update_descriptor_queue; 51 UpdateDescriptorQueue& update_descriptor_queue;
50 Shader::Info info; 52 Shader::Info info;
51 53
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 734c379b9..f91bb5a1d 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -234,13 +234,14 @@ ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& m
234 234
235GraphicsPipeline::GraphicsPipeline( 235GraphicsPipeline::GraphicsPipeline(
236 Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, 236 Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
237 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, 237 vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify,
238 const Device& device_, DescriptorPool& descriptor_pool,
238 UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, 239 UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
239 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, 240 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
240 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, 241 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
241 const std::array<const Shader::Info*, NUM_STAGES>& infos) 242 const std::array<const Shader::Info*, NUM_STAGES>& infos)
242 : key{key_}, device{device_}, texture_cache{texture_cache_}, 243 : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
243 buffer_cache{buffer_cache_}, scheduler{scheduler_}, 244 pipeline_cache(pipeline_cache_), scheduler{scheduler_},
244 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { 245 update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} {
245 if (shader_notify) { 246 if (shader_notify) {
246 shader_notify->MarkShaderBuilding(); 247 shader_notify->MarkShaderBuilding();
@@ -897,27 +898,29 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
897 if (device.IsKhrPipelineExecutablePropertiesEnabled()) { 898 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
898 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 899 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
899 } 900 }
900 pipeline = device.GetLogical().CreateGraphicsPipeline({ 901 pipeline = device.GetLogical().CreateGraphicsPipeline(
901 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 902 {
902 .pNext = nullptr, 903 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
903 .flags = flags, 904 .pNext = nullptr,
904 .stageCount = static_cast<u32>(shader_stages.size()), 905 .flags = flags,
905 .pStages = shader_stages.data(), 906 .stageCount = static_cast<u32>(shader_stages.size()),
906 .pVertexInputState = &vertex_input_ci, 907 .pStages = shader_stages.data(),
907 .pInputAssemblyState = &input_assembly_ci, 908 .pVertexInputState = &vertex_input_ci,
908 .pTessellationState = &tessellation_ci, 909 .pInputAssemblyState = &input_assembly_ci,
909 .pViewportState = &viewport_ci, 910 .pTessellationState = &tessellation_ci,
910 .pRasterizationState = &rasterization_ci, 911 .pViewportState = &viewport_ci,
911 .pMultisampleState = &multisample_ci, 912 .pRasterizationState = &rasterization_ci,
912 .pDepthStencilState = &depth_stencil_ci, 913 .pMultisampleState = &multisample_ci,
913 .pColorBlendState = &color_blend_ci, 914 .pDepthStencilState = &depth_stencil_ci,
914 .pDynamicState = &dynamic_state_ci, 915 .pColorBlendState = &color_blend_ci,
915 .layout = *pipeline_layout, 916 .pDynamicState = &dynamic_state_ci,
916 .renderPass = render_pass, 917 .layout = *pipeline_layout,
917 .subpass = 0, 918 .renderPass = render_pass,
918 .basePipelineHandle = nullptr, 919 .subpass = 0,
919 .basePipelineIndex = 0, 920 .basePipelineHandle = nullptr,
920 }); 921 .basePipelineIndex = 0,
922 },
923 *pipeline_cache);
921} 924}
922 925
923void GraphicsPipeline::Validate() { 926void GraphicsPipeline::Validate() {
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 1ed2967be..67c657d0e 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -70,16 +70,14 @@ class GraphicsPipeline {
70 static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; 70 static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage;
71 71
72public: 72public:
73 explicit GraphicsPipeline(Scheduler& scheduler, BufferCache& buffer_cache, 73 explicit GraphicsPipeline(
74 TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify, 74 Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
75 const Device& device, DescriptorPool& descriptor_pool, 75 vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify,
76 UpdateDescriptorQueue& update_descriptor_queue, 76 const Device& device, DescriptorPool& descriptor_pool,
77 Common::ThreadWorker* worker_thread, 77 UpdateDescriptorQueue& update_descriptor_queue, Common::ThreadWorker* worker_thread,
78 PipelineStatistics* pipeline_statistics, 78 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
79 RenderPassCache& render_pass_cache, 79 const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages,
80 const GraphicsPipelineCacheKey& key, 80 const std::array<const Shader::Info*, NUM_STAGES>& infos);
81 std::array<vk::ShaderModule, NUM_STAGES> stages,
82 const std::array<const Shader::Info*, NUM_STAGES>& infos);
83 81
84 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; 82 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
85 GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; 83 GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;
@@ -133,6 +131,7 @@ private:
133 const Device& device; 131 const Device& device;
134 TextureCache& texture_cache; 132 TextureCache& texture_cache;
135 BufferCache& buffer_cache; 133 BufferCache& buffer_cache;
134 vk::PipelineCache& pipeline_cache;
136 Scheduler& scheduler; 135 Scheduler& scheduler;
137 UpdateDescriptorQueue& update_descriptor_queue; 136 UpdateDescriptorQueue& update_descriptor_queue;
138 137
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 3046b72ab..67e5bc648 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -55,6 +55,7 @@ using VideoCommon::GenericEnvironment;
55using VideoCommon::GraphicsEnvironment; 55using VideoCommon::GraphicsEnvironment;
56 56
57constexpr u32 CACHE_VERSION = 10; 57constexpr u32 CACHE_VERSION = 10;
58constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
58 59
59template <typename Container> 60template <typename Container>
60auto MakeSpan(Container& container) { 61auto MakeSpan(Container& container) {
@@ -284,6 +285,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
284 render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, 285 render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_},
285 texture_cache{texture_cache_}, shader_notify{shader_notify_}, 286 texture_cache{texture_cache_}, shader_notify{shader_notify_},
286 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, 287 use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
288 use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()},
287 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), 289 workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"),
288 serialization_thread(1, "VkPipelineSerialization") { 290 serialization_thread(1, "VkPipelineSerialization") {
289 const auto& float_control{device.FloatControlProperties()}; 291 const auto& float_control{device.FloatControlProperties()};
@@ -362,7 +364,12 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
362 }; 364 };
363} 365}
364 366
365PipelineCache::~PipelineCache() = default; 367PipelineCache::~PipelineCache() {
368 if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) {
369 SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
370 CACHE_VERSION);
371 }
372}
366 373
367GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { 374GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
368 MICROPROFILE_SCOPE(Vulkan_PipelineCache); 375 MICROPROFILE_SCOPE(Vulkan_PipelineCache);
@@ -418,6 +425,12 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
418 } 425 }
419 pipeline_cache_filename = base_dir / "vulkan.bin"; 426 pipeline_cache_filename = base_dir / "vulkan.bin";
420 427
428 if (use_vulkan_pipeline_cache) {
429 vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin";
430 vulkan_pipeline_cache =
431 LoadVulkanPipelineCache(vulkan_pipeline_cache_filename, CACHE_VERSION);
432 }
433
421 struct { 434 struct {
422 std::mutex mutex; 435 std::mutex mutex;
423 size_t total{}; 436 size_t total{};
@@ -496,6 +509,11 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
496 509
497 workers.WaitForRequests(stop_loading); 510 workers.WaitForRequests(stop_loading);
498 511
512 if (use_vulkan_pipeline_cache) {
513 SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
514 CACHE_VERSION);
515 }
516
499 if (state.statistics) { 517 if (state.statistics) {
500 state.statistics->Report(); 518 state.statistics->Report();
501 } 519 }
@@ -616,10 +634,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
616 previous_stage = &program; 634 previous_stage = &program;
617 } 635 }
618 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 636 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
619 return std::make_unique<GraphicsPipeline>(scheduler, buffer_cache, texture_cache, 637 return std::make_unique<GraphicsPipeline>(
620 &shader_notify, device, descriptor_pool, 638 scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device,
621 update_descriptor_queue, thread_worker, statistics, 639 descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
622 render_pass_cache, key, std::move(modules), infos); 640 std::move(modules), infos);
623 641
624} catch (const Shader::Exception& exception) { 642} catch (const Shader::Exception& exception) {
625 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 643 LOG_ERROR(Render_Vulkan, "{}", exception.what());
@@ -689,13 +707,107 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
689 spv_module.SetObjectNameEXT(name.c_str()); 707 spv_module.SetObjectNameEXT(name.c_str());
690 } 708 }
691 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 709 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
692 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, 710 return std::make_unique<ComputePipeline>(device, vulkan_pipeline_cache, descriptor_pool,
693 thread_worker, statistics, &shader_notify, 711 update_descriptor_queue, thread_worker, statistics,
694 program.info, std::move(spv_module)); 712 &shader_notify, program.info, std::move(spv_module));
695 713
696} catch (const Shader::Exception& exception) { 714} catch (const Shader::Exception& exception) {
697 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 715 LOG_ERROR(Render_Vulkan, "{}", exception.what());
698 return nullptr; 716 return nullptr;
699} 717}
700 718
719void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename,
720 const vk::PipelineCache& pipeline_cache,
721 u32 cache_version) try {
722 std::ofstream file(filename, std::ios::binary);
723 file.exceptions(std::ifstream::failbit);
724 if (!file.is_open()) {
725 LOG_ERROR(Common_Filesystem, "Failed to open Vulkan driver pipeline cache file {}",
726 Common::FS::PathToUTF8String(filename));
727 return;
728 }
729 file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size())
730 .write(reinterpret_cast<const char*>(&cache_version), sizeof(cache_version));
731
732 size_t cache_size = 0;
733 std::vector<char> cache_data;
734 if (pipeline_cache) {
735 pipeline_cache.Read(&cache_size, nullptr);
736 cache_data.resize(cache_size);
737 pipeline_cache.Read(&cache_size, cache_data.data());
738 }
739 file.write(cache_data.data(), cache_size);
740
741 LOG_INFO(Render_Vulkan, "Vulkan driver pipelines cached at: {}",
742 Common::FS::PathToUTF8String(filename));
743
744} catch (const std::ios_base::failure& e) {
745 LOG_ERROR(Common_Filesystem, "{}", e.what());
746 if (!Common::FS::RemoveFile(filename)) {
747 LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}",
748 Common::FS::PathToUTF8String(filename));
749 }
750}
751
752vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::path& filename,
753 u32 expected_cache_version) {
754 const auto create_pipeline_cache = [this](size_t data_size, const void* data) {
755 VkPipelineCacheCreateInfo pipeline_cache_ci = {
756 .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
757 .pNext = nullptr,
758 .flags = 0,
759 .initialDataSize = data_size,
760 .pInitialData = data};
761 return device.GetLogical().CreatePipelineCache(pipeline_cache_ci);
762 };
763 try {
764 std::ifstream file(filename, std::ios::binary | std::ios::ate);
765 if (!file.is_open()) {
766 return create_pipeline_cache(0, nullptr);
767 }
768 file.exceptions(std::ifstream::failbit);
769 const auto end{file.tellg()};
770 file.seekg(0, std::ios::beg);
771
772 std::array<char, 8> magic_number;
773 u32 cache_version;
774 file.read(magic_number.data(), magic_number.size())
775 .read(reinterpret_cast<char*>(&cache_version), sizeof(cache_version));
776 if (magic_number != VULKAN_CACHE_MAGIC_NUMBER || cache_version != expected_cache_version) {
777 file.close();
778 if (Common::FS::RemoveFile(filename)) {
779 if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) {
780 LOG_ERROR(Common_Filesystem, "Invalid Vulkan driver pipeline cache file");
781 }
782 if (cache_version != expected_cache_version) {
783 LOG_INFO(Common_Filesystem, "Deleting old Vulkan driver pipeline cache");
784 }
785 } else {
786 LOG_ERROR(Common_Filesystem,
787 "Invalid Vulkan pipeline cache file and failed to delete it in \"{}\"",
788 Common::FS::PathToUTF8String(filename));
789 }
790 return create_pipeline_cache(0, nullptr);
791 }
792
793 const size_t cache_size = static_cast<size_t>(end) - magic_number.size();
794 std::vector<char> cache_data(cache_size);
795 file.read(cache_data.data(), cache_size);
796
797 LOG_INFO(Render_Vulkan,
798 "Loaded Vulkan driver pipeline cache: ", Common::FS::PathToUTF8String(filename));
799
800 return create_pipeline_cache(cache_size, cache_data.data());
801
802 } catch (const std::ios_base::failure& e) {
803 LOG_ERROR(Common_Filesystem, "{}", e.what());
804 if (!Common::FS::RemoveFile(filename)) {
805 LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}",
806 Common::FS::PathToUTF8String(filename));
807 }
808
809 return create_pipeline_cache(0, nullptr);
810 }
811}
812
701} // namespace Vulkan 813} // 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 b4f593ef5..5171912d7 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -135,6 +135,12 @@ private:
135 PipelineStatistics* statistics, 135 PipelineStatistics* statistics,
136 bool build_in_parallel); 136 bool build_in_parallel);
137 137
138 void SerializeVulkanPipelineCache(const std::filesystem::path& filename,
139 const vk::PipelineCache& pipeline_cache, u32 cache_version);
140
141 vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
142 u32 expected_cache_version);
143
138 const Device& device; 144 const Device& device;
139 Scheduler& scheduler; 145 Scheduler& scheduler;
140 DescriptorPool& descriptor_pool; 146 DescriptorPool& descriptor_pool;
@@ -144,6 +150,7 @@ private:
144 TextureCache& texture_cache; 150 TextureCache& texture_cache;
145 VideoCore::ShaderNotify& shader_notify; 151 VideoCore::ShaderNotify& shader_notify;
146 bool use_asynchronous_shaders{}; 152 bool use_asynchronous_shaders{};
153 bool use_vulkan_pipeline_cache{};
147 154
148 GraphicsPipelineCacheKey graphics_key{}; 155 GraphicsPipelineCacheKey graphics_key{};
149 GraphicsPipeline* current_pipeline{}; 156 GraphicsPipeline* current_pipeline{};
@@ -158,6 +165,9 @@ private:
158 165
159 std::filesystem::path pipeline_cache_filename; 166 std::filesystem::path pipeline_cache_filename;
160 167
168 std::filesystem::path vulkan_pipeline_cache_filename;
169 vk::PipelineCache vulkan_pipeline_cache;
170
161 Common::ThreadWorker workers; 171 Common::ThreadWorker workers;
162 Common::ThreadWorker serialization_thread; 172 Common::ThreadWorker serialization_thread;
163 DynamicFeatures dynamic_features; 173 DynamicFeatures dynamic_features;
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 861767c13..61be1fce1 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -152,6 +152,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
152 X(vkCreateGraphicsPipelines); 152 X(vkCreateGraphicsPipelines);
153 X(vkCreateImage); 153 X(vkCreateImage);
154 X(vkCreateImageView); 154 X(vkCreateImageView);
155 X(vkCreatePipelineCache);
155 X(vkCreatePipelineLayout); 156 X(vkCreatePipelineLayout);
156 X(vkCreateQueryPool); 157 X(vkCreateQueryPool);
157 X(vkCreateRenderPass); 158 X(vkCreateRenderPass);
@@ -171,6 +172,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
171 X(vkDestroyImage); 172 X(vkDestroyImage);
172 X(vkDestroyImageView); 173 X(vkDestroyImageView);
173 X(vkDestroyPipeline); 174 X(vkDestroyPipeline);
175 X(vkDestroyPipelineCache);
174 X(vkDestroyPipelineLayout); 176 X(vkDestroyPipelineLayout);
175 X(vkDestroyQueryPool); 177 X(vkDestroyQueryPool);
176 X(vkDestroyRenderPass); 178 X(vkDestroyRenderPass);
@@ -188,6 +190,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
188 X(vkGetEventStatus); 190 X(vkGetEventStatus);
189 X(vkGetFenceStatus); 191 X(vkGetFenceStatus);
190 X(vkGetImageMemoryRequirements); 192 X(vkGetImageMemoryRequirements);
193 X(vkGetPipelineCacheData);
191 X(vkGetMemoryFdKHR); 194 X(vkGetMemoryFdKHR);
192#ifdef _WIN32 195#ifdef _WIN32
193 X(vkGetMemoryWin32HandleKHR); 196 X(vkGetMemoryWin32HandleKHR);
@@ -431,6 +434,10 @@ void Destroy(VkDevice device, VkPipeline handle, const DeviceDispatch& dld) noex
431 dld.vkDestroyPipeline(device, handle, nullptr); 434 dld.vkDestroyPipeline(device, handle, nullptr);
432} 435}
433 436
437void Destroy(VkDevice device, VkPipelineCache handle, const DeviceDispatch& dld) noexcept {
438 dld.vkDestroyPipelineCache(device, handle, nullptr);
439}
440
434void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept { 441void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept {
435 dld.vkDestroyPipelineLayout(device, handle, nullptr); 442 dld.vkDestroyPipelineLayout(device, handle, nullptr);
436} 443}
@@ -651,6 +658,10 @@ void ShaderModule::SetObjectNameEXT(const char* name) const {
651 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name); 658 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name);
652} 659}
653 660
661void PipelineCache::SetObjectNameEXT(const char* name) const {
662 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_PIPELINE_CACHE, name);
663}
664
654void Semaphore::SetObjectNameEXT(const char* name) const { 665void Semaphore::SetObjectNameEXT(const char* name) const {
655 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SEMAPHORE, name); 666 SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SEMAPHORE, name);
656} 667}
@@ -746,21 +757,29 @@ DescriptorSetLayout Device::CreateDescriptorSetLayout(
746 return DescriptorSetLayout(object, handle, *dld); 757 return DescriptorSetLayout(object, handle, *dld);
747} 758}
748 759
760PipelineCache Device::CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const {
761 VkPipelineCache cache;
762 Check(dld->vkCreatePipelineCache(handle, &ci, nullptr, &cache));
763 return PipelineCache(cache, handle, *dld);
764}
765
749PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const { 766PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const {
750 VkPipelineLayout object; 767 VkPipelineLayout object;
751 Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object)); 768 Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object));
752 return PipelineLayout(object, handle, *dld); 769 return PipelineLayout(object, handle, *dld);
753} 770}
754 771
755Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const { 772Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
773 VkPipelineCache cache) const {
756 VkPipeline object; 774 VkPipeline object;
757 Check(dld->vkCreateGraphicsPipelines(handle, nullptr, 1, &ci, nullptr, &object)); 775 Check(dld->vkCreateGraphicsPipelines(handle, cache, 1, &ci, nullptr, &object));
758 return Pipeline(object, handle, *dld); 776 return Pipeline(object, handle, *dld);
759} 777}
760 778
761Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const { 779Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
780 VkPipelineCache cache) const {
762 VkPipeline object; 781 VkPipeline object;
763 Check(dld->vkCreateComputePipelines(handle, nullptr, 1, &ci, nullptr, &object)); 782 Check(dld->vkCreateComputePipelines(handle, cache, 1, &ci, nullptr, &object));
764 return Pipeline(object, handle, *dld); 783 return Pipeline(object, handle, *dld);
765} 784}
766 785
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index accfad8c1..412779b51 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -270,6 +270,7 @@ struct DeviceDispatch : InstanceDispatch {
270 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{}; 270 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{};
271 PFN_vkCreateImage vkCreateImage{}; 271 PFN_vkCreateImage vkCreateImage{};
272 PFN_vkCreateImageView vkCreateImageView{}; 272 PFN_vkCreateImageView vkCreateImageView{};
273 PFN_vkCreatePipelineCache vkCreatePipelineCache{};
273 PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; 274 PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
274 PFN_vkCreateQueryPool vkCreateQueryPool{}; 275 PFN_vkCreateQueryPool vkCreateQueryPool{};
275 PFN_vkCreateRenderPass vkCreateRenderPass{}; 276 PFN_vkCreateRenderPass vkCreateRenderPass{};
@@ -289,6 +290,7 @@ struct DeviceDispatch : InstanceDispatch {
289 PFN_vkDestroyImage vkDestroyImage{}; 290 PFN_vkDestroyImage vkDestroyImage{};
290 PFN_vkDestroyImageView vkDestroyImageView{}; 291 PFN_vkDestroyImageView vkDestroyImageView{};
291 PFN_vkDestroyPipeline vkDestroyPipeline{}; 292 PFN_vkDestroyPipeline vkDestroyPipeline{};
293 PFN_vkDestroyPipelineCache vkDestroyPipelineCache{};
292 PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{}; 294 PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{};
293 PFN_vkDestroyQueryPool vkDestroyQueryPool{}; 295 PFN_vkDestroyQueryPool vkDestroyQueryPool{};
294 PFN_vkDestroyRenderPass vkDestroyRenderPass{}; 296 PFN_vkDestroyRenderPass vkDestroyRenderPass{};
@@ -306,6 +308,7 @@ struct DeviceDispatch : InstanceDispatch {
306 PFN_vkGetEventStatus vkGetEventStatus{}; 308 PFN_vkGetEventStatus vkGetEventStatus{};
307 PFN_vkGetFenceStatus vkGetFenceStatus{}; 309 PFN_vkGetFenceStatus vkGetFenceStatus{};
308 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{}; 310 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{};
311 PFN_vkGetPipelineCacheData vkGetPipelineCacheData{};
309 PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{}; 312 PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{};
310#ifdef _WIN32 313#ifdef _WIN32
311 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; 314 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
@@ -351,6 +354,7 @@ void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept;
351void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; 354void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept;
352void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; 355void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept;
353void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; 356void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept;
357void Destroy(VkDevice, VkPipelineCache, const DeviceDispatch&) noexcept;
354void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; 358void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept;
355void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; 359void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept;
356void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; 360void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept;
@@ -773,6 +777,18 @@ public:
773 void SetObjectNameEXT(const char* name) const; 777 void SetObjectNameEXT(const char* name) const;
774}; 778};
775 779
780class PipelineCache : public Handle<VkPipelineCache, VkDevice, DeviceDispatch> {
781 using Handle<VkPipelineCache, VkDevice, DeviceDispatch>::Handle;
782
783public:
784 /// Set object name.
785 void SetObjectNameEXT(const char* name) const;
786
787 VkResult Read(size_t* size, void* data) const noexcept {
788 return dld->vkGetPipelineCacheData(owner, handle, size, data);
789 }
790};
791
776class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> { 792class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> {
777 using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle; 793 using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle;
778 794
@@ -844,11 +860,15 @@ public:
844 860
845 DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const; 861 DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const;
846 862
863 PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const;
864
847 PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; 865 PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const;
848 866
849 Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const; 867 Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
868 VkPipelineCache cache = nullptr) const;
850 869
851 Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const; 870 Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
871 VkPipelineCache cache = nullptr) const;
852 872
853 Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; 873 Sampler CreateSampler(const VkSamplerCreateInfo& ci) const;
854 874
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index e9425b5bd..fbfa3ba35 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -709,6 +709,7 @@ void Config::ReadRendererValues() {
709 ReadGlobalSetting(Settings::values.use_asynchronous_shaders); 709 ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
710 ReadGlobalSetting(Settings::values.use_fast_gpu_time); 710 ReadGlobalSetting(Settings::values.use_fast_gpu_time);
711 ReadGlobalSetting(Settings::values.use_pessimistic_flushes); 711 ReadGlobalSetting(Settings::values.use_pessimistic_flushes);
712 ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
712 ReadGlobalSetting(Settings::values.bg_red); 713 ReadGlobalSetting(Settings::values.bg_red);
713 ReadGlobalSetting(Settings::values.bg_green); 714 ReadGlobalSetting(Settings::values.bg_green);
714 ReadGlobalSetting(Settings::values.bg_blue); 715 ReadGlobalSetting(Settings::values.bg_blue);
@@ -1348,6 +1349,7 @@ void Config::SaveRendererValues() {
1348 WriteGlobalSetting(Settings::values.use_asynchronous_shaders); 1349 WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
1349 WriteGlobalSetting(Settings::values.use_fast_gpu_time); 1350 WriteGlobalSetting(Settings::values.use_fast_gpu_time);
1350 WriteGlobalSetting(Settings::values.use_pessimistic_flushes); 1351 WriteGlobalSetting(Settings::values.use_pessimistic_flushes);
1352 WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
1351 WriteGlobalSetting(Settings::values.bg_red); 1353 WriteGlobalSetting(Settings::values.bg_red);
1352 WriteGlobalSetting(Settings::values.bg_green); 1354 WriteGlobalSetting(Settings::values.bg_green);
1353 WriteGlobalSetting(Settings::values.bg_blue); 1355 WriteGlobalSetting(Settings::values.bg_blue);
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 01f074699..a3fbe2ad0 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -29,6 +29,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
29 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); 29 ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
30 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); 30 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
31 ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue()); 31 ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue());
32 ui->use_vulkan_driver_pipeline_cache->setChecked(
33 Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
32 34
33 if (Settings::IsConfiguringGlobal()) { 35 if (Settings::IsConfiguringGlobal()) {
34 ui->gpu_accuracy->setCurrentIndex( 36 ui->gpu_accuracy->setCurrentIndex(
@@ -58,6 +60,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
58 ui->use_fast_gpu_time, use_fast_gpu_time); 60 ui->use_fast_gpu_time, use_fast_gpu_time);
59 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes, 61 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes,
60 ui->use_pessimistic_flushes, use_pessimistic_flushes); 62 ui->use_pessimistic_flushes, use_pessimistic_flushes);
63 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache,
64 ui->use_vulkan_driver_pipeline_cache,
65 use_vulkan_driver_pipeline_cache);
61} 66}
62 67
63void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 68void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -82,6 +87,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
82 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); 87 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
83 ui->use_pessimistic_flushes->setEnabled( 88 ui->use_pessimistic_flushes->setEnabled(
84 Settings::values.use_pessimistic_flushes.UsingGlobal()); 89 Settings::values.use_pessimistic_flushes.UsingGlobal());
90 ui->use_vulkan_driver_pipeline_cache->setEnabled(
91 Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal());
85 ui->anisotropic_filtering_combobox->setEnabled( 92 ui->anisotropic_filtering_combobox->setEnabled(
86 Settings::values.max_anisotropy.UsingGlobal()); 93 Settings::values.max_anisotropy.UsingGlobal());
87 94
@@ -97,6 +104,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
97 ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes, 104 ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes,
98 Settings::values.use_pessimistic_flushes, 105 Settings::values.use_pessimistic_flushes,
99 use_pessimistic_flushes); 106 use_pessimistic_flushes);
107 ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache,
108 Settings::values.use_vulkan_driver_pipeline_cache,
109 use_vulkan_driver_pipeline_cache);
100 ConfigurationShared::SetColoredComboBox( 110 ConfigurationShared::SetColoredComboBox(
101 ui->gpu_accuracy, ui->label_gpu_accuracy, 111 ui->gpu_accuracy, ui->label_gpu_accuracy,
102 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); 112 static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 12e816905..891efc068 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -40,6 +40,7 @@ private:
40 ConfigurationShared::CheckState use_asynchronous_shaders; 40 ConfigurationShared::CheckState use_asynchronous_shaders;
41 ConfigurationShared::CheckState use_fast_gpu_time; 41 ConfigurationShared::CheckState use_fast_gpu_time;
42 ConfigurationShared::CheckState use_pessimistic_flushes; 42 ConfigurationShared::CheckState use_pessimistic_flushes;
43 ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
43 44
44 const Core::System& system; 45 const Core::System& system;
45}; 46};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 87a121471..ccbdcf08f 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -110,6 +110,16 @@
110 </widget> 110 </widget>
111 </item> 111 </item>
112 <item> 112 <item>
113 <widget class="QCheckBox" name="use_vulkan_driver_pipeline_cache">
114 <property name="toolTip">
115 <string>Enables GPU vendor-specific pipeline cache. This option can improve shader loading time significantly in cases where the Vulkan driver does not store pipeline cache files internally.</string>
116 </property>
117 <property name="text">
118 <string>Use Vulkan pipeline cache</string>
119 </property>
120 </widget>
121 </item>
122 <item>
113 <widget class="QWidget" name="af_layout" native="true"> 123 <widget class="QWidget" name="af_layout" native="true">
114 <layout class="QHBoxLayout" name="horizontalLayout_1"> 124 <layout class="QHBoxLayout" name="horizontalLayout_1">
115 <property name="leftMargin"> 125 <property name="leftMargin">
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 524650144..c55f81c2f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2229,8 +2229,10 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
2229 } 2229 }
2230 2230
2231 switch (target) { 2231 switch (target) {
2232 case GameListRemoveTarget::GlShaderCache:
2233 case GameListRemoveTarget::VkShaderCache: 2232 case GameListRemoveTarget::VkShaderCache:
2233 RemoveVulkanDriverPipelineCache(program_id);
2234 [[fallthrough]];
2235 case GameListRemoveTarget::GlShaderCache:
2234 RemoveTransferableShaderCache(program_id, target); 2236 RemoveTransferableShaderCache(program_id, target);
2235 break; 2237 break;
2236 case GameListRemoveTarget::AllShaderCache: 2238 case GameListRemoveTarget::AllShaderCache:
@@ -2271,6 +2273,22 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTa
2271 } 2273 }
2272} 2274}
2273 2275
2276void GMainWindow::RemoveVulkanDriverPipelineCache(u64 program_id) {
2277 static constexpr std::string_view target_file_name = "vulkan_pipelines.bin";
2278
2279 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
2280 const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
2281 const auto target_file = shader_cache_folder_path / target_file_name;
2282
2283 if (!Common::FS::Exists(target_file)) {
2284 return;
2285 }
2286 if (!Common::FS::RemoveFile(target_file)) {
2287 QMessageBox::warning(this, tr("Error Removing Vulkan Driver Pipeline Cache"),
2288 tr("Failed to remove the driver pipeline cache."));
2289 }
2290}
2291
2274void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) { 2292void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) {
2275 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); 2293 const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir);
2276 const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id); 2294 const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index db318485d..f25ce65a8 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -347,6 +347,7 @@ private:
347 void RemoveUpdateContent(u64 program_id, InstalledEntryType type); 347 void RemoveUpdateContent(u64 program_id, InstalledEntryType type);
348 void RemoveAddOnContent(u64 program_id, InstalledEntryType type); 348 void RemoveAddOnContent(u64 program_id, InstalledEntryType type);
349 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); 349 void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);
350 void RemoveVulkanDriverPipelineCache(u64 program_id);
350 void RemoveAllTransferableShaderCaches(u64 program_id); 351 void RemoveAllTransferableShaderCaches(u64 program_id);
351 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); 352 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
352 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 353 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 1e45e57bc..b2d690bb6 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -321,6 +321,7 @@ void Config::ReadValues() {
321 ReadSetting("Renderer", Settings::values.accelerate_astc); 321 ReadSetting("Renderer", Settings::values.accelerate_astc);
322 ReadSetting("Renderer", Settings::values.use_fast_gpu_time); 322 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
323 ReadSetting("Renderer", Settings::values.use_pessimistic_flushes); 323 ReadSetting("Renderer", Settings::values.use_pessimistic_flushes);
324 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
324 325
325 ReadSetting("Renderer", Settings::values.bg_red); 326 ReadSetting("Renderer", Settings::values.bg_red);
326 ReadSetting("Renderer", Settings::values.bg_green); 327 ReadSetting("Renderer", Settings::values.bg_green);