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