diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 71ae1f7cbc498..f0c0655278a5c 100644 --- a/ci/licenses_golden/excluded_files +++ b/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/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index fdf7f0ab4d4b4..d3defa62786c0 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -43108,6 +43108,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 @@ -45989,6 +45991,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/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index def2b26dbf703..a1f4a4154e12e 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/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/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc b/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc new file mode 100644 index 0000000000000..9cdd199aed79e --- /dev/null +++ b/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/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h b/impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h new file mode 100644 index 0000000000000..fa39062d7fe25 --- /dev/null +++ b/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/impeller/renderer/backend/vulkan/pipeline_cache_data_vk_unittests.cc b/impeller/renderer/backend/vulkan/pipeline_cache_data_vk_unittests.cc new file mode 100644 index 0000000000000..0a5f4ee41b41a --- /dev/null +++ b/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/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc b/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc index 49ee504e5bbcd..955a0c03a05d5 100644 --- a/impeller/renderer/backend/vulkan/pipeline_cache_vk.cc +++ b/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/impeller/renderer/backend/vulkan/pipeline_cache_vk.h b/impeller/renderer/backend/vulkan/pipeline_cache_vk.h index a1819d92dc740..216cf9d32e397 100644 --- a/impeller/renderer/backend/vulkan/pipeline_cache_vk.h +++ b/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;