diff options
| author | 2022-12-30 14:50:07 +0100 | |
|---|---|---|
| committer | 2023-01-05 21:02:44 +0100 | |
| commit | 16809c1fa7e2b4fb798749030645deaec79a2381 (patch) | |
| tree | e43ef31843da210165c529494652014a660268e6 /src/video_core/renderer_vulkan | |
| parent | Merge pull request #9557 from FernandoS27/ooops-i-killed-the-shitty-drivers (diff) | |
| download | yuzu-16809c1fa7e2b4fb798749030645deaec79a2381.tar.gz yuzu-16809c1fa7e2b4fb798749030645deaec79a2381.tar.xz yuzu-16809c1fa7e2b4fb798749030645deaec79a2381.zip | |
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.
Diffstat (limited to 'src/video_core/renderer_vulkan')
6 files changed, 181 insertions, 61 deletions
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; | |||
| 24 | using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; | 24 | using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; |
| 25 | using Tegra::Texture::TexturePair; | 25 | using Tegra::Texture::TexturePair; |
| 26 | 26 | ||
| 27 | ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, | 27 | ComputePipeline::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 | ||
| 29 | class ComputePipeline { | 29 | class ComputePipeline { |
| 30 | public: | 30 | public: |
| 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 | ||
| 47 | private: | 48 | private: |
| 48 | const Device& device; | 49 | const Device& device; |
| 50 | vk::PipelineCache& pipeline_cache; | ||
| 49 | UpdateDescriptorQueue& update_descriptor_queue; | 51 | UpdateDescriptorQueue& update_descriptor_queue; |
| 50 | Shader::Info info; | 52 | Shader::Info info; |
| 51 | 53 | ||
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 734c379b9..f91bb5a1d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -234,13 +234,14 @@ ConfigureFuncPtr ConfigureFunc(const std::array<vk::ShaderModule, NUM_STAGES>& m | |||
| 234 | 234 | ||
| 235 | GraphicsPipeline::GraphicsPipeline( | 235 | GraphicsPipeline::GraphicsPipeline( |
| 236 | Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, | 236 | Scheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, |
| 237 | VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, | 237 | vk::PipelineCache& pipeline_cache_, VideoCore::ShaderNotify* shader_notify, |
| 238 | const Device& device_, DescriptorPool& descriptor_pool, | ||
| 238 | UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, | 239 | UpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, |
| 239 | PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, | 240 | PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache, |
| 240 | const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, | 241 | const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages, |
| 241 | const std::array<const Shader::Info*, NUM_STAGES>& infos) | 242 | const std::array<const Shader::Info*, NUM_STAGES>& infos) |
| 242 | : key{key_}, device{device_}, texture_cache{texture_cache_}, | 243 | : key{key_}, device{device_}, texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, |
| 243 | buffer_cache{buffer_cache_}, scheduler{scheduler_}, | 244 | pipeline_cache(pipeline_cache_), scheduler{scheduler_}, |
| 244 | update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { | 245 | update_descriptor_queue{update_descriptor_queue_}, spv_modules{std::move(stages)} { |
| 245 | if (shader_notify) { | 246 | if (shader_notify) { |
| 246 | shader_notify->MarkShaderBuilding(); | 247 | shader_notify->MarkShaderBuilding(); |
| @@ -897,27 +898,29 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 897 | if (device.IsKhrPipelineExecutablePropertiesEnabled()) { | 898 | if (device.IsKhrPipelineExecutablePropertiesEnabled()) { |
| 898 | flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; | 899 | flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; |
| 899 | } | 900 | } |
| 900 | pipeline = device.GetLogical().CreateGraphicsPipeline({ | 901 | pipeline = device.GetLogical().CreateGraphicsPipeline( |
| 901 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | 902 | { |
| 902 | .pNext = nullptr, | 903 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, |
| 903 | .flags = flags, | 904 | .pNext = nullptr, |
| 904 | .stageCount = static_cast<u32>(shader_stages.size()), | 905 | .flags = flags, |
| 905 | .pStages = shader_stages.data(), | 906 | .stageCount = static_cast<u32>(shader_stages.size()), |
| 906 | .pVertexInputState = &vertex_input_ci, | 907 | .pStages = shader_stages.data(), |
| 907 | .pInputAssemblyState = &input_assembly_ci, | 908 | .pVertexInputState = &vertex_input_ci, |
| 908 | .pTessellationState = &tessellation_ci, | 909 | .pInputAssemblyState = &input_assembly_ci, |
| 909 | .pViewportState = &viewport_ci, | 910 | .pTessellationState = &tessellation_ci, |
| 910 | .pRasterizationState = &rasterization_ci, | 911 | .pViewportState = &viewport_ci, |
| 911 | .pMultisampleState = &multisample_ci, | 912 | .pRasterizationState = &rasterization_ci, |
| 912 | .pDepthStencilState = &depth_stencil_ci, | 913 | .pMultisampleState = &multisample_ci, |
| 913 | .pColorBlendState = &color_blend_ci, | 914 | .pDepthStencilState = &depth_stencil_ci, |
| 914 | .pDynamicState = &dynamic_state_ci, | 915 | .pColorBlendState = &color_blend_ci, |
| 915 | .layout = *pipeline_layout, | 916 | .pDynamicState = &dynamic_state_ci, |
| 916 | .renderPass = render_pass, | 917 | .layout = *pipeline_layout, |
| 917 | .subpass = 0, | 918 | .renderPass = render_pass, |
| 918 | .basePipelineHandle = nullptr, | 919 | .subpass = 0, |
| 919 | .basePipelineIndex = 0, | 920 | .basePipelineHandle = nullptr, |
| 920 | }); | 921 | .basePipelineIndex = 0, |
| 922 | }, | ||
| 923 | *pipeline_cache); | ||
| 921 | } | 924 | } |
| 922 | 925 | ||
| 923 | void GraphicsPipeline::Validate() { | 926 | 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 { | |||
| 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 | ||
| 72 | public: | 72 | public: |
| 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..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; | |||
| 55 | using VideoCommon::GraphicsEnvironment; | 55 | using VideoCommon::GraphicsEnvironment; |
| 56 | 56 | ||
| 57 | constexpr u32 CACHE_VERSION = 10; | 57 | constexpr u32 CACHE_VERSION = 10; |
| 58 | constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; | ||
| 58 | 59 | ||
| 59 | template <typename Container> | 60 | template <typename Container> |
| 60 | auto MakeSpan(Container& container) { | 61 | auto 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,11 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 362 | }; | 364 | }; |
| 363 | } | 365 | } |
| 364 | 366 | ||
| 365 | PipelineCache::~PipelineCache() = default; | 367 | PipelineCache::~PipelineCache() { |
| 368 | if (use_vulkan_pipeline_cache) { | ||
| 369 | SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache); | ||
| 370 | } | ||
| 371 | } | ||
| 366 | 372 | ||
| 367 | GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { | 373 | GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { |
| 368 | MICROPROFILE_SCOPE(Vulkan_PipelineCache); | 374 | MICROPROFILE_SCOPE(Vulkan_PipelineCache); |
| @@ -418,6 +424,11 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading | |||
| 418 | } | 424 | } |
| 419 | pipeline_cache_filename = base_dir / "vulkan.bin"; | 425 | pipeline_cache_filename = base_dir / "vulkan.bin"; |
| 420 | 426 | ||
| 427 | if (use_vulkan_pipeline_cache) { | ||
| 428 | vulkan_pipeline_cache_filename = base_dir / "vulkan_pipelines.bin"; | ||
| 429 | vulkan_pipeline_cache = LoadVulkanPipelineCache(vulkan_pipeline_cache_filename); | ||
| 430 | } | ||
| 431 | |||
| 421 | struct { | 432 | struct { |
| 422 | std::mutex mutex; | 433 | std::mutex mutex; |
| 423 | size_t total{}; | 434 | size_t total{}; |
| @@ -496,6 +507,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading | |||
| 496 | 507 | ||
| 497 | workers.WaitForRequests(stop_loading); | 508 | workers.WaitForRequests(stop_loading); |
| 498 | 509 | ||
| 510 | if (use_vulkan_pipeline_cache) { | ||
| 511 | SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache); | ||
| 512 | } | ||
| 513 | |||
| 499 | if (state.statistics) { | 514 | if (state.statistics) { |
| 500 | state.statistics->Report(); | 515 | state.statistics->Report(); |
| 501 | } | 516 | } |
| @@ -616,10 +631,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( | |||
| 616 | previous_stage = &program; | 631 | previous_stage = &program; |
| 617 | } | 632 | } |
| 618 | Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; | 633 | Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; |
| 619 | return std::make_unique<GraphicsPipeline>(scheduler, buffer_cache, texture_cache, | 634 | return std::make_unique<GraphicsPipeline>( |
| 620 | &shader_notify, device, descriptor_pool, | 635 | scheduler, buffer_cache, texture_cache, vulkan_pipeline_cache, &shader_notify, device, |
| 621 | update_descriptor_queue, thread_worker, statistics, | 636 | descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key, |
| 622 | render_pass_cache, key, std::move(modules), infos); | 637 | std::move(modules), infos); |
| 623 | 638 | ||
| 624 | } catch (const Shader::Exception& exception) { | 639 | } catch (const Shader::Exception& exception) { |
| 625 | LOG_ERROR(Render_Vulkan, "{}", exception.what()); | 640 | LOG_ERROR(Render_Vulkan, "{}", exception.what()); |
| @@ -689,13 +704,99 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( | |||
| 689 | spv_module.SetObjectNameEXT(name.c_str()); | 704 | spv_module.SetObjectNameEXT(name.c_str()); |
| 690 | } | 705 | } |
| 691 | Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; | 706 | Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; |
| 692 | return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, | 707 | return std::make_unique<ComputePipeline>(device, vulkan_pipeline_cache, descriptor_pool, |
| 693 | thread_worker, statistics, &shader_notify, | 708 | update_descriptor_queue, thread_worker, statistics, |
| 694 | program.info, std::move(spv_module)); | 709 | &shader_notify, program.info, std::move(spv_module)); |
| 695 | 710 | ||
| 696 | } catch (const Shader::Exception& exception) { | 711 | } catch (const Shader::Exception& exception) { |
| 697 | LOG_ERROR(Render_Vulkan, "{}", exception.what()); | 712 | LOG_ERROR(Render_Vulkan, "{}", exception.what()); |
| 698 | return nullptr; | 713 | return nullptr; |
| 699 | } | 714 | } |
| 700 | 715 | ||
| 716 | void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename, | ||
| 717 | const vk::PipelineCache& pipeline_cache) try { | ||
| 718 | std::ofstream file(filename, std::ios::binary); | ||
| 719 | file.exceptions(std::ifstream::failbit); | ||
| 720 | if (!file.is_open()) { | ||
| 721 | LOG_ERROR(Common_Filesystem, "Failed to open Vulkan pipeline cache file {}", | ||
| 722 | Common::FS::PathToUTF8String(filename)); | ||
| 723 | return; | ||
| 724 | } | ||
| 725 | file.write(VULKAN_CACHE_MAGIC_NUMBER.data(), VULKAN_CACHE_MAGIC_NUMBER.size()); | ||
| 726 | |||
| 727 | size_t cache_size = 0; | ||
| 728 | std::vector<char> cache_data; | ||
| 729 | if (pipeline_cache) { | ||
| 730 | pipeline_cache.Read(&cache_size, nullptr); | ||
| 731 | cache_data.resize(cache_size); | ||
| 732 | pipeline_cache.Read(&cache_size, cache_data.data()); | ||
| 733 | } | ||
| 734 | file.write(cache_data.data(), cache_size); | ||
| 735 | |||
| 736 | LOG_INFO(Render_Vulkan, "Vulkan pipelines cached at: {}", | ||
| 737 | Common::FS::PathToUTF8String(filename)); | ||
| 738 | |||
| 739 | } catch (const std::ios_base::failure& e) { | ||
| 740 | LOG_ERROR(Common_Filesystem, "{}", e.what()); | ||
| 741 | if (!Common::FS::RemoveFile(filename)) { | ||
| 742 | LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan pipeline cache file {}", | ||
| 743 | Common::FS::PathToUTF8String(filename)); | ||
| 744 | } | ||
| 745 | } | ||
| 746 | |||
| 747 | vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::path& filename) { | ||
| 748 | const auto create_pipeline_cache = [this](size_t data_size, const void* data) { | ||
| 749 | VkPipelineCacheCreateInfo pipeline_cache_ci = { | ||
| 750 | .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, | ||
| 751 | .pNext = nullptr, | ||
| 752 | .flags = 0, | ||
| 753 | .initialDataSize = data_size, | ||
| 754 | .pInitialData = data}; | ||
| 755 | return device.GetLogical().CreatePipelineCache(pipeline_cache_ci); | ||
| 756 | }; | ||
| 757 | try { | ||
| 758 | std::ifstream file(filename, std::ios::binary | std::ios::ate); | ||
| 759 | if (!file.is_open()) { | ||
| 760 | return create_pipeline_cache(0, nullptr); | ||
| 761 | } | ||
| 762 | file.exceptions(std::ifstream::failbit); | ||
| 763 | const auto end{file.tellg()}; | ||
| 764 | file.seekg(0, std::ios::beg); | ||
| 765 | |||
| 766 | std::array<char, 8> magic_number; | ||
| 767 | file.read(magic_number.data(), magic_number.size()); | ||
| 768 | if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) { | ||
| 769 | file.close(); | ||
| 770 | if (Common::FS::RemoveFile(filename)) { | ||
| 771 | if (magic_number != VULKAN_CACHE_MAGIC_NUMBER) { | ||
| 772 | LOG_ERROR(Common_Filesystem, "Invalid Vulkan pipeline cache file"); | ||
| 773 | } | ||
| 774 | } else { | ||
| 775 | LOG_ERROR(Common_Filesystem, | ||
| 776 | "Invalid Vulkan pipeline cache file and failed to delete it in \"{}\"", | ||
| 777 | Common::FS::PathToUTF8String(filename)); | ||
| 778 | } | ||
| 779 | return create_pipeline_cache(0, nullptr); | ||
| 780 | } | ||
| 781 | |||
| 782 | const size_t cache_size = static_cast<size_t>(end) - magic_number.size(); | ||
| 783 | std::vector<char> cache_data(cache_size); | ||
| 784 | file.read(cache_data.data(), cache_size); | ||
| 785 | |||
| 786 | LOG_INFO(Render_Vulkan, | ||
| 787 | "Loaded Vulkan pipeline cache: ", Common::FS::PathToUTF8String(filename)); | ||
| 788 | |||
| 789 | return create_pipeline_cache(cache_size, cache_data.data()); | ||
| 790 | |||
| 791 | } catch (const std::ios_base::failure& e) { | ||
| 792 | LOG_ERROR(Common_Filesystem, "{}", e.what()); | ||
| 793 | if (!Common::FS::RemoveFile(filename)) { | ||
| 794 | LOG_ERROR(Common_Filesystem, "Failed to delete Vulkan pipeline cache file {}", | ||
| 795 | Common::FS::PathToUTF8String(filename)); | ||
| 796 | } | ||
| 797 | |||
| 798 | return create_pipeline_cache(0, nullptr); | ||
| 799 | } | ||
| 800 | } | ||
| 801 | |||
| 701 | } // namespace Vulkan | 802 | } // 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: | |||
| 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); | ||
| 140 | |||
| 141 | vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename); | ||
| 142 | |||
| 138 | const Device& device; | 143 | const Device& device; |
| 139 | Scheduler& scheduler; | 144 | Scheduler& scheduler; |
| 140 | DescriptorPool& descriptor_pool; | 145 | DescriptorPool& descriptor_pool; |
| @@ -144,6 +149,7 @@ private: | |||
| 144 | TextureCache& texture_cache; | 149 | TextureCache& texture_cache; |
| 145 | VideoCore::ShaderNotify& shader_notify; | 150 | VideoCore::ShaderNotify& shader_notify; |
| 146 | bool use_asynchronous_shaders{}; | 151 | bool use_asynchronous_shaders{}; |
| 152 | bool use_vulkan_pipeline_cache{}; | ||
| 147 | 153 | ||
| 148 | GraphicsPipelineCacheKey graphics_key{}; | 154 | GraphicsPipelineCacheKey graphics_key{}; |
| 149 | GraphicsPipeline* current_pipeline{}; | 155 | GraphicsPipeline* current_pipeline{}; |
| @@ -158,6 +164,9 @@ private: | |||
| 158 | 164 | ||
| 159 | std::filesystem::path pipeline_cache_filename; | 165 | std::filesystem::path pipeline_cache_filename; |
| 160 | 166 | ||
| 167 | std::filesystem::path vulkan_pipeline_cache_filename; | ||
| 168 | vk::PipelineCache vulkan_pipeline_cache; | ||
| 169 | |||
| 161 | Common::ThreadWorker workers; | 170 | Common::ThreadWorker workers; |
| 162 | Common::ThreadWorker serialization_thread; | 171 | Common::ThreadWorker serialization_thread; |
| 163 | DynamicFeatures dynamic_features; | 172 | DynamicFeatures dynamic_features; |