mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Allow Flutter to automatically dump the skp that triggers new shader compilations. This is useful for writing custom ShaderWarmUp to reduce jank. By default, it's not enabled to reduce the overhead. This is only available in profile or debug build. Later, we can add service protocol support to pull the skp from the client to the host. Currently, it works fine for Android-based devices (including our urgent internal clients) where we can `adb shell` into the cache directory.
209 lines
6.3 KiB
C++
209 lines
6.3 KiB
C++
// 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 <memory>
|
|
#include <string>
|
|
|
|
#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<const char*>(data.data()), data.size());
|
|
|
|
auto encode_result = fml::Base32Encode(view);
|
|
|
|
if (!encode_result.first) {
|
|
return "";
|
|
}
|
|
|
|
return encode_result.second;
|
|
}
|
|
|
|
bool PersistentCache::gIsReadOnly = false;
|
|
|
|
PersistentCache* PersistentCache::GetCacheForProcess() {
|
|
static std::unique_ptr<PersistentCache> gPersistentCache;
|
|
static std::once_flag once = {};
|
|
std::call_once(
|
|
once, []() { gPersistentCache.reset(new PersistentCache(gIsReadOnly)); });
|
|
return gPersistentCache.get();
|
|
}
|
|
|
|
void PersistentCache::SetCacheDirectoryPath(std::string path) {
|
|
cache_base_path_ = path;
|
|
}
|
|
|
|
PersistentCache::PersistentCache(bool read_only) : is_read_only_(read_only) {
|
|
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<fml::UniqueFD>(
|
|
CreateDirectory(cache_base_dir,
|
|
{"flutter_engine", blink::GetFlutterEngineVersion(),
|
|
"skia", blink::GetSkiaVersion()},
|
|
read_only ? fml::FilePermission::kRead
|
|
: 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<SkData> 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<fml::FileMapping>(file);
|
|
if (mapping->GetSize() == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
TRACE_EVENT0("flutter", "PersistentCacheLoadHit");
|
|
return SkData::MakeWithCopy(mapping->GetMapping(), mapping->GetSize());
|
|
}
|
|
|
|
static void PersistentCacheStore(fml::RefPtr<fml::TaskRunner> worker,
|
|
std::shared_ptr<fml::UniqueFD> cache_directory,
|
|
std::string key,
|
|
std::unique_ptr<fml::Mapping> 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) {
|
|
stored_new_shaders_ = true;
|
|
|
|
if (is_read_only_) {
|
|
return;
|
|
}
|
|
|
|
if (!IsValid()) {
|
|
return;
|
|
}
|
|
|
|
auto file_name = SkKeyToFilePath(key);
|
|
|
|
if (file_name.size() == 0) {
|
|
return;
|
|
}
|
|
|
|
auto mapping = std::make_unique<fml::DataMapping>(
|
|
std::vector<uint8_t>{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::DumpSkp(const SkData& data) {
|
|
if (is_read_only_ || !IsValid()) {
|
|
FML_LOG(ERROR) << "Could not dump SKP from read-only or invalid persistent "
|
|
"cache.";
|
|
return;
|
|
}
|
|
|
|
std::stringstream name_stream;
|
|
auto ticks = fml::TimePoint::Now().ToEpochDelta().ToNanoseconds();
|
|
name_stream << "shader_dump_" << std::to_string(ticks) << ".skp";
|
|
std::string file_name = name_stream.str();
|
|
FML_LOG(ERROR) << "Dumping " << file_name;
|
|
auto mapping = std::make_unique<fml::DataMapping>(
|
|
std::vector<uint8_t>{data.bytes(), data.bytes() + data.size()});
|
|
PersistentCacheStore(GetWorkerTaskRunner(), cache_directory_,
|
|
std::move(file_name), std::move(mapping));
|
|
}
|
|
|
|
void PersistentCache::AddWorkerTaskRunner(
|
|
fml::RefPtr<fml::TaskRunner> task_runner) {
|
|
std::lock_guard<std::mutex> lock(worker_task_runners_mutex_);
|
|
worker_task_runners_.insert(task_runner);
|
|
}
|
|
|
|
void PersistentCache::RemoveWorkerTaskRunner(
|
|
fml::RefPtr<fml::TaskRunner> task_runner) {
|
|
std::lock_guard<std::mutex> 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<fml::TaskRunner> PersistentCache::GetWorkerTaskRunner() const {
|
|
fml::RefPtr<fml::TaskRunner> worker;
|
|
|
|
std::lock_guard<std::mutex> lock(worker_task_runners_mutex_);
|
|
if (!worker_task_runners_.empty()) {
|
|
worker = *worker_task_runners_.begin();
|
|
}
|
|
|
|
return worker;
|
|
}
|
|
|
|
} // namespace shell
|