blob: ac3760f5283bde2f0d3286ccba74641ec7104c65 [file] [log] [blame]
// 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.
// FLUTTER_NOLINT: https://github.com/flutter/flutter/issues/123467
#include "impeller/renderer/backend/vulkan/pipeline_cache_vk.h"
#include <sstream>
#include "flutter/fml/mapping.h"
#include "impeller/base/validation.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<fml::Mapping> DecorateCacheWithMetadata(
std::shared_ptr<fml::Mapping> data) {
return data;
}
static std::unique_ptr<fml::Mapping> RemoveMetadataFromCache(
std::unique_ptr<fml::Mapping> data) {
return data;
}
static std::unique_ptr<fml::Mapping> 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<fml::Mapping> 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<const Capabilities> caps,
std::shared_ptr<DeviceHolder> device_holder,
fml::UniqueFD cache_directory)
: caps_(std::move(caps)),
device_holder_(device_holder),
cache_directory_(std::move(cache_directory)) {
if (!caps_ || !device_holder->GetDevice()) {
return;
}
const auto& vk_caps = CapabilitiesVK::Cast(*caps_);
auto existing_cache_data =
OpenCacheFile(cache_directory_, kPipelineCacheFileName, vk_caps);
vk::PipelineCacheCreateInfo cache_info;
if (existing_cache_data) {
cache_info.initialDataSize = existing_cache_data->GetSize();
cache_info.pInitialData = existing_cache_data->GetMapping();
}
auto [result, existing_cache] =
device_holder->GetDevice().createPipelineCacheUnique(cache_info);
if (result == vk::Result::eSuccess) {
cache_ = std::move(existing_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
// reject the cache too.
FML_LOG(INFO) << "Existing pipeline cache was invalid: "
<< vk::to_string(result) << ". Starting with a fresh cache.";
cache_info.pInitialData = nullptr;
cache_info.initialDataSize = 0u;
auto [result2, new_cache] =
device_holder->GetDevice().createPipelineCacheUnique(cache_info);
if (result2 == vk::Result::eSuccess) {
cache_ = std::move(new_cache);
} else {
VALIDATION_LOG << "Could not create new pipeline cache: "
<< vk::to_string(result2);
}
}
is_valid_ = !!cache_;
}
PipelineCacheVK::~PipelineCacheVK() {
std::shared_ptr<DeviceHolder> device_holder = device_holder_.lock();
if (device_holder) {
cache_.reset();
} else {
cache_.release();
}
}
bool PipelineCacheVK::IsValid() const {
return is_valid_;
}
vk::UniquePipeline PipelineCacheVK::CreatePipeline(
const vk::GraphicsPipelineCreateInfo& info) {
std::shared_ptr<DeviceHolder> strong_device = device_holder_.lock();
if (!strong_device) {
return {};
}
auto [result, pipeline] =
strong_device->GetDevice().createGraphicsPipelineUnique(*cache_, info);
if (result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not create graphics pipeline: "
<< vk::to_string(result);
}
return std::move(pipeline);
}
vk::UniquePipeline PipelineCacheVK::CreatePipeline(
const vk::ComputePipelineCreateInfo& info) {
std::shared_ptr<DeviceHolder> strong_device = device_holder_.lock();
if (!strong_device) {
return {};
}
auto [result, pipeline] =
strong_device->GetDevice().createComputePipelineUnique(*cache_, info);
if (result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not create compute pipeline: "
<< vk::to_string(result);
}
return std::move(pipeline);
}
std::shared_ptr<fml::Mapping> PipelineCacheVK::CopyPipelineCacheData() const {
std::shared_ptr<DeviceHolder> 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::vector<uint8_t>>();
std::swap(*shared_data, data);
return std::make_shared<fml::NonOwnedMapping>(
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.";
return;
}
}
const CapabilitiesVK* PipelineCacheVK::GetCapabilities() const {
return CapabilitiesVK::Cast(caps_.get());
}
} // namespace impeller