summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordsinclair@chromium.org <dsinclair@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-13 00:01:37 +0000
committerdsinclair@chromium.org <dsinclair@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-13 00:01:37 +0000
commite3932abb978b39d8b28db6e9b7d15869d817dad2 (patch)
treee86df2495c23edf37fb72c2e6b306ef0f0458c70
parenta11b00cf82c61489fe42878204375524fa2f952a (diff)
downloadchromium_src-e3932abb978b39d8b28db6e9b7d15869d817dad2.zip
chromium_src-e3932abb978b39d8b28db6e9b7d15869d817dad2.tar.gz
chromium_src-e3932abb978b39d8b28db6e9b7d15869d817dad2.tar.bz2
Add per-profile disk caching of complied GPU shaders.
This CL adds a per-profile disk cache for any shaders that are complied while using the profile. When the profile is first opened the shaders will be loaded from disk and used to pre-populate the GPU memory shader cache. The disk cache takes the load time for From Dust from ~30 seconds to ~18 seconds on my Linux machine for any loads after the first. BUG=166763 Review URL: https://chromiumcodereview.appspot.com/12036056 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187704 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/login/chrome_restart_request.cc1
-rw-r--r--content/browser/gpu/gpu_process_host.cc67
-rw-r--r--content/browser/gpu/gpu_process_host.h16
-rw-r--r--content/browser/gpu/shader_disk_cache.cc424
-rw-r--r--content/browser/gpu/shader_disk_cache.h102
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc22
-rw-r--r--content/common/gpu/gpu_channel.cc8
-rw-r--r--content/common/gpu/gpu_channel.h2
-rw-r--r--content/common/gpu/gpu_channel_manager.cc7
-rw-r--r--content/common/gpu/gpu_channel_manager.h2
-rw-r--r--content/common/gpu/gpu_command_buffer_stub.cc8
-rw-r--r--content/common/gpu/gpu_command_buffer_stub.h2
-rw-r--r--content/common/gpu/gpu_messages.h14
-rw-r--r--content/content_browser.gypi2
-rw-r--r--gpu/DEPS1
-rw-r--r--gpu/command_buffer/common/constants.h3
-rw-r--r--gpu/command_buffer/service/disk_cache_proto.proto23
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc12
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.h4
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_mock.h2
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc7
-rw-r--r--gpu/command_buffer/service/gpu_switches.cc4
-rw-r--r--gpu/command_buffer/service/gpu_switches.h1
-rw-r--r--gpu/command_buffer/service/memory_program_cache.cc127
-rw-r--r--gpu/command_buffer/service/memory_program_cache.h8
-rw-r--r--gpu/command_buffer/service/memory_program_cache_unittest.cc137
-rw-r--r--gpu/command_buffer/service/mocks.h8
-rw-r--r--gpu/command_buffer/service/program_cache.cc4
-rw-r--r--gpu/command_buffer/service/program_cache.h8
-rw-r--r--gpu/command_buffer/service/program_cache_unittest.cc5
-rw-r--r--gpu/command_buffer/service/program_manager.cc6
-rw-r--r--gpu/command_buffer/service/program_manager.h5
-rw-r--r--gpu/command_buffer/service/program_manager_unittest.cc44
-rw-r--r--gpu/command_buffer_service.gypi1
-rw-r--r--gpu/gpu.gyp22
35 files changed, 1066 insertions, 43 deletions
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 0ee45c7..cb79685 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -76,6 +76,7 @@ std::string DeriveCommandLine(const GURL& start_url,
::switches::kDisableBrowserPluginCompositing,
::switches::kDisableEncryptedMedia,
::switches::kDisableForceCompositingMode,
+ ::switches::kDisableGpuShaderDiskCache,
::switches::kDisableGpuWatchdog,
::switches::kDisableLoginAnimations,
::switches::kDisableOobeAnimation,
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index ea817c1..607ef84f 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -4,6 +4,7 @@
#include "content/browser/gpu/gpu_process_host.h"
+#include "base/base64.h"
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -12,11 +13,13 @@
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram.h"
#include "base/process_util.h"
+#include "base/sha1.h"
#include "base/threading/thread.h"
#include "content/browser/browser_child_process_host_impl.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host_ui_shim.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
+#include "content/browser/gpu/shader_disk_cache.h"
#include "content/browser/renderer_host/render_widget_helper.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/common/child_process_host_impl.h"
@@ -28,8 +31,10 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
+#include "gpu/command_buffer/common/constants.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_switches.h"
@@ -586,6 +591,11 @@ bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceRelease,
OnAcceleratedSurfaceRelease)
#endif
+ IPC_MESSAGE_HANDLER(GpuHostMsg_DestroyChannel,
+ OnDestroyChannel)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_CacheShader,
+ OnCacheShader)
+
IPC_MESSAGE_UNHANDLED(RouteOnUIThread(message))
IPC_END_MESSAGE_MAP()
@@ -619,6 +629,11 @@ void GpuProcessHost::EstablishGpuChannel(
} else {
callback.Run(IPC::ChannelHandle(), GPUInfo());
}
+
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableGpuShaderDiskCache)) {
+ CreateChannelCache(client_id, gpu::kDefaultMaxProgramCacheMemoryBytes);
+ }
}
void GpuProcessHost::CreateViewCommandBuffer(
@@ -1091,4 +1106,56 @@ void GpuProcessHost::BlockLiveOffscreenContexts() {
}
}
+std::string GpuProcessHost::GetShaderPrefixKey() {
+ if (shader_prefix_key_.empty()) {
+ GPUInfo info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
+
+ std::string in_str = GetContentClient()->GetProduct() + "-" +
+ info.gl_vendor + "-" + info.gl_renderer + "-" +
+ info.driver_version + "-" + info.driver_vendor;
+
+ base::Base64Encode(base::SHA1HashString(in_str), &shader_prefix_key_);
+ }
+
+ return shader_prefix_key_;
+}
+
+void GpuProcessHost::LoadedShader(const std::string& key,
+ const std::string& data) {
+ std::string prefix = GetShaderPrefixKey();
+ if (!key.compare(0, prefix.length(), prefix))
+ Send(new GpuMsg_LoadedShader(data));
+}
+
+void GpuProcessHost::CreateChannelCache(int32 client_id, size_t cache_size) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::CreateChannelCache");
+
+ scoped_refptr<ShaderDiskCache> cache =
+ ShaderCacheFactory::GetInstance()->Get(client_id);
+ if (!cache)
+ return;
+
+ cache->set_max_cache_size(cache_size);
+ cache->set_host_id(host_id_);
+
+ client_id_to_shader_cache_[client_id] = cache;
+}
+
+void GpuProcessHost::OnDestroyChannel(int32 client_id) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnDestroyChannel");
+ client_id_to_shader_cache_.erase(client_id);
+}
+
+void GpuProcessHost::OnCacheShader(int32 client_id,
+ const std::string& key,
+ const std::string& shader) {
+ TRACE_EVENT0("gpu", "GpuProcessHost::OnCacheShader");
+ ClientIdToShaderCacheMap::iterator iter =
+ client_id_to_shader_cache_.find(client_id);
+ // If the cache doesn't exist then this is an off the record profile.
+ if (iter == client_id_to_shader_cache_.end())
+ return;
+ iter->second->Cache(GetShaderPrefixKey() + ":" + key, shader);
+}
+
} // namespace content
diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h
index aaee4e60..2655b36 100644
--- a/content/browser/gpu/gpu_process_host.h
+++ b/content/browser/gpu/gpu_process_host.h
@@ -39,6 +39,7 @@ struct ChannelHandle;
namespace content {
class BrowserChildProcessHostImpl;
class GpuMainThread;
+class ShaderDiskCache;
class GpuProcessHost : public BrowserChildProcessHostDelegate,
public IPC::Sender,
@@ -122,6 +123,8 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
void ForceShutdown();
+ void LoadedShader(const std::string& key, const std::string& data);
+
private:
static bool ValidateHost(GpuProcessHost* host);
@@ -166,12 +169,19 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
const GpuHostMsg_AcceleratedSurfaceRelease_Params& params);
#endif
+ void CreateChannelCache(int32 client_id, size_t cache_size);
+ void OnDestroyChannel(int32 client_id);
+ void OnCacheShader(int32 client_id, const std::string& key,
+ const std::string& shader);
+
bool LaunchGpuProcess(const std::string& channel_id);
void SendOutstandingReplies();
void BlockLiveOffscreenContexts();
+ std::string GetShaderPrefixKey();
+
// The serial number of the GpuProcessHost / GpuProcessHostUIShim pair.
int host_id_;
@@ -239,6 +249,12 @@ class GpuProcessHost : public BrowserChildProcessHostDelegate,
bool uma_memory_stats_received_;
GPUMemoryUmaStats uma_memory_stats_;
+ typedef std::map<int32, scoped_refptr<ShaderDiskCache> >
+ ClientIdToShaderCacheMap;
+ ClientIdToShaderCacheMap client_id_to_shader_cache_;
+
+ std::string shader_prefix_key_;
+
DISALLOW_COPY_AND_ASSIGN(GpuProcessHost);
};
diff --git a/content/browser/gpu/shader_disk_cache.cc b/content/browser/gpu/shader_disk_cache.cc
new file mode 100644
index 0000000..ca30937
--- /dev/null
+++ b/content/browser/gpu/shader_disk_cache.cc
@@ -0,0 +1,424 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gpu/shader_disk_cache.h"
+
+#include "base/threading/thread_checker.h"
+#include "content/browser/gpu/gpu_process_host.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace content {
+
+namespace {
+
+static const base::FilePath::CharType kGpuCachePath[] =
+ FILE_PATH_LITERAL("GPUCache");
+
+void EntryCloser(disk_cache::Entry* entry) {
+ entry->Close();
+}
+
+} // namespace
+
+// ShaderDiskCacheEntry handles the work of caching/updating the cached
+// shaders.
+class ShaderDiskCacheEntry
+ : public base::ThreadChecker,
+ public base::RefCounted<ShaderDiskCacheEntry> {
+ public:
+ ShaderDiskCacheEntry(base::WeakPtr<ShaderDiskCache> cache,
+ const std::string& key,
+ const std::string& shader);
+ void Cache();
+
+ private:
+ friend class base::RefCounted<ShaderDiskCacheEntry>;
+
+ enum OpType {
+ TERMINATE,
+ OPEN_ENTRY,
+ WRITE_DATA,
+ CREATE_ENTRY,
+ };
+
+ ~ShaderDiskCacheEntry();
+
+ void OnOpComplete(int rv);
+
+ int OpenCallback(int rv);
+ int WriteCallback(int rv);
+ int IOComplete(int rv);
+
+ base::WeakPtr<ShaderDiskCache> cache_;
+ OpType op_type_;
+ std::string key_;
+ std::string shader_;
+ disk_cache::Entry* entry_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShaderDiskCacheEntry);
+};
+
+// ShaderDiskReadHelper is used to load all of the cached shaders from the
+// disk cache and send to the memory cache.
+class ShaderDiskReadHelper
+ : public base::ThreadChecker,
+ public base::RefCounted<ShaderDiskReadHelper> {
+ public:
+ ShaderDiskReadHelper(base::WeakPtr<ShaderDiskCache> cache, int host_id);
+ void LoadCache();
+
+ private:
+ friend class base::RefCounted<ShaderDiskReadHelper>;
+
+ enum OpType {
+ TERMINATE,
+ OPEN_NEXT,
+ OPEN_NEXT_COMPLETE,
+ READ_COMPLETE,
+ ITERATION_FINISHED
+ };
+
+
+ ~ShaderDiskReadHelper();
+
+ void OnOpComplete(int rv);
+
+ int OpenNextEntry();
+ int OpenNextEntryComplete(int rv);
+ int ReadComplete(int rv);
+ int IterationComplete(int rv);
+
+ base::WeakPtr<ShaderDiskCache> cache_;
+ OpType op_type_;
+ void* iter_;
+ scoped_refptr<net::IOBufferWithSize> buf_;
+ int host_id_;
+ disk_cache::Entry* entry_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShaderDiskReadHelper);
+};
+
+ShaderDiskCacheEntry::ShaderDiskCacheEntry(base::WeakPtr<ShaderDiskCache> cache,
+ const std::string& key,
+ const std::string& shader)
+ : cache_(cache),
+ op_type_(OPEN_ENTRY),
+ key_(key),
+ shader_(shader),
+ entry_(NULL) {
+}
+
+ShaderDiskCacheEntry::~ShaderDiskCacheEntry() {
+ if (entry_)
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&EntryCloser, entry_));
+}
+
+void ShaderDiskCacheEntry::Cache() {
+ DCHECK(CalledOnValidThread());
+ if (!cache_)
+ return;
+
+ int rv = cache_->backend()->OpenEntry(
+ key_,
+ &entry_,
+ base::Bind(&ShaderDiskCacheEntry::OnOpComplete, this));
+ if (rv != net::ERR_IO_PENDING)
+ OnOpComplete(rv);
+}
+
+void ShaderDiskCacheEntry::OnOpComplete(int rv) {
+ DCHECK(CalledOnValidThread());
+ if (!cache_)
+ return;
+
+ do {
+ switch (op_type_) {
+ case OPEN_ENTRY:
+ rv = OpenCallback(rv);
+ break;
+ case CREATE_ENTRY:
+ rv = WriteCallback(rv);
+ break;
+ case WRITE_DATA:
+ rv = IOComplete(rv);
+ break;
+ case TERMINATE:
+ rv = net::ERR_IO_PENDING; // break the loop.
+ break;
+ default:
+ NOTREACHED(); // Invalid op_type_ provided.
+ break;
+ }
+ } while (rv != net::ERR_IO_PENDING);
+}
+
+int ShaderDiskCacheEntry::OpenCallback(int rv) {
+ DCHECK(CalledOnValidThread());
+ // Called through OnOpComplete, so we know |cache_| is valid.
+ if (rv == net::OK) {
+ cache_->backend()->OnExternalCacheHit(key_);
+ cache_->EntryComplete(this);
+ op_type_ = TERMINATE;
+ return rv;
+ }
+
+ op_type_ = CREATE_ENTRY;
+ return cache_->backend()->CreateEntry(
+ key_,
+ &entry_,
+ base::Bind(&ShaderDiskCacheEntry::OnOpComplete, this));
+}
+
+int ShaderDiskCacheEntry::WriteCallback(int rv) {
+ DCHECK(CalledOnValidThread());
+ // Called through OnOpComplete, so we know |cache_| is valid.
+ if (rv != net::OK) {
+ LOG(ERROR) << "Failed to create shader cache entry: " << rv;
+ cache_->EntryComplete(this);
+ op_type_ = TERMINATE;
+ return rv;
+ }
+
+ op_type_ = WRITE_DATA;
+ scoped_refptr<net::StringIOBuffer> io_buf = new net::StringIOBuffer(shader_);
+ return entry_->WriteData(1, 0, io_buf, shader_.length(),
+ base::Bind(&ShaderDiskCacheEntry::OnOpComplete,
+ this),
+ false);
+}
+
+int ShaderDiskCacheEntry::IOComplete(int rv) {
+ DCHECK(CalledOnValidThread());
+ // Called through OnOpComplete, so we know |cache_| is valid.
+ cache_->EntryComplete(this);
+ op_type_ = TERMINATE;
+ return rv;
+}
+
+ShaderDiskReadHelper::ShaderDiskReadHelper(
+ base::WeakPtr<ShaderDiskCache> cache,
+ int host_id)
+ : cache_(cache),
+ op_type_(OPEN_NEXT),
+ iter_(NULL),
+ buf_(NULL),
+ host_id_(host_id),
+ entry_(NULL) {
+}
+
+void ShaderDiskReadHelper::LoadCache() {
+ DCHECK(CalledOnValidThread());
+ if (!cache_)
+ return;
+ OnOpComplete(net::OK);
+}
+
+void ShaderDiskReadHelper::OnOpComplete(int rv) {
+ DCHECK(CalledOnValidThread());
+ if (!cache_)
+ return;
+
+ do {
+ switch (op_type_) {
+ case OPEN_NEXT:
+ rv = OpenNextEntry();
+ break;
+ case OPEN_NEXT_COMPLETE:
+ rv = OpenNextEntryComplete(rv);
+ break;
+ case READ_COMPLETE:
+ rv = ReadComplete(rv);
+ break;
+ case ITERATION_FINISHED:
+ rv = IterationComplete(rv);
+ break;
+ case TERMINATE:
+ cache_->ReadComplete();
+ rv = net::ERR_IO_PENDING; // break the loop
+ break;
+ default:
+ NOTREACHED(); // Invalid state for read helper
+ rv = net::ERR_FAILED;
+ break;
+ }
+ } while (rv != net::ERR_IO_PENDING);
+}
+
+int ShaderDiskReadHelper::OpenNextEntry() {
+ DCHECK(CalledOnValidThread());
+ // Called through OnOpComplete, so we know |cache_| is valid.
+ op_type_ = OPEN_NEXT_COMPLETE;
+ return cache_->backend()->OpenNextEntry(
+ &iter_,
+ &entry_,
+ base::Bind(&ShaderDiskReadHelper::OnOpComplete, this));
+}
+
+int ShaderDiskReadHelper::OpenNextEntryComplete(int rv) {
+ DCHECK(CalledOnValidThread());
+ // Called through OnOpComplete, so we know |cache_| is valid.
+ if (rv == net::ERR_FAILED) {
+ op_type_ = ITERATION_FINISHED;
+ return net::OK;
+ }
+
+ if (rv < 0)
+ return rv;
+
+ op_type_ = READ_COMPLETE;
+ buf_ = new net::IOBufferWithSize(entry_->GetDataSize(1));
+ return entry_->ReadData(1, 0, buf_, buf_->size(),
+ base::Bind(&ShaderDiskReadHelper::OnOpComplete,
+ this));
+}
+
+int ShaderDiskReadHelper::ReadComplete(int rv) {
+ DCHECK(CalledOnValidThread());
+ // Called through OnOpComplete, so we know |cache_| is valid.
+ if (rv && rv == buf_->size()) {
+ GpuProcessHost* host = GpuProcessHost::FromID(host_id_);
+ if (host)
+ host->LoadedShader(entry_->GetKey(), std::string(buf_->data(),
+ buf_->size()));
+ }
+
+ buf_ = NULL;
+ entry_->Close();
+ entry_ = NULL;
+
+ op_type_ = OPEN_NEXT;
+ return net::OK;
+}
+
+int ShaderDiskReadHelper::IterationComplete(int rv) {
+ DCHECK(CalledOnValidThread());
+ // Called through OnOpComplete, so we know |cache_| is valid.
+ cache_->backend()->EndEnumeration(&iter_);
+ iter_ = NULL;
+ op_type_ = TERMINATE;
+ return net::OK;
+}
+
+ShaderDiskReadHelper::~ShaderDiskReadHelper() {
+ if (entry_)
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&EntryCloser, entry_));
+}
+
+ShaderCacheFactory* ShaderCacheFactory::GetInstance() {
+ return Singleton<ShaderCacheFactory,
+ LeakySingletonTraits<ShaderCacheFactory> >::get();
+}
+
+ShaderCacheFactory::ShaderCacheFactory() {
+}
+
+ShaderCacheFactory::~ShaderCacheFactory() {
+}
+
+void ShaderCacheFactory::SetCacheInfo(int32 client_id,
+ const base::FilePath& path) {
+ client_id_to_path_map_[client_id] = path.Append(kGpuCachePath);
+}
+
+void ShaderCacheFactory::RemoveCacheInfo(int32 client_id) {
+ client_id_to_path_map_.erase(client_id);
+}
+
+scoped_refptr<ShaderDiskCache> ShaderCacheFactory::Get(int32 client_id) {
+ ClientIdToPathMap::iterator client_iter =
+ client_id_to_path_map_.find(client_id);
+ if (client_iter == client_id_to_path_map_.end())
+ return NULL;
+
+ ShaderCacheMap::iterator iter = shader_cache_map_.find(client_iter->second);
+ if (iter != shader_cache_map_.end())
+ return iter->second;
+
+ ShaderDiskCache* cache = new ShaderDiskCache(client_iter->second);
+ cache->Init();
+
+ return cache;
+}
+
+void ShaderCacheFactory::AddToCache(const base::FilePath& key,
+ ShaderDiskCache* cache) {
+ shader_cache_map_[key] = cache;
+}
+
+void ShaderCacheFactory::RemoveFromCache(const base::FilePath& key) {
+ shader_cache_map_.erase(key);
+}
+
+ShaderDiskCache::ShaderDiskCache(const base::FilePath& cache_path)
+ : cache_available_(false),
+ max_cache_size_(0),
+ host_id_(0),
+ cache_path_(cache_path),
+ is_initialized_(false),
+ backend_(NULL) {
+ ShaderCacheFactory::GetInstance()->AddToCache(cache_path_, this);
+}
+
+ShaderDiskCache::~ShaderDiskCache() {
+ ShaderCacheFactory::GetInstance()->RemoveFromCache(cache_path_);
+}
+
+void ShaderDiskCache::Init() {
+ if (is_initialized_) {
+ NOTREACHED(); // can't initialize disk cache twice.
+ return;
+ }
+ is_initialized_ = true;
+
+ int rv = disk_cache::CreateCacheBackend(
+ net::SHADER_CACHE,
+ cache_path_,
+ max_cache_size_,
+ true,
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE),
+ NULL,
+ &backend_,
+ base::Bind(&ShaderDiskCache::CacheCreatedCallback, this));
+
+ if (rv == net::OK)
+ cache_available_ = true;
+}
+
+void ShaderDiskCache::Cache(const std::string& key, const std::string& shader) {
+ if (!cache_available_)
+ return;
+
+ ShaderDiskCacheEntry* shim =
+ new ShaderDiskCacheEntry(AsWeakPtr(), key, shader);
+ shim->Cache();
+
+ entry_map_[shim] = shim;
+}
+
+void ShaderDiskCache::CacheCreatedCallback(int rv) {
+ if (rv != net::OK) {
+ LOG(ERROR) << "Shader Cache Creation failed: " << rv;
+ return;
+ }
+
+ cache_available_ = true;
+
+ helper_ = new ShaderDiskReadHelper(AsWeakPtr(), host_id_);
+ helper_->LoadCache();
+}
+
+void ShaderDiskCache::EntryComplete(void* entry) {
+ entry_map_.erase(entry);
+}
+
+void ShaderDiskCache::ReadComplete() {
+ helper_ = NULL;
+}
+
+} // namespace content
+
diff --git a/content/browser/gpu/shader_disk_cache.h b/content/browser/gpu/shader_disk_cache.h
new file mode 100644
index 0000000..3ca5420
--- /dev/null
+++ b/content/browser/gpu/shader_disk_cache.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GPU_SHADER_DISK_CACHE_H_
+#define CONTENT_BROWSER_GPU_SHADER_DISK_CACHE_H_
+
+#include <map>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "net/disk_cache/disk_cache.h"
+
+namespace content {
+
+class ShaderDiskCacheEntry;
+class ShaderDiskReadHelper;
+
+// ShaderDiskCache is the interface to the on disk cache for
+// GL shaders.
+//
+// While this class is both RefCounted and SupportsWeakPtr
+// when using this class you should work with the RefCounting.
+// The WeakPtr is needed interally.
+class ShaderDiskCache
+ : public base::RefCounted<ShaderDiskCache>,
+ public base::SupportsWeakPtr<ShaderDiskCache> {
+ public:
+ void Init();
+
+ void set_host_id(int host_id) { host_id_ = host_id; }
+ void set_max_cache_size(size_t max_cache_size) {
+ max_cache_size_ = max_cache_size;
+ }
+
+ void Cache(const std::string& key, const std::string& shader);
+
+ private:
+ friend class base::RefCounted<ShaderDiskCache>;
+ friend class ShaderDiskCacheEntry;
+ friend class ShaderDiskReadHelper;
+ friend class ShaderCacheFactory;
+
+ explicit ShaderDiskCache(const base::FilePath& cache_path);
+ ~ShaderDiskCache();
+
+ void CacheCreatedCallback(int rv);
+
+ disk_cache::Backend* backend() { return backend_; }
+
+ void EntryComplete(void* entry);
+ void ReadComplete();
+
+ bool cache_available_;
+ size_t max_cache_size_;
+ int host_id_;
+ base::FilePath cache_path_;
+ bool is_initialized_;
+
+ disk_cache::Backend* backend_;
+
+ scoped_refptr<ShaderDiskReadHelper> helper_;
+ std::map<void*, scoped_refptr<ShaderDiskCacheEntry> > entry_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShaderDiskCache);
+};
+
+// ShaderCacheFactory maintains a cache of ShaderDiskCache objects
+// so we only create one per profile directory.
+class ShaderCacheFactory {
+ public:
+ static ShaderCacheFactory* GetInstance();
+
+ scoped_refptr<ShaderDiskCache> Get(int32 client_id);
+
+ void SetCacheInfo(int32 client_id, const base::FilePath& path);
+ void RemoveCacheInfo(int32 client_id);
+
+ void AddToCache(const base::FilePath& path, ShaderDiskCache* cache);
+ void RemoveFromCache(const base::FilePath& path);
+
+ private:
+ friend struct DefaultSingletonTraits<ShaderCacheFactory>;
+
+ ShaderCacheFactory();
+ ~ShaderCacheFactory();
+
+ typedef std::map<base::FilePath, ShaderDiskCache*> ShaderCacheMap;
+ ShaderCacheMap shader_cache_map_;
+
+ typedef std::map<int32, base::FilePath> ClientIdToPathMap;
+ ClientIdToPathMap client_id_to_path_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShaderCacheFactory);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GPU_SHADER_DISK_CACHE_H_
+
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index fa7fb67..62f6687 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -53,6 +53,7 @@
#include "content/browser/geolocation/geolocation_dispatcher_host.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
+#include "content/browser/gpu/shader_disk_cache.h"
#include "content/browser/histogram_message_filter.h"
#include "content/browser/hyphenator/hyphenator_message_filter.h"
#include "content/browser/in_process_webkit/indexed_db_context_impl.h"
@@ -136,6 +137,17 @@ extern bool g_exited_main_message_loop;
static const char* kSiteProcessMapKeyName = "content_site_process_map";
namespace content {
+namespace {
+
+void CacheShaderInfo(int32 id, base::FilePath path) {
+ ShaderCacheFactory::GetInstance()->SetCacheInfo(id, path);
+}
+
+void RemoveShaderInfo(int32 id) {
+ ShaderCacheFactory::GetInstance()->RemoveCacheInfo(id);
+}
+
+} // namespace
// This class creates the IO thread for the renderer when running in
// single-process mode. It's not used in multi-process mode.
@@ -355,6 +367,13 @@ RenderProcessHostImpl::RenderProcessHostImpl(
g_all_hosts.Get().set_check_on_null_data(true);
// Initialize |child_process_activity_time_| to a reasonable value.
mark_child_process_activity_time();
+
+ if (!GetBrowserContext()->IsOffTheRecord()) {
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&CacheShaderInfo, GetID(),
+ GetBrowserContext()->GetPath()));
+ }
+
// Note: When we create the RenderProcessHostImpl, it's technically
// backgrounded, because it has no visible listeners. But the process
// doesn't actually exist yet, so we'll Background it later, after
@@ -374,6 +393,9 @@ RenderProcessHostImpl::~RenderProcessHostImpl() {
ClearTransportDIBCache();
UnregisterHost(GetID());
+
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&RemoveShaderInfo, GetID()));
}
void RenderProcessHostImpl::EnableSendQueue() {
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc
index 52554284..d9b26d4 100644
--- a/content/common/gpu/gpu_channel.cc
+++ b/content/common/gpu/gpu_channel.cc
@@ -26,9 +26,9 @@
#include "content/public/common/content_switches.h"
#include "crypto/hmac.h"
#include "gpu/command_buffer/common/mailbox.h"
+#include "gpu/command_buffer/service/gpu_scheduler.h"
#include "gpu/command_buffer/service/image_manager.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
-#include "gpu/command_buffer/service/gpu_scheduler.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_proxy.h"
#include "ui/gl/gl_context.h"
@@ -966,4 +966,10 @@ void GpuChannel::MessageProcessed() {
}
}
+void GpuChannel::CacheShader(const std::string& key,
+ const std::string& shader) {
+ gpu_channel_manager_->Send(
+ new GpuHostMsg_CacheShader(client_id_, key, shader));
+}
+
} // namespace content
diff --git a/content/common/gpu/gpu_channel.h b/content/common/gpu/gpu_channel.h
index 8f21b35..44fd75d8 100644
--- a/content/common/gpu/gpu_channel.h
+++ b/content/common/gpu/gpu_channel.h
@@ -148,6 +148,8 @@ class GpuChannel : public IPC::Listener,
}
#endif
+ void CacheShader(const std::string& key, const std::string& shader);
+
protected:
virtual ~GpuChannel();
diff --git a/content/common/gpu/gpu_channel_manager.cc b/content/common/gpu/gpu_channel_manager.cc
index 7f775df..808c8d9 100644
--- a/content/common/gpu/gpu_channel_manager.cc
+++ b/content/common/gpu/gpu_channel_manager.cc
@@ -69,6 +69,7 @@ gpu::gles2::ProgramCache* GpuChannelManager::program_cache() {
}
void GpuChannelManager::RemoveChannel(int client_id) {
+ Send(new GpuHostMsg_DestroyChannel(client_id));
gpu_channels_.erase(client_id);
}
@@ -103,6 +104,7 @@ bool GpuChannelManager::OnMessageReceived(const IPC::Message& msg) {
OnCreateViewCommandBuffer)
IPC_MESSAGE_HANDLER(GpuMsg_CreateImage, OnCreateImage)
IPC_MESSAGE_HANDLER(GpuMsg_DeleteImage, OnDeleteImage)
+ IPC_MESSAGE_HANDLER(GpuMsg_LoadedShader, OnLoadedShader)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP_EX()
return handled;
@@ -252,6 +254,11 @@ void GpuChannelManager::OnDeleteImageSyncPointRetired(
}
}
+void GpuChannelManager::OnLoadedShader(std::string program_proto) {
+ if (program_cache())
+ program_cache()->LoadProgram(program_proto);
+}
+
void GpuChannelManager::LoseAllContexts() {
MessageLoop::current()->PostTask(
FROM_HERE,
diff --git a/content/common/gpu/gpu_channel_manager.h b/content/common/gpu/gpu_channel_manager.h
index 0892cda..bc75f56 100644
--- a/content/common/gpu/gpu_channel_manager.h
+++ b/content/common/gpu/gpu_channel_manager.h
@@ -6,6 +6,7 @@
#define CONTENT_COMMON_GPU_GPU_CHANNEL_MANAGER_H_
#include <deque>
+#include <string>
#include <vector>
#include "base/hash_tables.h"
@@ -121,6 +122,7 @@ class GpuChannelManager : public IPC::Listener,
void DeleteImage(int32 client_id, int32 image_id);
void OnDeleteImage(int32 client_id, int32 image_id, int32 sync_point);
void OnDeleteImageSyncPointRetired(ImageOperation*);
+ void OnLoadedShader(std::string shader);
void OnLoseAllContexts();
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc
index 7e53996..c74d437 100644
--- a/content/common/gpu/gpu_command_buffer_stub.cc
+++ b/content/common/gpu/gpu_command_buffer_stub.cc
@@ -467,6 +467,9 @@ void GpuCommandBufferStub::OnInitialize(
decoder_->SetMsgCallback(
base::Bind(&GpuCommandBufferStub::SendConsoleMessage,
base::Unretained(this)));
+ decoder_->SetShaderCacheCallback(
+ base::Bind(&GpuCommandBufferStub::SendCachedShader,
+ base::Unretained(this)));
decoder_->SetWaitSyncPointCallback(
base::Bind(&GpuCommandBufferStub::OnWaitSyncPoint,
base::Unretained(this)));
@@ -824,6 +827,11 @@ void GpuCommandBufferStub::SendConsoleMessage(
Send(msg);
}
+void GpuCommandBufferStub::SendCachedShader(
+ const std::string& key, const std::string& shader) {
+ channel_->CacheShader(key, shader);
+}
+
void GpuCommandBufferStub::AddDestructionObserver(
DestructionObserver* observer) {
destruction_observers_.AddObserver(observer);
diff --git a/content/common/gpu/gpu_command_buffer_stub.h b/content/common/gpu/gpu_command_buffer_stub.h
index 400b125..03de927 100644
--- a/content/common/gpu/gpu_command_buffer_stub.h
+++ b/content/common/gpu/gpu_command_buffer_stub.h
@@ -119,6 +119,8 @@ class GpuCommandBufferStub
// Sends a message to the console.
void SendConsoleMessage(int32 id, const std::string& message);
+ void SendCachedShader(const std::string& key, const std::string& shader);
+
gfx::GLSurface* surface() const { return surface_; }
void AddDestructionObserver(DestructionObserver* observer);
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h
index 0c1ccdb..1cac2e7 100644
--- a/content/common/gpu/gpu_messages.h
+++ b/content/common/gpu/gpu_messages.h
@@ -319,6 +319,20 @@ IPC_MESSAGE_CONTROL1(GpuHostMsg_Initialized,
IPC_MESSAGE_CONTROL1(GpuHostMsg_ChannelEstablished,
IPC::ChannelHandle /* channel_handle */)
+// Message from GPU to notify to destroy the channel.
+IPC_MESSAGE_CONTROL1(GpuHostMsg_DestroyChannel,
+ int32 /* client_id */)
+
+// Message to cache the given shader information.
+IPC_MESSAGE_CONTROL3(GpuHostMsg_CacheShader,
+ int32 /* client_id */,
+ std::string /* key */,
+ std::string /* shader */)
+
+// Message to the GPU that a shader was loaded from disk.
+IPC_MESSAGE_CONTROL1(GpuMsg_LoadedShader,
+ std::string /* encoded shader */)
+
// Respond from GPU to a GpuMsg_CreateViewCommandBuffer message.
IPC_MESSAGE_CONTROL1(GpuHostMsg_CommandBufferCreated,
int32 /* route_id */)
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index e31f9b6..332e4f7 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -492,6 +492,8 @@
'browser/gpu/gpu_surface_tracker.h',
'browser/gpu/gpu_util.cc',
'browser/gpu/gpu_util.h',
+ 'browser/gpu/shader_disk_cache.cc',
+ 'browser/gpu/shader_disk_cache.h',
'browser/histogram_controller.cc',
'browser/histogram_controller.h',
'browser/histogram_internals_request_job.cc',
diff --git a/gpu/DEPS b/gpu/DEPS
index 580cf63..43b6a19 100644
--- a/gpu/DEPS
+++ b/gpu/DEPS
@@ -4,6 +4,7 @@ include_rules = [
"+third_party/amd",
"+third_party/re2",
"+third_party/smhasher",
+ "+third_party/protbuf",
"+../../gpu_export.h",
"+../command_buffer",
"+../client",
diff --git a/gpu/command_buffer/common/constants.h b/gpu/command_buffer/common/constants.h
index a2eeb74..491f443 100644
--- a/gpu/command_buffer/common/constants.h
+++ b/gpu/command_buffer/common/constants.h
@@ -50,6 +50,9 @@ const int32 kInvalidSharedMemoryId = -1;
// Common Command Buffer shared memory transfer buffer ID.
const int32 kCommandBufferSharedMemoryId = 4;
+// The size to set for the program cache.
+const size_t kDefaultMaxProgramCacheMemoryBytes = 6 * 1024 * 1024;
+
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_COMMON_CONSTANTS_H_
diff --git a/gpu/command_buffer/service/disk_cache_proto.proto b/gpu/command_buffer/service/disk_cache_proto.proto
new file mode 100644
index 0000000..c165443
--- /dev/null
+++ b/gpu/command_buffer/service/disk_cache_proto.proto
@@ -0,0 +1,23 @@
+option optimize_for = LITE_RUNTIME;
+
+message ShaderInfoProto {
+ optional int32 type = 1;
+ optional int32 size = 2;
+ optional string name = 3;
+ optional string key = 4;
+}
+
+message ShaderProto {
+ optional bytes sha = 1;
+ repeated ShaderInfoProto attribs = 2;
+ repeated ShaderInfoProto uniforms = 3;
+}
+
+message GpuProgramProto {
+ optional bytes sha = 1;
+ optional int32 format = 2;
+ optional bytes program = 3;
+
+ optional ShaderProto vertex_shader = 4;
+ optional ShaderProto fragment_shader = 5;
+}
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 388525a..94de0f5 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -632,6 +632,8 @@ class GLES2DecoderImpl : public GLES2Decoder {
const base::Callback<void(gfx::Size)>& callback) OVERRIDE;
virtual void SetMsgCallback(const MsgCallback& callback) OVERRIDE;
+ virtual void SetShaderCacheCallback(
+ const ShaderCacheCallback& callback) OVERRIDE;
virtual void SetWaitSyncPointCallback(
const WaitSyncPointCallback& callback) OVERRIDE;
@@ -1720,6 +1722,8 @@ class GLES2DecoderImpl : public GLES2Decoder {
MsgCallback msg_callback_;
WaitSyncPointCallback wait_sync_point_callback_;
+ ShaderCacheCallback shader_cache_callback_;
+
StreamTextureManager* stream_texture_manager_;
scoped_ptr<gfx::AsyncPixelTransferDelegate> async_pixel_transfer_delegate_;
@@ -3104,6 +3108,11 @@ void GLES2DecoderImpl::SetMsgCallback(const MsgCallback& callback) {
msg_callback_ = callback;
}
+void GLES2DecoderImpl::SetShaderCacheCallback(
+ const ShaderCacheCallback& callback) {
+ shader_cache_callback_ = callback;
+}
+
void GLES2DecoderImpl::SetWaitSyncPointCallback(
const WaitSyncPointCallback& callback) {
wait_sync_point_callback_ = callback;
@@ -5149,7 +5158,8 @@ void GLES2DecoderImpl::DoLinkProgram(GLuint program_id) {
if (program->Link(shader_manager(),
vertex_translator,
fragment_translator,
- feature_info_)) {
+ feature_info_,
+ shader_cache_callback_)) {
if (program == state_.current_program.get()) {
if (workarounds().use_current_program_after_successful_link) {
glUseProgram(program->service_id());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index da25df6..27fe968 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -78,6 +78,9 @@ struct DisallowedFeatures {
bool gpu_memory_manager;
};
+typedef base::Callback<void(const std::string& key,
+ const std::string& shader)> ShaderCacheCallback;
+
// This class implements the AsyncAPIInterface interface, decoding GLES2
// commands and calling GL.
class GPU_EXPORT GLES2Decoder : public base::SupportsWeakPtr<GLES2Decoder>,
@@ -271,6 +274,7 @@ class GPU_EXPORT GLES2Decoder : public base::SupportsWeakPtr<GLES2Decoder>,
// A callback for messages from the decoder.
virtual void SetMsgCallback(const MsgCallback& callback) = 0;
+ virtual void SetShaderCacheCallback(const ShaderCacheCallback& callback) = 0;
// Sets the callback for waiting on a sync point. The callback returns the
// scheduling status (i.e. true if the channel is still scheduled).
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index eb3487c..2dd0df8 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -109,6 +109,8 @@ class MockGLES2Decoder : public GLES2Decoder {
const char* file, int line, const char* filename));
MOCK_METHOD1(SetMsgCallback, void(const MsgCallback& callback));
+ MOCK_METHOD1(SetShaderCacheCallback,
+ void(const ShaderCacheCallback& callback));
MOCK_METHOD1(SetWaitSyncPointCallback,
void(const WaitSyncPointCallback& callback));
MOCK_METHOD0(GetTextureUploadCount, uint32());
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc
index 70ce437..daa98ae 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc
@@ -27,6 +27,11 @@ using ::testing::StrEq;
namespace gpu {
namespace gles2 {
+namespace {
+void ShaderCacheCb(const std::string& key, const std::string& shader) {
+}
+} // namespace
+
class GLES2DecoderTest1 : public GLES2DecoderTestBase {
public:
GLES2DecoderTest1() { }
@@ -246,7 +251,7 @@ void GLES2DecoderTestBase::SpecializedSetup<cmds::GetProgramInfoLog, 0>(
attach_cmd.Init(client_program_id_, kClientFragmentShaderId);
EXPECT_EQ(error::kNoError, ExecuteCmd(attach_cmd));
- program->Link(NULL, NULL, NULL, NULL);
+ program->Link(NULL, NULL, NULL, NULL, base::Bind(&ShaderCacheCb));
};
template <>
diff --git a/gpu/command_buffer/service/gpu_switches.cc b/gpu/command_buffer/service/gpu_switches.cc
index d029cfb..ce9e49b 100644
--- a/gpu/command_buffer/service/gpu_switches.cc
+++ b/gpu/command_buffer/service/gpu_switches.cc
@@ -53,6 +53,9 @@ const char kGpuProgramCacheSizeKb[] = "gpu-program-cache-size-kb";
const char kTraceGL[] = "trace-gl";
+// Disables the GPU shader on disk cache.
+const char kDisableGpuShaderDiskCache[] = "disable-gpu-shader-disk-cache";
+
const char* kGpuSwitches[] = {
kCompileShaderAlwaysSucceeds,
kDisableGLErrorLimit,
@@ -68,6 +71,7 @@ const char* kGpuSwitches[] = {
kForceGpuMemAvailableMb,
kGpuProgramCacheSizeKb,
kTraceGL,
+ kDisableGpuShaderDiskCache,
};
const int kNumGpuSwitches = arraysize(kGpuSwitches);
diff --git a/gpu/command_buffer/service/gpu_switches.h b/gpu/command_buffer/service/gpu_switches.h
index 0a51e70..72e5f92 100644
--- a/gpu/command_buffer/service/gpu_switches.h
+++ b/gpu/command_buffer/service/gpu_switches.h
@@ -25,6 +25,7 @@ GPU_EXPORT extern const char kForceGLFinishWorkaround[];
GPU_EXPORT extern const char kForceGpuMemAvailableMb[];
GPU_EXPORT extern const char kGpuProgramCacheSizeKb[];
GPU_EXPORT extern const char kTraceGL[];
+GPU_EXPORT extern const char kDisableGpuShaderDiskCache[];
GPU_EXPORT extern const char* kGpuSwitches[];
GPU_EXPORT extern const int kNumGpuSwitches;
diff --git a/gpu/command_buffer/service/memory_program_cache.cc b/gpu/command_buffer/service/memory_program_cache.cc
index c153b75..32db0c7 100644
--- a/gpu/command_buffer/service/memory_program_cache.cc
+++ b/gpu/command_buffer/service/memory_program_cache.cc
@@ -4,16 +4,21 @@
#include "gpu/command_buffer/service/memory_program_cache.h"
+#include "base/base64.h"
#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "base/sha1.h"
#include "base/string_number_conversions.h"
+#include "gpu/command_buffer/common/constants.h"
+#include "gpu/command_buffer/service/disk_cache_proto.pb.h"
#include "gpu/command_buffer/service/gl_utils.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/shader_manager.h"
#include "ui/gl/gl_bindings.h"
namespace {
+
size_t GetCacheSizeBytes() {
size_t size;
const CommandLine* command_line = CommandLine::ForCurrentProcess();
@@ -23,13 +28,48 @@ size_t GetCacheSizeBytes() {
&size)) {
return size * 1024;
}
- return gpu::gles2::MemoryProgramCache::kDefaultMaxProgramCacheMemoryBytes;
+ return gpu::kDefaultMaxProgramCacheMemoryBytes;
}
+
} // anonymous namespace
namespace gpu {
namespace gles2 {
+namespace {
+
+enum ShaderMapType {
+ ATTRIB_MAP = 0,
+ UNIFORM_MAP
+};
+
+void StoreShaderInfo(ShaderMapType type, ShaderProto *proto,
+ const ShaderTranslator::VariableMap& map) {
+ ShaderTranslator::VariableMap::const_iterator iter;
+ for (iter = map.begin(); iter != map.end(); iter++) {
+ ShaderInfoProto* info;
+ if (type == UNIFORM_MAP) {
+ info = proto->add_uniforms();
+ } else {
+ info = proto->add_attribs();
+ }
+
+ info->set_key(iter->first);
+ info->set_type(iter->second.type);
+ info->set_size(iter->second.size);
+ info->set_name(iter->second.name);
+ }
+}
+
+void RetrieveShaderInfo(const ShaderInfoProto& proto,
+ ShaderTranslator::VariableMap* map) {
+ ShaderTranslator::VariableInfo info(proto.type(), proto.size(),
+ proto.name());
+ (*map)[proto.key()] = info;
+}
+
+} // namespace
+
MemoryProgramCache::MemoryProgramCache()
: max_size_bytes_(GetCacheSizeBytes()),
curr_size_bytes_(0) { }
@@ -88,7 +128,8 @@ void MemoryProgramCache::SaveLinkedProgram(
GLuint program,
const Shader* shader_a,
const Shader* shader_b,
- const LocationMap* bind_attrib_location_map) {
+ const LocationMap* bind_attrib_location_map,
+ const ShaderCacheCallback& shader_callback) {
GLenum format;
GLsizei length = 0;
glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &length);
@@ -136,6 +177,34 @@ void MemoryProgramCache::SaveLinkedProgram(
store_.erase(found);
eviction_helper_.PopKey();
}
+
+ if (!shader_callback.is_null() &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableGpuShaderDiskCache)) {
+ std::string key;
+ base::Base64Encode(sha_string, &key);
+
+ GpuProgramProto* proto = GpuProgramProto::default_instance().New();
+ proto->set_sha(sha, kHashLength);
+ proto->set_format(format);
+ proto->set_program(binary.get(), length);
+
+ ShaderProto* vertex_shader = proto->mutable_vertex_shader();
+ vertex_shader->set_sha(a_sha, kHashLength);
+ StoreShaderInfo(ATTRIB_MAP, vertex_shader, shader_a->attrib_map());
+ StoreShaderInfo(UNIFORM_MAP, vertex_shader, shader_a->uniform_map());
+
+ ShaderProto* fragment_shader = proto->mutable_fragment_shader();
+ fragment_shader->set_sha(b_sha, kHashLength);
+ StoreShaderInfo(ATTRIB_MAP, fragment_shader, shader_b->attrib_map());
+ StoreShaderInfo(UNIFORM_MAP, fragment_shader, shader_b->uniform_map());
+
+ std::string shader;
+ proto->SerializeToString(&shader);
+
+ shader_callback.Run(key, shader);
+ }
+
store_[sha_string] = new ProgramCacheValue(length,
format,
binary.release(),
@@ -156,6 +225,60 @@ void MemoryProgramCache::SaveLinkedProgram(
std::string(b_sha, kHashLength));
}
+void MemoryProgramCache::LoadProgram(const std::string& program) {
+ GpuProgramProto* proto = GpuProgramProto::default_instance().New();
+ if (proto->ParseFromString(program)) {
+ ShaderTranslator::VariableMap vertex_attribs;
+ ShaderTranslator::VariableMap vertex_uniforms;
+
+ for (int i = 0; i < proto->vertex_shader().attribs_size(); i++) {
+ RetrieveShaderInfo(proto->vertex_shader().attribs(i), &vertex_attribs);
+ }
+
+ for (int i = 0; i < proto->vertex_shader().uniforms_size(); i++) {
+ RetrieveShaderInfo(proto->vertex_shader().uniforms(i), &vertex_uniforms);
+ }
+
+ ShaderTranslator::VariableMap fragment_attribs;
+ ShaderTranslator::VariableMap fragment_uniforms;
+
+ for (int i = 0; i < proto->fragment_shader().attribs_size(); i++) {
+ RetrieveShaderInfo(proto->fragment_shader().attribs(i),
+ &fragment_attribs);
+ }
+
+ for (int i = 0; i < proto->fragment_shader().uniforms_size(); i++) {
+ RetrieveShaderInfo(proto->fragment_shader().uniforms(i),
+ &fragment_uniforms);
+ }
+
+ scoped_array<char> binary(new char[proto->program().length()]);
+ memcpy(binary.get(), proto->program().c_str(), proto->program().length());
+
+ store_[proto->sha()] = new ProgramCacheValue(proto->program().length(),
+ proto->format(), binary.release(),
+ proto->vertex_shader().sha().c_str(), vertex_attribs, vertex_uniforms,
+ proto->fragment_shader().sha().c_str(), fragment_attribs,
+ fragment_uniforms);
+
+ ShaderCompilationSucceededSha(proto->sha());
+ ShaderCompilationSucceededSha(proto->vertex_shader().sha());
+ ShaderCompilationSucceededSha(proto->fragment_shader().sha());
+
+ curr_size_bytes_ += proto->program().length();
+ eviction_helper_.KeyUsed(proto->sha());
+
+ UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb",
+ curr_size_bytes_ / 1024);
+
+ LinkedProgramCacheSuccess(proto->sha(),
+ proto->vertex_shader().sha(),
+ proto->fragment_shader().sha());
+ } else {
+ LOG(ERROR) << "Failed to parse proto file.";
+ }
+}
+
MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
GLsizei _length,
GLenum _format,
diff --git a/gpu/command_buffer/service/memory_program_cache.h b/gpu/command_buffer/service/memory_program_cache.h
index de699dd..be487d9 100644
--- a/gpu/command_buffer/service/memory_program_cache.h
+++ b/gpu/command_buffer/service/memory_program_cache.h
@@ -11,6 +11,7 @@
#include "base/hash_tables.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/program_cache.h"
#include "gpu/command_buffer/service/program_cache_lru_helper.h"
#include "gpu/command_buffer/service/shader_translator.h"
@@ -21,8 +22,6 @@ namespace gles2 {
// Program cache that stores binaries completely in-memory
class GPU_EXPORT MemoryProgramCache : public ProgramCache {
public:
- static const size_t kDefaultMaxProgramCacheMemoryBytes = 6 * 1024 * 1024;
-
MemoryProgramCache();
explicit MemoryProgramCache(const size_t max_cache_size_bytes);
virtual ~MemoryProgramCache();
@@ -36,7 +35,10 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache {
GLuint program,
const Shader* shader_a,
const Shader* shader_b,
- const LocationMap* bind_attrib_location_map) OVERRIDE;
+ const LocationMap* bind_attrib_location_map,
+ const ShaderCacheCallback& shader_callback) OVERRIDE;
+
+ virtual void LoadProgram(const std::string& program) OVERRIDE;
private:
virtual void ClearBackend() OVERRIDE;
diff --git a/gpu/command_buffer/service/memory_program_cache_unittest.cc b/gpu/command_buffer/service/memory_program_cache_unittest.cc
index 3c64d3f..f20caeb 100644
--- a/gpu/command_buffer/service/memory_program_cache_unittest.cc
+++ b/gpu/command_buffer/service/memory_program_cache_unittest.cc
@@ -4,6 +4,7 @@
#include "gpu/command_buffer/service/memory_program_cache.h"
+#include "base/bind.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/shader_manager.h"
@@ -75,11 +76,20 @@ class MemoryProgramCacheTest : public testing::Test {
MemoryProgramCacheTest()
: cache_(new MemoryProgramCache(kCacheSizeBytes)),
vertex_shader_(NULL),
- fragment_shader_(NULL) { }
+ fragment_shader_(NULL),
+ shader_cache_count_(0) { }
virtual ~MemoryProgramCacheTest() {
shader_manager_.Destroy(false);
}
+ void ShaderCacheCb(const std::string& key, const std::string& shader) {
+ shader_cache_count_++;
+ shader_cache_shader_ = shader;
+ }
+
+ int32 shader_cache_count() { return shader_cache_count_; }
+ const std::string& shader_cache_shader() { return shader_cache_shader_; }
+
protected:
virtual void SetUp() {
gl_.reset(new ::testing::StrictMock<gfx::MockGLInterface>());
@@ -171,6 +181,8 @@ class MemoryProgramCacheTest : public testing::Test {
ShaderManager shader_manager_;
Shader* vertex_shader_;
Shader* fragment_shader_;
+ int32 shader_cache_count_;
+ std::string shader_cache_shader_;
};
TEST_F(MemoryProgramCacheTest, CacheSave) {
@@ -184,12 +196,45 @@ TEST_F(MemoryProgramCacheTest, CacheSave) {
ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
*vertex_shader_->deferred_compilation_source(),
*fragment_shader_->deferred_compilation_source(),
NULL));
+ EXPECT_EQ(1, shader_cache_count());
+}
+
+TEST_F(MemoryProgramCacheTest, LoadProgram) {
+ const GLenum kFormat = 1;
+ const int kProgramId = 10;
+ const int kBinaryLength = 20;
+ char test_binary[kBinaryLength];
+ for (int i = 0; i < kBinaryLength; ++i) {
+ test_binary[i] = i;
+ }
+ ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
+
+ SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
+
+ EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
+ *vertex_shader_->deferred_compilation_source(),
+ *fragment_shader_->deferred_compilation_source(),
+ NULL));
+ EXPECT_EQ(1, shader_cache_count());
+
+ cache_->Clear();
+
+ cache_->LoadProgram(shader_cache_shader());
+ EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
+ *vertex_shader_->deferred_compilation_source(),
+ *fragment_shader_->deferred_compilation_source(),
+ NULL));
}
TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
@@ -203,7 +248,54 @@ TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
+ EXPECT_EQ(1, shader_cache_count());
+
+ VariableMap vertex_attrib_map = vertex_shader_->attrib_map();
+ VariableMap vertex_uniform_map = vertex_shader_->uniform_map();
+ VariableMap fragment_attrib_map = fragment_shader_->attrib_map();
+ VariableMap fragment_uniform_map = fragment_shader_->uniform_map();
+
+ vertex_shader_->set_attrib_map(VariableMap());
+ vertex_shader_->set_uniform_map(VariableMap());
+ fragment_shader_->set_attrib_map(VariableMap());
+ fragment_shader_->set_uniform_map(VariableMap());
+
+ SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
+
+ EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
+ kProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ NULL));
+
+ // apparently the hash_map implementation on android doesn't have the
+ // equality operator
+#if !defined(OS_ANDROID)
+ EXPECT_EQ(vertex_attrib_map, vertex_shader_->attrib_map());
+ EXPECT_EQ(vertex_attrib_map, vertex_shader_->uniform_map());
+ EXPECT_EQ(vertex_attrib_map, fragment_shader_->attrib_map());
+ EXPECT_EQ(vertex_attrib_map, fragment_shader_->uniform_map());
+#endif
+}
+
+TEST_F(MemoryProgramCacheTest, LoadProgramMatchesSave) {
+ const GLenum kFormat = 1;
+ const int kProgramId = 10;
+ const int kBinaryLength = 20;
+ char test_binary[kBinaryLength];
+ for (int i = 0; i < kBinaryLength; ++i) {
+ test_binary[i] = i;
+ }
+ ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
+
+ SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
+ EXPECT_EQ(1, shader_cache_count());
VariableMap vertex_attrib_map = vertex_shader_->attrib_map();
VariableMap vertex_uniform_map = vertex_shader_->uniform_map();
@@ -217,6 +309,9 @@ TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
+ cache_->Clear();
+ cache_->LoadProgram(shader_cache_shader());
+
EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
kProgramId,
vertex_shader_,
@@ -244,7 +339,9 @@ TEST_F(MemoryProgramCacheTest, LoadFailOnLinkFalse) {
ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
SetExpectationsForLoadLinkedProgramFailure(kProgramId, &emulator);
EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
@@ -265,7 +362,9 @@ TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentSource) {
ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
const std::string vertex_orig_source =
*vertex_shader_->deferred_compilation_source();
@@ -304,7 +403,9 @@ TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentMap) {
cache_->SaveLinkedProgram(kProgramId,
vertex_shader_,
fragment_shader_,
- &binding_map);
+ &binding_map,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
binding_map["different!"] = 59;
EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
@@ -332,7 +433,9 @@ TEST_F(MemoryProgramCacheTest, MemoryProgramCacheEviction) {
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
const int kEvictingProgramId = 11;
const GLuint kEvictingBinaryLength = kCacheSizeBytes - kBinaryLength + 1;
@@ -356,7 +459,9 @@ TEST_F(MemoryProgramCacheTest, MemoryProgramCacheEviction) {
cache_->SaveLinkedProgram(kEvictingProgramId,
vertex_shader_,
fragment_shader_,
- NULL);
+ NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
*vertex_shader_->deferred_compilation_source(),
@@ -380,7 +485,9 @@ TEST_F(MemoryProgramCacheTest, SaveCorrectProgram) {
vertex_shader_->UpdateSource("different!");
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
*vertex_shader_->deferred_compilation_source(),
@@ -399,7 +506,9 @@ TEST_F(MemoryProgramCacheTest, LoadCorrectProgram) {
ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
*vertex_shader_->deferred_compilation_source(),
@@ -427,7 +536,9 @@ TEST_F(MemoryProgramCacheTest, OverwriteOnNewSave) {
ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary);
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
char test_binary2[kBinaryLength];
@@ -436,7 +547,9 @@ TEST_F(MemoryProgramCacheTest, OverwriteOnNewSave) {
}
ProgramBinaryEmulator emulator2(kBinaryLength, kFormat, test_binary2);
SetExpectationsForSaveLinkedProgram(kProgramId, &emulator2);
- cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL,
+ base::Bind(&MemoryProgramCacheTest::ShaderCacheCb,
+ base::Unretained(this)));
SetExpectationsForLoadLinkedProgram(kProgramId, &emulator2);
EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
diff --git a/gpu/command_buffer/service/mocks.h b/gpu/command_buffer/service/mocks.h
index 35fc9c1..949314d 100644
--- a/gpu/command_buffer/service/mocks.h
+++ b/gpu/command_buffer/service/mocks.h
@@ -10,6 +10,7 @@
#ifndef GPU_COMMAND_BUFFER_SERVICE_MOCKS_H_
#define GPU_COMMAND_BUFFER_SERVICE_MOCKS_H_
+#include <string>
#include <vector>
#include "base/logging.h"
@@ -103,11 +104,14 @@ class MockProgramCache : public ProgramCache {
Shader* shader_b,
const LocationMap* bind_attrib_location_map));
- MOCK_METHOD4(SaveLinkedProgram, void(
+ MOCK_METHOD5(SaveLinkedProgram, void(
GLuint program,
const Shader* shader_a,
const Shader* shader_b,
- const LocationMap* bind_attrib_location_map));
+ const LocationMap* bind_attrib_location_map,
+ const ShaderCacheCallback& callback));
+ MOCK_METHOD1(LoadProgram, void(const std::string&));
+
private:
MOCK_METHOD0(ClearBackend, void());
};
diff --git a/gpu/command_buffer/service/program_cache.cc b/gpu/command_buffer/service/program_cache.cc
index 821b6e1..bc32dfc 100644
--- a/gpu/command_buffer/service/program_cache.cc
+++ b/gpu/command_buffer/service/program_cache.cc
@@ -39,7 +39,11 @@ void ProgramCache::ShaderCompilationSucceeded(
char sha[kHashLength];
ComputeShaderHash(shader_src, sha);
const std::string sha_string(sha, kHashLength);
+ ShaderCompilationSucceededSha(sha_string);
+}
+void ProgramCache::ShaderCompilationSucceededSha(
+ const std::string& sha_string) {
CompileStatusMap::iterator it = shader_status_.find(sha_string);
if (it == shader_status_.end()) {
shader_status_[sha_string] = CompiledShaderInfo(COMPILATION_SUCCEEDED);
diff --git a/gpu/command_buffer/service/program_cache.h b/gpu/command_buffer/service/program_cache.h
index c7917d9..e7057e5 100644
--- a/gpu/command_buffer/service/program_cache.h
+++ b/gpu/command_buffer/service/program_cache.h
@@ -11,6 +11,8 @@
#include "base/hash_tables.h"
#include "base/sha1.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "gpu/command_buffer/service/shader_manager.h"
namespace gpu {
namespace gles2 {
@@ -45,6 +47,7 @@ class GPU_EXPORT ProgramCache {
CompiledShaderStatus GetShaderCompilationStatus(
const std::string& shader_src) const;
void ShaderCompilationSucceeded(const std::string& shader_src);
+ void ShaderCompilationSucceededSha(const std::string& sha_string);
LinkedProgramStatus GetLinkedProgramStatus(
const std::string& untranslated_a,
@@ -65,7 +68,10 @@ class GPU_EXPORT ProgramCache {
GLuint program,
const Shader* shader_a,
const Shader* shader_b,
- const LocationMap* bind_attrib_location_map) = 0;
+ const LocationMap* bind_attrib_location_map,
+ const ShaderCacheCallback& shader_callback) = 0;
+
+ virtual void LoadProgram(const std::string& program) = 0;
// clears the cache
void Clear();
diff --git a/gpu/command_buffer/service/program_cache_unittest.cc b/gpu/command_buffer/service/program_cache_unittest.cc
index d764df7..65f39be 100644
--- a/gpu/command_buffer/service/program_cache_unittest.cc
+++ b/gpu/command_buffer/service/program_cache_unittest.cc
@@ -23,7 +23,10 @@ class NoBackendProgramCache : public ProgramCache {
GLuint /* program */,
const Shader* /* shader_a */,
const Shader* /* shader_b */,
- const LocationMap* /* bind_attrib_location_map */) OVERRIDE { }
+ const LocationMap* /* bind_attrib_location_map */,
+ const ShaderCacheCallback& /* callback */) OVERRIDE { }
+
+ virtual void LoadProgram(const std::string& /* program */) OVERRIDE {}
virtual void ClearBackend() OVERRIDE {}
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc
index 2725476..db935b7c 100644
--- a/gpu/command_buffer/service/program_manager.cc
+++ b/gpu/command_buffer/service/program_manager.cc
@@ -534,7 +534,8 @@ void ProgramManager::ForceCompileShader(const std::string* source,
bool Program::Link(ShaderManager* manager,
ShaderTranslator* vertex_translator,
ShaderTranslator* fragment_translator,
- FeatureInfo* feature_info) {
+ FeatureInfo* feature_info,
+ const ShaderCacheCallback& shader_callback) {
ClearLinkStatus();
if (!CanLink()) {
set_log_info("missing shaders");
@@ -605,7 +606,8 @@ bool Program::Link(ShaderManager* manager,
cache->SaveLinkedProgram(service_id(),
attached_shaders_[0],
attached_shaders_[1],
- &bind_attrib_location_map_);
+ &bind_attrib_location_map_,
+ shader_callback);
}
UMA_HISTOGRAM_CUSTOM_COUNTS(
"GPU.ProgramCache.BinaryCacheMissTime",
diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h
index 4513d45..16dd97d 100644
--- a/gpu/command_buffer/service/program_manager.h
+++ b/gpu/command_buffer/service/program_manager.h
@@ -13,6 +13,8 @@
#include "base/memory/ref_counted.h"
#include "gpu/command_buffer/service/common_decoder.h"
#include "gpu/command_buffer/service/gl_utils.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "gpu/command_buffer/service/shader_manager.h"
#include "gpu/gpu_export.h"
namespace gpu {
@@ -155,7 +157,8 @@ class GPU_EXPORT Program : public base::RefCounted<Program> {
bool Link(ShaderManager* manager,
ShaderTranslator* vertex_translator,
ShaderTranslator* fragment_shader,
- FeatureInfo* feature_info);
+ FeatureInfo* feature_info,
+ const ShaderCacheCallback& shader_callback);
// Performs glValidateProgram and related activities.
void Validate();
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index 46ba708..94bce93 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -35,6 +35,10 @@ using ::testing::StrictMock;
namespace gpu {
namespace gles2 {
+namespace {
+void ShaderCacheCb(const std::string& key, const std::string& shader) {}
+} // namespace
+
class ProgramManagerTest : public testing::Test {
public:
ProgramManagerTest() : manager_(NULL) { }
@@ -219,7 +223,7 @@ class ProgramManagerWithShaderTest : public testing::Test {
program_->AttachShader(&shader_manager_, vertex_shader);
program_->AttachShader(&shader_manager_, fragment_shader);
- program_->Link(NULL, NULL, NULL, NULL);
+ program_->Link(NULL, NULL, NULL, NULL, base::Bind(&ShaderCacheCb));
}
void SetupShader(AttribInfo* attribs, size_t num_attribs,
@@ -252,7 +256,7 @@ class ProgramManagerWithShaderTest : public testing::Test {
SetupShader(kAttribs, kNumAttribs, kUniforms, kNumUniforms,
service_id);
}
- program->Link(NULL, NULL, NULL, NULL);
+ program->Link(NULL, NULL, NULL, NULL, base::Bind(&ShaderCacheCb));
GLint link_status;
program->GetProgramiv(GL_LINK_STATUS, &link_status);
return (static_cast<bool>(link_status) == expected_link_status);
@@ -579,7 +583,7 @@ TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsGLUnderscoreUniform) {
ASSERT_TRUE(program != NULL);
EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader));
EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader));
- program->Link(NULL, NULL, NULL, NULL);
+ program->Link(NULL, NULL, NULL, NULL, base::Bind(&ShaderCacheCb));
GLint value = 0;
program->GetProgramiv(GL_ACTIVE_ATTRIBUTES, &value);
EXPECT_EQ(3, value);
@@ -647,7 +651,7 @@ TEST_F(ProgramManagerWithShaderTest, SimilarArrayNames) {
ASSERT_TRUE(program != NULL);
EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader));
EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader));
- program->Link(NULL, NULL, NULL, NULL);
+ program->Link(NULL, NULL, NULL, NULL, base::Bind(&ShaderCacheCb));
// Check that we get the correct locations.
EXPECT_EQ(kUniform2FakeLocation,
@@ -736,10 +740,10 @@ TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsWrongTypeInfo) {
kServiceProgramId);
Program* program = manager_.CreateProgram(
kClientProgramId, kServiceProgramId);
- ASSERT_TRUE(program != NULL);
+ ASSERT_TRUE(program!= NULL);
EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader));
EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader));
- program->Link(NULL, NULL, NULL, NULL);
+ program->Link(NULL, NULL, NULL, NULL, base::Bind(&ShaderCacheCb));
// Check that we got the good type, not the bad.
// Check Attribs
for (unsigned index = 0; index < kNumAttribs; ++index) {
@@ -1058,7 +1062,7 @@ TEST_F(ProgramManagerWithShaderTest, ClearWithSamplerTypes) {
const size_t kNumUniforms = arraysize(kUniforms);
SetupShader(kAttribs, kNumAttribs, kUniforms, kNumUniforms,
kServiceProgramId);
- program->Link(NULL, NULL, NULL, NULL);
+ program->Link(NULL, NULL, NULL, NULL, base::Bind(&ShaderCacheCb));
SetupExpectationsForClearingUniforms(kUniforms, kNumUniforms);
manager_.ClearUniforms(program);
}
@@ -1130,7 +1134,7 @@ TEST_F(ProgramManagerWithShaderTest, BindUniformLocation) {
const size_t kNumUniforms = arraysize(kUniforms);
SetupShader(kAttribs, kNumAttribs, kUniforms, kNumUniforms,
kServiceProgramId);
- program->Link(NULL, NULL, NULL, NULL);
+ program->Link(NULL, NULL, NULL, NULL, base::Bind(&ShaderCacheCb));
EXPECT_EQ(kUniform1DesiredLocation,
program->GetUniformFakeLocation(kUniform1Name));
@@ -1223,7 +1227,8 @@ class ProgramManagerWithCacheTest : public testing::Test {
program->service_id(),
vertex_shader,
fragment_shader,
- &program->bind_attrib_location_map())).Times(1);
+ &program->bind_attrib_location_map(),
+ _)).Times(1);
}
void SetExpectationsForNotCachingProgram() {
@@ -1240,7 +1245,8 @@ class ProgramManagerWithCacheTest : public testing::Test {
program->service_id(),
vertex_shader,
fragment_shader,
- &program->bind_attrib_location_map())).Times(0);
+ &program->bind_attrib_location_map(),
+ _)).Times(0);
}
void SetExpectationsForProgramLoad(ProgramCache::ProgramLoadResult result) {
@@ -1375,7 +1381,8 @@ TEST_F(ProgramManagerWithCacheTest, CacheProgramOnSuccessfulLink) {
SetShadersCompiled();
SetExpectationsForProgramLink();
SetExpectationsForProgramCached();
- EXPECT_TRUE(program_->Link(NULL, NULL, NULL, NULL));
+ EXPECT_TRUE(program_->Link(NULL, NULL, NULL, NULL,
+ base::Bind(&ShaderCacheCb)));
}
TEST_F(ProgramManagerWithCacheTest, CompileShaderOnLinkCacheMiss) {
@@ -1387,7 +1394,8 @@ TEST_F(ProgramManagerWithCacheTest, CompileShaderOnLinkCacheMiss) {
SetExpectationsForSuccessCompile(vertex_shader_);
SetExpectationsForProgramLink();
SetExpectationsForProgramCached();
- EXPECT_TRUE(program_->Link(&shader_manager_, NULL, NULL, info.get()));
+ EXPECT_TRUE(program_->Link(&shader_manager_, NULL, NULL,
+ info.get(), base::Bind(&ShaderCacheCb)));
}
TEST_F(ProgramManagerWithCacheTest, LoadProgramOnProgramCacheHit) {
@@ -1400,7 +1408,8 @@ TEST_F(ProgramManagerWithCacheTest, LoadProgramOnProgramCacheHit) {
SetExpectationsForNotCachingProgram();
SetExpectationsForProgramLoadSuccess();
- EXPECT_TRUE(program_->Link(NULL, NULL, NULL, NULL));
+ EXPECT_TRUE(program_->Link(NULL, NULL, NULL, NULL,
+ base::Bind(&ShaderCacheCb)));
}
TEST_F(ProgramManagerWithCacheTest, CompileAndLinkOnProgramCacheError) {
@@ -1414,7 +1423,8 @@ TEST_F(ProgramManagerWithCacheTest, CompileAndLinkOnProgramCacheError) {
SetExpectationsForProgramCached();
scoped_refptr<FeatureInfo> info(new FeatureInfo());
- EXPECT_TRUE(program_->Link(&shader_manager_, NULL, NULL, info.get()));
+ EXPECT_TRUE(program_->Link(&shader_manager_, NULL, NULL, info.get(),
+ base::Bind(&ShaderCacheCb)));
}
TEST_F(ProgramManagerWithCacheTest, CorrectCompileOnSourceChangeNoCompile) {
@@ -1467,7 +1477,8 @@ TEST_F(ProgramManagerWithCacheTest, CorrectCompileOnSourceChangeNoCompile) {
SetExpectationsForProgramLoadSuccess(kNewProgramServiceId);
scoped_refptr<FeatureInfo> info(new FeatureInfo());
- EXPECT_TRUE(program->Link(&shader_manager_, NULL, NULL, info.get()));
+ EXPECT_TRUE(program->Link(&shader_manager_, NULL, NULL, info.get(),
+ base::Bind(&ShaderCacheCb)));
}
TEST_F(ProgramManagerWithCacheTest, CorrectCompileOnSourceChangeWithCompile) {
@@ -1518,7 +1529,8 @@ TEST_F(ProgramManagerWithCacheTest, CorrectCompileOnSourceChangeWithCompile) {
fragment_shader_);
SetExpectationsForProgramLink(kNewProgramServiceId);
- EXPECT_TRUE(program->Link(&shader_manager_, NULL, NULL, info.get()));
+ EXPECT_TRUE(program->Link(&shader_manager_, NULL, NULL,
+ info.get(), base::Bind(&ShaderCacheCb)));
}
} // namespace gles2
diff --git a/gpu/command_buffer_service.gypi b/gpu/command_buffer_service.gypi
index f42c527e..c3913b3 100644
--- a/gpu/command_buffer_service.gypi
+++ b/gpu/command_buffer_service.gypi
@@ -20,6 +20,7 @@
'../ui/ui.gyp:ui',
'../third_party/angle/src/build_angle.gyp:translator_glsl',
'../third_party/khronos/khronos.gyp:khronos_headers',
+ '../third_party/protobuf/protobuf.gyp:protobuf_lite',
'../third_party/smhasher/smhasher.gyp:cityhash',
'../third_party/re2/re2.gyp:re2',
],
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index 005573d..8255fd88 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -288,6 +288,16 @@
'conditions': [
['component=="static_library"', {
'targets': [
+ {
+ 'target_name': 'disk_cache_proto',
+ 'type': 'static_library',
+ 'sources': [ 'command_buffer/service/disk_cache_proto.proto' ],
+ 'variables': {
+ 'proto_in_dir': 'command_buffer/service',
+ 'proto_out_dir': 'gpu/command_buffer/service',
+ },
+ 'includes': [ '../build/protoc.gypi' ],
+ },
{
'target_name': 'gpu',
'type': 'none',
@@ -351,6 +361,7 @@
],
'dependencies': [
'command_buffer_common',
+ 'disk_cache_proto',
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
@@ -369,6 +380,16 @@
},
{ # component != static_library
'targets': [
+ {
+ 'target_name': 'disk_cache_proto',
+ 'type': 'static_library',
+ 'sources': [ 'command_buffer/service/disk_cache_proto.proto' ],
+ 'variables': {
+ 'proto_in_dir': 'command_buffer/service',
+ 'proto_out_dir': 'gpu/command_buffer/service',
+ },
+ 'includes': [ '../build/protoc.gypi' ],
+ },
{
'target_name': 'gpu',
'type': 'shared_library',
@@ -388,6 +409,7 @@
'dependencies': [
'../base/base.gyp:base',
'command_buffer/command_buffer.gyp:gles2_utils',
+ 'disk_cache_proto',
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],