From 16809c1fa7e2b4fb798749030645deaec79a2381 Mon Sep 17 00:00:00 2001 From: Wollnashorn Date: Fri, 30 Dec 2022 14:50:07 +0100 Subject: [PATCH 1/7] video_core/vulkan: Added `VkPipelineCache` to store Vulkan pipelines As an optional feature which can be enabled in the advanced graphics configuration, all pipelines that get built at the initial shader loading are stored in a VkPipelineCache object and are dumped to the disk. These vendor specific pipeline cache files are located at `/shader/GAME_ID/vulkan_pipelines.bin`. This feature was mainly added because of an issue with the AMD driver (see yuzu-emu#8507) causing invalidation of the cache files the driver builds automatically. --- src/common/settings.h | 2 + .../renderer_vulkan/vk_compute_pipeline.cpp | 42 ++++--- .../renderer_vulkan/vk_compute_pipeline.h | 4 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 51 ++++---- .../renderer_vulkan/vk_graphics_pipeline.h | 19 ++- .../renderer_vulkan/vk_pipeline_cache.cpp | 117 ++++++++++++++++-- .../renderer_vulkan/vk_pipeline_cache.h | 9 ++ .../vulkan_common/vulkan_wrapper.cpp | 27 +++- src/video_core/vulkan_common/vulkan_wrapper.h | 24 +++- src/yuzu/configuration/config.cpp | 2 + .../configure_graphics_advanced.cpp | 7 ++ .../configure_graphics_advanced.h | 1 + .../configure_graphics_advanced.ui | 10 ++ src/yuzu_cmd/config.cpp | 1 + src/yuzu_cmd/default_ini.h | 4 + 15 files changed, 253 insertions(+), 67 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index 5017951c5..0d58d6171 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -451,6 +451,8 @@ struct Values { SwitchableSetting use_asynchronous_shaders{false, "use_asynchronous_shaders"}; SwitchableSetting use_fast_gpu_time{true, "use_fast_gpu_time"}; SwitchableSetting use_pessimistic_flushes{false, "use_pessimistic_flushes"}; + SwitchableSetting use_vulkan_driver_pipeline_cache{false, + "use_vulkan_driver_pipeline_cache"}; SwitchableSetting bg_red{0, "bg_red"}; SwitchableSetting 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; using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; using Tegra::Texture::TexturePair; -ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, +ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_, + DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* thread_worker, PipelineStatistics* pipeline_statistics, VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, vk::ShaderModule spv_module_) - : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, + : device{device_}, pipeline_cache(pipeline_cache_), + update_descriptor_queue{update_descriptor_queue_}, info{info_}, spv_module(std::move(spv_module_)) { if (shader_notify) { shader_notify->MarkShaderBuilding(); @@ -56,23 +58,27 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript if (device.IsKhrPipelineExecutablePropertiesEnabled()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } - pipeline = device.GetLogical().CreateComputePipeline({ - .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = flags, - .stage{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_COMPUTE_BIT, - .module = *spv_module, - .pName = "main", - .pSpecializationInfo = nullptr, + pipeline = device.GetLogical().CreateComputePipeline( + { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = flags, + .stage{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = + device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = *spv_module, + .pName = "main", + .pSpecializationInfo = nullptr, + }, + .layout = *pipeline_layout, + .basePipelineHandle = 0, + .basePipelineIndex = 0, }, - .layout = *pipeline_layout, - .basePipelineHandle = 0, - .basePipelineIndex = 0, - }); + *pipeline_cache); + if (pipeline_statistics) { pipeline_statistics->Collect(*pipeline); } 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; class ComputePipeline { public: - explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, + explicit ComputePipeline(const Device& device, vk::PipelineCache& pipeline_cache, + DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue, Common::ThreadWorker* thread_worker, PipelineStatistics* pipeline_statistics, @@ -46,6 +47,7 @@ public: private: const Device& device; + vk::PipelineCache& pipeline_cache; UpdateDescriptorQueue& update_descriptor_queue; Shader::Info info; 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& m GraphicsPipeline::GraphicsPipeline( Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, - VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, + vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify, + const Device& device_, DescriptorPool& descriptor_pool, UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, std::array stages, const std::array& infos) - : key{key_}, device{device_}, texture_cache{texture_cache_}, - buffer_cache{buffer_cache_}, scheduler{scheduler_}, + : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, + pipeline_cache(pipeline_cache_), scheduler{scheduler_}, update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { if (shader_notify) { shader_notify->MarkShaderBuilding(); @@ -897,27 +898,29 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (device.IsKhrPipelineExecutablePropertiesEnabled()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } - pipeline = device.GetLogical().CreateGraphicsPipeline({ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = flags, - .stageCount = static_cast(shader_stages.size()), - .pStages = shader_stages.data(), - .pVertexInputState = &vertex_input_ci, - .pInputAssemblyState = &input_assembly_ci, - .pTessellationState = &tessellation_ci, - .pViewportState = &viewport_ci, - .pRasterizationState = &rasterization_ci, - .pMultisampleState = &multisample_ci, - .pDepthStencilState = &depth_stencil_ci, - .pColorBlendState = &color_blend_ci, - .pDynamicState = &dynamic_state_ci, - .layout = *pipeline_layout, - .renderPass = render_pass, - .subpass = 0, - .basePipelineHandle = nullptr, - .basePipelineIndex = 0, - }); + pipeline = device.GetLogical().CreateGraphicsPipeline( + { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = flags, + .stageCount = static_cast(shader_stages.size()), + .pStages = shader_stages.data(), + .pVertexInputState = &vertex_input_ci, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = &tessellation_ci, + .pViewportState = &viewport_ci, + .pRasterizationState = &rasterization_ci, + .pMultisampleState = &multisample_ci, + .pDepthStencilState = &depth_stencil_ci, + .pColorBlendState = &color_blend_ci, + .pDynamicState = &dynamic_state_ci, + .layout = *pipeline_layout, + .renderPass = render_pass, + .subpass = 0, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0, + }, + *pipeline_cache); } void 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 { static constexpr size_t NUM_STAGES = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; public: - explicit GraphicsPipeline(Scheduler& scheduler, BufferCache& buffer_cache, - TextureCache& texture_cache, VideoCore::ShaderNotify* shader_notify, - const Device& device, DescriptorPool& descriptor_pool, - UpdateDescriptorQueue& update_descriptor_queue, - Common::ThreadWorker* worker_thread, - PipelineStatistics* pipeline_statistics, - RenderPassCache& render_pass_cache, - const GraphicsPipelineCacheKey& key, - std::array stages, - const std::array& infos); + explicit GraphicsPipeline( + Scheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, + vk::PipelineCache& pipeline_cache, VideoCore::ShaderNotify* shader_notify, + const Device& device, DescriptorPool& descriptor_pool, + UpdateDescriptorQueue& update_descriptor_queue, Common::ThreadWorker* worker_thread, + PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, + const GraphicsPipelineCacheKey& key, std::array stages, + const std::array& infos); GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; @@ -133,6 +131,7 @@ private: const Device& device; TextureCache& texture_cache; BufferCache& buffer_cache; + vk::PipelineCache& pipeline_cache; Scheduler& scheduler; UpdateDescriptorQueue& update_descriptor_queue; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 3046b72ab..e0e2be90c 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; using VideoCommon::GraphicsEnvironment; constexpr u32 CACHE_VERSION = 10; +constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; template auto MakeSpan(Container& container) { @@ -284,6 +285,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device render_pass_cache{render_pass_cache_}, buffer_cache{buffer_cache_}, texture_cache{texture_cache_}, shader_notify{shader_notify_}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, + use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()}, workers(std::max(std::thread::hardware_concurrency(), 2U) - 1, "VkPipelineBuilder"), serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; @@ -362,7 +364,11 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device }; } -PipelineCache::~PipelineCache() = default; +PipelineCache::~PipelineCache() { + if (use_vulkan_pipeline_cache) { + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache); + } +} GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { MICROPROFILE_SCOPE(Vulkan_PipelineCache); @@ -418,6 +424,11 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading } pipeline_cache_filename = base_dir / "vulkan.bin"; + if (use_vulkan_pipeline_cache) { + vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin"; + vulkan_pipeline_cache = LoadVulkanPipelineCache(vulkan_pipeline_cache_filename); + } + struct { std::mutex mutex; size_t total{}; @@ -496,6 +507,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading workers.WaitForRequests(stop_loading); + if (use_vulkan_pipeline_cache) { + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache); + } + if (state.statistics) { state.statistics->Report(); } @@ -616,10 +631,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( previous_stage = &program; } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; - return std::make_unique(scheduler, buffer_cache, texture_cache, - &shader_notify, device, descriptor_pool, - update_descriptor_queue, thread_worker, statistics, - render_pass_cache, key, std::move(modules), infos); + return std::make_unique( + scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device, + descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key, + std::move(modules), infos); } catch (const Shader::Exception& exception) { LOG_ERROR(Render_Vulkan, "{}", exception.what()); @@ -689,13 +704,99 @@ std::unique_ptr PipelineCache::CreateComputePipeline( spv_module.SetObjectNameEXT(name.c_str()); } Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; - return std::make_unique(device, descriptor_pool, update_descriptor_queue, - thread_worker, statistics, &shader_notify, - program.info, std::move(spv_module)); + return std::make_unique(device, vulkan_pipeline_cache, descriptor_pool, + update_descriptor_queue, thread_worker, statistics, + &shader_notify, program.info, std::move(spv_module)); } catch (const Shader::Exception& exception) { LOG_ERROR(Render_Vulkan, "{}", exception.what()); return nullptr; } +void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename, + const vk::PipelineCache& pipeline_cache) try { + std::ofstream file(filename, std::ios::binary); + file.exceptions(std::ifstream::failbit); + if (!file.is_open()) { + LOG_ERROR(Common_Filesystem, "Failed to open Vulkan pipeline cache file {}", + Common::FS::PathToUTF8String(filename)); + return; + } + file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size()); + + size_t cache_size = 0; + std::vector cache_data; + if (pipeline_cache) { + pipeline_cache.Read(&cache_size, nullptr); + cache_data.resize(cache_size); + pipeline_cache.Read(&cache_size, cache_data.data()); + } + file.write(cache_data.data(), cache_size); + + LOG_INFO(Render_Vulkan, "Vulkan pipelines cached at: {}", + Common::FS::PathToUTF8String(filename)); + +} catch (const std::ios_base::failure& e) { + LOG_ERROR(Common_Filesystem, "{}", e.what()); + if (!Common::FS::RemoveFile(filename)) { + LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan pipeline cache file {}", + Common::FS::PathToUTF8String(filename)); + } +} + +vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::path& filename) { + const auto create_pipeline_cache = [this](size_t data_size, const void* data) { + VkPipelineCacheCreateInfo pipeline_cache_ci = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .initialDataSize = data_size, + .pInitialData = data}; + return device.GetLogical().CreatePipelineCache(pipeline_cache_ci); + }; + try { + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + return create_pipeline_cache(0, nullptr); + } + file.exceptions(std::ifstream::failbit); + const auto end{file.tellg()}; + file.seekg(0, std::ios::beg); + + std::array magic_number; + file.read(magic_number.data(), magic_number.size()); + if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) { + file.close(); + if (Common::FS::RemoveFile(filename)) { + if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) { + LOG_ERROR(Common_Filesystem, "Invalid Vulkan pipeline cache file"); + } + } else { + LOG_ERROR(Common_Filesystem, + "Invalid Vulkan pipeline cache file and failed to delete it in \"{}\"", + Common::FS::PathToUTF8String(filename)); + } + return create_pipeline_cache(0, nullptr); + } + + const size_t cache_size = static_cast(end) - magic_number.size(); + std::vector cache_data(cache_size); + file.read(cache_data.data(), cache_size); + + LOG_INFO(Render_Vulkan, + "Loaded Vulkan pipeline cache: ", Common::FS::PathToUTF8String(filename)); + + return create_pipeline_cache(cache_size, cache_data.data()); + + } catch (const std::ios_base::failure& e) { + LOG_ERROR(Common_Filesystem, "{}", e.what()); + if (!Common::FS::RemoveFile(filename)) { + LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan pipeline cache file {}", + Common::FS::PathToUTF8String(filename)); + } + + return create_pipeline_cache(0, nullptr); + } +} + } // 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..cf3bd6b85 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -135,6 +135,11 @@ private: PipelineStatistics* statistics, bool build_in_parallel); + void SerializeVulkanPipelineCache(const std::filesystem::path& filename, + const vk::PipelineCache& pipeline_cache); + + vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename); + const Device& device; Scheduler& scheduler; DescriptorPool& descriptor_pool; @@ -144,6 +149,7 @@ private: TextureCache& texture_cache; VideoCore::ShaderNotify& shader_notify; bool use_asynchronous_shaders{}; + bool use_vulkan_pipeline_cache{}; GraphicsPipelineCacheKey graphics_key{}; GraphicsPipeline* current_pipeline{}; @@ -158,6 +164,9 @@ private: std::filesystem::path pipeline_cache_filename; + std::filesystem::path vulkan_pipeline_cache_filename; + vk::PipelineCache vulkan_pipeline_cache; + Common::ThreadWorker workers; Common::ThreadWorker serialization_thread; 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 { X(vkCreateGraphicsPipelines); X(vkCreateImage); X(vkCreateImageView); + X(vkCreatePipelineCache); X(vkCreatePipelineLayout); X(vkCreateQueryPool); X(vkCreateRenderPass); @@ -171,6 +172,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkDestroyImage); X(vkDestroyImageView); X(vkDestroyPipeline); + X(vkDestroyPipelineCache); X(vkDestroyPipelineLayout); X(vkDestroyQueryPool); X(vkDestroyRenderPass); @@ -188,6 +190,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkGetEventStatus); X(vkGetFenceStatus); X(vkGetImageMemoryRequirements); + X(vkGetPipelineCacheData); X(vkGetMemoryFdKHR); #ifdef _WIN32 X(vkGetMemoryWin32HandleKHR); @@ -431,6 +434,10 @@ void Destroy(VkDevice device, VkPipeline handle, const DeviceDispatch& dld) noex dld.vkDestroyPipeline(device, handle, nullptr); } +void Destroy(VkDevice device, VkPipelineCache handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyPipelineCache(device, handle, nullptr); +} + void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept { dld.vkDestroyPipelineLayout(device, handle, nullptr); } @@ -651,6 +658,10 @@ void ShaderModule::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SHADER_MODULE, name); } +void PipelineCache::SetObjectNameEXT(const char* name) const { + SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_PIPELINE_CACHE, name); +} + void Semaphore::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_SEMAPHORE, name); } @@ -746,21 +757,29 @@ DescriptorSetLayout Device::CreateDescriptorSetLayout( return DescriptorSetLayout(object, handle, *dld); } +PipelineCache Device::CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const { + VkPipelineCache cache; + Check(dld->vkCreatePipelineCache(handle, &ci, nullptr, &cache)); + return PipelineCache(cache, handle, *dld); +} + PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const { VkPipelineLayout object; Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object)); return PipelineLayout(object, handle, *dld); } -Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const { +Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci, + VkPipelineCache cache) const { VkPipeline object; - Check(dld->vkCreateGraphicsPipelines(handle, nullptr, 1, &ci, nullptr, &object)); + Check(dld->vkCreateGraphicsPipelines(handle, cache, 1, &ci, nullptr, &object)); return Pipeline(object, handle, *dld); } -Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const { +Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci, + VkPipelineCache cache) const { VkPipeline object; - Check(dld->vkCreateComputePipelines(handle, nullptr, 1, &ci, nullptr, &object)); + Check(dld->vkCreateComputePipelines(handle, cache, 1, &ci, nullptr, &object)); return Pipeline(object, handle, *dld); } 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 { PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{}; PFN_vkCreateImage vkCreateImage{}; PFN_vkCreateImageView vkCreateImageView{}; + PFN_vkCreatePipelineCache vkCreatePipelineCache{}; PFN_vkCreatePipelineLayout vkCreatePipelineLayout{}; PFN_vkCreateQueryPool vkCreateQueryPool{}; PFN_vkCreateRenderPass vkCreateRenderPass{}; @@ -289,6 +290,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkDestroyImage vkDestroyImage{}; PFN_vkDestroyImageView vkDestroyImageView{}; PFN_vkDestroyPipeline vkDestroyPipeline{}; + PFN_vkDestroyPipelineCache vkDestroyPipelineCache{}; PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{}; PFN_vkDestroyQueryPool vkDestroyQueryPool{}; PFN_vkDestroyRenderPass vkDestroyRenderPass{}; @@ -306,6 +308,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkGetEventStatus vkGetEventStatus{}; PFN_vkGetFenceStatus vkGetFenceStatus{}; PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{}; + PFN_vkGetPipelineCacheData vkGetPipelineCacheData{}; PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{}; #ifdef _WIN32 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; @@ -351,6 +354,7 @@ void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkPipelineCache, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; @@ -773,6 +777,18 @@ public: void SetObjectNameEXT(const char* name) const; }; +class PipelineCache : public Handle { + using Handle::Handle; + +public: + /// Set object name. + void SetObjectNameEXT(const char* name) const; + + VkResult Read(size_t* size, void* data) const noexcept { + return dld->vkGetPipelineCacheData(owner, handle, size, data); + } +}; + class Semaphore : public Handle { using Handle::Handle; @@ -844,11 +860,15 @@ public: DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const; + PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const; + PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; - Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const; + Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci, + VkPipelineCache cache = nullptr) const; - Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const; + Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci, + VkPipelineCache cache = nullptr) const; Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; 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() { ReadGlobalSetting(Settings::values.use_asynchronous_shaders); ReadGlobalSetting(Settings::values.use_fast_gpu_time); ReadGlobalSetting(Settings::values.use_pessimistic_flushes); + ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); ReadGlobalSetting(Settings::values.bg_red); ReadGlobalSetting(Settings::values.bg_green); ReadGlobalSetting(Settings::values.bg_blue); @@ -1348,6 +1349,7 @@ void Config::SaveRendererValues() { WriteGlobalSetting(Settings::values.use_asynchronous_shaders); WriteGlobalSetting(Settings::values.use_fast_gpu_time); WriteGlobalSetting(Settings::values.use_pessimistic_flushes); + WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache); WriteGlobalSetting(Settings::values.bg_red); WriteGlobalSetting(Settings::values.bg_green); 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..b3862e707 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -29,6 +29,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue()); + ui->use_vulkan_driver_pipeline_cache->setChecked( + Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); if (Settings::IsConfiguringGlobal()) { ui->gpu_accuracy->setCurrentIndex( @@ -58,6 +60,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { ui->use_fast_gpu_time, use_fast_gpu_time); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes, ui->use_pessimistic_flushes, use_pessimistic_flushes); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vulkan_driver_pipeline_cache, + ui->use_vulkan_driver_pipeline_cache, + use_vulkan_driver_pipeline_cache); } void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { @@ -82,6 +87,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); ui->use_pessimistic_flushes->setEnabled( Settings::values.use_pessimistic_flushes.UsingGlobal()); + ui->use_vulkan_driver_pipeline_cache->setEnabled( + Settings::values.use_vulkan_driver_pipeline_cache.UsingGlobal()); ui->anisotropic_filtering_combobox->setEnabled( Settings::values.max_anisotropy.UsingGlobal()); 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: ConfigurationShared::CheckState use_asynchronous_shaders; ConfigurationShared::CheckState use_fast_gpu_time; ConfigurationShared::CheckState use_pessimistic_flushes; + ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; const Core::System& system; }; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 87a121471..67878b057 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -109,6 +109,16 @@ + + + + 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 automatically. + + + Use Vulkan pipeline cache (Hack) + + + 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() { ReadSetting("Renderer", Settings::values.accelerate_astc); ReadSetting("Renderer", Settings::values.use_fast_gpu_time); ReadSetting("Renderer", Settings::values.use_pessimistic_flushes); + ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); ReadSetting("Renderer", Settings::values.bg_red); ReadSetting("Renderer", Settings::values.bg_green); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 6fcf04e1b..7f8c8af0c 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -350,6 +350,10 @@ use_fast_gpu_time = # 0: Off (default), 1: On use_pessimistic_flushes = +# Enables storing a cache file for Vulkan pipelines, which can significantly improve shader load time. +# 0: Off (default), 1: On +use_vulkan_driver_pipeline_cache = + # Whether to use garbage collection or not for GPU caches. # 0 (default): Off, 1: On use_caches_gc = From 67d4f190f7fd5e607096bfecf9cbf1e7b0fd802d Mon Sep 17 00:00:00 2001 From: Wollnashorn Date: Fri, 30 Dec 2022 19:31:01 +0100 Subject: [PATCH 2/7] yuzu-cmd: Removed `use_vulkan_driver_pipeline_cache` from default_ini.h The addition of the use_vulkan_driver_pipeline_cache option into the default ini string literal caused the 16,384-byte limit of the MSVC compiler to be exceeded. --- src/yuzu_cmd/default_ini.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 7f8c8af0c..6fcf04e1b 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -350,10 +350,6 @@ use_fast_gpu_time = # 0: Off (default), 1: On use_pessimistic_flushes = -# Enables storing a cache file for Vulkan pipelines, which can significantly improve shader load time. -# 0: Off (default), 1: On -use_vulkan_driver_pipeline_cache = - # Whether to use garbage collection or not for GPU caches. # 0 (default): Off, 1: On use_caches_gc = From f4626512ff67f6a27235b831111330b42313c312 Mon Sep 17 00:00:00 2001 From: Wollnashorn Date: Sat, 31 Dec 2022 02:49:08 +0100 Subject: [PATCH 3/7] config: Better wording for VK pipeline cache option and enable by default --- src/common/settings.h | 2 +- src/yuzu/configuration/configure_graphics_advanced.ui | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index 0d58d6171..d9e82087d 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -451,7 +451,7 @@ struct Values { SwitchableSetting use_asynchronous_shaders{false, "use_asynchronous_shaders"}; SwitchableSetting use_fast_gpu_time{true, "use_fast_gpu_time"}; SwitchableSetting use_pessimistic_flushes{false, "use_pessimistic_flushes"}; - SwitchableSetting use_vulkan_driver_pipeline_cache{false, + SwitchableSetting use_vulkan_driver_pipeline_cache{true, "use_vulkan_driver_pipeline_cache"}; SwitchableSetting bg_red{0, "bg_red"}; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 67878b057..ccbdcf08f 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -112,10 +112,10 @@ - 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 automatically. + 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. - Use Vulkan pipeline cache (Hack) + Use Vulkan pipeline cache From f2aa8166796742b16b1a09204a63cb990a311100 Mon Sep 17 00:00:00 2001 From: Wollnashorn Date: Sat, 31 Dec 2022 21:39:14 +0100 Subject: [PATCH 4/7] video_core/vulkan: Added check if Vulkan pipeline path has been set --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index e0e2be90c..7bded3ec5 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -365,7 +365,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device } PipelineCache::~PipelineCache() { - if (use_vulkan_pipeline_cache) { + if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) { SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache); } } From 8945fafcc0ee6c28dcd956e1ffe82df72b0d9d4c Mon Sep 17 00:00:00 2001 From: Wollnashorn Date: Mon, 2 Jan 2023 02:51:07 +0100 Subject: [PATCH 5/7] config: Set the Vulkan driver pipeline cache option to be global --- src/common/settings.cpp | 1 + src/yuzu/configuration/configure_graphics_advanced.cpp | 3 +++ 2 files changed, 4 insertions(+) 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) { values.use_asynchronous_shaders.SetGlobal(true); values.use_fast_gpu_time.SetGlobal(true); values.use_pessimistic_flushes.SetGlobal(true); + values.use_vulkan_driver_pipeline_cache.SetGlobal(true); values.bg_red.SetGlobal(true); values.bg_green.SetGlobal(true); values.bg_blue.SetGlobal(true); diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index b3862e707..a3fbe2ad0 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -104,6 +104,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes, Settings::values.use_pessimistic_flushes, use_pessimistic_flushes); + ConfigurationShared::SetColoredTristate(ui->use_vulkan_driver_pipeline_cache, + Settings::values.use_vulkan_driver_pipeline_cache, + use_vulkan_driver_pipeline_cache); ConfigurationShared::SetColoredComboBox( ui->gpu_accuracy, ui->label_gpu_accuracy, static_cast(Settings::values.gpu_accuracy.GetValue(true))); From 9c9008ac813161bfbb6489ee128199e27c9515f7 Mon Sep 17 00:00:00 2001 From: Wollnashorn Date: Tue, 3 Jan 2023 04:18:45 +0100 Subject: [PATCH 6/7] video_core/vulkan: Driver pipeline cache will now be deleted with the shader cache --- src/yuzu/main.cpp | 20 +++++++++++++++++++- src/yuzu/main.h | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) 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 } switch (target) { - case GameListRemoveTarget::GlShaderCache: case GameListRemoveTarget::VkShaderCache: + RemoveVulkanDriverPipelineCache(program_id); + [[fallthrough]]; + case GameListRemoveTarget::GlShaderCache: RemoveTransferableShaderCache(program_id, target); break; case GameListRemoveTarget::AllShaderCache: @@ -2271,6 +2273,22 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTa } } +void GMainWindow::RemoveVulkanDriverPipelineCache(u64 program_id) { + static constexpr std::string_view target_file_name = "vulkan_pipelines.bin"; + + const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); + const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id); + const auto target_file = shader_cache_folder_path / target_file_name; + + if (!Common::FS::Exists(target_file)) { + return; + } + if (!Common::FS::RemoveFile(target_file)) { + QMessageBox::warning(this, tr("Error Removing Vulkan Driver Pipeline Cache"), + tr("Failed to remove the driver pipeline cache.")); + } +} + void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) { const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); 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: void RemoveUpdateContent(u64 program_id, InstalledEntryType type); void RemoveAddOnContent(u64 program_id, InstalledEntryType type); void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); + void RemoveVulkanDriverPipelineCache(u64 program_id); void RemoveAllTransferableShaderCaches(u64 program_id); void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); std::optional SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); From e07976a22bcbfe8a2a3c166cc35c654f9b893a5d Mon Sep 17 00:00:00 2001 From: Wollnashorn Date: Thu, 5 Jan 2023 04:36:17 +0100 Subject: [PATCH 7/7] video_core/vulkan: Vulkan driver pipelines now contain cache version So that old cache can get deleted when the cache version changes and does not grow infinitely --- .../renderer_vulkan/vk_pipeline_cache.cpp | 39 ++++++++++++------- .../renderer_vulkan/vk_pipeline_cache.h | 5 ++- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 7bded3ec5..67e5bc648 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -366,7 +366,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device PipelineCache::~PipelineCache() { if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) { - SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache); + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, + CACHE_VERSION); } } @@ -426,7 +427,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading if (use_vulkan_pipeline_cache) { vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin"; - vulkan_pipeline_cache = LoadVulkanPipelineCache(vulkan_pipeline_cache_filename); + vulkan_pipeline_cache = + LoadVulkanPipelineCache(vulkan_pipeline_cache_filename, CACHE_VERSION); } struct { @@ -508,7 +510,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading workers.WaitForRequests(stop_loading); if (use_vulkan_pipeline_cache) { - SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache); + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, + CACHE_VERSION); } if (state.statistics) { @@ -714,15 +717,17 @@ std::unique_ptr PipelineCache::CreateComputePipeline( } void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename, - const vk::PipelineCache& pipeline_cache) try { + const vk::PipelineCache& pipeline_cache, + u32 cache_version) try { std::ofstream file(filename, std::ios::binary); file.exceptions(std::ifstream::failbit); if (!file.is_open()) { - LOG_ERROR(Common_Filesystem, "Failed to open Vulkan pipeline cache file {}", + LOG_ERROR(Common_Filesystem, "Failed to open Vulkan driver pipeline cache file {}", Common::FS::PathToUTF8String(filename)); return; } - file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size()); + file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size()) + .write(reinterpret_cast(&cache_version), sizeof(cache_version)); size_t cache_size = 0; std::vector cache_data; @@ -733,18 +738,19 @@ void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& fi } file.write(cache_data.data(), cache_size); - LOG_INFO(Render_Vulkan, "Vulkan pipelines cached at: {}", + LOG_INFO(Render_Vulkan, "Vulkan driver pipelines cached at: {}", Common::FS::PathToUTF8String(filename)); } catch (const std::ios_base::failure& e) { LOG_ERROR(Common_Filesystem, "{}", e.what()); if (!Common::FS::RemoveFile(filename)) { - LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan pipeline cache file {}", + LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}", Common::FS::PathToUTF8String(filename)); } } -vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::path& filename) { +vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::path& filename, + u32 expected_cache_version) { const auto create_pipeline_cache = [this](size_t data_size, const void* data) { VkPipelineCacheCreateInfo pipeline_cache_ci = { .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, @@ -764,12 +770,17 @@ vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem:: file.seekg(0, std::ios::beg); std::array magic_number; - file.read(magic_number.data(), magic_number.size()); - if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) { + u32 cache_version; + file.read(magic_number.data(), magic_number.size()) + .read(reinterpret_cast(&cache_version), sizeof(cache_version)); + if (magic_number != VULKAN_CACHE_MAGIC_NUMBER || cache_version != expected_cache_version) { file.close(); if (Common::FS::RemoveFile(filename)) { if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) { - LOG_ERROR(Common_Filesystem, "Invalid Vulkan pipeline cache file"); + LOG_ERROR(Common_Filesystem, "Invalid Vulkan driver pipeline cache file"); + } + if (cache_version != expected_cache_version) { + LOG_INFO(Common_Filesystem, "Deleting old Vulkan driver pipeline cache"); } } else { LOG_ERROR(Common_Filesystem, @@ -784,14 +795,14 @@ vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem:: file.read(cache_data.data(), cache_size); LOG_INFO(Render_Vulkan, - "Loaded Vulkan pipeline cache: ", Common::FS::PathToUTF8String(filename)); + "Loaded Vulkan driver pipeline cache: ", Common::FS::PathToUTF8String(filename)); return create_pipeline_cache(cache_size, cache_data.data()); } catch (const std::ios_base::failure& e) { LOG_ERROR(Common_Filesystem, "{}", e.what()); if (!Common::FS::RemoveFile(filename)) { - LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan pipeline cache file {}", + LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan driver pipeline cache file {}", Common::FS::PathToUTF8String(filename)); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index cf3bd6b85..5171912d7 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -136,9 +136,10 @@ private: bool build_in_parallel); void SerializeVulkanPipelineCache(const std::filesystem::path& filename, - const vk::PipelineCache& pipeline_cache); + const vk::PipelineCache& pipeline_cache, u32 cache_version); - vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename); + vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename, + u32 expected_cache_version); const Device& device; Scheduler& scheduler;