- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / gpu / shader_disk_cache.cc
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
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
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
150 ShaderDiskCacheEntry::~ShaderDiskCacheEntry() {
151   if (entry_)
152     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
153                             base::Bind(&EntryCloser, entry_));
154 }
155
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
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
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
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
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
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
252 void ShaderDiskReadHelper::LoadCache() {
253   DCHECK(CalledOnValidThread());
254   if (!cache_.get())
255     return;
256   OnOpComplete(net::OK);
257 }
258
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
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
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
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
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
348 ShaderDiskReadHelper::~ShaderDiskReadHelper() {
349   if (entry_)
350     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
351                             base::Bind(&EntryCloser, entry_));
352 }
353
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
367 ShaderClearHelper::~ShaderClearHelper() {
368   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
369 }
370
371 void ShaderClearHelper::Clear() {
372   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
373   DoClearShaderCache(net::OK);
374 }
375
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
410 ShaderCacheFactory* ShaderCacheFactory::GetInstance() {
411   return Singleton<ShaderCacheFactory,
412       LeakySingletonTraits<ShaderCacheFactory> >::get();
413 }
414
415 ShaderCacheFactory::ShaderCacheFactory() {
416 }
417
418 ShaderCacheFactory::~ShaderCacheFactory() {
419 }
420
421 void ShaderCacheFactory::SetCacheInfo(int32 client_id,
422                                       const base::FilePath& path) {
423   client_id_to_path_map_[client_id] = path;
424 }
425
426 void ShaderCacheFactory::RemoveCacheInfo(int32 client_id) {
427   client_id_to_path_map_.erase(client_id);
428 }
429
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
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
449 void ShaderCacheFactory::AddToCache(const base::FilePath& key,
450                                     ShaderDiskCache* cache) {
451   shader_cache_map_[key] = cache;
452 }
453
454 void ShaderCacheFactory::RemoveFromCache(const base::FilePath& key) {
455   shader_cache_map_.erase(key);
456 }
457
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
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
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
514 ShaderDiskCache::~ShaderDiskCache() {
515   ShaderCacheFactory::GetInstance()->RemoveFromCache(cache_path_);
516 }
517
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_BLOCKFILE,
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
540 void ShaderDiskCache::Cache(const std::string& key, const std::string& shader) {
541   if (!cache_available_)
542     return;
543
544   ShaderDiskCacheEntry* shim =
545       new ShaderDiskCacheEntry(AsWeakPtr(), key, shader);
546   shim->Cache();
547
548   entry_map_[shim] = shim;
549 }
550
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
564 int32 ShaderDiskCache::Size() {
565   if (!cache_available_)
566     return -1;
567   return backend_->GetEntryCount();
568 }
569
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
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
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
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
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