ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / content / browser / gpu / shader_disk_cache.cc
blob24f07035bfe846f209a33766c9da81283f5362ad
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.
5 #include "content/browser/gpu/shader_disk_cache.h"
7 #include "base/profiler/scoped_tracker.h"
8 #include "base/threading/thread_checker.h"
9 #include "content/browser/gpu/gpu_process_host.h"
10 #include "content/public/browser/browser_thread.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"
16 namespace content {
18 namespace {
20 static const base::FilePath::CharType kGpuCachePath[] =
21 FILE_PATH_LITERAL("GPUCache");
23 void EntryCloser(disk_cache::Entry* entry) {
24 entry->Close();
27 void FreeDiskCacheIterator(scoped_ptr<disk_cache::Backend::Iterator> iterator) {
30 } // namespace
32 // ShaderDiskCacheEntry handles the work of caching/updating the cached
33 // shaders.
34 class ShaderDiskCacheEntry
35 : public base::ThreadChecker,
36 public base::RefCounted<ShaderDiskCacheEntry> {
37 public:
38 ShaderDiskCacheEntry(base::WeakPtr<ShaderDiskCache> cache,
39 const std::string& key,
40 const std::string& shader);
41 void Cache();
43 private:
44 friend class base::RefCounted<ShaderDiskCacheEntry>;
46 enum OpType {
47 TERMINATE,
48 OPEN_ENTRY,
49 WRITE_DATA,
50 CREATE_ENTRY,
53 ~ShaderDiskCacheEntry();
55 void OnOpComplete(int rv);
57 int OpenCallback(int rv);
58 int WriteCallback(int rv);
59 int IOComplete(int rv);
61 base::WeakPtr<ShaderDiskCache> cache_;
62 OpType op_type_;
63 std::string key_;
64 std::string shader_;
65 disk_cache::Entry* entry_;
67 DISALLOW_COPY_AND_ASSIGN(ShaderDiskCacheEntry);
70 // ShaderDiskReadHelper is used to load all of the cached shaders from the
71 // disk cache and send to the memory cache.
72 class ShaderDiskReadHelper
73 : public base::ThreadChecker,
74 public base::RefCounted<ShaderDiskReadHelper> {
75 public:
76 ShaderDiskReadHelper(base::WeakPtr<ShaderDiskCache> cache, int host_id);
77 void LoadCache();
79 private:
80 friend class base::RefCounted<ShaderDiskReadHelper>;
82 enum OpType {
83 TERMINATE,
84 OPEN_NEXT,
85 OPEN_NEXT_COMPLETE,
86 READ_COMPLETE,
87 ITERATION_FINISHED
91 ~ShaderDiskReadHelper();
93 void OnOpComplete(int rv);
95 int OpenNextEntry();
96 int OpenNextEntryComplete(int rv);
97 int ReadComplete(int rv);
98 int IterationComplete(int rv);
100 base::WeakPtr<ShaderDiskCache> cache_;
101 OpType op_type_;
102 scoped_ptr<disk_cache::Backend::Iterator> iter_;
103 scoped_refptr<net::IOBufferWithSize> buf_;
104 int host_id_;
105 disk_cache::Entry* entry_;
107 DISALLOW_COPY_AND_ASSIGN(ShaderDiskReadHelper);
110 class ShaderClearHelper
111 : public base::RefCounted<ShaderClearHelper>,
112 public base::SupportsWeakPtr<ShaderClearHelper> {
113 public:
114 ShaderClearHelper(scoped_refptr<ShaderDiskCache> cache,
115 const base::FilePath& path,
116 const base::Time& delete_begin,
117 const base::Time& delete_end,
118 const base::Closure& callback);
119 void Clear();
121 private:
122 friend class base::RefCounted<ShaderClearHelper>;
124 enum OpType {
125 TERMINATE,
126 VERIFY_CACHE_SETUP,
127 DELETE_CACHE
130 ~ShaderClearHelper();
132 void DoClearShaderCache(int rv);
134 scoped_refptr<ShaderDiskCache> cache_;
135 OpType op_type_;
136 base::FilePath path_;
137 base::Time delete_begin_;
138 base::Time delete_end_;
139 base::Closure callback_;
141 DISALLOW_COPY_AND_ASSIGN(ShaderClearHelper);
144 ShaderDiskCacheEntry::ShaderDiskCacheEntry(base::WeakPtr<ShaderDiskCache> cache,
145 const std::string& key,
146 const std::string& shader)
147 : cache_(cache),
148 op_type_(OPEN_ENTRY),
149 key_(key),
150 shader_(shader),
151 entry_(NULL) {
154 ShaderDiskCacheEntry::~ShaderDiskCacheEntry() {
155 if (entry_)
156 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
157 base::Bind(&EntryCloser, entry_));
160 void ShaderDiskCacheEntry::Cache() {
161 DCHECK(CalledOnValidThread());
162 if (!cache_.get())
163 return;
165 int rv = cache_->backend()->OpenEntry(
166 key_,
167 &entry_,
168 base::Bind(&ShaderDiskCacheEntry::OnOpComplete, this));
169 if (rv != net::ERR_IO_PENDING)
170 OnOpComplete(rv);
173 void ShaderDiskCacheEntry::OnOpComplete(int rv) {
174 DCHECK(CalledOnValidThread());
175 if (!cache_.get())
176 return;
178 do {
179 switch (op_type_) {
180 case OPEN_ENTRY:
181 rv = OpenCallback(rv);
182 break;
183 case CREATE_ENTRY:
184 rv = WriteCallback(rv);
185 break;
186 case WRITE_DATA:
187 rv = IOComplete(rv);
188 break;
189 case TERMINATE:
190 rv = net::ERR_IO_PENDING; // break the loop.
191 break;
192 default:
193 NOTREACHED(); // Invalid op_type_ provided.
194 break;
196 } while (rv != net::ERR_IO_PENDING);
199 int ShaderDiskCacheEntry::OpenCallback(int rv) {
200 DCHECK(CalledOnValidThread());
201 // Called through OnOpComplete, so we know |cache_| is valid.
202 if (rv == net::OK) {
203 cache_->backend()->OnExternalCacheHit(key_);
204 cache_->EntryComplete(this);
205 op_type_ = TERMINATE;
206 return rv;
209 op_type_ = CREATE_ENTRY;
210 return cache_->backend()->CreateEntry(
211 key_,
212 &entry_,
213 base::Bind(&ShaderDiskCacheEntry::OnOpComplete, this));
216 int ShaderDiskCacheEntry::WriteCallback(int rv) {
217 DCHECK(CalledOnValidThread());
218 // Called through OnOpComplete, so we know |cache_| is valid.
219 if (rv != net::OK) {
220 LOG(ERROR) << "Failed to create shader cache entry: " << rv;
221 cache_->EntryComplete(this);
222 op_type_ = TERMINATE;
223 return rv;
226 op_type_ = WRITE_DATA;
227 scoped_refptr<net::StringIOBuffer> io_buf = new net::StringIOBuffer(shader_);
228 return entry_->WriteData(
231 io_buf.get(),
232 shader_.length(),
233 base::Bind(&ShaderDiskCacheEntry::OnOpComplete, this),
234 false);
237 int ShaderDiskCacheEntry::IOComplete(int rv) {
238 DCHECK(CalledOnValidThread());
239 // Called through OnOpComplete, so we know |cache_| is valid.
240 cache_->EntryComplete(this);
241 op_type_ = TERMINATE;
242 return rv;
245 ShaderDiskReadHelper::ShaderDiskReadHelper(
246 base::WeakPtr<ShaderDiskCache> cache,
247 int host_id)
248 : cache_(cache),
249 op_type_(OPEN_NEXT),
250 buf_(NULL),
251 host_id_(host_id),
252 entry_(NULL) {
255 void ShaderDiskReadHelper::LoadCache() {
256 DCHECK(CalledOnValidThread());
257 if (!cache_.get())
258 return;
259 OnOpComplete(net::OK);
262 void ShaderDiskReadHelper::OnOpComplete(int rv) {
263 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
264 tracked_objects::ScopedTracker tracking_profile(
265 FROM_HERE_WITH_EXPLICIT_FUNCTION(
266 "422516 ShaderDiskReadHelper::OnOpComplete"));
268 DCHECK(CalledOnValidThread());
269 if (!cache_.get())
270 return;
272 do {
273 switch (op_type_) {
274 case OPEN_NEXT:
275 rv = OpenNextEntry();
276 break;
277 case OPEN_NEXT_COMPLETE:
278 rv = OpenNextEntryComplete(rv);
279 break;
280 case READ_COMPLETE:
281 rv = ReadComplete(rv);
282 break;
283 case ITERATION_FINISHED:
284 rv = IterationComplete(rv);
285 break;
286 case TERMINATE:
287 cache_->ReadComplete();
288 rv = net::ERR_IO_PENDING; // break the loop
289 break;
290 default:
291 NOTREACHED(); // Invalid state for read helper
292 rv = net::ERR_FAILED;
293 break;
295 } while (rv != net::ERR_IO_PENDING);
298 int ShaderDiskReadHelper::OpenNextEntry() {
299 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
300 tracked_objects::ScopedTracker tracking_profile(
301 FROM_HERE_WITH_EXPLICIT_FUNCTION(
302 "422516 ShaderDiskReadHelper::OpenNextEntry"));
304 DCHECK(CalledOnValidThread());
305 // Called through OnOpComplete, so we know |cache_| is valid.
306 op_type_ = OPEN_NEXT_COMPLETE;
307 if (!iter_)
308 iter_ = cache_->backend()->CreateIterator();
309 return iter_->OpenNextEntry(
310 &entry_, base::Bind(&ShaderDiskReadHelper::OnOpComplete, this));
313 int ShaderDiskReadHelper::OpenNextEntryComplete(int rv) {
314 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
315 tracked_objects::ScopedTracker tracking_profile(
316 FROM_HERE_WITH_EXPLICIT_FUNCTION(
317 "422516 ShaderDiskReadHelper::OpenNextEntryComplete"));
319 DCHECK(CalledOnValidThread());
320 // Called through OnOpComplete, so we know |cache_| is valid.
321 if (rv == net::ERR_FAILED) {
322 iter_.reset();
323 op_type_ = ITERATION_FINISHED;
324 return net::OK;
327 if (rv < 0)
328 return rv;
330 op_type_ = READ_COMPLETE;
331 buf_ = new net::IOBufferWithSize(entry_->GetDataSize(1));
332 return entry_->ReadData(
335 buf_.get(),
336 buf_->size(),
337 base::Bind(&ShaderDiskReadHelper::OnOpComplete, this));
340 int ShaderDiskReadHelper::ReadComplete(int rv) {
341 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
342 tracked_objects::ScopedTracker tracking_profile(
343 FROM_HERE_WITH_EXPLICIT_FUNCTION(
344 "422516 ShaderDiskReadHelper::ReadComplete"));
346 DCHECK(CalledOnValidThread());
347 // Called through OnOpComplete, so we know |cache_| is valid.
348 if (rv && rv == buf_->size()) {
349 GpuProcessHost* host = GpuProcessHost::FromID(host_id_);
350 if (host)
351 host->LoadedShader(entry_->GetKey(), std::string(buf_->data(),
352 buf_->size()));
355 buf_ = NULL;
356 entry_->Close();
357 entry_ = NULL;
359 op_type_ = OPEN_NEXT;
360 return net::OK;
363 int ShaderDiskReadHelper::IterationComplete(int rv) {
364 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
365 tracked_objects::ScopedTracker tracking_profile(
366 FROM_HERE_WITH_EXPLICIT_FUNCTION(
367 "422516 ShaderDiskReadHelper::IterationComplete"));
369 DCHECK(CalledOnValidThread());
370 // Called through OnOpComplete, so we know |cache_| is valid.
371 iter_.reset();
372 op_type_ = TERMINATE;
373 return net::OK;
376 ShaderDiskReadHelper::~ShaderDiskReadHelper() {
377 if (entry_) {
378 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
379 base::Bind(&EntryCloser, entry_));
381 if (iter_) {
382 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
383 base::Bind(&FreeDiskCacheIterator,
384 base::Passed(&iter_)));
388 ShaderClearHelper::ShaderClearHelper(scoped_refptr<ShaderDiskCache> cache,
389 const base::FilePath& path,
390 const base::Time& delete_begin,
391 const base::Time& delete_end,
392 const base::Closure& callback)
393 : cache_(cache),
394 op_type_(VERIFY_CACHE_SETUP),
395 path_(path),
396 delete_begin_(delete_begin),
397 delete_end_(delete_end),
398 callback_(callback) {
401 ShaderClearHelper::~ShaderClearHelper() {
402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
405 void ShaderClearHelper::Clear() {
406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
407 DoClearShaderCache(net::OK);
410 void ShaderClearHelper::DoClearShaderCache(int rv) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
413 // Hold a ref to ourselves so when we do the CacheCleared call we don't get
414 // auto-deleted when our ref count drops to zero.
415 scoped_refptr<ShaderClearHelper> helper = this;
417 while (rv != net::ERR_IO_PENDING) {
418 switch (op_type_) {
419 case VERIFY_CACHE_SETUP:
420 rv = cache_->SetAvailableCallback(
421 base::Bind(&ShaderClearHelper::DoClearShaderCache, AsWeakPtr()));
422 op_type_ = DELETE_CACHE;
423 break;
424 case DELETE_CACHE:
425 rv = cache_->Clear(
426 delete_begin_, delete_end_,
427 base::Bind(&ShaderClearHelper::DoClearShaderCache, AsWeakPtr()));
428 op_type_ = TERMINATE;
429 break;
430 case TERMINATE:
431 ShaderCacheFactory::GetInstance()->CacheCleared(path_);
432 callback_.Run();
433 rv = net::ERR_IO_PENDING; // Break the loop.
434 break;
435 default:
436 NOTREACHED(); // Invalid state provided.
437 op_type_ = TERMINATE;
438 break;
443 // static
444 ShaderCacheFactory* ShaderCacheFactory::GetInstance() {
445 return Singleton<ShaderCacheFactory,
446 LeakySingletonTraits<ShaderCacheFactory> >::get();
449 ShaderCacheFactory::ShaderCacheFactory() {
452 ShaderCacheFactory::~ShaderCacheFactory() {
455 void ShaderCacheFactory::SetCacheInfo(int32 client_id,
456 const base::FilePath& path) {
457 client_id_to_path_map_[client_id] = path;
460 void ShaderCacheFactory::RemoveCacheInfo(int32 client_id) {
461 client_id_to_path_map_.erase(client_id);
464 scoped_refptr<ShaderDiskCache> ShaderCacheFactory::Get(int32 client_id) {
465 ClientIdToPathMap::iterator iter =
466 client_id_to_path_map_.find(client_id);
467 if (iter == client_id_to_path_map_.end())
468 return NULL;
469 return ShaderCacheFactory::GetByPath(iter->second);
472 scoped_refptr<ShaderDiskCache> ShaderCacheFactory::GetByPath(
473 const base::FilePath& path) {
474 ShaderCacheMap::iterator iter = shader_cache_map_.find(path);
475 if (iter != shader_cache_map_.end())
476 return iter->second;
478 ShaderDiskCache* cache = new ShaderDiskCache(path);
479 cache->Init();
480 return cache;
483 void ShaderCacheFactory::AddToCache(const base::FilePath& key,
484 ShaderDiskCache* cache) {
485 shader_cache_map_[key] = cache;
488 void ShaderCacheFactory::RemoveFromCache(const base::FilePath& key) {
489 shader_cache_map_.erase(key);
492 void ShaderCacheFactory::ClearByPath(const base::FilePath& path,
493 const base::Time& delete_begin,
494 const base::Time& delete_end,
495 const base::Closure& callback) {
496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
497 DCHECK(!callback.is_null());
499 scoped_refptr<ShaderClearHelper> helper = new ShaderClearHelper(
500 GetByPath(path), path, delete_begin, delete_end, callback);
502 // We could receive requests to clear the same path with different
503 // begin/end times. So, we keep a list of requests. If we haven't seen this
504 // path before we kick off the clear and add it to the list. If we have see it
505 // already, then we already have a clear running. We add this clear to the
506 // list and wait for any previous clears to finish.
507 ShaderClearMap::iterator iter = shader_clear_map_.find(path);
508 if (iter != shader_clear_map_.end()) {
509 iter->second.push(helper);
510 return;
513 shader_clear_map_.insert(
514 std::pair<base::FilePath, ShaderClearQueue>(path, ShaderClearQueue()));
515 shader_clear_map_[path].push(helper);
516 helper->Clear();
519 void ShaderCacheFactory::CacheCleared(const base::FilePath& path) {
520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
522 ShaderClearMap::iterator iter = shader_clear_map_.find(path);
523 if (iter == shader_clear_map_.end()) {
524 LOG(ERROR) << "Completed clear but missing clear helper.";
525 return;
528 iter->second.pop();
530 // If there are remaining items in the list we trigger the Clear on the
531 // next one.
532 if (!iter->second.empty()) {
533 iter->second.front()->Clear();
534 return;
537 shader_clear_map_.erase(path);
540 ShaderDiskCache::ShaderDiskCache(const base::FilePath& cache_path)
541 : cache_available_(false),
542 host_id_(0),
543 cache_path_(cache_path),
544 is_initialized_(false) {
545 ShaderCacheFactory::GetInstance()->AddToCache(cache_path_, this);
548 ShaderDiskCache::~ShaderDiskCache() {
549 ShaderCacheFactory::GetInstance()->RemoveFromCache(cache_path_);
552 void ShaderDiskCache::Init() {
553 if (is_initialized_) {
554 NOTREACHED(); // can't initialize disk cache twice.
555 return;
557 is_initialized_ = true;
559 int rv = disk_cache::CreateCacheBackend(
560 net::SHADER_CACHE,
561 net::CACHE_BACKEND_DEFAULT,
562 cache_path_.Append(kGpuCachePath),
563 gpu::kDefaultMaxProgramCacheMemoryBytes,
564 true,
565 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
566 NULL,
567 &backend_,
568 base::Bind(&ShaderDiskCache::CacheCreatedCallback, this));
570 if (rv == net::OK)
571 cache_available_ = true;
574 void ShaderDiskCache::Cache(const std::string& key, const std::string& shader) {
575 if (!cache_available_)
576 return;
578 scoped_refptr<ShaderDiskCacheEntry> shim =
579 new ShaderDiskCacheEntry(AsWeakPtr(), key, shader);
580 shim->Cache();
582 entry_map_[shim.get()] = shim;
585 int ShaderDiskCache::Clear(
586 const base::Time begin_time, const base::Time end_time,
587 const net::CompletionCallback& completion_callback) {
588 int rv;
589 if (begin_time.is_null()) {
590 rv = backend_->DoomAllEntries(completion_callback);
591 } else {
592 rv = backend_->DoomEntriesBetween(begin_time, end_time,
593 completion_callback);
595 return rv;
598 int32 ShaderDiskCache::Size() {
599 if (!cache_available_)
600 return -1;
601 return backend_->GetEntryCount();
604 int ShaderDiskCache::SetAvailableCallback(
605 const net::CompletionCallback& callback) {
606 if (cache_available_)
607 return net::OK;
608 available_callback_ = callback;
609 return net::ERR_IO_PENDING;
612 void ShaderDiskCache::CacheCreatedCallback(int rv) {
613 if (rv != net::OK) {
614 LOG(ERROR) << "Shader Cache Creation failed: " << rv;
615 return;
617 helper_ = new ShaderDiskReadHelper(AsWeakPtr(), host_id_);
618 helper_->LoadCache();
621 void ShaderDiskCache::EntryComplete(void* entry) {
622 entry_map_.erase(entry);
624 if (entry_map_.empty() && !cache_complete_callback_.is_null())
625 cache_complete_callback_.Run(net::OK);
628 void ShaderDiskCache::ReadComplete() {
629 helper_ = NULL;
631 // The cache is considered available after we have finished reading any
632 // of the old cache values off disk. This prevents a potential race where we
633 // are reading from disk and execute a cache clear at the same time.
634 cache_available_ = true;
635 if (!available_callback_.is_null()) {
636 available_callback_.Run(net::OK);
637 available_callback_.Reset();
641 int ShaderDiskCache::SetCacheCompleteCallback(
642 const net::CompletionCallback& callback) {
643 if (entry_map_.empty()) {
644 return net::OK;
646 cache_complete_callback_ = callback;
647 return net::ERR_IO_PENDING;
650 } // namespace content