From ee63d2a233db1277aed9d2d32974e3dbff6c89f8 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 21 Aug 2024 12:48:05 -0700 Subject: [PATCH] [Impeller] Perform integrity checks for Vulkan pipeline caches. (flutter/engine#54654) Fixes https://github.com/flutter/flutter/issues/128126 I scared myself looking into the recent Vulkan driver issues and decided to fix this to follow best-practices. In addition to the comments, see the linked issue for the article on how this works. I didn't perform the data hashing because we use ::rename in fml::WriteAtomically and I am not as concerned about that. But we can add it later if needed. We also don't have a good utility to hash data. This also gets rid of one intermediate allocation. We could also write directly into the file mapping but FML has no utilities to msync. Something to fix later if needed. --- .../flutter/ci/licenses_golden/excluded_files | 1 + .../ci/licenses_golden/licenses_flutter | 4 + .../impeller/renderer/backend/vulkan/BUILD.gn | 3 + .../backend/vulkan/pipeline_cache_data_vk.cc | 117 ++++++++++++++ .../backend/vulkan/pipeline_cache_data_vk.h | 109 +++++++++++++ .../pipeline_cache_data_vk_unittests.cc | 148 ++++++++++++++++++ .../backend/vulkan/pipeline_cache_vk.cc | 92 ++--------- .../backend/vulkan/pipeline_cache_vk.h | 2 - 8 files changed, 395 insertions(+), 81 deletions(-) create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk_unittests.cc diff --git a/engine/src/flutter/ci/licenses_golden/excluded_files b/engine/src/flutter/ci/licenses_golden/excluded_files index 71ae1f7cbc..f0c0655278 100644 --- a/engine/src/flutter/ci/licenses_golden/excluded_files +++ b/engine/src/flutter/ci/licenses_golden/excluded_files @@ -194,6 +194,7 @@ ../../../flutter/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/fence_waiter_vk_unittests.cc +../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/render_pass_builder_vk_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/render_pass_cache_unittests.cc ../../../flutter/impeller/renderer/backend/vulkan/resource_manager_vk_unittests.cc diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 7d8dd29178..89e1065b3b 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -43109,6 +43109,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.cc + ../../ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/gpu_tracer_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc + ../../../flutter/LICENSE @@ -45991,6 +45993,8 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/formats_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/gpu_tracer_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/gpu_tracer_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/limits_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn index def2b26dbf..a1f4a4154e 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn @@ -15,6 +15,7 @@ impeller_component("vulkan_unittests") { "descriptor_pool_vk_unittests.cc", "driver_info_vk_unittests.cc", "fence_waiter_vk_unittests.cc", + "pipeline_cache_data_vk_unittests.cc", "render_pass_builder_vk_unittests.cc", "render_pass_cache_unittests.cc", "resource_manager_vk_unittests.cc", @@ -71,6 +72,8 @@ impeller_component("vulkan") { "gpu_tracer_vk.cc", "gpu_tracer_vk.h", "limits_vk.h", + "pipeline_cache_data_vk.cc", + "pipeline_cache_data_vk.h", "pipeline_cache_vk.cc", "pipeline_cache_vk.h", "pipeline_library_vk.cc", diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc new file mode 100644 index 0000000000..9cdd199aed --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc @@ -0,0 +1,117 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h" + +#include "flutter/fml/file.h" +#include "impeller/base/allocation.h" +#include "impeller/base/validation.h" + +namespace impeller { + +static constexpr const char* kPipelineCacheFileName = + "flutter.impeller.vkcache"; + +bool PipelineCacheDataPersist(const fml::UniqueFD& cache_directory, + const VkPhysicalDeviceProperties& props, + const vk::UniquePipelineCache& cache) { + if (!cache_directory.is_valid()) { + return false; + } + size_t data_size = 0u; + if (cache.getOwner().getPipelineCacheData(*cache, &data_size, nullptr) != + vk::Result::eSuccess) { + VALIDATION_LOG << "Could not fetch pipeline cache size."; + return false; + } + if (data_size == 0u) { + return true; + } + auto allocation = std::make_shared(); + if (!allocation->Truncate(Bytes{sizeof(PipelineCacheHeaderVK) + data_size}, + false)) { + VALIDATION_LOG << "Could not allocate pipeline cache data staging buffer."; + return false; + } + const auto header = PipelineCacheHeaderVK{props, data_size}; + std::memcpy(allocation->GetBuffer(), &header, sizeof(header)); + if (cache.getOwner().getPipelineCacheData( + *cache, &data_size, allocation->GetBuffer() + sizeof(header)) != + vk::Result::eSuccess) { + VALIDATION_LOG << "Could not copy pipeline cache data."; + return false; + } + + auto allocation_mapping = CreateMappingFromAllocation(allocation); + if (!allocation_mapping) { + return false; + } + if (!fml::WriteAtomically(cache_directory, kPipelineCacheFileName, + *allocation_mapping)) { + VALIDATION_LOG << "Could not write cache file to disk."; + return false; + } + return true; +} + +std::unique_ptr PipelineCacheDataRetrieve( + const fml::UniqueFD& cache_directory, + const VkPhysicalDeviceProperties& props) { + if (!cache_directory.is_valid()) { + return nullptr; + } + std::shared_ptr on_disk_data = + fml::FileMapping::CreateReadOnly(cache_directory, kPipelineCacheFileName); + if (!on_disk_data) { + return nullptr; + } + if (on_disk_data->GetSize() < sizeof(PipelineCacheHeaderVK)) { + VALIDATION_LOG << "Pipeline cache data size is too small."; + return nullptr; + } + auto on_disk_header = PipelineCacheHeaderVK{}; + std::memcpy(&on_disk_header, // + on_disk_data->GetMapping(), // + sizeof(on_disk_header) // + ); + const auto current_header = PipelineCacheHeaderVK{props, 0u}; + if (!on_disk_header.IsCompatibleWith(current_header)) { + FML_LOG(WARNING) + << "Persisted pipeline cache is not compatible with current " + "Vulkan context. Ignoring."; + return nullptr; + } + // Zero sized data is known to cause issues. + if (on_disk_header.data_size == 0u) { + return nullptr; + } + return std::make_unique( + on_disk_data->GetMapping() + sizeof(on_disk_header), + on_disk_header.data_size, [on_disk_data](auto, auto) {}); +} + +PipelineCacheHeaderVK::PipelineCacheHeaderVK() = default; + +PipelineCacheHeaderVK::PipelineCacheHeaderVK( + const VkPhysicalDeviceProperties& props, + uint64_t p_data_size) + : driver_version(props.driverVersion), + vendor_id(props.vendorID), + device_id(props.deviceID), + data_size(p_data_size) { + std::memcpy(uuid, props.pipelineCacheUUID, VK_UUID_SIZE); +} + +bool PipelineCacheHeaderVK::IsCompatibleWith( + const PipelineCacheHeaderVK& o) const { + // Check for everything but the data size. + return magic == o.magic && // + driver_version == o.driver_version && // + vendor_id == o.vendor_id && // + device_id == o.device_id && // + abi == o.abi && // + std::memcmp(uuid, o.uuid, VK_UUID_SIZE) == 0; +} + +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h new file mode 100644 index 0000000000..fa39062d7f --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h @@ -0,0 +1,109 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_CACHE_DATA_VK_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_CACHE_DATA_VK_H_ + +#include "flutter/fml/mapping.h" +#include "flutter/fml/unique_fd.h" +#include "impeller/renderer/backend/vulkan/vk.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief An Impeller specific header prepended to all pipeline cache +/// information that is persisted on disk. This information is used +/// to perform additional integrity checks that may have been missed +/// by the Vulkan driver. +/// +/// Inspired by +/// https://medium.com/@zeuxcg/creating-a-robust-pipeline-cache-with-vulkan-961d09416cda. +/// +struct PipelineCacheHeaderVK { + // This can be used by Impeller to manually invalidate all old caches. + uint32_t magic = 0xC0DEF00D; + // Notably, this field is missing from checks the Vulkan driver performs. For + // drivers that don't correctly check the UUID, explicitly disregarding caches + // generated by previous driver versions sidesteps some landmines. + uint32_t driver_version = 0; + uint32_t vendor_id = 0; + uint32_t device_id = 0; + // If applications are published as 32-bit and updated via the app store to be + // 64-bits, this check comes in handy to disregard previous caches. + uint32_t abi = sizeof(void*); + uint8_t uuid[VK_UUID_SIZE] = {}; + uint64_t data_size = 0; + + //---------------------------------------------------------------------------- + /// @brief Constructs a new empty instance. + /// + PipelineCacheHeaderVK(); + + //---------------------------------------------------------------------------- + /// @brief Constructs a new instance that will be compatible with the + /// given physical device properties. + /// + /// @param[in] props The properties. + /// @param[in] p_data_size The data size. + /// + explicit PipelineCacheHeaderVK(const VkPhysicalDeviceProperties& props, + uint64_t p_data_size); + + //---------------------------------------------------------------------------- + /// @brief Determines whether the specified o is compatible with. + /// + /// The size of the data following the header may be different and + /// is not part of compatibility checks. + /// + /// @param[in] other The other header. + /// + /// @return True if the specified header is compatible with this one, + /// False otherwise. The size of the data following the header may + /// be different. + /// + bool IsCompatibleWith(const PipelineCacheHeaderVK& other) const; +}; + +//------------------------------------------------------------------------------ +/// @brief Persist the pipeline cache to a file in the given cache +/// directory. This function performs integrity checks the Vulkan +/// driver may have missed. +/// +/// @warning The pipeline cache must be externally synchronized for most +/// complete results. If additional pipelines are being created +/// while this function is executing, this function may fail to +/// persist data. +/// +/// @param[in] cache_directory The cache directory +/// @param[in] props The physical device properties +/// @param[in] cache The cache +/// +/// @return If the cache data could be persisted to disk. +/// +bool PipelineCacheDataPersist(const fml::UniqueFD& cache_directory, + const VkPhysicalDeviceProperties& props, + const vk::UniquePipelineCache& cache); + +//------------------------------------------------------------------------------ +/// @brief Retrieve the previously persisted pipeline cache data. This +/// function provides integrity checks the Vulkan driver may have +/// missed. +/// +/// The data is stripped of any additional headers that perform +/// integrity checks. It can be used directly to construct a +/// pre-initialized Vulkan pipeline cache. +/// +/// @param[in] cache_directory The cache directory +/// @param[in] props The properties +/// +/// @return The cache data if it was found and checked to have passed +/// additional integrity checks. +/// +std::unique_ptr PipelineCacheDataRetrieve( + const fml::UniqueFD& cache_directory, + const VkPhysicalDeviceProperties& props); + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_CACHE_DATA_VK_H_ diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk_unittests.cc new file mode 100644 index 0000000000..0a5f4ee41b --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_data_vk_unittests.cc @@ -0,0 +1,148 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/build_config.h" +#include "flutter/fml/file.h" +#include "flutter/testing/testing.h" +#include "impeller/playground/playground_test.h" +#include "impeller/renderer/backend/vulkan/capabilities_vk.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" +#include "impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h" +#include "impeller/renderer/backend/vulkan/surface_context_vk.h" + +namespace impeller::testing { + +TEST(PipelineCacheDataVKTest, CanTestHeaderCompatibility) { + { + PipelineCacheHeaderVK a; + PipelineCacheHeaderVK b; + EXPECT_EQ(a.abi, sizeof(void*)); +#ifdef FML_ARCH_CPU_64_BITS + EXPECT_EQ(a.abi, 8u); +#elif FML_ARCH_CPU_32_BITS + EXPECT_EQ(a.abi, 4u); +#endif + EXPECT_TRUE(a.IsCompatibleWith(b)); + } + // Different data sizes don't matter. + { + PipelineCacheHeaderVK a; + PipelineCacheHeaderVK b; + a.data_size = b.data_size + 100u; + EXPECT_TRUE(a.IsCompatibleWith(b)); + } + // Magic, Driver, vendor, ABI, and UUID matter. + { + PipelineCacheHeaderVK a; + PipelineCacheHeaderVK b; + b.magic = 100; + EXPECT_FALSE(a.IsCompatibleWith(b)); + } + { + PipelineCacheHeaderVK a; + PipelineCacheHeaderVK b; + b.driver_version = 100; + EXPECT_FALSE(a.IsCompatibleWith(b)); + } + { + PipelineCacheHeaderVK a; + PipelineCacheHeaderVK b; + b.vendor_id = 100; + EXPECT_FALSE(a.IsCompatibleWith(b)); + } + { + PipelineCacheHeaderVK a; + PipelineCacheHeaderVK b; + b.device_id = 100; + EXPECT_FALSE(a.IsCompatibleWith(b)); + } + { + PipelineCacheHeaderVK a; + PipelineCacheHeaderVK b; + b.abi = a.abi / 2u; + EXPECT_FALSE(a.IsCompatibleWith(b)); + } + { + PipelineCacheHeaderVK a; + PipelineCacheHeaderVK b; + for (size_t i = 0; i < VK_UUID_SIZE; i++) { + b.uuid[i] = a.uuid[i] + 1; + } + EXPECT_FALSE(a.IsCompatibleWith(b)); + } +} + +TEST(PipelineCacheDataVKTest, CanCreateFromDeviceProperties) { + vk::PhysicalDeviceProperties props; + std::array uuid{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + }; + props.pipelineCacheUUID = uuid; + props.deviceID = 10; + props.vendorID = 11; + props.driverVersion = 12; + PipelineCacheHeaderVK header(props, 99); + EXPECT_EQ(uuid.size(), std::size(header.uuid)); + EXPECT_EQ(props.deviceID, header.device_id); + EXPECT_EQ(props.vendorID, header.vendor_id); + EXPECT_EQ(props.driverVersion, header.driver_version); + for (size_t i = 0; i < uuid.size(); i++) { + EXPECT_EQ(header.uuid[i], uuid.at(i)); + } +} + +using PipelineCacheDataVKPlaygroundTest = PlaygroundTest; +INSTANTIATE_VULKAN_PLAYGROUND_SUITE(PipelineCacheDataVKPlaygroundTest); + +TEST_P(PipelineCacheDataVKPlaygroundTest, CanPersistAndRetrievePipelineCache) { + fml::ScopedTemporaryDirectory temp_dir; + const auto& surface_context = SurfaceContextVK::Cast(*GetContext()); + const auto& context_vk = ContextVK::Cast(*surface_context.GetParent()); + const auto& caps = CapabilitiesVK::Cast(*context_vk.GetCapabilities()); + + { + auto cache = context_vk.GetDevice().createPipelineCacheUnique({}); + ASSERT_EQ(cache.result, vk::Result::eSuccess); + ASSERT_FALSE(fml::FileExists(temp_dir.fd(), "flutter.impeller.vkcache")); + ASSERT_TRUE(PipelineCacheDataPersist( + temp_dir.fd(), caps.GetPhysicalDeviceProperties(), cache.value)); + } + ASSERT_TRUE(fml::FileExists(temp_dir.fd(), "flutter.impeller.vkcache")); + + auto mapping = PipelineCacheDataRetrieve(temp_dir.fd(), + caps.GetPhysicalDeviceProperties()); + ASSERT_NE(mapping, nullptr); + // Assert that the utility has stripped away the cache header giving us clean + // pipeline cache bootstrap information. + vk::PipelineCacheHeaderVersionOne vk_cache_header; + ASSERT_GE(mapping->GetSize(), sizeof(vk_cache_header)); + std::memcpy(&vk_cache_header, mapping->GetMapping(), sizeof(vk_cache_header)); + ASSERT_EQ(vk_cache_header.headerVersion, + vk::PipelineCacheHeaderVersion::eOne); +} + +TEST_P(PipelineCacheDataVKPlaygroundTest, + IntegrityChecksArePerformedOnPersistedData) { + fml::ScopedTemporaryDirectory temp_dir; + const auto& surface_context = SurfaceContextVK::Cast(*GetContext()); + const auto& context_vk = ContextVK::Cast(*surface_context.GetParent()); + const auto& caps = CapabilitiesVK::Cast(*context_vk.GetCapabilities()); + + { + auto cache = context_vk.GetDevice().createPipelineCacheUnique({}); + ASSERT_EQ(cache.result, vk::Result::eSuccess); + ASSERT_FALSE(fml::FileExists(temp_dir.fd(), "flutter.impeller.vkcache")); + ASSERT_TRUE(PipelineCacheDataPersist( + temp_dir.fd(), caps.GetPhysicalDeviceProperties(), cache.value)); + } + ASSERT_TRUE(fml::FileExists(temp_dir.fd(), "flutter.impeller.vkcache")); + auto incompatible_caps = caps.GetPhysicalDeviceProperties(); + // Simulate a driver version bump. + incompatible_caps.driverVersion = + caps.GetPhysicalDeviceProperties().driverVersion + 1u; + auto mapping = PipelineCacheDataRetrieve(temp_dir.fd(), incompatible_caps); + ASSERT_EQ(mapping, nullptr); +} + +} // namespace impeller::testing diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc index 49ee504e5b..955a0c03a0 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc @@ -9,50 +9,12 @@ #include #include "flutter/fml/mapping.h" +#include "impeller/base/allocation_size.h" #include "impeller/base/validation.h" +#include "impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h" namespace impeller { -static constexpr const char* kPipelineCacheFileName = - "flutter.impeller.vkcache"; - -static bool VerifyExistingCache(const fml::Mapping& mapping, - const CapabilitiesVK& caps) { - return true; -} - -static std::shared_ptr DecorateCacheWithMetadata( - std::shared_ptr data) { - return data; -} - -static std::unique_ptr RemoveMetadataFromCache( - std::unique_ptr data) { - return data; -} - -static std::unique_ptr OpenCacheFile( - const fml::UniqueFD& base_directory, - const std::string& cache_file_name, - const CapabilitiesVK& caps) { - if (!base_directory.is_valid()) { - return nullptr; - } - std::unique_ptr mapping = - fml::FileMapping::CreateReadOnly(base_directory, cache_file_name); - if (!mapping) { - return nullptr; - } - if (!VerifyExistingCache(*mapping, caps)) { - return nullptr; - } - mapping = RemoveMetadataFromCache(std::move(mapping)); - if (!mapping) { - return nullptr; - } - return mapping; -} - PipelineCacheVK::PipelineCacheVK(std::shared_ptr caps, std::shared_ptr device_holder, fml::UniqueFD cache_directory) @@ -65,8 +27,8 @@ PipelineCacheVK::PipelineCacheVK(std::shared_ptr caps, const auto& vk_caps = CapabilitiesVK::Cast(*caps_); - auto existing_cache_data = - OpenCacheFile(cache_directory_, kPipelineCacheFileName, vk_caps); + auto existing_cache_data = PipelineCacheDataRetrieve( + cache_directory_, vk_caps.GetPhysicalDeviceProperties()); vk::PipelineCacheCreateInfo cache_info; if (existing_cache_data) { @@ -79,6 +41,9 @@ PipelineCacheVK::PipelineCacheVK(std::shared_ptr caps, if (result == vk::Result::eSuccess) { cache_ = std::move(existing_cache); + FML_LOG(INFO) + << Bytes{cache_info.initialDataSize}.ConvertTo().GetSize() + << " MB of data was used to construct a pipeline cache."; } else { // Even though we perform consistency checks because we don't trust the // driver, the driver may have additional information that may cause it to @@ -145,46 +110,15 @@ vk::UniquePipeline PipelineCacheVK::CreatePipeline( return std::move(pipeline); } -std::shared_ptr PipelineCacheVK::CopyPipelineCacheData() const { - std::shared_ptr strong_device = device_holder_.lock(); - if (!strong_device) { - return nullptr; - } - - if (!IsValid()) { - return nullptr; - } - auto [result, data] = - strong_device->GetDevice().getPipelineCacheData(*cache_); - if (result != vk::Result::eSuccess) { - VALIDATION_LOG << "Could not get pipeline cache data to persist."; - return nullptr; - } - auto shared_data = std::make_shared>(); - std::swap(*shared_data, data); - return std::make_shared( - shared_data->data(), shared_data->size(), [shared_data](auto, auto) {}); -} - void PipelineCacheVK::PersistCacheToDisk() const { - if (!cache_directory_.is_valid()) { - return; - } - auto data = CopyPipelineCacheData(); - if (!data) { - VALIDATION_LOG << "Could not copy pipeline cache data."; - return; - } - data = DecorateCacheWithMetadata(std::move(data)); - if (!data) { - VALIDATION_LOG - << "Could not decorate pipeline cache with additional metadata."; - return; - } - if (!fml::WriteAtomically(cache_directory_, kPipelineCacheFileName, *data)) { - VALIDATION_LOG << "Could not persist pipeline cache to disk."; + if (!is_valid_) { return; } + const auto& vk_caps = CapabilitiesVK::Cast(*caps_); + PipelineCacheDataPersist(cache_directory_, // + vk_caps.GetPhysicalDeviceProperties(), // + cache_ // + ); } const CapabilitiesVK* PipelineCacheVK::GetCapabilities() const { diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.h index a1819d92dc..216cf9d32e 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_cache_vk.h @@ -40,8 +40,6 @@ class PipelineCacheVK { vk::UniquePipelineCache cache_; bool is_valid_ = false; - std::shared_ptr CopyPipelineCacheData() const; - PipelineCacheVK(const PipelineCacheVK&) = delete; PipelineCacheVK& operator=(const PipelineCacheVK&) = delete;