// 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/shell/common/persistent_cache.h" #include #include #include "flutter/common/version/version.h" #include "flutter/fml/base32.h" #include "flutter/fml/file.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/mapping.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" namespace shell { std::string PersistentCache::cache_base_path_; static std::string SkKeyToFilePath(const SkData& data) { if (data.data() == nullptr || data.size() == 0) { return ""; } fml::StringView view(reinterpret_cast(data.data()), data.size()); auto encode_result = fml::Base32Encode(view); if (!encode_result.first) { return ""; } return encode_result.second; } PersistentCache* PersistentCache::GetCacheForProcess() { static std::unique_ptr gPersistentCache; static std::once_flag once = {}; std::call_once(once, []() { gPersistentCache.reset(new PersistentCache()); }); return gPersistentCache.get(); } void PersistentCache::SetCacheDirectoryPath(std::string path) { cache_base_path_ = path; } PersistentCache::PersistentCache() { fml::UniqueFD cache_base_dir; if (cache_base_path_.length()) { cache_base_dir = fml::OpenDirectory(cache_base_path_.c_str(), false, fml::FilePermission::kRead); } else { cache_base_dir = fml::paths::GetCachesDirectory(); } if (cache_base_dir.is_valid()) { cache_directory_ = std::make_shared( CreateDirectory(cache_base_dir, {"flutter_engine", blink::GetFlutterEngineVersion(), "skia", blink::GetSkiaVersion()}, fml::FilePermission::kReadWrite)); } if (!IsValid()) { FML_LOG(WARNING) << "Could not acquire the persistent cache directory. " "Caching of GPU resources on disk is disabled."; } } PersistentCache::~PersistentCache() = default; bool PersistentCache::IsValid() const { return cache_directory_ && cache_directory_->is_valid(); } // |GrContextOptions::PersistentCache| sk_sp PersistentCache::load(const SkData& key) { TRACE_EVENT0("flutter", "PersistentCacheLoad"); if (!IsValid()) { return nullptr; } auto file_name = SkKeyToFilePath(key); if (file_name.size() == 0) { return nullptr; } auto file = fml::OpenFile(*cache_directory_, file_name.c_str(), false, fml::FilePermission::kRead); if (!file.is_valid()) { return nullptr; } auto mapping = std::make_unique(file); if (mapping->GetSize() == 0) { return nullptr; } TRACE_EVENT0("flutter", "PersistentCacheLoadHit"); return SkData::MakeWithCopy(mapping->GetMapping(), mapping->GetSize()); } static void PersistentCacheStore(fml::RefPtr worker, std::shared_ptr cache_directory, std::string key, std::unique_ptr value) { auto task = fml::MakeCopyable([cache_directory, // file_name = std::move(key), // mapping = std::move(value) // ]() mutable { TRACE_EVENT0("flutter", "PersistentCacheStore"); if (!fml::WriteAtomically(*cache_directory, // file_name.c_str(), // *mapping) // ) { FML_DLOG(WARNING) << "Could not write cache contents to persistent store."; } }); if (!worker) { FML_LOG(WARNING) << "The persistent cache has no available workers. Performing the task " "on the current thread. This slow operation is going to occur on a " "frame workload."; task(); } else { worker->PostTask(std::move(task)); } } // |GrContextOptions::PersistentCache| void PersistentCache::store(const SkData& key, const SkData& data) { if (!IsValid()) { return; } auto file_name = SkKeyToFilePath(key); if (file_name.size() == 0) { return; } auto mapping = std::make_unique( std::vector{data.bytes(), data.bytes() + data.size()}); if (mapping == nullptr || mapping->GetSize() == 0) { return; } PersistentCacheStore(GetWorkerTaskRunner(), cache_directory_, std::move(file_name), std::move(mapping)); } void PersistentCache::AddWorkerTaskRunner( fml::RefPtr task_runner) { std::lock_guard lock(worker_task_runners_mutex_); worker_task_runners_.insert(task_runner); } void PersistentCache::RemoveWorkerTaskRunner( fml::RefPtr task_runner) { std::lock_guard lock(worker_task_runners_mutex_); auto found = worker_task_runners_.find(task_runner); if (found != worker_task_runners_.end()) { worker_task_runners_.erase(found); } } fml::RefPtr PersistentCache::GetWorkerTaskRunner() const { fml::RefPtr worker; std::lock_guard lock(worker_task_runners_mutex_); if (!worker_task_runners_.empty()) { worker = *worker_task_runners_.begin(); } return worker; } } // namespace shell