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/macro/macro_hle.cpp42
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp2
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp11
-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.cpp68
-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_device.cpp3
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h5
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.h5
-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
25 files changed, 325 insertions, 129 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/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index a5476e795..6272a4652 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -50,38 +50,6 @@ protected:
50 Maxwell3D& maxwell3d; 50 Maxwell3D& maxwell3d;
51}; 51};
52 52
53class HLE_DrawArrays final : public HLEMacroImpl {
54public:
55 explicit HLE_DrawArrays(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
56
57 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
58 maxwell3d.RefreshParameters();
59
60 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
61 maxwell3d.draw_manager->DrawArray(topology, parameters[1], parameters[2],
62 maxwell3d.regs.global_base_instance_index, 1);
63 }
64};
65
66class HLE_DrawIndexed final : public HLEMacroImpl {
67public:
68 explicit HLE_DrawIndexed(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
69
70 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
71 maxwell3d.RefreshParameters();
72 maxwell3d.regs.index_buffer.start_addr_high = parameters[1];
73 maxwell3d.regs.index_buffer.start_addr_low = parameters[2];
74 maxwell3d.regs.index_buffer.format =
75 static_cast<Engines::Maxwell3D::Regs::IndexFormat>(parameters[3]);
76 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
77
78 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0]);
79 maxwell3d.draw_manager->DrawIndex(topology, 0, parameters[4],
80 maxwell3d.regs.global_base_vertex_index,
81 maxwell3d.regs.global_base_instance_index, 1);
82 }
83};
84
85/* 53/*
86 * @note: these macros have two versions, a normal and extended version, with the extended version 54 * @note: these macros have two versions, a normal and extended version, with the extended version
87 * also assigning the base vertex/instance. 55 * also assigning the base vertex/instance.
@@ -497,11 +465,6 @@ public:
497} // Anonymous namespace 465} // Anonymous namespace
498 466
499HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} { 467HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {
500 builders.emplace(0xDD6A7FA92A7D2674ULL,
501 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
502 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
503 return std::make_unique<HLE_DrawArrays>(maxwell3d__);
504 }));
505 builders.emplace(0x0D61FC9FAAC9FCADULL, 468 builders.emplace(0x0D61FC9FAAC9FCADULL,
506 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( 469 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
507 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { 470 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
@@ -512,11 +475,6 @@ HLEMacro::HLEMacro(Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {
512 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { 475 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
513 return std::make_unique<HLE_DrawArraysIndirect<true>>(maxwell3d__); 476 return std::make_unique<HLE_DrawArraysIndirect<true>>(maxwell3d__);
514 })); 477 }));
515 builders.emplace(0x2DB33AADB741839CULL,
516 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
517 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
518 return std::make_unique<HLE_DrawIndexed>(maxwell3d__);
519 }));
520 builders.emplace(0x771BB18C62444DA0ULL, 478 builders.emplace(0x771BB18C62444DA0ULL,
521 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>( 479 std::function<std::unique_ptr<CachedMacro>(Maxwell3D&)>(
522 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> { 480 [](Maxwell3D& maxwell3d__) -> std::unique_ptr<CachedMacro> {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 3d328a250..f8398b511 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -148,7 +148,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
148 }); 148 });
149 } 149 }
150 if (!extended_dynamic_state_2_extra) { 150 if (!extended_dynamic_state_2_extra) {
151 dynamic_state.Refresh2(regs, topology, extended_dynamic_state_2); 151 dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
152 } 152 }
153 if (!extended_dynamic_state_3_blend) { 153 if (!extended_dynamic_state_3_blend) {
154 if (maxwell3d.dirty.flags[Dirty::Blending]) { 154 if (maxwell3d.dirty.flags[Dirty::Blending]) {
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index f502a7d09..bf97d25a4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -98,7 +98,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
98 : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), 98 : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_),
99 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), 99 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()),
100 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, 100 instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
101 true, Settings::values.renderer_debug.GetValue())), 101 Settings::values.renderer_debug.GetValue())),
102 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), 102 debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
103 surface(CreateSurface(instance, render_window)), 103 surface(CreateSurface(instance, render_window)),
104 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), 104 device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 487d8b416..b0153a502 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -336,6 +336,9 @@ void BufferCacheRuntime::Finish() {
336 336
337void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, 337void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
338 std::span<const VideoCommon::BufferCopy> copies, bool barrier) { 338 std::span<const VideoCommon::BufferCopy> copies, bool barrier) {
339 if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) {
340 return;
341 }
339 static constexpr VkMemoryBarrier READ_BARRIER{ 342 static constexpr VkMemoryBarrier READ_BARRIER{
340 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 343 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
341 .pNext = nullptr, 344 .pNext = nullptr,
@@ -394,6 +397,9 @@ void BufferCacheRuntime::PostCopyBarrier() {
394} 397}
395 398
396void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) { 399void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t size, u32 value) {
400 if (dest_buffer == VK_NULL_HANDLE) {
401 return;
402 }
397 static constexpr VkMemoryBarrier READ_BARRIER{ 403 static constexpr VkMemoryBarrier READ_BARRIER{
398 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 404 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
399 .pNext = nullptr, 405 .pNext = nullptr,
@@ -473,6 +479,11 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
473 cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); 479 cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
474 }); 480 });
475 } else { 481 } else {
482 if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
483 ReserveNullBuffer();
484 buffer = *null_buffer;
485 offset = 0;
486 }
476 scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) { 487 scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
477 cmdbuf.BindVertexBuffer(index, buffer, offset); 488 cmdbuf.BindVertexBuffer(index, buffer, offset);
478 }); 489 });
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 d11383bf1..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();
@@ -644,12 +645,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
644 .pNext = nullptr, 645 .pNext = nullptr,
645 .flags = 0, 646 .flags = 0,
646 .topology = input_assembly_topology, 647 .topology = input_assembly_topology,
647 .primitiveRestartEnable = dynamic.primitive_restart_enable != 0 && 648 .primitiveRestartEnable =
648 ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && 649 dynamic.primitive_restart_enable != 0 &&
649 device.IsTopologyListPrimitiveRestartSupported()) || 650 ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
650 SupportsPrimitiveRestart(input_assembly_topology) || 651 device.IsTopologyListPrimitiveRestartSupported()) ||
651 (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && 652 SupportsPrimitiveRestart(input_assembly_topology) ||
652 device.IsPatchListPrimitiveRestartSupported())), 653 (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
654 device.IsPatchListPrimitiveRestartSupported()))
655 ? VK_TRUE
656 : VK_FALSE,
653 }; 657 };
654 const VkPipelineTessellationStateCreateInfo tessellation_ci{ 658 const VkPipelineTessellationStateCreateInfo tessellation_ci{
655 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, 659 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
@@ -699,7 +703,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
699 .cullMode = static_cast<VkCullModeFlags>( 703 .cullMode = static_cast<VkCullModeFlags>(
700 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), 704 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
701 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), 705 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
702 .depthBiasEnable = (dynamic.depth_bias_enable == 0 ? VK_TRUE : VK_FALSE), 706 .depthBiasEnable = (dynamic.depth_bias_enable != 0 ? VK_TRUE : VK_FALSE),
703 .depthBiasConstantFactor = 0.0f, 707 .depthBiasConstantFactor = 0.0f,
704 .depthBiasClamp = 0.0f, 708 .depthBiasClamp = 0.0f,
705 .depthBiasSlopeFactor = 0.0f, 709 .depthBiasSlopeFactor = 0.0f,
@@ -894,27 +898,29 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
894 if (device.IsKhrPipelineExecutablePropertiesEnabled()) { 898 if (device.IsKhrPipelineExecutablePropertiesEnabled()) {
895 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; 899 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
896 } 900 }
897 pipeline = device.GetLogical().CreateGraphicsPipeline({ 901 pipeline = device.GetLogical().CreateGraphicsPipeline(
898 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 902 {
899 .pNext = nullptr, 903 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
900 .flags = flags, 904 .pNext = nullptr,
901 .stageCount = static_cast<u32>(shader_stages.size()), 905 .flags = flags,
902 .pStages = shader_stages.data(), 906 .stageCount = static_cast<u32>(shader_stages.size()),
903 .pVertexInputState = &vertex_input_ci, 907 .pStages = shader_stages.data(),
904 .pInputAssemblyState = &input_assembly_ci, 908 .pVertexInputState = &vertex_input_ci,
905 .pTessellationState = &tessellation_ci, 909 .pInputAssemblyState = &input_assembly_ci,
906 .pViewportState = &viewport_ci, 910 .pTessellationState = &tessellation_ci,
907 .pRasterizationState = &rasterization_ci, 911 .pViewportState = &viewport_ci,
908 .pMultisampleState = &multisample_ci, 912 .pRasterizationState = &rasterization_ci,
909 .pDepthStencilState = &depth_stencil_ci, 913 .pMultisampleState = &multisample_ci,
910 .pColorBlendState = &color_blend_ci, 914 .pDepthStencilState = &depth_stencil_ci,
911 .pDynamicState = &dynamic_state_ci, 915 .pColorBlendState = &color_blend_ci,
912 .layout = *pipeline_layout, 916 .pDynamicState = &dynamic_state_ci,
913 .renderPass = render_pass, 917 .layout = *pipeline_layout,
914 .subpass = 0, 918 .renderPass = render_pass,
915 .basePipelineHandle = nullptr, 919 .subpass = 0,
916 .basePipelineIndex = 0, 920 .basePipelineHandle = nullptr,
917 }); 921 .basePipelineIndex = 0,
922 },
923 *pipeline_cache);
918} 924}
919 925
920void 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_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 5c5bfa18d..8e77f5aa3 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -1487,6 +1487,9 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1487 1487
1488 max_push_descriptors = push_descriptor.maxPushDescriptors; 1488 max_push_descriptors = push_descriptor.maxPushDescriptors;
1489 } 1489 }
1490
1491 has_null_descriptor = true;
1492
1490 return extensions; 1493 return extensions;
1491} 1494}
1492 1495
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 920a8f4e3..6042046e1 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -397,6 +397,10 @@ public:
397 return must_emulate_bgr565; 397 return must_emulate_bgr565;
398 } 398 }
399 399
400 bool HasNullDescriptor() const {
401 return has_null_descriptor;
402 }
403
400 u32 GetMaxVertexInputAttributes() const { 404 u32 GetMaxVertexInputAttributes() const {
401 return max_vertex_input_attributes; 405 return max_vertex_input_attributes;
402 } 406 }
@@ -511,6 +515,7 @@ private:
511 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 515 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
512 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. 516 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
513 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. 517 bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
518 bool has_null_descriptor{}; ///< Has support for null descriptors.
514 u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline 519 u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline
515 u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline 520 u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline
516 521
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index 562039b56..b6d83e446 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -32,7 +32,7 @@
32namespace Vulkan { 32namespace Vulkan {
33namespace { 33namespace {
34[[nodiscard]] std::vector<const char*> RequiredExtensions( 34[[nodiscard]] std::vector<const char*> RequiredExtensions(
35 Core::Frontend::WindowSystemType window_type, bool enable_debug_utils) { 35 Core::Frontend::WindowSystemType window_type, bool enable_validation) {
36 std::vector<const char*> extensions; 36 std::vector<const char*> extensions;
37 extensions.reserve(6); 37 extensions.reserve(6);
38 switch (window_type) { 38 switch (window_type) {
@@ -65,7 +65,7 @@ namespace {
65 if (window_type != Core::Frontend::WindowSystemType::Headless) { 65 if (window_type != Core::Frontend::WindowSystemType::Headless) {
66 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); 66 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
67 } 67 }
68 if (enable_debug_utils) { 68 if (enable_validation) {
69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
70 } 70 }
71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); 71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
@@ -95,9 +95,9 @@ namespace {
95 return true; 95 return true;
96} 96}
97 97
98[[nodiscard]] std::vector<const char*> Layers(bool enable_layers) { 98[[nodiscard]] std::vector<const char*> Layers(bool enable_validation) {
99 std::vector<const char*> layers; 99 std::vector<const char*> layers;
100 if (enable_layers) { 100 if (enable_validation) {
101 layers.push_back("VK_LAYER_KHRONOS_validation"); 101 layers.push_back("VK_LAYER_KHRONOS_validation");
102 } 102 }
103 return layers; 103 return layers;
@@ -125,7 +125,7 @@ void RemoveUnavailableLayers(const vk::InstanceDispatch& dld, std::vector<const
125 125
126vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, 126vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld,
127 u32 required_version, Core::Frontend::WindowSystemType window_type, 127 u32 required_version, Core::Frontend::WindowSystemType window_type,
128 bool enable_debug_utils, bool enable_layers) { 128 bool enable_validation) {
129 if (!library.IsOpen()) { 129 if (!library.IsOpen()) {
130 LOG_ERROR(Render_Vulkan, "Vulkan library not available"); 130 LOG_ERROR(Render_Vulkan, "Vulkan library not available");
131 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 131 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
@@ -138,11 +138,11 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
138 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); 138 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
139 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 139 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
140 } 140 }
141 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_debug_utils); 141 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_validation);
142 if (!AreExtensionsSupported(dld, extensions)) { 142 if (!AreExtensionsSupported(dld, extensions)) {
143 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 143 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
144 } 144 }
145 std::vector<const char*> layers = Layers(enable_layers); 145 std::vector<const char*> layers = Layers(enable_validation);
146 RemoveUnavailableLayers(dld, layers); 146 RemoveUnavailableLayers(dld, layers);
147 147
148 const u32 available_version = vk::AvailableVersion(dld); 148 const u32 available_version = vk::AvailableVersion(dld);
diff --git a/src/video_core/vulkan_common/vulkan_instance.h b/src/video_core/vulkan_common/vulkan_instance.h
index 40419d802..b59b92f83 100644
--- a/src/video_core/vulkan_common/vulkan_instance.h
+++ b/src/video_core/vulkan_common/vulkan_instance.h
@@ -17,8 +17,7 @@ namespace Vulkan {
17 * @param dld Dispatch table to load function pointers into 17 * @param dld Dispatch table to load function pointers into
18 * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1) 18 * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1)
19 * @param window_type Window system type's enabled extension 19 * @param window_type Window system type's enabled extension
20 * @param enable_debug_utils Whether to enable VK_EXT_debug_utils_extension_name or not 20 * @param enable_validation Whether to enable Vulkan validation layers or not
21 * @param enable_layers Whether to enable Vulkan validation layers or not
22 * 21 *
23 * @return A new Vulkan instance 22 * @return A new Vulkan instance
24 * @throw vk::Exception on failure 23 * @throw vk::Exception on failure
@@ -26,6 +25,6 @@ namespace Vulkan {
26[[nodiscard]] vk::Instance CreateInstance( 25[[nodiscard]] vk::Instance CreateInstance(
27 const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version, 26 const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version,
28 Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless, 27 Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless,
29 bool enable_debug_utils = false, bool enable_layers = false); 28 bool enable_validation = false);
30 29
31} // namespace Vulkan 30} // namespace Vulkan
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);