This repository was archived by the owner on Feb 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] Perform integrity checks for Vulkan pipeline caches. #54654
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
3bc5b1f
[Impeller] Perform integrity checks for Vulkan pipeline caches.
chinmaygarde dfe8dc3
Tests.
chinmaygarde 5cb6fd5
Assert.
chinmaygarde b2c750b
Truncate
chinmaygarde aafaa32
Licenses.
chinmaygarde eddf424
EXPECT
chinmaygarde File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
impeller/renderer/backend/vulkan/pipeline_cache_data_vk.cc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Allocation>(); | ||
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<fml::Mapping> PipelineCacheDataRetrieve( | ||
const fml::UniqueFD& cache_directory, | ||
const VkPhysicalDeviceProperties& props) { | ||
if (!cache_directory.is_valid()) { | ||
return nullptr; | ||
} | ||
std::shared_ptr<fml::FileMapping> 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<fml::NonOwnedMapping>( | ||
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 |
109 changes: 109 additions & 0 deletions
109
impeller/renderer/backend/vulkan/pipeline_cache_data_vk.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I left this in warning in there because it is theoretically possible if we are still in the middle of constructing pipelines. The failure mode is not fatal though and we only persist after a set number of frames. So I'm not too worried about this. |
||
/// 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<fml::Mapping> PipelineCacheDataRetrieve( | ||
const fml::UniqueFD& cache_directory, | ||
const VkPhysicalDeviceProperties& props); | ||
|
||
} // namespace impeller | ||
|
||
#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_CACHE_DATA_VK_H_ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if we had a unique_ptr variant for when we don't need shared ownership like here. Not blocking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once we get concepts, I'd like to apply one for any pointer like thing that can be moved. So it would work for both.