• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/shell/common/persistent_cache.h"
6 
7 #include <memory>
8 #include <string>
9 #include <string_view>
10 
11 #include "flutter/fml/base32.h"
12 #include "flutter/fml/file.h"
13 #include "flutter/fml/make_copyable.h"
14 #include "flutter/fml/mapping.h"
15 #include "flutter/fml/paths.h"
16 #include "flutter/fml/trace_event.h"
17 #include "flutter/shell/version/version.h"
18 
19 namespace flutter {
20 
21 std::string PersistentCache::cache_base_path_;
22 
SkKeyToFilePath(const SkData & data)23 static std::string SkKeyToFilePath(const SkData& data) {
24   if (data.data() == nullptr || data.size() == 0) {
25     return "";
26   }
27 
28   std::string_view view(reinterpret_cast<const char*>(data.data()),
29                         data.size());
30 
31   auto encode_result = fml::Base32Encode(view);
32 
33   if (!encode_result.first) {
34     return "";
35   }
36 
37   return encode_result.second;
38 }
39 
40 bool PersistentCache::gIsReadOnly = false;
41 
GetCacheForProcess()42 PersistentCache* PersistentCache::GetCacheForProcess() {
43   static std::unique_ptr<PersistentCache> gPersistentCache;
44   static std::once_flag once = {};
45   std::call_once(
46       once, []() { gPersistentCache.reset(new PersistentCache(gIsReadOnly)); });
47   return gPersistentCache.get();
48 }
49 
SetCacheDirectoryPath(std::string path)50 void PersistentCache::SetCacheDirectoryPath(std::string path) {
51   cache_base_path_ = path;
52 }
53 
54 namespace {
MakeCacheDirectory(const std::string & global_cache_base_path,bool read_only)55 std::shared_ptr<fml::UniqueFD> MakeCacheDirectory(
56     const std::string& global_cache_base_path,
57     bool read_only) {
58   fml::UniqueFD cache_base_dir;
59   if (global_cache_base_path.length()) {
60     cache_base_dir = fml::OpenDirectory(global_cache_base_path.c_str(), false,
61                                         fml::FilePermission::kRead);
62   } else {
63     cache_base_dir = fml::paths::GetCachesDirectory();
64   }
65 
66   if (cache_base_dir.is_valid()) {
67     return std::make_shared<fml::UniqueFD>(CreateDirectory(
68         cache_base_dir,
69         {"flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion()},
70         read_only ? fml::FilePermission::kRead
71                   : fml::FilePermission::kReadWrite));
72   } else {
73     return std::make_shared<fml::UniqueFD>();
74   }
75 }
76 }  // namespace
77 
PersistentCache(bool read_only)78 PersistentCache::PersistentCache(bool read_only)
79     : is_read_only_(read_only),
80       cache_directory_(MakeCacheDirectory(cache_base_path_, read_only)) {
81   if (!IsValid()) {
82     FML_LOG(WARNING) << "Could not acquire the persistent cache directory. "
83                         "Caching of GPU resources on disk is disabled.";
84   }
85 }
86 
87 PersistentCache::~PersistentCache() = default;
88 
IsValid() const89 bool PersistentCache::IsValid() const {
90   return cache_directory_ && cache_directory_->is_valid();
91 }
92 
93 // |GrContextOptions::PersistentCache|
load(const SkData & key)94 sk_sp<SkData> PersistentCache::load(const SkData& key) {
95   TRACE_EVENT0("flutter", "PersistentCacheLoad");
96   if (!IsValid()) {
97     return nullptr;
98   }
99   auto file_name = SkKeyToFilePath(key);
100   if (file_name.size() == 0) {
101     return nullptr;
102   }
103   auto file = fml::OpenFile(*cache_directory_, file_name.c_str(), false,
104                             fml::FilePermission::kRead);
105   if (!file.is_valid()) {
106     return nullptr;
107   }
108   auto mapping = std::make_unique<fml::FileMapping>(file);
109   if (mapping->GetSize() == 0) {
110     return nullptr;
111   }
112 
113   TRACE_EVENT0("flutter", "PersistentCacheLoadHit");
114   return SkData::MakeWithCopy(mapping->GetMapping(), mapping->GetSize());
115 }
116 
PersistentCacheStore(fml::RefPtr<fml::TaskRunner> worker,std::shared_ptr<fml::UniqueFD> cache_directory,std::string key,std::unique_ptr<fml::Mapping> value)117 static void PersistentCacheStore(fml::RefPtr<fml::TaskRunner> worker,
118                                  std::shared_ptr<fml::UniqueFD> cache_directory,
119                                  std::string key,
120                                  std::unique_ptr<fml::Mapping> value) {
121   auto task =
122       fml::MakeCopyable([cache_directory,             //
123                          file_name = std::move(key),  //
124                          mapping = std::move(value)   //
125   ]() mutable {
126         TRACE_EVENT0("flutter", "PersistentCacheStore");
127         if (!fml::WriteAtomically(*cache_directory,   //
128                                   file_name.c_str(),  //
129                                   *mapping)           //
130         ) {
131           FML_DLOG(WARNING)
132               << "Could not write cache contents to persistent store.";
133         }
134       });
135 
136   if (!worker) {
137     FML_LOG(WARNING)
138         << "The persistent cache has no available workers. Performing the task "
139            "on the current thread. This slow operation is going to occur on a "
140            "frame workload.";
141     task();
142   } else {
143     worker->PostTask(std::move(task));
144   }
145 }
146 
147 // |GrContextOptions::PersistentCache|
store(const SkData & key,const SkData & data)148 void PersistentCache::store(const SkData& key, const SkData& data) {
149   stored_new_shaders_ = true;
150 
151   if (is_read_only_) {
152     return;
153   }
154 
155   if (!IsValid()) {
156     return;
157   }
158 
159   auto file_name = SkKeyToFilePath(key);
160 
161   if (file_name.size() == 0) {
162     return;
163   }
164 
165   auto mapping = std::make_unique<fml::DataMapping>(
166       std::vector<uint8_t>{data.bytes(), data.bytes() + data.size()});
167 
168   if (mapping == nullptr || mapping->GetSize() == 0) {
169     return;
170   }
171 
172   PersistentCacheStore(GetWorkerTaskRunner(), cache_directory_,
173                        std::move(file_name), std::move(mapping));
174 }
175 
DumpSkp(const SkData & data)176 void PersistentCache::DumpSkp(const SkData& data) {
177   if (is_read_only_ || !IsValid()) {
178     FML_LOG(ERROR) << "Could not dump SKP from read-only or invalid persistent "
179                       "cache.";
180     return;
181   }
182 
183   std::stringstream name_stream;
184   auto ticks = fml::TimePoint::Now().ToEpochDelta().ToNanoseconds();
185   name_stream << "shader_dump_" << std::to_string(ticks) << ".skp";
186   std::string file_name = name_stream.str();
187   FML_LOG(INFO) << "Dumping " << file_name;
188   auto mapping = std::make_unique<fml::DataMapping>(
189       std::vector<uint8_t>{data.bytes(), data.bytes() + data.size()});
190   PersistentCacheStore(GetWorkerTaskRunner(), cache_directory_,
191                        std::move(file_name), std::move(mapping));
192 }
193 
AddWorkerTaskRunner(fml::RefPtr<fml::TaskRunner> task_runner)194 void PersistentCache::AddWorkerTaskRunner(
195     fml::RefPtr<fml::TaskRunner> task_runner) {
196   std::scoped_lock lock(worker_task_runners_mutex_);
197   worker_task_runners_.insert(task_runner);
198 }
199 
RemoveWorkerTaskRunner(fml::RefPtr<fml::TaskRunner> task_runner)200 void PersistentCache::RemoveWorkerTaskRunner(
201     fml::RefPtr<fml::TaskRunner> task_runner) {
202   std::scoped_lock lock(worker_task_runners_mutex_);
203   auto found = worker_task_runners_.find(task_runner);
204   if (found != worker_task_runners_.end()) {
205     worker_task_runners_.erase(found);
206   }
207 }
208 
GetWorkerTaskRunner() const209 fml::RefPtr<fml::TaskRunner> PersistentCache::GetWorkerTaskRunner() const {
210   fml::RefPtr<fml::TaskRunner> worker;
211 
212   std::scoped_lock lock(worker_task_runners_mutex_);
213   if (!worker_task_runners_.empty()) {
214     worker = *worker_task_runners_.begin();
215   }
216 
217   return worker;
218 }
219 
220 }  // namespace flutter
221