OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 The Chromium 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 "content/browser/gpu/shader_disk_cache.h" | |
6 | |
7 #include "base/macros.h" | |
8 #include "base/memory/ptr_util.h" | |
9 #include "base/single_thread_task_runner.h" | |
10 #include "base/threading/thread_checker.h" | |
11 #include "gpu/command_buffer/common/constants.h" | |
12 #include "net/base/cache_type.h" | |
13 #include "net/base/io_buffer.h" | |
14 #include "net/base/net_errors.h" | |
15 | |
16 namespace content { | |
17 | |
18 namespace { | |
19 | |
20 static const base::FilePath::CharType kGpuCachePath[] = | |
21 FILE_PATH_LITERAL("GPUCache"); | |
22 | |
23 } // namespace | |
24 | |
25 // ShaderDiskCacheEntry handles the work of caching/updating the cached | |
26 // shaders. | |
27 class ShaderDiskCacheEntry : public base::ThreadChecker { | |
28 public: | |
29 ShaderDiskCacheEntry(ShaderDiskCache* cache, | |
30 const std::string& key, | |
31 const std::string& shader); | |
32 ~ShaderDiskCacheEntry(); | |
33 | |
34 void Cache(); | |
35 | |
36 private: | |
37 enum OpType { | |
38 OPEN_ENTRY, | |
39 WRITE_DATA, | |
40 CREATE_ENTRY, | |
41 }; | |
42 | |
43 void OnOpComplete(int rv); | |
44 | |
45 int OpenCallback(int rv); | |
46 int WriteCallback(int rv); | |
47 int IOComplete(int rv); | |
48 | |
49 ShaderDiskCache* cache_; | |
50 OpType op_type_; | |
51 std::string key_; | |
52 std::string shader_; | |
53 disk_cache::Entry* entry_; | |
54 base::WeakPtr<ShaderDiskCacheEntry> weak_ptr_; | |
55 base::WeakPtrFactory<ShaderDiskCacheEntry> weak_ptr_factory_; | |
56 | |
57 DISALLOW_COPY_AND_ASSIGN(ShaderDiskCacheEntry); | |
58 }; | |
59 | |
60 // ShaderDiskReadHelper is used to load all of the cached shaders from the | |
61 // disk cache and send to the memory cache. | |
62 class ShaderDiskReadHelper : public base::ThreadChecker { | |
63 public: | |
64 using ShaderLoadedCallback = ShaderDiskCache::ShaderLoadedCallback; | |
65 ShaderDiskReadHelper(ShaderDiskCache* cache, | |
66 const ShaderLoadedCallback& callback); | |
67 ~ShaderDiskReadHelper(); | |
68 | |
69 void LoadCache(); | |
70 | |
71 private: | |
72 enum OpType { | |
73 TERMINATE, | |
74 OPEN_NEXT, | |
75 OPEN_NEXT_COMPLETE, | |
76 READ_COMPLETE, | |
77 ITERATION_FINISHED | |
78 }; | |
79 | |
80 | |
81 void OnOpComplete(int rv); | |
82 | |
83 int OpenNextEntry(); | |
84 int OpenNextEntryComplete(int rv); | |
85 int ReadComplete(int rv); | |
86 int IterationComplete(int rv); | |
87 | |
88 ShaderDiskCache* cache_; | |
89 ShaderLoadedCallback shader_loaded_callback_; | |
90 OpType op_type_; | |
91 std::unique_ptr<disk_cache::Backend::Iterator> iter_; | |
92 scoped_refptr<net::IOBufferWithSize> buf_; | |
93 disk_cache::Entry* entry_; | |
94 base::WeakPtrFactory<ShaderDiskReadHelper> weak_ptr_factory_; | |
95 | |
96 DISALLOW_COPY_AND_ASSIGN(ShaderDiskReadHelper); | |
97 }; | |
98 | |
99 class ShaderClearHelper : public base::ThreadChecker { | |
100 public: | |
101 ShaderClearHelper(ShaderCacheFactory* factory, | |
102 scoped_refptr<ShaderDiskCache> cache, | |
103 const base::FilePath& path, | |
104 const base::Time& delete_begin, | |
105 const base::Time& delete_end, | |
106 const base::Closure& callback); | |
107 ~ShaderClearHelper(); | |
108 | |
109 void Clear(); | |
110 | |
111 private: | |
112 enum OpType { | |
113 TERMINATE, | |
114 VERIFY_CACHE_SETUP, | |
115 DELETE_CACHE | |
116 }; | |
117 | |
118 void DoClearShaderCache(int rv); | |
119 | |
120 ShaderCacheFactory* factory_; | |
121 scoped_refptr<ShaderDiskCache> cache_; | |
122 OpType op_type_; | |
123 base::FilePath path_; | |
124 base::Time delete_begin_; | |
125 base::Time delete_end_; | |
126 base::Closure callback_; | |
127 base::WeakPtrFactory<ShaderClearHelper> weak_ptr_factory_; | |
128 | |
129 DISALLOW_COPY_AND_ASSIGN(ShaderClearHelper); | |
130 }; | |
131 | |
132 //////////////////////////////////////////////////////////////////////////////// | |
133 // ShaderDiskCacheEntry | |
134 | |
135 ShaderDiskCacheEntry::ShaderDiskCacheEntry(ShaderDiskCache* cache, | |
136 const std::string& key, | |
137 const std::string& shader) | |
138 : cache_(cache), | |
139 op_type_(OPEN_ENTRY), | |
140 key_(key), | |
141 shader_(shader), | |
142 entry_(nullptr), | |
143 weak_ptr_factory_(this) { | |
144 weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); | |
145 } | |
146 | |
147 ShaderDiskCacheEntry::~ShaderDiskCacheEntry() { | |
148 DCHECK(CalledOnValidThread()); | |
149 if (entry_) | |
150 entry_->Close(); | |
151 } | |
152 | |
153 void ShaderDiskCacheEntry::Cache() { | |
154 DCHECK(CalledOnValidThread()); | |
155 int rv = cache_->backend()->OpenEntry( | |
156 key_, &entry_, base::Bind(&ShaderDiskCacheEntry::OnOpComplete, | |
157 weak_ptr_factory_.GetWeakPtr())); | |
158 if (rv != net::ERR_IO_PENDING) | |
159 OnOpComplete(rv); | |
160 } | |
161 | |
162 void ShaderDiskCacheEntry::OnOpComplete(int rv) { | |
163 DCHECK(CalledOnValidThread()); | |
164 // The function calls inside the switch block below can end up destroying | |
165 // |this|. So hold on to a WeakPtr<>, and terminate the while loop if |this| | |
166 // has been destroyed. | |
167 auto weak_ptr = std::move(weak_ptr_); | |
168 do { | |
169 switch (op_type_) { | |
170 case OPEN_ENTRY: | |
171 rv = OpenCallback(rv); | |
172 break; | |
173 case CREATE_ENTRY: | |
174 rv = WriteCallback(rv); | |
175 break; | |
176 case WRITE_DATA: | |
177 rv = IOComplete(rv); | |
178 break; | |
179 } | |
180 } while (rv != net::ERR_IO_PENDING && weak_ptr); | |
181 if (weak_ptr) | |
182 weak_ptr_ = std::move(weak_ptr); | |
183 } | |
184 | |
185 int ShaderDiskCacheEntry::OpenCallback(int rv) { | |
186 DCHECK(CalledOnValidThread()); | |
187 if (rv == net::OK) { | |
188 cache_->backend()->OnExternalCacheHit(key_); | |
189 cache_->EntryComplete(this); | |
190 return rv; | |
191 } | |
192 | |
193 op_type_ = CREATE_ENTRY; | |
194 return cache_->backend()->CreateEntry( | |
195 key_, &entry_, base::Bind(&ShaderDiskCacheEntry::OnOpComplete, | |
196 weak_ptr_factory_.GetWeakPtr())); | |
197 } | |
198 | |
199 int ShaderDiskCacheEntry::WriteCallback(int rv) { | |
200 DCHECK(CalledOnValidThread()); | |
201 if (rv != net::OK) { | |
202 LOG(ERROR) << "Failed to create shader cache entry: " << rv; | |
203 cache_->EntryComplete(this); | |
204 return rv; | |
205 } | |
206 | |
207 op_type_ = WRITE_DATA; | |
208 scoped_refptr<net::StringIOBuffer> io_buf = new net::StringIOBuffer(shader_); | |
209 return entry_->WriteData(1, 0, io_buf.get(), shader_.length(), | |
210 base::Bind(&ShaderDiskCacheEntry::OnOpComplete, | |
211 weak_ptr_factory_.GetWeakPtr()), | |
212 false); | |
213 } | |
214 | |
215 int ShaderDiskCacheEntry::IOComplete(int rv) { | |
216 DCHECK(CalledOnValidThread()); | |
217 cache_->EntryComplete(this); | |
218 return rv; | |
219 } | |
220 | |
221 //////////////////////////////////////////////////////////////////////////////// | |
222 // ShaderDiskReadHelper | |
223 | |
224 ShaderDiskReadHelper::ShaderDiskReadHelper(ShaderDiskCache* cache, | |
225 const ShaderLoadedCallback& callback) | |
226 : cache_(cache), | |
227 shader_loaded_callback_(callback), | |
228 op_type_(OPEN_NEXT), | |
229 buf_(NULL), | |
230 entry_(NULL), | |
231 weak_ptr_factory_(this) {} | |
232 | |
233 ShaderDiskReadHelper::~ShaderDiskReadHelper() { | |
234 DCHECK(CalledOnValidThread()); | |
235 if (entry_) | |
236 entry_->Close(); | |
237 iter_ = nullptr; | |
238 } | |
239 | |
240 void ShaderDiskReadHelper::LoadCache() { | |
241 DCHECK(CalledOnValidThread()); | |
242 OnOpComplete(net::OK); | |
243 } | |
244 | |
245 void ShaderDiskReadHelper::OnOpComplete(int rv) { | |
246 DCHECK(CalledOnValidThread()); | |
247 do { | |
248 switch (op_type_) { | |
249 case OPEN_NEXT: | |
250 rv = OpenNextEntry(); | |
251 break; | |
252 case OPEN_NEXT_COMPLETE: | |
253 rv = OpenNextEntryComplete(rv); | |
254 break; | |
255 case READ_COMPLETE: | |
256 rv = ReadComplete(rv); | |
257 break; | |
258 case ITERATION_FINISHED: | |
259 rv = IterationComplete(rv); | |
260 break; | |
261 case TERMINATE: | |
262 cache_->ReadComplete(); | |
263 rv = net::ERR_IO_PENDING; // break the loop | |
264 break; | |
265 } | |
266 } while (rv != net::ERR_IO_PENDING); | |
267 } | |
268 | |
269 int ShaderDiskReadHelper::OpenNextEntry() { | |
270 DCHECK(CalledOnValidThread()); | |
271 op_type_ = OPEN_NEXT_COMPLETE; | |
272 if (!iter_) | |
273 iter_ = cache_->backend()->CreateIterator(); | |
274 return iter_->OpenNextEntry(&entry_, | |
275 base::Bind(&ShaderDiskReadHelper::OnOpComplete, | |
276 weak_ptr_factory_.GetWeakPtr())); | |
277 } | |
278 | |
279 int ShaderDiskReadHelper::OpenNextEntryComplete(int rv) { | |
280 DCHECK(CalledOnValidThread()); | |
281 if (rv == net::ERR_FAILED) { | |
282 iter_.reset(); | |
283 op_type_ = ITERATION_FINISHED; | |
284 return net::OK; | |
285 } | |
286 | |
287 if (rv < 0) | |
288 return rv; | |
289 | |
290 op_type_ = READ_COMPLETE; | |
291 buf_ = new net::IOBufferWithSize(entry_->GetDataSize(1)); | |
292 return entry_->ReadData(1, 0, buf_.get(), buf_->size(), | |
293 base::Bind(&ShaderDiskReadHelper::OnOpComplete, | |
294 weak_ptr_factory_.GetWeakPtr())); | |
295 } | |
296 | |
297 int ShaderDiskReadHelper::ReadComplete(int rv) { | |
298 DCHECK(CalledOnValidThread()); | |
299 if (rv && rv == buf_->size() && !shader_loaded_callback_.is_null()) { | |
300 shader_loaded_callback_.Run(entry_->GetKey(), | |
301 std::string(buf_->data(), buf_->size())); | |
302 } | |
303 | |
304 buf_ = NULL; | |
305 entry_->Close(); | |
306 entry_ = NULL; | |
307 | |
308 op_type_ = OPEN_NEXT; | |
309 return net::OK; | |
310 } | |
311 | |
312 int ShaderDiskReadHelper::IterationComplete(int rv) { | |
313 DCHECK(CalledOnValidThread()); | |
314 iter_.reset(); | |
315 op_type_ = TERMINATE; | |
316 return net::OK; | |
317 } | |
318 | |
319 //////////////////////////////////////////////////////////////////////////////// | |
320 // ShaderClearHelper | |
321 | |
322 ShaderClearHelper::ShaderClearHelper(ShaderCacheFactory* factory, | |
323 scoped_refptr<ShaderDiskCache> cache, | |
324 const base::FilePath& path, | |
325 const base::Time& delete_begin, | |
326 const base::Time& delete_end, | |
327 const base::Closure& callback) | |
328 : factory_(factory), | |
329 cache_(std::move(cache)), | |
330 op_type_(VERIFY_CACHE_SETUP), | |
331 path_(path), | |
332 delete_begin_(delete_begin), | |
333 delete_end_(delete_end), | |
334 callback_(callback), | |
335 weak_ptr_factory_(this) {} | |
336 | |
337 ShaderClearHelper::~ShaderClearHelper() { | |
338 DCHECK(CalledOnValidThread()); | |
339 } | |
340 | |
341 void ShaderClearHelper::Clear() { | |
342 DCHECK(CalledOnValidThread()); | |
343 DoClearShaderCache(net::OK); | |
344 } | |
345 | |
346 void ShaderClearHelper::DoClearShaderCache(int rv) { | |
347 DCHECK(CalledOnValidThread()); | |
348 while (rv != net::ERR_IO_PENDING) { | |
349 switch (op_type_) { | |
350 case VERIFY_CACHE_SETUP: | |
351 rv = cache_->SetAvailableCallback( | |
352 base::Bind(&ShaderClearHelper::DoClearShaderCache, | |
353 weak_ptr_factory_.GetWeakPtr())); | |
354 op_type_ = DELETE_CACHE; | |
355 break; | |
356 case DELETE_CACHE: | |
357 rv = cache_->Clear(delete_begin_, delete_end_, | |
358 base::Bind(&ShaderClearHelper::DoClearShaderCache, | |
359 weak_ptr_factory_.GetWeakPtr())); | |
360 op_type_ = TERMINATE; | |
361 break; | |
362 case TERMINATE: | |
363 callback_.Run(); | |
364 // Calling CacheCleared() destroys |this|. | |
365 factory_->CacheCleared(path_); | |
366 rv = net::ERR_IO_PENDING; // Break the loop. | |
367 break; | |
368 } | |
369 } | |
370 } | |
371 | |
372 //////////////////////////////////////////////////////////////////////////////// | |
373 // ShaderCacheFactory | |
374 | |
375 ShaderCacheFactory::ShaderCacheFactory( | |
376 scoped_refptr<base::SingleThreadTaskRunner> cache_task_runner) | |
377 : cache_task_runner_(std::move(cache_task_runner)) {} | |
378 | |
379 ShaderCacheFactory::~ShaderCacheFactory() { | |
380 } | |
381 | |
382 void ShaderCacheFactory::SetCacheInfo(int32_t client_id, | |
383 const base::FilePath& path) { | |
384 DCHECK(CalledOnValidThread()); | |
385 client_id_to_path_map_[client_id] = path; | |
386 } | |
387 | |
388 void ShaderCacheFactory::RemoveCacheInfo(int32_t client_id) { | |
389 DCHECK(CalledOnValidThread()); | |
390 client_id_to_path_map_.erase(client_id); | |
391 } | |
392 | |
393 scoped_refptr<ShaderDiskCache> ShaderCacheFactory::Get(int32_t client_id) { | |
394 DCHECK(CalledOnValidThread()); | |
395 ClientIdToPathMap::iterator iter = client_id_to_path_map_.find(client_id); | |
396 if (iter == client_id_to_path_map_.end()) | |
397 return NULL; | |
398 return ShaderCacheFactory::GetByPath(iter->second); | |
399 } | |
400 | |
401 scoped_refptr<ShaderDiskCache> ShaderCacheFactory::GetByPath( | |
402 const base::FilePath& path) { | |
403 DCHECK(CalledOnValidThread()); | |
404 ShaderCacheMap::iterator iter = shader_cache_map_.find(path); | |
405 if (iter != shader_cache_map_.end()) | |
406 return iter->second; | |
407 | |
408 ShaderDiskCache* cache = new ShaderDiskCache(this, path); | |
409 cache->Init(cache_task_runner_); | |
410 return cache; | |
411 } | |
412 | |
413 void ShaderCacheFactory::AddToCache(const base::FilePath& key, | |
414 ShaderDiskCache* cache) { | |
415 DCHECK(CalledOnValidThread()); | |
416 shader_cache_map_[key] = cache; | |
417 } | |
418 | |
419 void ShaderCacheFactory::RemoveFromCache(const base::FilePath& key) { | |
420 DCHECK(CalledOnValidThread()); | |
421 shader_cache_map_.erase(key); | |
422 } | |
423 | |
424 void ShaderCacheFactory::ClearByPath(const base::FilePath& path, | |
425 const base::Time& delete_begin, | |
426 const base::Time& delete_end, | |
427 const base::Closure& callback) { | |
428 DCHECK(CalledOnValidThread()); | |
429 DCHECK(!callback.is_null()); | |
430 | |
431 auto helper = base::MakeUnique<ShaderClearHelper>( | |
432 this, GetByPath(path), path, delete_begin, delete_end, callback); | |
433 | |
434 // We could receive requests to clear the same path with different | |
435 // begin/end times. So, we keep a list of requests. If we haven't seen this | |
436 // path before we kick off the clear and add it to the list. If we have see it | |
437 // already, then we already have a clear running. We add this clear to the | |
438 // list and wait for any previous clears to finish. | |
439 ShaderClearMap::iterator iter = shader_clear_map_.find(path); | |
440 if (iter != shader_clear_map_.end()) { | |
441 iter->second.push(std::move(helper)); | |
442 return; | |
443 } | |
444 | |
445 // Insert the helper in the map before calling Clear(), since it can lead to a | |
446 // call back into CacheCleared(). | |
447 ShaderClearHelper* helper_ptr = helper.get(); | |
448 shader_clear_map_.insert( | |
449 std::pair<base::FilePath, ShaderClearQueue>(path, ShaderClearQueue())); | |
450 shader_clear_map_[path].push(std::move(helper)); | |
451 helper_ptr->Clear(); | |
452 } | |
453 | |
454 void ShaderCacheFactory::CacheCleared(const base::FilePath& path) { | |
455 DCHECK(CalledOnValidThread()); | |
456 | |
457 ShaderClearMap::iterator iter = shader_clear_map_.find(path); | |
458 if (iter == shader_clear_map_.end()) { | |
459 LOG(ERROR) << "Completed clear but missing clear helper."; | |
460 return; | |
461 } | |
462 | |
463 iter->second.pop(); | |
464 | |
465 // If there are remaining items in the list we trigger the Clear on the | |
466 // next one. | |
467 if (!iter->second.empty()) { | |
468 iter->second.front()->Clear(); | |
469 return; | |
470 } | |
471 | |
472 shader_clear_map_.erase(iter); | |
473 } | |
474 | |
475 //////////////////////////////////////////////////////////////////////////////// | |
476 // ShaderDiskCache | |
477 | |
478 ShaderDiskCache::ShaderDiskCache(ShaderCacheFactory* factory, | |
479 const base::FilePath& cache_path) | |
480 : factory_(factory), | |
481 cache_available_(false), | |
482 cache_path_(cache_path), | |
483 is_initialized_(false) { | |
484 factory_->AddToCache(cache_path_, this); | |
485 } | |
486 | |
487 ShaderDiskCache::~ShaderDiskCache() { | |
488 factory_->RemoveFromCache(cache_path_); | |
489 } | |
490 | |
491 void ShaderDiskCache::Init( | |
492 scoped_refptr<base::SingleThreadTaskRunner> cache_task_runner) { | |
493 if (is_initialized_) { | |
494 NOTREACHED(); // can't initialize disk cache twice. | |
495 return; | |
496 } | |
497 is_initialized_ = true; | |
498 | |
499 int rv = disk_cache::CreateCacheBackend( | |
500 net::SHADER_CACHE, net::CACHE_BACKEND_DEFAULT, | |
501 cache_path_.Append(kGpuCachePath), | |
502 gpu::kDefaultMaxProgramCacheMemoryBytes, true, cache_task_runner, NULL, | |
503 &backend_, base::Bind(&ShaderDiskCache::CacheCreatedCallback, this)); | |
504 | |
505 if (rv == net::OK) | |
506 cache_available_ = true; | |
507 } | |
508 | |
509 void ShaderDiskCache::Cache(const std::string& key, const std::string& shader) { | |
510 if (!cache_available_) | |
511 return; | |
512 | |
513 auto shim = base::MakeUnique<ShaderDiskCacheEntry>(this, key, shader); | |
514 shim->Cache(); | |
515 auto* raw_ptr = shim.get(); | |
516 entries_.insert(std::make_pair(raw_ptr, std::move(shim))); | |
517 } | |
518 | |
519 int ShaderDiskCache::Clear( | |
520 const base::Time begin_time, const base::Time end_time, | |
521 const net::CompletionCallback& completion_callback) { | |
522 int rv; | |
523 if (begin_time.is_null()) { | |
524 rv = backend_->DoomAllEntries(completion_callback); | |
525 } else { | |
526 rv = backend_->DoomEntriesBetween(begin_time, end_time, | |
527 completion_callback); | |
528 } | |
529 return rv; | |
530 } | |
531 | |
532 int32_t ShaderDiskCache::Size() { | |
533 if (!cache_available_) | |
534 return -1; | |
535 return backend_->GetEntryCount(); | |
536 } | |
537 | |
538 int ShaderDiskCache::SetAvailableCallback( | |
539 const net::CompletionCallback& callback) { | |
540 if (cache_available_) | |
541 return net::OK; | |
542 available_callback_ = callback; | |
543 return net::ERR_IO_PENDING; | |
544 } | |
545 | |
546 void ShaderDiskCache::CacheCreatedCallback(int rv) { | |
547 if (rv != net::OK) { | |
548 LOG(ERROR) << "Shader Cache Creation failed: " << rv; | |
549 return; | |
550 } | |
551 helper_ = | |
552 base::MakeUnique<ShaderDiskReadHelper>(this, shader_loaded_callback_); | |
553 helper_->LoadCache(); | |
554 } | |
555 | |
556 void ShaderDiskCache::EntryComplete(ShaderDiskCacheEntry* entry) { | |
557 entries_.erase(entry); | |
558 if (entries_.empty() && !cache_complete_callback_.is_null()) | |
559 cache_complete_callback_.Run(net::OK); | |
560 } | |
561 | |
562 void ShaderDiskCache::ReadComplete() { | |
563 helper_ = nullptr; | |
564 | |
565 // The cache is considered available after we have finished reading any | |
566 // of the old cache values off disk. This prevents a potential race where we | |
567 // are reading from disk and execute a cache clear at the same time. | |
568 cache_available_ = true; | |
569 if (!available_callback_.is_null()) { | |
570 available_callback_.Run(net::OK); | |
571 available_callback_.Reset(); | |
572 } | |
573 } | |
574 | |
575 int ShaderDiskCache::SetCacheCompleteCallback( | |
576 const net::CompletionCallback& callback) { | |
577 if (entries_.empty()) { | |
578 return net::OK; | |
579 } | |
580 cache_complete_callback_ = callback; | |
581 return net::ERR_IO_PENDING; | |
582 } | |
583 | |
584 } // namespace content | |
OLD | NEW |