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