summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/common/gpu/gpu_channel.h1
-rw-r--r--content/common/gpu/gpu_channel_manager.cc19
-rw-r--r--content/common/gpu/gpu_channel_manager.h7
-rw-r--r--content/common/gpu/gpu_command_buffer_stub.cc5
-rw-r--r--content/common/gpu/gpu_command_buffer_stub.h1
-rw-r--r--gpu/command_buffer/common/gl_mock.h11
-rw-r--r--gpu/command_buffer/service/context_group.cc4
-rw-r--r--gpu/command_buffer/service/context_group.h11
-rw-r--r--gpu/command_buffer/service/gl_utils.h7
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc60
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc2
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc4
-rw-r--r--gpu/command_buffer/service/memory_program_cache.cc173
-rw-r--r--gpu/command_buffer/service/memory_program_cache.h88
-rw-r--r--gpu/command_buffer/service/memory_program_cache_unittest.cc417
-rw-r--r--gpu/command_buffer/service/mocks.cc5
-rw-r--r--gpu/command_buffer/service/mocks.h23
-rw-r--r--gpu/command_buffer/service/program_cache.cc175
-rw-r--r--gpu/command_buffer/service/program_cache.h127
-rw-r--r--gpu/command_buffer/service/program_cache_lru_helper.cc49
-rw-r--r--gpu/command_buffer/service/program_cache_lru_helper.h51
-rw-r--r--gpu/command_buffer/service/program_cache_lru_helper_unittest.cc84
-rw-r--r--gpu/command_buffer/service/program_cache_unittest.cc249
-rw-r--r--gpu/command_buffer/service/program_manager.cc162
-rw-r--r--gpu/command_buffer/service/program_manager.h36
-rw-r--r--gpu/command_buffer/service/program_manager_unittest.cc394
-rw-r--r--gpu/command_buffer/service/shader_manager.cc6
-rw-r--r--gpu/command_buffer/service/shader_manager.h54
-rw-r--r--gpu/command_buffer/service/shader_manager_unittest.cc38
-rw-r--r--gpu/command_buffer/service/shader_translator.h6
-rw-r--r--gpu/command_buffer/service/test_helper.cc29
-rw-r--r--gpu/command_buffer/service/test_helper.h5
-rw-r--r--gpu/command_buffer/tests/gl_manager.cc9
-rw-r--r--gpu/command_buffer_service.gypi6
-rw-r--r--gpu/demos/framework/window.cc2
-rw-r--r--gpu/gpu_common.gypi3
-rwxr-xr-xui/gl/generate_bindings.py16
-rw-r--r--ui/gl/gl_interface.h13
-rw-r--r--webkit/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc1
39 files changed, 2260 insertions, 93 deletions
diff --git a/content/common/gpu/gpu_channel.h b/content/common/gpu/gpu_channel.h
index e28ea4e..30f967f 100644
--- a/content/common/gpu/gpu_channel.h
+++ b/content/common/gpu/gpu_channel.h
@@ -7,6 +7,7 @@
#include <deque>
#include <string>
+#include <vector>
#include "base/id_map.h"
#include "base/memory/ref_counted.h"
diff --git a/content/common/gpu/gpu_channel_manager.cc b/content/common/gpu/gpu_channel_manager.cc
index 5978b2e..be60a19 100644
--- a/content/common/gpu/gpu_channel_manager.cc
+++ b/content/common/gpu/gpu_channel_manager.cc
@@ -5,12 +5,17 @@
#include "content/common/gpu/gpu_channel_manager.h"
#include "base/bind.h"
+#include "base/command_line.h"
#include "content/common/child_thread.h"
#include "content/common/gpu/gpu_channel.h"
#include "content/common/gpu/gpu_memory_manager.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/common/gpu/sync_point_manager.h"
+#include "gpu/command_buffer/service/feature_info.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/memory_program_cache.h"
+#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_share_group.h"
GpuChannelManager::GpuChannelManager(ChildThread* gpu_child_thread,
@@ -24,7 +29,8 @@ GpuChannelManager::GpuChannelManager(ChildThread* gpu_child_thread,
ALLOW_THIS_IN_INITIALIZER_LIST(gpu_memory_manager_(this,
GpuMemoryManager::kDefaultMaxSurfacesWithFrontbufferSoftLimit)),
watchdog_(watchdog),
- sync_point_manager_(new SyncPointManager) {
+ sync_point_manager_(new SyncPointManager),
+ program_cache_(NULL) {
DCHECK(gpu_child_thread);
DCHECK(io_message_loop);
DCHECK(shutdown_event);
@@ -34,6 +40,16 @@ GpuChannelManager::~GpuChannelManager() {
gpu_channels_.clear();
}
+gpu::gles2::ProgramCache* GpuChannelManager::program_cache() {
+ if (!program_cache_.get() &&
+ (gfx::g_ARB_get_program_binary || gfx::g_OES_get_program_binary) &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableGpuProgramCache)) {
+ program_cache_.reset(new gpu::gles2::MemoryProgramCache());
+ }
+ return program_cache_.get();
+}
+
void GpuChannelManager::RemoveChannel(int client_id) {
gpu_channels_.erase(client_id);
}
@@ -65,7 +81,6 @@ void GpuChannelManager::AppendAllCommandBufferStubs(
it != gpu_channels_.end(); ++it ) {
it->second->AppendAllCommandBufferStubs(stubs);
}
-
}
bool GpuChannelManager::OnMessageReceived(const IPC::Message& msg) {
diff --git a/content/common/gpu/gpu_channel_manager.h b/content/common/gpu/gpu_channel_manager.h
index 7b1c997..3e4af65 100644
--- a/content/common/gpu/gpu_channel_manager.h
+++ b/content/common/gpu/gpu_channel_manager.h
@@ -5,8 +5,11 @@
#ifndef CONTENT_COMMON_GPU_GPU_CHANNEL_MANAGER_H_
#define CONTENT_COMMON_GPU_GPU_CHANNEL_MANAGER_H_
+#include <vector>
+
#include "base/hash_tables.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop_proxy.h"
#include "build/build_config.h"
@@ -26,6 +29,7 @@ class GLShareGroup;
namespace gpu {
namespace gles2 {
class MailboxManager;
+class ProgramCache;
}
}
@@ -80,6 +84,8 @@ class GpuChannelManager : public IPC::Listener,
void AddRoute(int32 routing_id, IPC::Listener* listener);
void RemoveRoute(int32 routing_id);
+ gpu::gles2::ProgramCache* program_cache();
+
GpuMemoryManager* gpu_memory_manager() { return &gpu_memory_manager_; }
GpuChannel* LookupChannel(int32 client_id);
@@ -116,6 +122,7 @@ class GpuChannelManager : public IPC::Listener,
GpuMemoryManager gpu_memory_manager_;
GpuWatchdog* watchdog_;
scoped_refptr<SyncPointManager> sync_point_manager_;
+ scoped_ptr<gpu::gles2::ProgramCache> program_cache_;
DISALLOW_COPY_AND_ASSIGN(GpuChannelManager);
};
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc
index 6a281e4..12cda4b 100644
--- a/content/common/gpu/gpu_command_buffer_stub.cc
+++ b/content/common/gpu/gpu_command_buffer_stub.cc
@@ -376,6 +376,11 @@ void GpuCommandBufferStub::OnInitialize(
return;
}
+ if (!context_group_->has_program_cache()) {
+ context_group_->set_program_cache(
+ channel_->gpu_channel_manager()->program_cache());
+ }
+
// Initialize the decoder with either the view or pbuffer GLContext.
if (!decoder_->Initialize(surface_,
context_,
diff --git a/content/common/gpu/gpu_command_buffer_stub.h b/content/common/gpu/gpu_command_buffer_stub.h
index 55c2be9..a8de609 100644
--- a/content/common/gpu/gpu_command_buffer_stub.h
+++ b/content/common/gpu/gpu_command_buffer_stub.h
@@ -14,7 +14,6 @@
#include "base/observer_list.h"
#include "content/common/content_export.h"
#include "content/common/gpu/gpu_memory_allocation.h"
-#include "content/common/gpu/gpu_memory_allocation.h"
#include "googleurl/src/gurl.h"
#include "gpu/command_buffer/common/constants.h"
#include "gpu/command_buffer/service/command_buffer_service.h"
diff --git a/gpu/command_buffer/common/gl_mock.h b/gpu/command_buffer/common/gl_mock.h
index c68f787..6416a61 100644
--- a/gpu/command_buffer/common/gl_mock.h
+++ b/gpu/command_buffer/common/gl_mock.h
@@ -213,6 +213,10 @@ class MockGLInterface : public GLInterface {
MOCK_METHOD2(GetIntegerv, void(GLenum pname, GLint* params));
+ MOCK_METHOD5(GetProgramBinary, void(
+ GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat,
+ GLvoid* binary));
+
MOCK_METHOD3(GetProgramiv, void(GLuint program, GLenum pname, GLint* params));
MOCK_METHOD4(GetProgramInfoLog, void(
@@ -307,6 +311,13 @@ class MockGLInterface : public GLInterface {
MOCK_METHOD2(PolygonOffset, void(GLfloat factor, GLfloat units));
+ MOCK_METHOD4(ProgramBinary, void(
+ GLuint program, GLenum binaryFormat, const GLvoid* binary,
+ GLsizei length));
+
+ MOCK_METHOD3(ProgramParameteri, void(
+ GLuint program, GLenum pname, GLint value));
+
MOCK_METHOD2(QueryCounter, void(GLuint id, GLenum target));
MOCK_METHOD1(ReadBuffer, void(GLenum src));
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
index f4f16c3..a1ec47c 100644
--- a/gpu/command_buffer/service/context_group.cc
+++ b/gpu/command_buffer/service/context_group.cc
@@ -4,6 +4,7 @@
#include "gpu/command_buffer/service/context_group.h"
+#include <algorithm>
#include <string>
#include "base/command_line.h"
@@ -39,6 +40,7 @@ ContextGroup::ContextGroup(
max_fragment_uniform_vectors_(0u),
max_varying_vectors_(0u),
max_vertex_uniform_vectors_(0u),
+ program_cache_(NULL),
feature_info_(new FeatureInfo()) {
{
TransferBufferManager* manager = new TransferBufferManager();
@@ -93,7 +95,7 @@ bool ContextGroup::Initialize(const DisallowedFeatures& disallowed_features,
renderbuffer_manager_.reset(new RenderbufferManager(
max_renderbuffer_size, max_samples));
shader_manager_.reset(new ShaderManager());
- program_manager_.reset(new ProgramManager());
+ program_manager_.reset(new ProgramManager(program_cache_));
// Lookup GL things we need to know.
const GLint kGLES2RequiredMinimumVertexAttribs = 8u;
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index 2f3eaf9..55e02e6 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -23,6 +23,7 @@ class TransferBufferManagerInterface;
namespace gles2 {
+class ProgramCache;
class BufferManager;
class GLES2Decoder;
class FramebufferManager;
@@ -112,6 +113,14 @@ class GPU_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
return program_manager_.get();
}
+ bool has_program_cache() const {
+ return program_cache_ != NULL;
+ }
+
+ void set_program_cache(ProgramCache* program_cache) {
+ program_cache_ = program_cache;
+ }
+
ShaderManager* shader_manager() const {
return shader_manager_.get();
}
@@ -149,6 +158,8 @@ class GPU_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
uint32 max_varying_vectors_;
uint32 max_vertex_uniform_vectors_;
+ ProgramCache* program_cache_;
+
scoped_ptr<BufferManager> buffer_manager_;
scoped_ptr<FramebufferManager> framebuffer_manager_;
diff --git a/gpu/command_buffer/service/gl_utils.h b/gpu/command_buffer/service/gl_utils.h
index 5788670..e31126f 100644
--- a/gpu/command_buffer/service/gl_utils.h
+++ b/gpu/command_buffer/service/gl_utils.h
@@ -103,6 +103,13 @@
#define GL_GLEXT_PROTOTYPES 1
+// GL_ARB_get_program_binary
+#define PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257
+// GL_OES_get_program_binary
+#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741
+#define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE
+#define GL_PROGRAM_BINARY_FORMATS_OES 0x87FF
+
// Define this for extra GL error debugging (slower).
// #define GL_ERROR_DEBUGGING
#ifdef GL_ERROR_DEBUGGING
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index a3bf4bf..9955d98 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -4719,7 +4719,16 @@ void GLES2DecoderImpl::DoLinkProgram(GLuint program) {
return;
}
- if (info->Link()) {
+ ShaderTranslator* vertex_translator = NULL;
+ ShaderTranslator* fragment_translator = NULL;
+ if (use_shader_translator_) {
+ vertex_translator = vertex_translator_;
+ fragment_translator = fragment_translator_;
+ }
+ if (info->Link(shader_manager(),
+ vertex_translator,
+ fragment_translator,
+ feature_info_)) {
if (info == current_program_.get()) {
program_manager()->ClearUniforms(info);
}
@@ -5812,58 +5821,13 @@ void GLES2DecoderImpl::DoCompileShader(GLuint client_id) {
if (!info) {
return;
}
- // Translate GL ES 2.0 shader to Desktop GL shader and pass that to
- // glShaderSource and then glCompileShader.
- const char* shader_src = info->source() ? info->source()->c_str() : "";
ShaderTranslator* translator = NULL;
if (use_shader_translator_) {
translator = info->shader_type() == GL_VERTEX_SHADER ?
vertex_translator_.get() : fragment_translator_.get();
-
- if (!translator->Translate(shader_src)) {
- info->SetStatus(false, translator->info_log(), NULL);
- return;
- }
- shader_src = translator->translated_shader();
- if (!feature_info_->feature_flags().angle_translated_shader_source)
- info->UpdateTranslatedSource(shader_src);
- }
-
- glShaderSource(info->service_id(), 1, &shader_src, NULL);
- glCompileShader(info->service_id());
- if (feature_info_->feature_flags().angle_translated_shader_source) {
- GLint max_len = 0;
- glGetShaderiv(info->service_id(),
- GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE,
- &max_len);
- scoped_array<char> temp(new char[max_len]);
- GLint len = 0;
- glGetTranslatedShaderSourceANGLE(
- info->service_id(), max_len, &len, temp.get());
- DCHECK(max_len == 0 || len < max_len);
- DCHECK(len == 0 || temp[len] == '\0');
- info->UpdateTranslatedSource(temp.get());
- }
-
- GLint status = GL_FALSE;
- glGetShaderiv(info->service_id(), GL_COMPILE_STATUS, &status);
- if (status) {
- info->SetStatus(true, "", translator);
- } else {
- // We cannot reach here if we are using the shader translator.
- // All invalid shaders must be rejected by the translator.
- // All translated shaders must compile.
- LOG_IF(ERROR, use_shader_translator_)
- << "Shader translator allowed/produced an invalid shader.";
- GLint max_len = 0;
- glGetShaderiv(info->service_id(), GL_INFO_LOG_LENGTH, &max_len);
- scoped_array<char> temp(new char[max_len]);
- GLint len = 0;
- glGetShaderInfoLog(info->service_id(), max_len, &len, temp.get());
- DCHECK(max_len == 0 || len < max_len);
- DCHECK(len == 0 || temp[len] == '\0');
- info->SetStatus(false, std::string(temp.get(), len).c_str(), NULL);
}
+
+ program_manager()->DoCompileShader(info, translator, feature_info_);
};
void GLES2DecoderImpl::DoGetShaderiv(
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 0484183..5039cd6 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc
@@ -238,7 +238,7 @@ void GLES2DecoderTestBase::SpecializedSetup<GetProgramInfoLog, 0>(
attach_cmd.Init(client_program_id_, kClientFragmentShaderId);
EXPECT_EQ(error::kNoError, ExecuteCmd(attach_cmd));
- info->Link();
+ info->Link(NULL, NULL, NULL, NULL);
};
template <>
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index dd73b65..b865db4 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -6,6 +6,7 @@
#include <algorithm>
#include <string>
+#include <vector>
#include "base/string_number_conversions.h"
#include "gpu/command_buffer/common/gl_mock.h"
@@ -874,8 +875,7 @@ void GLES2DecoderTestBase::DoTexImage2DSameSize(
GLsizei width, GLsizei height, GLint border,
GLenum format, GLenum type,
uint32 shared_memory_id, uint32 shared_memory_offset) {
- if (GLES2Decoder::IsAngle())
- {
+ if (GLES2Decoder::IsAngle()) {
EXPECT_CALL(*gl_, TexSubImage2D(
target, level, 0, 0, width, height, format, type, _))
.Times(1)
diff --git a/gpu/command_buffer/service/memory_program_cache.cc b/gpu/command_buffer/service/memory_program_cache.cc
new file mode 100644
index 0000000..0a061cb
--- /dev/null
+++ b/gpu/command_buffer/service/memory_program_cache.cc
@@ -0,0 +1,173 @@
+// Copyright (c) 2012 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 "gpu/command_buffer/service/memory_program_cache.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/service/gl_utils.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
+#include "ui/gl/gl_bindings.h"
+
+namespace {
+size_t GetCacheSize() {
+ size_t size;
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kGpuProgramCacheSizeKb) &&
+ base::StringToSizeT(command_line->GetSwitchValueNative(
+ switches::kGpuProgramCacheSizeKb),
+ &size)) {
+ return size;
+ }
+ return gpu::gles2::MemoryProgramCache::kDefaultMaxProgramCacheMemoryBytes;
+}
+} // anonymous namespace
+
+namespace gpu {
+namespace gles2 {
+
+MemoryProgramCache::MemoryProgramCache()
+ : max_size_bytes_(GetCacheSize()),
+ curr_size_bytes_(0) { }
+
+MemoryProgramCache::MemoryProgramCache(const size_t max_cache_size_bytes)
+ : max_size_bytes_(max_cache_size_bytes),
+ curr_size_bytes_(0) {}
+
+MemoryProgramCache::~MemoryProgramCache() {}
+
+void MemoryProgramCache::ClearBackend() {
+ curr_size_bytes_ = 0;
+ store_.clear();
+ eviction_helper_.Clear();
+}
+
+ProgramCache::ProgramLoadResult MemoryProgramCache::LoadLinkedProgram(
+ GLuint program,
+ ShaderManager::ShaderInfo* shader_a,
+ ShaderManager::ShaderInfo* shader_b,
+ const LocationMap* bind_attrib_location_map) const {
+ char a_sha[kHashLength];
+ char b_sha[kHashLength];
+ ComputeShaderHash(*shader_a->deferred_compilation_source(), a_sha);
+ ComputeShaderHash(*shader_b->deferred_compilation_source(), b_sha);
+
+ char sha[kHashLength];
+ ComputeProgramHash(a_sha,
+ b_sha,
+ bind_attrib_location_map,
+ sha);
+ const std::string sha_string(sha, kHashLength);
+
+ StoreMap::const_iterator found = store_.find(sha_string);
+ if (found == store_.end()) {
+ return PROGRAM_LOAD_FAILURE;
+ }
+ const scoped_refptr<ProgramCacheValue> value = found->second;
+ glProgramBinary(program,
+ value->format,
+ static_cast<const GLvoid*>(value->data.get()),
+ value->length);
+ GLint success = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, &success);
+ if (success == GL_FALSE) {
+ return PROGRAM_LOAD_FAILURE;
+ }
+ shader_a->set_attrib_map(value->attrib_map_0);
+ shader_a->set_uniform_map(value->uniform_map_0);
+ shader_b->set_attrib_map(value->attrib_map_1);
+ shader_b->set_uniform_map(value->uniform_map_1);
+ return PROGRAM_LOAD_SUCCESS;
+}
+
+void MemoryProgramCache::SaveLinkedProgram(
+ GLuint program,
+ const ShaderManager::ShaderInfo* shader_a,
+ const ShaderManager::ShaderInfo* shader_b,
+ const LocationMap* bind_attrib_location_map) {
+ GLsizei length;
+ GLenum format;
+ GLsizei buffer_length = 0;
+ glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &buffer_length);
+ if (static_cast<unsigned int>(buffer_length) > max_size_bytes_) {
+ return;
+ }
+ scoped_array<char> binary(new char[buffer_length]);
+ glGetProgramBinary(program,
+ buffer_length,
+ &length,
+ &format,
+ binary.get());
+ if (length == 0) {
+ return;
+ }
+
+ char a_sha[kHashLength];
+ char b_sha[kHashLength];
+ ComputeShaderHash(*shader_a->deferred_compilation_source(), a_sha);
+ ComputeShaderHash(*shader_b->deferred_compilation_source(), b_sha);
+
+ char sha[kHashLength];
+ ComputeProgramHash(a_sha,
+ b_sha,
+ bind_attrib_location_map,
+ sha);
+ const std::string sha_string(sha, sizeof(sha));
+
+ if (store_.find(sha_string) != store_.end()) {
+ return;
+ }
+
+ while (curr_size_bytes_ + length > max_size_bytes_) {
+ DCHECK(!eviction_helper_.IsEmpty());
+ const std::string* program = eviction_helper_.PeekKey();
+ const StoreMap::iterator found = store_.find(*program);
+ const ProgramCacheValue* evicting = found->second.get();
+ curr_size_bytes_ -= evicting->length;
+ Evict(*program, evicting->shader_0_hash, evicting->shader_1_hash);
+ store_.erase(found);
+ eviction_helper_.PopKey();
+ }
+ store_[sha_string] = new ProgramCacheValue(length,
+ format,
+ binary.release(),
+ a_sha,
+ shader_a->attrib_map(),
+ shader_a->uniform_map(),
+ b_sha,
+ shader_b->attrib_map(),
+ shader_b->uniform_map());
+ curr_size_bytes_ += length;
+ eviction_helper_.KeyUsed(sha_string);
+ LinkedProgramCacheSuccess(sha_string,
+ std::string(a_sha, kHashLength),
+ std::string(b_sha, kHashLength));
+}
+
+MemoryProgramCache::ProgramCacheValue::ProgramCacheValue(
+ GLsizei _length,
+ GLenum _format,
+ const char* _data,
+ const char* _shader_0_hash,
+ const ShaderTranslator::VariableMap& _attrib_map_0,
+ const ShaderTranslator::VariableMap& _uniform_map_0,
+ const char* _shader_1_hash,
+ const ShaderTranslator::VariableMap& _attrib_map_1,
+ const ShaderTranslator::VariableMap& _uniform_map_1)
+ : length(_length),
+ format(_format),
+ data(_data),
+ shader_0_hash(_shader_0_hash, kHashLength),
+ attrib_map_0(_attrib_map_0),
+ uniform_map_0(_uniform_map_0),
+ shader_1_hash(_shader_1_hash, kHashLength),
+ attrib_map_1(_attrib_map_1),
+ uniform_map_1(_uniform_map_1) {}
+
+MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() {}
+
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/memory_program_cache.h b/gpu/command_buffer/service/memory_program_cache.h
new file mode 100644
index 0000000..222e61e
--- /dev/null
+++ b/gpu/command_buffer/service/memory_program_cache.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 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 GPU_COMMAND_BUFFER_SERVICE_MEMORY_PROGRAM_CACHE_H_
+#define GPU_COMMAND_BUFFER_SERVICE_MEMORY_PROGRAM_CACHE_H_
+
+#include <map>
+#include <string>
+
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.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"
+
+namespace gpu {
+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();
+
+ virtual ProgramLoadResult LoadLinkedProgram(
+ GLuint program,
+ ShaderManager::ShaderInfo* shader_a,
+ ShaderManager::ShaderInfo* shader_b,
+ const LocationMap* bind_attrib_location_map) const OVERRIDE;
+ virtual void SaveLinkedProgram(
+ GLuint program,
+ const ShaderManager::ShaderInfo* shader_a,
+ const ShaderManager::ShaderInfo* shader_b,
+ const LocationMap* bind_attrib_location_map) OVERRIDE;
+
+ private:
+ virtual void ClearBackend() OVERRIDE;
+
+ struct ProgramCacheValue : public base::RefCounted<ProgramCacheValue> {
+ public:
+ ProgramCacheValue(GLsizei _length,
+ GLenum _format,
+ const char* _data,
+ const char* _shader_0_hash,
+ const ShaderTranslator::VariableMap& _attrib_map_0,
+ const ShaderTranslator::VariableMap& _uniform_map_0,
+ const char* _shader_1_hash,
+ const ShaderTranslator::VariableMap& _attrib_map_1,
+ const ShaderTranslator::VariableMap& _uniform_map_1);
+ const GLsizei length;
+ const GLenum format;
+ const scoped_array<const char> data;
+ const std::string shader_0_hash;
+ const ShaderTranslator::VariableMap attrib_map_0;
+ const ShaderTranslator::VariableMap uniform_map_0;
+ const std::string shader_1_hash;
+ const ShaderTranslator::VariableMap attrib_map_1;
+ const ShaderTranslator::VariableMap uniform_map_1;
+
+ protected:
+ friend class base::RefCounted<ProgramCacheValue>;
+
+ ~ProgramCacheValue();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProgramCacheValue);
+ };
+
+ typedef base::hash_map<std::string,
+ scoped_refptr<ProgramCacheValue> > StoreMap;
+
+ const size_t max_size_bytes_;
+ size_t curr_size_bytes_;
+ StoreMap store_;
+ ProgramCacheLruHelper eviction_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryProgramCache);
+};
+
+} // namespace gles2
+} // namespace gpu
+
+#endif // GPU_COMMAND_BUFFER_SERVICE_MEMORY_PROGRAM_CACHE_H_
diff --git a/gpu/command_buffer/service/memory_program_cache_unittest.cc b/gpu/command_buffer/service/memory_program_cache_unittest.cc
new file mode 100644
index 0000000..5d40785
--- /dev/null
+++ b/gpu/command_buffer/service/memory_program_cache_unittest.cc
@@ -0,0 +1,417 @@
+// Copyright (c) 2012 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 "gpu/command_buffer/service/memory_program_cache.h"
+
+#include "gpu/command_buffer/common/gl_mock.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
+#include "gpu/command_buffer/service/gl_utils.h"
+#include "gpu/command_buffer/service/shader_translator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_bindings.h"
+
+using ::testing::_;
+using ::testing::ElementsAreArray;
+using ::testing::Invoke;
+using ::testing::SetArgPointee;
+using ::testing::SetArrayArgument;
+
+namespace {
+typedef gpu::gles2::ShaderTranslator::VariableMap VariableMap;
+} // anonymous namespace
+
+namespace gpu {
+namespace gles2 {
+
+class ProgramBinaryEmulator {
+ public:
+ ProgramBinaryEmulator(GLsizei length,
+ GLenum format,
+ const char* binary)
+ : length_(length),
+ format_(format),
+ binary_(binary) { }
+
+ void GetProgramBinary(GLuint program,
+ GLsizei buffer_size,
+ GLsizei* length,
+ GLenum* format,
+ GLvoid* binary) {
+ *length = length_;
+ *format = format_;
+ memcpy(binary, binary_, length_);
+ }
+
+ void ProgramBinary(GLuint program,
+ GLenum format,
+ const GLvoid* binary,
+ GLsizei length) {
+ // format and length are verified by matcher
+ EXPECT_EQ(0, memcmp(binary_, binary, length));
+ }
+
+ GLsizei length() const { return length_; }
+ GLenum format() const { return format_; }
+ const char* binary() const { return binary_; }
+
+ private:
+ GLsizei length_;
+ GLenum format_;
+ const char* binary_;
+};
+
+class MemoryProgramCacheTest : public testing::Test {
+ public:
+ static const size_t kCacheSizeBytes = 1024;
+ static const GLuint kVertexShaderClientId = 90;
+ static const GLuint kVertexShaderServiceId = 100;
+ static const GLuint kFragmentShaderClientId = 91;
+ static const GLuint kFragmentShaderServiceId = 100;
+
+ MemoryProgramCacheTest()
+ : cache_(new MemoryProgramCache(kCacheSizeBytes)),
+ vertex_shader_(NULL),
+ fragment_shader_(NULL) { }
+ ~MemoryProgramCacheTest() {
+ shader_manager_.Destroy(false);
+ }
+
+ protected:
+ virtual void SetUp() {
+ gl_.reset(new ::testing::StrictMock<gfx::MockGLInterface>());
+ ::gfx::GLInterface::SetGLInterface(gl_.get());
+
+ vertex_shader_ = shader_manager_.CreateShaderInfo(kVertexShaderClientId,
+ kVertexShaderServiceId,
+ GL_VERTEX_SHADER);
+ fragment_shader_ = shader_manager_.CreateShaderInfo(
+ kFragmentShaderClientId,
+ kFragmentShaderServiceId,
+ GL_FRAGMENT_SHADER);
+ ASSERT_TRUE(vertex_shader_ != NULL);
+ ASSERT_TRUE(fragment_shader_ != NULL);
+ typedef ShaderTranslatorInterface::VariableInfo VariableInfo;
+ typedef ShaderTranslator::VariableMap VariableMap;
+ VariableMap vertex_attrib_map;
+ VariableMap vertex_uniform_map;
+ VariableMap fragment_attrib_map;
+ VariableMap fragment_uniform_map;
+
+ vertex_attrib_map["a"] = VariableInfo(1, 34, "a");
+ vertex_uniform_map["a"] = VariableInfo(0, 10, "a");
+ vertex_uniform_map["b"] = VariableInfo(2, 3114, "b");
+ fragment_attrib_map["jjjbb"] = VariableInfo(463, 1114, "jjjbb");
+ fragment_uniform_map["k"] = VariableInfo(10, 34413, "k");
+
+ vertex_shader_->set_attrib_map(vertex_attrib_map);
+ vertex_shader_->set_uniform_map(vertex_uniform_map);
+ fragment_shader_->set_attrib_map(vertex_attrib_map);
+ fragment_shader_->set_uniform_map(vertex_uniform_map);
+
+ vertex_shader_->UpdateSource("bbbalsldkdkdkd");
+ fragment_shader_->UpdateSource("bbbal sldkdkdkas 134 ad");
+ vertex_shader_->FlagSourceAsCompiled(true);
+ fragment_shader_->FlagSourceAsCompiled(true);
+
+ vertex_shader_->SetStatus(true, NULL, NULL);
+ fragment_shader_->SetStatus(true, NULL, NULL);
+ }
+
+ virtual void TearDown() {
+ ::gfx::GLInterface::SetGLInterface(NULL);
+ gl_.reset();
+ }
+
+ void SetExpectationsForSaveLinkedProgram(
+ const GLint program_id,
+ ProgramBinaryEmulator* emulator) const {
+ EXPECT_CALL(*gl_.get(),
+ GetProgramiv(program_id, GL_PROGRAM_BINARY_LENGTH_OES, _))
+ .WillOnce(SetArgPointee<2>(emulator->length()));
+ EXPECT_CALL(*gl_.get(),
+ GetProgramBinary(program_id, emulator->length(), _, _, _))
+ .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::GetProgramBinary));
+ }
+
+ void SetExpectationsForLoadLinkedProgram(
+ const GLint program_id,
+ ProgramBinaryEmulator* emulator) const {
+ EXPECT_CALL(*gl_.get(),
+ ProgramBinary(program_id,
+ emulator->format(),
+ _,
+ emulator->length()))
+ .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::ProgramBinary));
+ EXPECT_CALL(*gl_.get(),
+ GetProgramiv(program_id, GL_LINK_STATUS, _))
+ .WillOnce(SetArgPointee<2>(GL_TRUE));
+ }
+
+ void SetExpectationsForLoadLinkedProgramFailure(
+ const GLint program_id,
+ ProgramBinaryEmulator* emulator) const {
+ EXPECT_CALL(*gl_.get(),
+ ProgramBinary(program_id,
+ emulator->format(),
+ _,
+ emulator->length()))
+ .WillOnce(Invoke(emulator, &ProgramBinaryEmulator::ProgramBinary));
+ EXPECT_CALL(*gl_.get(),
+ GetProgramiv(program_id, GL_LINK_STATUS, _))
+ .WillOnce(SetArgPointee<2>(GL_FALSE));
+ }
+
+ // Use StrictMock to make 100% sure we know how GL will be called.
+ scoped_ptr< ::testing::StrictMock<gfx::MockGLInterface> > gl_;
+ scoped_ptr<MemoryProgramCache> cache_;
+ ShaderManager shader_manager_;
+ ShaderManager::ShaderInfo* vertex_shader_;
+ ShaderManager::ShaderInfo* fragment_shader_;
+};
+
+TEST_F(MemoryProgramCacheTest, CacheSave) {
+ 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);
+
+ EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
+ *vertex_shader_->deferred_compilation_source(),
+ *fragment_shader_->deferred_compilation_source(),
+ NULL));
+}
+
+TEST_F(MemoryProgramCacheTest, CacheLoadMatchesSave) {
+ 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);
+
+ 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, LoadFailOnLinkFalse) {
+ 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);
+
+ SetExpectationsForLoadLinkedProgramFailure(kProgramId, &emulator);
+ EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
+ kProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ NULL));
+}
+
+TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentSource) {
+ 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);
+
+ const std::string vertex_orig_source =
+ *vertex_shader_->deferred_compilation_source();
+ vertex_shader_->UpdateSource("different!");
+ vertex_shader_->FlagSourceAsCompiled(true);
+ EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
+ kProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ NULL));
+
+ vertex_shader_->UpdateSource(vertex_orig_source.c_str());
+ vertex_shader_->FlagSourceAsCompiled(true);
+ fragment_shader_->UpdateSource("different!");
+ fragment_shader_->FlagSourceAsCompiled(true);
+ EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
+ kProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ NULL));
+}
+
+TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentMap) {
+ 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);
+ ProgramCache::LocationMap binding_map;
+ binding_map["test"] = 512;
+ cache_->SaveLinkedProgram(kProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ &binding_map);
+
+ binding_map["different!"] = 59;
+ EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
+ kProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ &binding_map));
+ EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram(
+ kProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ NULL));
+}
+
+TEST_F(MemoryProgramCacheTest, MemoryProgramCacheEviction) {
+ typedef ShaderTranslator::VariableMap VariableMap;
+ 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 emulator1(kBinaryLength, kFormat, test_binary);
+
+
+ SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+
+ const int kEvictingProgramId = 11;
+ const GLuint kEvictingBinaryLength = kCacheSizeBytes - kBinaryLength + 1;
+
+ // save old source and modify for new program
+ const std::string old_source =
+ *fragment_shader_->deferred_compilation_source();
+ fragment_shader_->UpdateSource("al sdfkjdk");
+ fragment_shader_->FlagSourceAsCompiled(true);
+
+ scoped_array<char> bigTestBinary =
+ scoped_array<char>(new char[kEvictingBinaryLength]);
+ for (size_t i = 0; i < kEvictingBinaryLength; ++i) {
+ bigTestBinary[i] = i % 250;
+ }
+ ProgramBinaryEmulator emulator2(kEvictingBinaryLength,
+ kFormat,
+ bigTestBinary.get());
+
+ SetExpectationsForSaveLinkedProgram(kEvictingProgramId, &emulator2);
+ cache_->SaveLinkedProgram(kEvictingProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ NULL);
+
+ EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
+ *vertex_shader_->deferred_compilation_source(),
+ *fragment_shader_->deferred_compilation_source(),
+ NULL));
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN, cache_->GetLinkedProgramStatus(
+ old_source,
+ *fragment_shader_->deferred_compilation_source(),
+ NULL));
+}
+
+TEST_F(MemoryProgramCacheTest, SaveCorrectProgram) {
+ 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 emulator1(kBinaryLength, kFormat, test_binary);
+
+ vertex_shader_->UpdateSource("different!");
+ SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1);
+ cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL);
+
+ EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
+ *vertex_shader_->deferred_compilation_source(),
+ *fragment_shader_->deferred_compilation_source(),
+ NULL));
+}
+
+TEST_F(MemoryProgramCacheTest, LoadCorrectProgram) {
+ 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);
+
+ EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus(
+ *vertex_shader_->deferred_compilation_source(),
+ *fragment_shader_->deferred_compilation_source(),
+ NULL));
+
+ SetExpectationsForLoadLinkedProgram(kProgramId, &emulator);
+
+ fragment_shader_->UpdateSource("different!");
+ EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram(
+ kProgramId,
+ vertex_shader_,
+ fragment_shader_,
+ NULL));
+}
+
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/mocks.cc b/gpu/command_buffer/service/mocks.cc
index 46a8977..a71c019 100644
--- a/gpu/command_buffer/service/mocks.cc
+++ b/gpu/command_buffer/service/mocks.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -33,5 +33,8 @@ MockShaderTranslator::MockShaderTranslator() {}
MockShaderTranslator::~MockShaderTranslator() {}
+MockProgramCache::MockProgramCache() {}
+MockProgramCache::~MockProgramCache() {}
+
} // namespace gles2
} // namespace gpu
diff --git a/gpu/command_buffer/service/mocks.h b/gpu/command_buffer/service/mocks.h
index 01dcbd8..ecfcd9d 100644
--- a/gpu/command_buffer/service/mocks.h
+++ b/gpu/command_buffer/service/mocks.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -15,6 +15,7 @@
#include "base/logging.h"
#include "gpu/command_buffer/service/cmd_parser.h"
#include "gpu/command_buffer/service/cmd_buffer_engine.h"
+#include "gpu/command_buffer/service/program_cache.h"
#include "gpu/command_buffer/service/shader_translator.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -89,6 +90,26 @@ class MockShaderTranslator : public ShaderTranslatorInterface {
MOCK_CONST_METHOD0(uniform_map, const VariableMap&());
};
+class MockProgramCache : public ProgramCache {
+ public:
+ MockProgramCache();
+ virtual ~MockProgramCache();
+
+ MOCK_CONST_METHOD4(LoadLinkedProgram, ProgramLoadResult(
+ GLuint program,
+ ShaderManager::ShaderInfo* shader_a,
+ ShaderManager::ShaderInfo* shader_b,
+ const LocationMap* bind_attrib_location_map));
+
+ MOCK_METHOD4(SaveLinkedProgram, void(
+ GLuint program,
+ const ShaderManager::ShaderInfo* shader_a,
+ const ShaderManager::ShaderInfo* shader_b,
+ const LocationMap* bind_attrib_location_map));
+ private:
+ MOCK_METHOD0(ClearBackend, void());
+};
+
} // namespace gles2
} // namespace gpu
diff --git a/gpu/command_buffer/service/program_cache.cc b/gpu/command_buffer/service/program_cache.cc
new file mode 100644
index 0000000..55afa86
--- /dev/null
+++ b/gpu/command_buffer/service/program_cache.cc
@@ -0,0 +1,175 @@
+// Copyright (c) 2012 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 "gpu/command_buffer/service/program_cache.h"
+
+#include "base/memory/scoped_ptr.h"
+
+namespace gpu {
+namespace gles2 {
+
+ProgramCache::ProgramCache() {}
+ProgramCache::~ProgramCache() {}
+
+void ProgramCache::Clear() {
+ shader_status_.clear();
+ link_status_.clear();
+ ClearBackend();
+}
+
+ProgramCache::CompiledShaderStatus ProgramCache::GetShaderCompilationStatus(
+ const std::string& shader_src) const {
+ char sha[kHashLength];
+ ComputeShaderHash(shader_src, sha);
+ const std::string sha_string(sha, kHashLength);
+
+ CompileStatusMap::const_iterator found = shader_status_.find(sha_string);
+
+ if (found == shader_status_.end()) {
+ return ProgramCache::COMPILATION_UNKNOWN;
+ } else {
+ return found->second.status;
+ }
+}
+
+void ProgramCache::ShaderCompilationSucceeded(
+ const std::string& shader_src) {
+ char sha[kHashLength];
+ ComputeShaderHash(shader_src, sha);
+ const std::string sha_string(sha, kHashLength);
+
+ CompileStatusMap::iterator it = shader_status_.find(sha_string);
+ if (it == shader_status_.end()) {
+ shader_status_[sha_string] = CompiledShaderInfo(COMPILATION_SUCCEEDED);
+ } else {
+ it->second.status = COMPILATION_SUCCEEDED;
+ }
+}
+
+ProgramCache::LinkedProgramStatus ProgramCache::GetLinkedProgramStatus(
+ const std::string& untranslated_a,
+ const std::string& untranslated_b,
+ const std::map<std::string, GLint>* bind_attrib_location_map) const {
+ char a_sha[kHashLength];
+ char b_sha[kHashLength];
+ ComputeShaderHash(untranslated_a, a_sha);
+ ComputeShaderHash(untranslated_b, b_sha);
+
+ char sha[kHashLength];
+ ComputeProgramHash(a_sha,
+ b_sha,
+ bind_attrib_location_map,
+ sha);
+ const std::string sha_string(sha, kHashLength);
+
+ LinkStatusMap::const_iterator found = link_status_.find(sha_string);
+ if (found == link_status_.end()) {
+ return ProgramCache::LINK_UNKNOWN;
+ } else {
+ return found->second;
+ }
+}
+
+void ProgramCache::LinkedProgramCacheSuccess(
+ const std::string& shader_a,
+ const std::string& shader_b,
+ const LocationMap* bind_attrib_location_map) {
+ char a_sha[kHashLength];
+ char b_sha[kHashLength];
+ ComputeShaderHash(shader_a, a_sha);
+ ComputeShaderHash(shader_b, b_sha);
+ char sha[kHashLength];
+ ComputeProgramHash(a_sha,
+ b_sha,
+ bind_attrib_location_map,
+ sha);
+ const std::string sha_string(sha, kHashLength);
+
+ LinkedProgramCacheSuccess(sha_string,
+ std::string(a_sha, kHashLength),
+ std::string(b_sha, kHashLength));
+}
+
+void ProgramCache::LinkedProgramCacheSuccess(const std::string& program_hash,
+ const std::string& shader_a_hash,
+ const std::string& shader_b_hash) {
+ link_status_[program_hash] = LINK_SUCCEEDED;
+ shader_status_[shader_a_hash].ref_count++;
+ shader_status_[shader_b_hash].ref_count++;
+}
+
+void ProgramCache::ComputeShaderHash(const std::string& str,
+ char* result) const {
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.c_str()),
+ str.length(), reinterpret_cast<unsigned char*>(result));
+}
+
+void ProgramCache::Evict(const std::string& program_hash,
+ const std::string& shader_0_hash,
+ const std::string& shader_1_hash) {
+ CompileStatusMap::iterator info0 = shader_status_.find(shader_0_hash);
+ CompileStatusMap::iterator info1 = shader_status_.find(shader_1_hash);
+ DCHECK(info0 != shader_status_.end());
+ DCHECK(info1 != shader_status_.end());
+ DCHECK(info0->second.ref_count > 0);
+ DCHECK(info1->second.ref_count > 0);
+ if (--info0->second.ref_count <= 0) {
+ shader_status_.erase(shader_0_hash);
+ }
+ if (--info1->second.ref_count <= 0) {
+ shader_status_.erase(shader_1_hash);
+ }
+ link_status_.erase(program_hash);
+}
+
+namespace {
+size_t CalculateMapSize(const std::map<std::string, GLint>* map) {
+ if (!map) {
+ return 0;
+ }
+ std::map<std::string, GLint>::const_iterator it;
+ size_t total = 0;
+ for (it = map->begin(); it != map->end(); ++it) {
+ total += 4 + it->first.length();
+ }
+ return total;
+}
+} // anonymous namespace
+
+void ProgramCache::ComputeProgramHash(
+ const char* hashed_shader_0,
+ const char* hashed_shader_1,
+ const std::map<std::string, GLint>* bind_attrib_location_map,
+ char* result) const {
+ const size_t shader0_size = kHashLength;
+ const size_t shader1_size = kHashLength;
+ const size_t map_size = CalculateMapSize(bind_attrib_location_map);
+ const size_t total_size = shader0_size + shader1_size + map_size;
+
+ scoped_array<unsigned char> buffer(new unsigned char[total_size]);
+ memcpy(buffer.get(), hashed_shader_0, shader0_size);
+ memcpy(&buffer[shader0_size], hashed_shader_1, shader1_size);
+ if (map_size != 0) {
+ // copy our map
+ size_t current_pos = shader0_size + shader1_size;
+ std::map<std::string, GLint>::const_iterator it;
+ for (it = bind_attrib_location_map->begin();
+ it != bind_attrib_location_map->end();
+ ++it) {
+ const size_t name_size = it->first.length();
+ memcpy(&buffer.get()[current_pos], it->first.c_str(), name_size);
+ current_pos += name_size;
+ const GLint value = it->second;
+ buffer[current_pos++] = value >> 24;
+ buffer[current_pos++] = value >> 16;
+ buffer[current_pos++] = value >> 8;
+ buffer[current_pos++] = value;
+ }
+ }
+ base::SHA1HashBytes(buffer.get(),
+ total_size, reinterpret_cast<unsigned char*>(result));
+}
+
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/program_cache.h b/gpu/command_buffer/service/program_cache.h
new file mode 100644
index 0000000..310908b
--- /dev/null
+++ b/gpu/command_buffer/service/program_cache.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2012 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 GPU_COMMAND_BUFFER_SERVICE_PROGRAM_CACHE_H_
+#define GPU_COMMAND_BUFFER_SERVICE_PROGRAM_CACHE_H_
+
+#include <map>
+#include <string>
+
+#include "base/hash_tables.h"
+#include "base/sha1.h"
+#include "gpu/command_buffer/common/gles2_cmd_format.h"
+#include "gpu/command_buffer/service/shader_manager.h"
+
+namespace gpu {
+namespace gles2 {
+
+// Program cache base class for caching linked gpu programs
+class GPU_EXPORT ProgramCache {
+ public:
+ static const size_t kHashLength = base::kSHA1Length;
+
+ typedef std::map<std::string, GLint> LocationMap;
+
+ enum CompiledShaderStatus {
+ COMPILATION_UNKNOWN,
+ COMPILATION_SUCCEEDED
+ };
+
+ enum LinkedProgramStatus {
+ LINK_UNKNOWN,
+ LINK_SUCCEEDED
+ };
+
+ enum ProgramLoadResult {
+ PROGRAM_LOAD_FAILURE,
+ PROGRAM_LOAD_SUCCESS
+ };
+
+ ProgramCache();
+ virtual ~ProgramCache();
+
+ CompiledShaderStatus GetShaderCompilationStatus(
+ const std::string& shader_src) const;
+ void ShaderCompilationSucceeded(const std::string& shader_src);
+
+ LinkedProgramStatus GetLinkedProgramStatus(
+ const std::string& untranslated_a,
+ const std::string& untranslated_b,
+ const LocationMap* bind_attrib_location_map) const;
+
+ // Loads the linked program from the cache. If the program is not found or
+ // there was an error, PROGRAM_LOAD_FAILURE should be returned.
+ virtual ProgramLoadResult LoadLinkedProgram(
+ GLuint program,
+ ShaderManager::ShaderInfo* shader_a,
+ ShaderManager::ShaderInfo* shader_b,
+ const LocationMap* bind_attrib_location_map) const = 0;
+
+ // Saves the program into the cache. If successful, the implementation should
+ // call LinkedProgramCacheSuccess.
+ virtual void SaveLinkedProgram(
+ GLuint program,
+ const ShaderManager::ShaderInfo* shader_a,
+ const ShaderManager::ShaderInfo* shader_b,
+ const LocationMap* bind_attrib_location_map) = 0;
+
+ // clears the cache
+ void Clear();
+
+ // Only for testing
+ void LinkedProgramCacheSuccess(const std::string& shader_a,
+ const std::string& shader_b,
+ const LocationMap* bind_attrib_location_map);
+
+ protected:
+ // called by implementing class after a shader was successfully cached
+ void LinkedProgramCacheSuccess(const std::string& program_hash,
+ const std::string& shader_a_hash,
+ const std::string& shader_b_hash);
+
+ // result is not null terminated
+ void ComputeShaderHash(const std::string& shader,
+ char* result) const;
+
+ // result is not null terminated. hashed shaders are expected to be
+ // kHashLength in length
+ void ComputeProgramHash(
+ const char* hashed_shader_0,
+ const char* hashed_shader_1,
+ const LocationMap* bind_attrib_location_map,
+ char* result) const;
+
+ void Evict(const std::string& program_hash,
+ const std::string& shader_0_hash,
+ const std::string& shader_1_hash);
+
+ private:
+ struct CompiledShaderInfo {
+ CompiledShaderInfo() : status(COMPILATION_UNKNOWN), ref_count(0) { }
+ explicit CompiledShaderInfo(CompiledShaderStatus status_)
+ : status(status_),
+ ref_count(0) { }
+
+ CompiledShaderStatus status;
+ size_t ref_count;
+ };
+
+ typedef base::hash_map<std::string,
+ CompiledShaderInfo> CompileStatusMap;
+ typedef base::hash_map<std::string,
+ LinkedProgramStatus> LinkStatusMap;
+
+ // called to clear the backend cache
+ virtual void ClearBackend() = 0;
+
+ CompileStatusMap shader_status_;
+ LinkStatusMap link_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProgramCache);
+};
+
+} // namespace gles2
+} // namespace gpu
+
+#endif // GPU_COMMAND_BUFFER_SERVICE_PROGRAM_CACHE_H_
diff --git a/gpu/command_buffer/service/program_cache_lru_helper.cc b/gpu/command_buffer/service/program_cache_lru_helper.cc
new file mode 100644
index 0000000..a1f4555
--- /dev/null
+++ b/gpu/command_buffer/service/program_cache_lru_helper.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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 "gpu/command_buffer/service/program_cache_lru_helper.h"
+
+namespace gpu {
+namespace gles2 {
+
+ProgramCacheLruHelper::ProgramCacheLruHelper() {}
+ProgramCacheLruHelper::~ProgramCacheLruHelper() {}
+
+void ProgramCacheLruHelper::Clear() {
+ location_map.clear();
+ queue.clear();
+}
+
+bool ProgramCacheLruHelper::IsEmpty() {
+ return queue.empty();
+}
+
+void ProgramCacheLruHelper::KeyUsed(const std::string& key) {
+ IteratorMap::iterator location_iterator = location_map.find(key);
+ if (location_iterator != location_map.end()) {
+ // already exists, erase it
+ queue.erase(location_iterator->second);
+ }
+ queue.push_front(key);
+ location_map[key] = queue.begin();
+}
+
+const std::string* ProgramCacheLruHelper::PeekKey() {
+ if (queue.empty()) {
+ return NULL;
+ }
+ return &queue.back();
+}
+
+void ProgramCacheLruHelper::PopKey() {
+ if (queue.empty()) {
+ return;
+ }
+ const std::string& last = queue.back();
+ location_map.erase(last);
+ queue.pop_back();
+}
+
+} // namespace gpu
+} // namespace gles2
diff --git a/gpu/command_buffer/service/program_cache_lru_helper.h b/gpu/command_buffer/service/program_cache_lru_helper.h
new file mode 100644
index 0000000..8560305
--- /dev/null
+++ b/gpu/command_buffer/service/program_cache_lru_helper.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 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 GPU_COMMAND_BUFFER_SERVICE_PROGRAM_CACHE_LRU_HELPER_H_
+#define GPU_COMMAND_BUFFER_SERVICE_PROGRAM_CACHE_LRU_HELPER_H_
+
+#include <list>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "gpu/gpu_export.h"
+
+namespace gpu {
+namespace gles2 {
+
+// LRU helper for the program cache, operates in O(1) time.
+// This class uses a linked list with a hash map. Both copy their string keys,
+// so be mindful that keys you insert will be stored again twice in memory.
+class GPU_EXPORT ProgramCacheLruHelper {
+ public:
+ ProgramCacheLruHelper();
+ ~ProgramCacheLruHelper();
+
+ // clears the lru queue
+ void Clear();
+ // returns true if the lru queue is empty
+ bool IsEmpty();
+ // inserts or refreshes a key in the queue
+ void KeyUsed(const std::string& key);
+ // Peeks at the next key. Use IsEmpty() first (if the queue is empty then
+ // null is returned).
+ const std::string* PeekKey();
+ // evicts the next key from the queue.
+ void PopKey();
+
+ private:
+ typedef std::list<std::string> StringList;
+ typedef base::hash_map<std::string,
+ StringList::iterator> IteratorMap;
+ StringList queue;
+ IteratorMap location_map;
+
+ DISALLOW_COPY_AND_ASSIGN(ProgramCacheLruHelper);
+};
+
+} // namespace gles2
+} // namespace gpu
+
+#endif // GPU_COMMAND_BUFFER_SERVICE_PROGRAM_CACHE_LRU_HELPER_H_
diff --git a/gpu/command_buffer/service/program_cache_lru_helper_unittest.cc b/gpu/command_buffer/service/program_cache_lru_helper_unittest.cc
new file mode 100644
index 0000000..5aaa088
--- /dev/null
+++ b/gpu/command_buffer/service/program_cache_lru_helper_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 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 "gpu/command_buffer/service/program_cache_lru_helper.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+namespace gles2 {
+
+class ProgramCacheLruHelperTest : public testing::Test {
+ public:
+ ProgramCacheLruHelperTest() :
+ lru_helper_(new ProgramCacheLruHelper()) { }
+
+ protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ lru_helper_->Clear();
+ }
+
+ scoped_ptr<ProgramCacheLruHelper> lru_helper_;
+};
+
+TEST_F(ProgramCacheLruHelperTest, ProgramCacheLruHelperEvictionOrderNoReuse) {
+ lru_helper_->KeyUsed("1");
+ lru_helper_->KeyUsed("2");
+ lru_helper_->KeyUsed("3");
+ lru_helper_->KeyUsed("4");
+ const std::string* key = lru_helper_->PeekKey();
+ EXPECT_EQ("1", *key);
+ EXPECT_EQ("1", *lru_helper_->PeekKey());
+ lru_helper_->PopKey();
+ EXPECT_FALSE(lru_helper_->IsEmpty());
+ EXPECT_EQ("2", *lru_helper_->PeekKey());
+ lru_helper_->PopKey();
+ EXPECT_FALSE(lru_helper_->IsEmpty());
+ EXPECT_EQ("3", *lru_helper_->PeekKey());
+ lru_helper_->PopKey();
+ EXPECT_FALSE(lru_helper_->IsEmpty());
+ EXPECT_EQ("4", *lru_helper_->PeekKey());
+ lru_helper_->PopKey();
+ EXPECT_TRUE(lru_helper_->IsEmpty());
+}
+
+TEST_F(ProgramCacheLruHelperTest, ProgramCacheLruHelperClear) {
+ EXPECT_TRUE(lru_helper_->IsEmpty());
+ lru_helper_->KeyUsed("1");
+ lru_helper_->KeyUsed("2");
+ lru_helper_->KeyUsed("3");
+ lru_helper_->KeyUsed("4");
+ EXPECT_FALSE(lru_helper_->IsEmpty());
+ lru_helper_->Clear();
+ EXPECT_TRUE(lru_helper_->IsEmpty());
+}
+
+TEST_F(ProgramCacheLruHelperTest, ProgramCacheLruHelperEvictionOrderWithReuse) {
+ lru_helper_->KeyUsed("1");
+ lru_helper_->KeyUsed("2");
+ lru_helper_->KeyUsed("4");
+ lru_helper_->KeyUsed("2");
+ lru_helper_->KeyUsed("3");
+ lru_helper_->KeyUsed("1");
+ lru_helper_->KeyUsed("1");
+ lru_helper_->KeyUsed("2");
+ EXPECT_EQ("4", *lru_helper_->PeekKey());
+ EXPECT_EQ("4", *lru_helper_->PeekKey());
+ lru_helper_->PopKey();
+ EXPECT_EQ("3", *lru_helper_->PeekKey());
+ lru_helper_->PopKey();
+ EXPECT_EQ("1", *lru_helper_->PeekKey());
+ lru_helper_->PopKey();
+ EXPECT_FALSE(lru_helper_->IsEmpty());
+ EXPECT_EQ("2", *lru_helper_->PeekKey());
+ lru_helper_->PopKey();
+ EXPECT_TRUE(lru_helper_->IsEmpty());
+}
+
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/program_cache_unittest.cc b/gpu/command_buffer/service/program_cache_unittest.cc
new file mode 100644
index 0000000..eee7dab
--- /dev/null
+++ b/gpu/command_buffer/service/program_cache_unittest.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2012 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 "gpu/command_buffer/service/program_cache.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gpu {
+namespace gles2 {
+
+class NoBackendProgramCache : public ProgramCache {
+ public:
+ virtual ProgramLoadResult LoadLinkedProgram(
+ GLuint /* program */,
+ ShaderManager::ShaderInfo* /* shader_a */,
+ ShaderManager::ShaderInfo* /* shader_b */,
+ const LocationMap* /* bind_attrib_location_map */) const OVERRIDE {
+ return PROGRAM_LOAD_SUCCESS;
+ }
+ virtual void SaveLinkedProgram(
+ GLuint /* program */,
+ const ShaderManager::ShaderInfo* /* shader_a */,
+ const ShaderManager::ShaderInfo* /* shader_b */,
+ const LocationMap* /* bind_attrib_location_map */) OVERRIDE { }
+
+ virtual void ClearBackend() OVERRIDE {}
+
+ void SaySuccessfullyCached(const std::string& shader1,
+ const std::string& shader2,
+ std::map<std::string, GLint>* attrib_map) {
+ char a_sha[kHashLength];
+ char b_sha[kHashLength];
+ ComputeShaderHash(shader1, a_sha);
+ ComputeShaderHash(shader2, b_sha);
+
+ char sha[kHashLength];
+ ComputeProgramHash(a_sha,
+ b_sha,
+ attrib_map,
+ sha);
+ const std::string shaString(sha, kHashLength);
+
+ LinkedProgramCacheSuccess(shaString,
+ std::string(a_sha, kHashLength),
+ std::string(b_sha, kHashLength));
+ }
+
+ void ComputeShaderHash(const std::string& shader,
+ char* result) const {
+ ProgramCache::ComputeShaderHash(shader, result);
+ }
+
+ void ComputeProgramHash(const char* hashed_shader_0,
+ const char* hashed_shader_1,
+ const LocationMap* bind_attrib_location_map,
+ char* result) const {
+ ProgramCache::ComputeProgramHash(hashed_shader_0,
+ hashed_shader_1,
+ bind_attrib_location_map,
+ result);
+ }
+
+ void Evict(const std::string& program_hash,
+ const std::string& shader_0_hash,
+ const std::string& shader_1_hash) {
+ ProgramCache::Evict(program_hash, shader_0_hash, shader_1_hash);
+ }
+};
+
+class ProgramCacheTest : public testing::Test {
+ public:
+ ProgramCacheTest() :
+ cache_(new NoBackendProgramCache()) { }
+
+ protected:
+ scoped_ptr<NoBackendProgramCache> cache_;
+};
+
+TEST_F(ProgramCacheTest, CompilationStatusSave) {
+ const std::string shader1 = "abcd1234";
+ {
+ std::string shader = shader1;
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader));
+ cache_->ShaderCompilationSucceeded(shader);
+ shader.clear();
+ }
+ // make sure it was copied
+ EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
+ cache_->GetShaderCompilationStatus(shader1));
+}
+
+TEST_F(ProgramCacheTest, CompilationUnknownOnSourceChange) {
+ std::string shader1 = "abcd1234";
+ cache_->ShaderCompilationSucceeded(shader1);
+
+ shader1 = "different!";
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader1));
+}
+
+TEST_F(ProgramCacheTest, LinkStatusSave) {
+ const std::string shader1 = "abcd1234";
+ const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
+ {
+ std::string shader_a = shader1;
+ std::string shader_b = shader2;
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader_a, shader_b, NULL));
+ cache_->SaySuccessfullyCached(shader_a, shader_b, NULL);
+
+ shader_a.clear();
+ shader_b.clear();
+ }
+ // make sure it was copied
+ EXPECT_EQ(ProgramCache::LINK_SUCCEEDED,
+ cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+}
+
+TEST_F(ProgramCacheTest, LinkUnknownOnFragmentSourceChange) {
+ const std::string shader1 = "abcd1234";
+ std::string shader2 = "abcda sda b1~#4 bbbbb1234";
+ cache_->SaySuccessfullyCached(shader1, shader2, NULL);
+
+ shader2 = "different!";
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+}
+
+TEST_F(ProgramCacheTest, LinkUnknownOnVertexSourceChange) {
+ std::string shader1 = "abcd1234";
+ const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
+ cache_->SaySuccessfullyCached(shader1, shader2, NULL);
+
+ shader1 = "different!";
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+}
+
+TEST_F(ProgramCacheTest, StatusEviction) {
+ const std::string shader1 = "abcd1234";
+ const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
+ cache_->ShaderCompilationSucceeded(shader1);
+ cache_->ShaderCompilationSucceeded(shader2);
+ cache_->SaySuccessfullyCached(shader1, shader2, NULL);
+ char a_sha[ProgramCache::kHashLength];
+ char b_sha[ProgramCache::kHashLength];
+ cache_->ComputeShaderHash(shader1, a_sha);
+ cache_->ComputeShaderHash(shader2, b_sha);
+
+ char sha[ProgramCache::kHashLength];
+ cache_->ComputeProgramHash(a_sha,
+ b_sha,
+ NULL,
+ sha);
+ cache_->Evict(std::string(sha, ProgramCache::kHashLength),
+ std::string(a_sha, ProgramCache::kHashLength),
+ std::string(b_sha, ProgramCache::kHashLength));
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader1));
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader2));
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+}
+
+TEST_F(ProgramCacheTest, EvictionWithReusedShader) {
+ const std::string shader1 = "abcd1234";
+ const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
+ const std::string shader3 = "asbjbbjj239a";
+ cache_->ShaderCompilationSucceeded(shader1);
+ cache_->ShaderCompilationSucceeded(shader2);
+ cache_->SaySuccessfullyCached(shader1, shader2, NULL);
+ cache_->ShaderCompilationSucceeded(shader1);
+ cache_->ShaderCompilationSucceeded(shader3);
+ cache_->SaySuccessfullyCached(shader1, shader3, NULL);
+
+ char a_sha[ProgramCache::kHashLength];
+ char b_sha[ProgramCache::kHashLength];
+ char c_sha[ProgramCache::kHashLength];
+ cache_->ComputeShaderHash(shader1, a_sha);
+ cache_->ComputeShaderHash(shader2, b_sha);
+ cache_->ComputeShaderHash(shader3, c_sha);
+
+ char sha[ProgramCache::kHashLength];
+ cache_->ComputeProgramHash(a_sha,
+ b_sha,
+ NULL,
+ sha);
+ cache_->Evict(std::string(sha, ProgramCache::kHashLength),
+ std::string(a_sha, ProgramCache::kHashLength),
+ std::string(b_sha, ProgramCache::kHashLength));
+ EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
+ cache_->GetShaderCompilationStatus(shader1));
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader2));
+ EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
+ cache_->GetShaderCompilationStatus(shader3));
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+ EXPECT_EQ(ProgramCache::LINK_SUCCEEDED,
+ cache_->GetLinkedProgramStatus(shader1, shader3, NULL));
+
+
+ cache_->ComputeProgramHash(a_sha,
+ c_sha,
+ NULL,
+ sha);
+ cache_->Evict(std::string(sha, ProgramCache::kHashLength),
+ std::string(a_sha, ProgramCache::kHashLength),
+ std::string(c_sha, ProgramCache::kHashLength));
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader1));
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader2));
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader3));
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader1, shader3, NULL));
+}
+
+TEST_F(ProgramCacheTest, StatusClear) {
+ const std::string shader1 = "abcd1234";
+ const std::string shader2 = "abcda sda b1~#4 bbbbb1234";
+ const std::string shader3 = "asbjbbjj239a";
+ cache_->ShaderCompilationSucceeded(shader1);
+ cache_->ShaderCompilationSucceeded(shader2);
+ cache_->SaySuccessfullyCached(shader1, shader2, NULL);
+ cache_->ShaderCompilationSucceeded(shader3);
+ cache_->SaySuccessfullyCached(shader1, shader3, NULL);
+ cache_->Clear();
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader1));
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader2));
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(shader3));
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader1, shader2, NULL));
+ EXPECT_EQ(ProgramCache::LINK_UNKNOWN,
+ cache_->GetLinkedProgramStatus(shader1, shader3, NULL));
+}
+
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc
index 8b94e8b..c73cab4 100644
--- a/gpu/command_buffer/service/program_manager.cc
+++ b/gpu/command_buffer/service/program_manager.cc
@@ -4,7 +4,9 @@
#include "gpu/command_buffer/service/program_manager.h"
+#include <algorithm>
#include <set>
+#include <utility>
#include <vector>
#include "base/basictypes.h"
@@ -14,8 +16,10 @@
#include "base/string_number_conversions.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
+#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/gpu_switches.h"
+#include "gpu/command_buffer/service/program_cache.h"
namespace gpu {
namespace gles2 {
@@ -34,6 +38,21 @@ int ShaderTypeToIndex(GLenum shader_type) {
}
}
+ShaderTranslator* ShaderIndexToTranslator(
+ int index,
+ ShaderTranslator* vertex_translator,
+ ShaderTranslator* fragment_translator) {
+ switch (index) {
+ case 0:
+ return vertex_translator;
+ case 1:
+ return fragment_translator;
+ default:
+ NOTREACHED();
+ return NULL;
+ }
+}
+
// Given a name like "foo.bar[123].moo[456]" sets new_name to "foo.bar[123].moo"
// and sets element_index to 456. returns false if element expression was not a
// whole decimal number. For example: "foo[1b2]"
@@ -360,7 +379,83 @@ void ProgramManager::ProgramInfo::ExecuteBindAttribLocationCalls() {
}
}
-bool ProgramManager::ProgramInfo::Link() {
+void ProgramManager::DoCompileShader(ShaderManager::ShaderInfo* info,
+ ShaderTranslator* translator,
+ FeatureInfo* feature_info) {
+ if (program_cache_ &&
+ program_cache_->GetShaderCompilationStatus(*info->source()) ==
+ ProgramCache::COMPILATION_SUCCEEDED) {
+ info->SetStatus(true, "", translator);
+ info->FlagSourceAsCompiled(false);
+ return;
+ }
+ ForceCompileShader(info->source(), info, translator, feature_info);
+}
+
+void ProgramManager::ForceCompileShader(const std::string* source,
+ ShaderManager::ShaderInfo* info,
+ ShaderTranslator* translator,
+ FeatureInfo* feature_info) {
+ info->FlagSourceAsCompiled(true);
+
+ // Translate GL ES 2.0 shader to Desktop GL shader and pass that to
+ // glShaderSource and then glCompileShader.
+ const char* shader_src = source ? source->c_str() : "";
+ if (translator) {
+ if (!translator->Translate(shader_src)) {
+ info->SetStatus(false, translator->info_log(), NULL);
+ return;
+ }
+ shader_src = translator->translated_shader();
+ if (!feature_info->feature_flags().angle_translated_shader_source)
+ info->UpdateTranslatedSource(shader_src);
+ }
+
+ glShaderSource(info->service_id(), 1, &shader_src, NULL);
+ glCompileShader(info->service_id());
+ if (feature_info->feature_flags().angle_translated_shader_source) {
+ GLint max_len = 0;
+ glGetShaderiv(info->service_id(),
+ GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE,
+ &max_len);
+ scoped_array<char> temp(new char[max_len]);
+ GLint len = 0;
+ glGetTranslatedShaderSourceANGLE(
+ info->service_id(), max_len, &len, temp.get());
+ DCHECK(max_len == 0 || len < max_len);
+ DCHECK(len == 0 || temp[len] == '\0');
+ info->UpdateTranslatedSource(temp.get());
+ }
+
+ GLint status = GL_FALSE;
+ glGetShaderiv(info->service_id(), GL_COMPILE_STATUS, &status);
+ if (status) {
+ info->SetStatus(true, "", translator);
+ if (program_cache_) {
+ const char* untranslated_source = source ? source->c_str() : "";
+ program_cache_->ShaderCompilationSucceeded(untranslated_source);
+ }
+ } else {
+ // We cannot reach here if we are using the shader translator.
+ // All invalid shaders must be rejected by the translator.
+ // All translated shaders must compile.
+ LOG_IF(ERROR, translator)
+ << "Shader translator allowed/produced an invalid shader.";
+ GLint max_len = 0;
+ glGetShaderiv(info->service_id(), GL_INFO_LOG_LENGTH, &max_len);
+ scoped_array<char> temp(new char[max_len]);
+ GLint len = 0;
+ glGetShaderInfoLog(info->service_id(), max_len, &len, temp.get());
+ DCHECK(max_len == 0 || len < max_len);
+ DCHECK(len == 0 || temp[len] == '\0');
+ info->SetStatus(false, std::string(temp.get(), len).c_str(), NULL);
+ }
+}
+
+bool ProgramManager::ProgramInfo::Link(ShaderManager* manager,
+ ShaderTranslator* vertex_translator,
+ ShaderTranslator* fragment_translator,
+ FeatureInfo* feature_info) {
ClearLinkStatus();
if (!CanLink()) {
set_log_info("missing shaders");
@@ -371,11 +466,63 @@ bool ProgramManager::ProgramInfo::Link() {
return false;
}
ExecuteBindAttribLocationCalls();
- glLinkProgram(service_id());
+
+ bool link = true;
+ ProgramCache* cache = manager_->program_cache_;
+ if (cache) {
+ ProgramCache::LinkedProgramStatus status = cache->GetLinkedProgramStatus(
+ *attached_shaders_[0]->deferred_compilation_source(),
+ *attached_shaders_[1]->deferred_compilation_source(),
+ &bind_attrib_location_map_);
+
+ if (status == ProgramCache::LINK_SUCCEEDED) {
+ ProgramCache::ProgramLoadResult success = cache->LoadLinkedProgram(
+ service_id(),
+ attached_shaders_[0],
+ attached_shaders_[1],
+ &bind_attrib_location_map_);
+ link = success != ProgramCache::PROGRAM_LOAD_SUCCESS;
+ }
+
+ if (link) {
+ // compile our shaders if they're pending
+ const int kShaders = ProgramManager::ProgramInfo::kMaxAttachedShaders;
+ for (int i = 0; i < kShaders; ++i) {
+ ShaderManager::ShaderInfo* info = attached_shaders_[i].get();
+ if (!info->source_compiled()) {
+ ShaderTranslator* translator = ShaderIndexToTranslator(
+ i,
+ vertex_translator,
+ fragment_translator);
+ manager_->ForceCompileShader(info->deferred_compilation_source(),
+ attached_shaders_[i],
+ translator,
+ feature_info);
+ CHECK(info->IsValid());
+ }
+ }
+ }
+ }
+
+ if (link) {
+ if (cache && gfx::g_GL_ARB_get_program_binary) {
+ glProgramParameteri(service_id(),
+ PROGRAM_BINARY_RETRIEVABLE_HINT,
+ GL_TRUE);
+ }
+ glLinkProgram(service_id());
+ }
+
GLint success = 0;
glGetProgramiv(service_id(), GL_LINK_STATUS, &success);
if (success == GL_TRUE) {
Update();
+ if (cache && link) {
+ cache->SaveLinkedProgram(service_id(),
+ attached_shaders_[0],
+ attached_shaders_[1],
+ &bind_attrib_location_map_);
+ }
} else {
UpdateLogInfo();
}
@@ -848,13 +995,14 @@ ProgramManager::ProgramInfo::~ProgramInfo() {
}
}
-ProgramManager::ProgramManager()
+
+ProgramManager::ProgramManager(ProgramCache* program_cache)
: program_info_count_(0),
have_context_(true),
disable_workarounds_(
CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableGpuDriverBugWorkarounds)) {
-}
+ switches::kDisableGpuDriverBugWorkarounds)),
+ program_cache_(program_cache) { }
ProgramManager::~ProgramManager() {
DCHECK(program_infos_.empty());
@@ -900,6 +1048,10 @@ bool ProgramManager::GetClientId(GLuint service_id, GLuint* client_id) const {
return false;
}
+ProgramCache* ProgramManager::program_cache() const {
+ return program_cache_;
+}
+
bool ProgramManager::IsOwned(ProgramManager::ProgramInfo* info) {
for (ProgramInfoMap::iterator it = program_infos_.begin();
it != program_infos_.end(); ++it) {
diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h
index 964b933..bf8b348 100644
--- a/gpu/command_buffer/service/program_manager.h
+++ b/gpu/command_buffer/service/program_manager.h
@@ -11,7 +11,6 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
#include "gpu/command_buffer/service/common_decoder.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/shader_manager.h"
@@ -19,6 +18,8 @@
namespace gpu {
namespace gles2 {
+class ProgramCache;
+class FeatureInfo;
// Tracks the Programs.
//
@@ -26,6 +27,8 @@ namespace gles2 {
// need to be shared by multiple GLES2Decoders.
class GPU_EXPORT ProgramManager {
public:
+ typedef std::map<std::string, GLint> LocationMap;
+
// This is used to track which attributes a particular program needs
// so we can verify at glDrawXXX time that every attribute is either disabled
// or if enabled that it points to a valid source.
@@ -150,7 +153,10 @@ class GPU_EXPORT ProgramManager {
bool CanLink() const;
// Performs glLinkProgram and related activities.
- bool Link();
+ bool Link(ShaderManager* manager,
+ ShaderTranslator* vertex_translator,
+ ShaderTranslator* fragment_shader,
+ FeatureInfo* feature_info);
// Performs glValidateProgram and related activities.
void Validate();
@@ -178,12 +184,15 @@ class GPU_EXPORT ProgramManager {
// We only consider the declared attributes in the program.
bool DetectAttribLocationBindingConflicts() const;
+ // Visible for testing
+ const LocationMap& bind_attrib_location_map() const {
+ return bind_attrib_location_map_;
+ }
+
private:
friend class base::RefCounted<ProgramInfo>;
friend class ProgramManager;
- typedef std::map<std::string, GLint> LocationMap;
-
~ProgramInfo();
void set_log_info(const char* str) {
@@ -299,7 +308,7 @@ class GPU_EXPORT ProgramManager {
LocationMap bind_uniform_location_map_;
};
- ProgramManager();
+ explicit ProgramManager(ProgramCache* program_cache);
~ProgramManager();
// Must call before destruction.
@@ -314,6 +323,9 @@ class GPU_EXPORT ProgramManager {
// Gets a client id for a given service id.
bool GetClientId(GLuint service_id, GLuint* client_id) const;
+ // Gets the shader cache
+ ProgramCache* program_cache() const;
+
// Marks a program as deleted. If it is not used the info will be deleted.
void MarkAsDeleted(ShaderManager* shader_manager, ProgramInfo* info);
@@ -334,7 +346,19 @@ class GPU_EXPORT ProgramManager {
static int32 MakeFakeLocation(int32 index, int32 element);
+ // Cache-aware shader compiling. If no cache or if the shader wasn't
+ // previously compiled, ForceCompileShader is called
+ void DoCompileShader(ShaderManager::ShaderInfo* info,
+ ShaderTranslator* translator,
+ FeatureInfo* feature_info);
+
private:
+ // Actually compiles the shader
+ void ForceCompileShader(const std::string* source,
+ ShaderManager::ShaderInfo* info,
+ ShaderTranslator* translator,
+ FeatureInfo* feature_info);
+
void StartTracking(ProgramInfo* info);
void StopTracking(ProgramInfo* info);
@@ -354,6 +378,8 @@ class GPU_EXPORT ProgramManager {
// Used to clear uniforms.
std::vector<uint8> zero_;
+ ProgramCache* program_cache_;
+
void RemoveProgramInfoIfUnused(
ShaderManager* shader_manager, ProgramInfo* info);
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index 8dca84d..e1cc441 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -13,6 +13,7 @@
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/service/common_decoder.h"
+#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/mocks.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -35,7 +36,7 @@ namespace gles2 {
class ProgramManagerTest : public testing::Test {
public:
- ProgramManagerTest() { }
+ ProgramManagerTest() : manager_(NULL) { }
~ProgramManagerTest() {
manager_.Destroy(false);
}
@@ -134,7 +135,7 @@ TEST_F(ProgramManagerTest, ProgramInfo) {
class ProgramManagerWithShaderTest : public testing::Test {
public:
ProgramManagerWithShaderTest()
- : program_info_(NULL) {
+ : manager_(NULL), program_info_(NULL) {
}
~ProgramManagerWithShaderTest() {
@@ -218,7 +219,7 @@ class ProgramManagerWithShaderTest : public testing::Test {
program_info_->AttachShader(&shader_manager_, vertex_shader);
program_info_->AttachShader(&shader_manager_, fragment_shader);
- program_info_->Link();
+ program_info_->Link(NULL, NULL, NULL, NULL);
}
void SetupShader(AttribInfo* attribs, size_t num_attribs,
@@ -251,7 +252,7 @@ class ProgramManagerWithShaderTest : public testing::Test {
SetupShader(kAttribs, kNumAttribs, kUniforms, kNumUniforms,
service_id);
}
- program_info->Link();
+ program_info->Link(NULL, NULL, NULL, NULL);
GLint link_status;
program_info->GetProgramiv(GL_LINK_STATUS, &link_status);
return (static_cast<bool>(link_status) == expected_link_status);
@@ -584,7 +585,7 @@ TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsGLUnderscoreUniform) {
ASSERT_TRUE(program_info != NULL);
EXPECT_TRUE(program_info->AttachShader(&shader_manager_, vshader));
EXPECT_TRUE(program_info->AttachShader(&shader_manager_, fshader));
- program_info->Link();
+ program_info->Link(NULL, NULL, NULL, NULL);
GLint value = 0;
program_info->GetProgramiv(GL_ACTIVE_ATTRIBUTES, &value);
EXPECT_EQ(3, value);
@@ -678,7 +679,7 @@ TEST_F(ProgramManagerWithShaderTest, GLDriverReturnsWrongTypeInfo) {
ASSERT_TRUE(program_info != NULL);
EXPECT_TRUE(program_info->AttachShader(&shader_manager_, vshader));
EXPECT_TRUE(program_info->AttachShader(&shader_manager_, fshader));
- program_info->Link();
+ program_info->Link(NULL, NULL, NULL, NULL);
// Check that we got the good type, not the bad.
// Check Attribs
for (unsigned index = 0; index < kNumAttribs; ++index) {
@@ -997,7 +998,7 @@ TEST_F(ProgramManagerWithShaderTest, ClearWithSamplerTypes) {
const size_t kNumUniforms = arraysize(kUniforms);
SetupShader(kAttribs, kNumAttribs, kUniforms, kNumUniforms,
kServiceProgramId);
- program_info->Link();
+ program_info->Link(NULL, NULL, NULL, NULL);
SetupExpectationsForClearingUniforms(kUniforms, kNumUniforms);
manager_.ClearUniforms(program_info);
}
@@ -1069,7 +1070,7 @@ TEST_F(ProgramManagerWithShaderTest, BindUniformLocation) {
const size_t kNumUniforms = arraysize(kUniforms);
SetupShader(kAttribs, kNumAttribs, kUniforms, kNumUniforms,
kServiceProgramId);
- program_info->Link();
+ program_info->Link(NULL, NULL, NULL, NULL);
EXPECT_EQ(kUniform1DesiredLocation,
program_info->GetUniformFakeLocation(kUniform1Name));
@@ -1079,7 +1080,380 @@ TEST_F(ProgramManagerWithShaderTest, BindUniformLocation) {
program_info->GetUniformFakeLocation(kUniform3GoodName));
}
-} // namespace gles2
-} // namespace gpu
+class ProgramManagerWithCacheTest : public testing::Test {
+ public:
+ static const GLuint kClientProgramId = 1;
+ static const GLuint kServiceProgramId = 10;
+ static const GLuint kVertexShaderClientId = 2;
+ static const GLuint kFragmentShaderClientId = 20;
+ static const GLuint kVertexShaderServiceId = 3;
+ static const GLuint kFragmentShaderServiceId = 30;
+
+ ProgramManagerWithCacheTest()
+ : cache_(new MockProgramCache()),
+ manager_(cache_.get()),
+ vertex_shader_(NULL),
+ fragment_shader_(NULL),
+ program_info_(NULL) {
+ }
+ ~ProgramManagerWithCacheTest() {
+ manager_.Destroy(false);
+ shader_manager_.Destroy(false);
+ }
+
+ protected:
+ virtual void SetUp() {
+ gl_.reset(new StrictMock<gfx::MockGLInterface>());
+ ::gfx::GLInterface::SetGLInterface(gl_.get());
+
+ vertex_shader_ = shader_manager_.CreateShaderInfo(
+ kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER);
+ fragment_shader_ = shader_manager_.CreateShaderInfo(
+ kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER);
+ ASSERT_TRUE(vertex_shader_ != NULL);
+ ASSERT_TRUE(fragment_shader_ != NULL);
+ vertex_shader_->UpdateSource("lka asjf bjajsdfj");
+ fragment_shader_->UpdateSource("lka asjf a fasgag 3rdsf3 bjajsdfj");
+
+ program_info_ = manager_.CreateProgramInfo(
+ kClientProgramId, kServiceProgramId);
+ ASSERT_TRUE(program_info_ != NULL);
+
+ program_info_->AttachShader(&shader_manager_, vertex_shader_);
+ program_info_->AttachShader(&shader_manager_, fragment_shader_);
+ }
+
+ virtual void TearDown() {
+ ::gfx::GLInterface::SetGLInterface(NULL);
+ }
+
+ void SetShadersCompiled() {
+ cache_->ShaderCompilationSucceeded(*vertex_shader_->source());
+ cache_->ShaderCompilationSucceeded(*fragment_shader_->source());
+ vertex_shader_->SetStatus(true, NULL, NULL);
+ fragment_shader_->SetStatus(true, NULL, NULL);
+ vertex_shader_->FlagSourceAsCompiled(true);
+ fragment_shader_->FlagSourceAsCompiled(true);
+ }
+
+ void SetShadersNotCompiledButCached() {
+ SetShadersCompiled();
+ vertex_shader_->FlagSourceAsCompiled(false);
+ fragment_shader_->FlagSourceAsCompiled(false);
+ }
+
+ void SetProgramCached() {
+ cache_->LinkedProgramCacheSuccess(
+ vertex_shader_->source()->c_str(),
+ fragment_shader_->source()->c_str(),
+ &program_info_->bind_attrib_location_map());
+ }
+
+ void SetExpectationsForProgramCached() {
+ SetExpectationsForProgramCached(program_info_,
+ vertex_shader_,
+ fragment_shader_);
+ }
+
+ void SetExpectationsForProgramCached(
+ ProgramManager::ProgramInfo* program_info,
+ ShaderManager::ShaderInfo* vertex_shader,
+ ShaderManager::ShaderInfo* fragment_shader) {
+ EXPECT_CALL(*cache_.get(), SaveLinkedProgram(
+ program_info->service_id(),
+ vertex_shader,
+ fragment_shader,
+ &program_info->bind_attrib_location_map())).Times(1);
+ }
+
+ void SetExpectationsForNotCachingProgram() {
+ SetExpectationsForNotCachingProgram(program_info_,
+ vertex_shader_,
+ fragment_shader_);
+ }
+
+ void SetExpectationsForNotCachingProgram(
+ ProgramManager::ProgramInfo* program_info,
+ ShaderManager::ShaderInfo* vertex_shader,
+ ShaderManager::ShaderInfo* fragment_shader) {
+ EXPECT_CALL(*cache_.get(), SaveLinkedProgram(
+ program_info->service_id(),
+ vertex_shader,
+ fragment_shader,
+ &program_info->bind_attrib_location_map())).Times(0);
+ }
+
+ void SetExpectationsForProgramLoad(ProgramCache::ProgramLoadResult result) {
+ SetExpectationsForProgramLoad(kServiceProgramId,
+ program_info_,
+ vertex_shader_,
+ fragment_shader_,
+ result);
+ }
+
+ void SetExpectationsForProgramLoad(
+ GLuint service_program_id,
+ ProgramManager::ProgramInfo* program_info,
+ ShaderManager::ShaderInfo* vertex_shader,
+ ShaderManager::ShaderInfo* fragment_shader,
+ ProgramCache::ProgramLoadResult result) {
+ EXPECT_CALL(*cache_.get(),
+ LoadLinkedProgram(service_program_id,
+ vertex_shader,
+ fragment_shader,
+ &program_info->bind_attrib_location_map()))
+ .WillOnce(Return(result));
+ }
+
+ void SetExpectationsForProgramLoadSuccess() {
+ SetExpectationsForProgramLoadSuccess(kServiceProgramId);
+ }
+
+ void SetExpectationsForProgramLoadSuccess(GLuint service_program_id) {
+ TestHelper::SetupProgramSuccessExpectations(gl_.get(),
+ NULL,
+ 0,
+ NULL,
+ 0,
+ service_program_id);
+ }
+
+ void SetExpectationsForProgramLink() {
+ SetExpectationsForProgramLink(kServiceProgramId);
+ }
+
+ void SetExpectationsForProgramLink(GLuint service_program_id) {
+ TestHelper::SetupShader(gl_.get(), NULL, 0, NULL, 0, service_program_id);
+ if (gfx::g_GL_ARB_get_program_binary) {
+ EXPECT_CALL(*gl_.get(),
+ ProgramParameteri(service_program_id,
+ PROGRAM_BINARY_RETRIEVABLE_HINT,
+ GL_TRUE)).Times(1);
+ }
+ }
+
+ void SetExpectationsForSuccessCompile(
+ const ShaderManager::ShaderInfo* shader) {
+ const GLuint shader_id = shader->service_id();
+ const char* src = shader->source()->c_str();
+ EXPECT_CALL(*gl_.get(),
+ ShaderSource(shader_id, 1, Pointee(src), NULL)).Times(1);
+ EXPECT_CALL(*gl_.get(), CompileShader(shader_id)).Times(1);
+ EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_COMPILE_STATUS, _))
+ .WillOnce(SetArgumentPointee<2>(GL_TRUE));
+ }
+
+ void SetExpectationsForNoCompile(const ShaderManager::ShaderInfo* shader) {
+ const GLuint shader_id = shader->service_id();
+ const char* src = shader->source()->c_str();
+ EXPECT_CALL(*gl_.get(),
+ ShaderSource(shader_id, 1, Pointee(src), NULL)).Times(0);
+ EXPECT_CALL(*gl_.get(), CompileShader(shader_id)).Times(0);
+ EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_COMPILE_STATUS, _))
+ .Times(0);
+ }
+
+ void SetExpectationsForErrorCompile(const ShaderManager::ShaderInfo* shader) {
+ const GLuint shader_id = shader->service_id();
+ const char* src = shader->source()->c_str();
+ EXPECT_CALL(*gl_.get(),
+ ShaderSource(shader_id, 1, Pointee(src), NULL)).Times(1);
+ EXPECT_CALL(*gl_.get(), CompileShader(shader_id)).Times(1);
+ EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_COMPILE_STATUS, _))
+ .WillOnce(SetArgumentPointee<2>(GL_FALSE));
+ EXPECT_CALL(*gl_.get(), GetShaderiv(shader_id, GL_INFO_LOG_LENGTH, _))
+ .WillOnce(SetArgumentPointee<2>(0));
+ EXPECT_CALL(*gl_.get(), GetShaderInfoLog(shader_id, 0, _, _))
+ .Times(1);
+ }
+
+ scoped_ptr<StrictMock<gfx::MockGLInterface> > gl_;
+
+ scoped_ptr<MockProgramCache> cache_;
+ ProgramManager manager_;
+
+ ShaderManager::ShaderInfo* vertex_shader_;
+ ShaderManager::ShaderInfo* fragment_shader_;
+ ProgramManager::ProgramInfo* program_info_;
+ ShaderManager shader_manager_;
+};
+
+// GCC requires these declarations, but MSVC requires they not be present
+#ifndef COMPILER_MSVC
+const GLuint ProgramManagerWithCacheTest::kClientProgramId;
+const GLuint ProgramManagerWithCacheTest::kServiceProgramId;
+const GLuint ProgramManagerWithCacheTest::kVertexShaderClientId;
+const GLuint ProgramManagerWithCacheTest::kFragmentShaderClientId;
+const GLuint ProgramManagerWithCacheTest::kVertexShaderServiceId;
+const GLuint ProgramManagerWithCacheTest::kFragmentShaderServiceId;
+#endif
+
+TEST_F(ProgramManagerWithCacheTest, CacheSuccessAfterShaderCompile) {
+ SetExpectationsForSuccessCompile(vertex_shader_);
+ FeatureInfo::Ref info(new FeatureInfo());
+ manager_.DoCompileShader(vertex_shader_, NULL, info.get());
+ EXPECT_EQ(ProgramCache::COMPILATION_SUCCEEDED,
+ cache_->GetShaderCompilationStatus(*vertex_shader_->source()));
+}
+
+TEST_F(ProgramManagerWithCacheTest, CacheUnknownAfterShaderError) {
+ SetExpectationsForErrorCompile(vertex_shader_);
+ FeatureInfo::Ref info(new FeatureInfo());
+ manager_.DoCompileShader(vertex_shader_, NULL, info.get());
+ EXPECT_EQ(ProgramCache::COMPILATION_UNKNOWN,
+ cache_->GetShaderCompilationStatus(*vertex_shader_->source()));
+}
+
+TEST_F(ProgramManagerWithCacheTest, NoCompileWhenShaderCached) {
+ cache_->ShaderCompilationSucceeded(vertex_shader_->source()->c_str());
+ SetExpectationsForNoCompile(vertex_shader_);
+ FeatureInfo::Ref info(new FeatureInfo());
+ manager_.DoCompileShader(vertex_shader_, NULL, info.get());
+}
+
+TEST_F(ProgramManagerWithCacheTest, CacheProgramOnSuccessfulLink) {
+ SetShadersCompiled();
+ SetExpectationsForProgramLink();
+ SetExpectationsForProgramCached();
+ EXPECT_TRUE(program_info_->Link(NULL, NULL, NULL, NULL));
+}
+
+TEST_F(ProgramManagerWithCacheTest, CompileShaderOnLinkCacheMiss) {
+ SetShadersCompiled();
+ vertex_shader_->FlagSourceAsCompiled(false);
+
+ FeatureInfo::Ref info(new FeatureInfo());
+
+ SetExpectationsForSuccessCompile(vertex_shader_);
+ SetExpectationsForProgramLink();
+ SetExpectationsForProgramCached();
+ EXPECT_TRUE(program_info_->Link(&shader_manager_, NULL, NULL, info.get()));
+}
+
+TEST_F(ProgramManagerWithCacheTest, LoadProgramOnProgramCacheHit) {
+ SetShadersNotCompiledButCached();
+ SetProgramCached();
+
+ SetExpectationsForNoCompile(vertex_shader_);
+ SetExpectationsForNoCompile(fragment_shader_);
+ SetExpectationsForProgramLoad(ProgramCache::PROGRAM_LOAD_SUCCESS);
+ SetExpectationsForNotCachingProgram();
+ SetExpectationsForProgramLoadSuccess();
+
+ EXPECT_TRUE(program_info_->Link(NULL, NULL, NULL, NULL));
+}
+
+TEST_F(ProgramManagerWithCacheTest, CompileAndLinkOnProgramCacheError) {
+ SetShadersNotCompiledButCached();
+ SetProgramCached();
+
+ SetExpectationsForSuccessCompile(vertex_shader_);
+ SetExpectationsForSuccessCompile(fragment_shader_);
+ SetExpectationsForProgramLoad(ProgramCache::PROGRAM_LOAD_FAILURE);
+ SetExpectationsForProgramLink();
+ SetExpectationsForProgramCached();
+
+ FeatureInfo::Ref info(new FeatureInfo());
+ EXPECT_TRUE(program_info_->Link(&shader_manager_, NULL, NULL, info.get()));
+}
+
+TEST_F(ProgramManagerWithCacheTest, CorrectCompileOnSourceChangeNoCompile) {
+ SetShadersNotCompiledButCached();
+ SetProgramCached();
+ const GLuint kNewShaderClientId = 4;
+ const GLuint kNewShaderServiceId = 40;
+ const GLuint kNewProgramClientId = 5;
+ const GLuint kNewProgramServiceId = 50;
+ ShaderManager::ShaderInfo* new_vertex_shader =
+ shader_manager_.CreateShaderInfo(kNewShaderClientId,
+ kNewShaderServiceId,
+ GL_VERTEX_SHADER);
+
+ const std::string original_source = *vertex_shader_->source();
+ new_vertex_shader->UpdateSource(original_source.c_str());
+
+ ProgramManager::ProgramInfo* program_info = manager_.CreateProgramInfo(
+ kNewProgramClientId, kNewProgramServiceId);
+ ASSERT_TRUE(program_info != NULL);
+ program_info->AttachShader(&shader_manager_, new_vertex_shader);
+ program_info->AttachShader(&shader_manager_, fragment_shader_);
+
+ SetExpectationsForNoCompile(new_vertex_shader);
+
+ manager_.DoCompileShader(new_vertex_shader, NULL, NULL);
+
+ new_vertex_shader->UpdateSource("different!");
+ EXPECT_EQ(original_source,
+ *new_vertex_shader->deferred_compilation_source());
+
+ EXPECT_FALSE(new_vertex_shader->source_compiled());
+ EXPECT_FALSE(fragment_shader_->source_compiled());
+
+ SetExpectationsForNoCompile(fragment_shader_);
+ SetExpectationsForNotCachingProgram(program_info,
+ new_vertex_shader,
+ fragment_shader_);
+ SetExpectationsForProgramLoad(kNewProgramServiceId,
+ program_info,
+ new_vertex_shader,
+ fragment_shader_,
+ ProgramCache::PROGRAM_LOAD_SUCCESS);
+ SetExpectationsForProgramLoadSuccess(kNewProgramServiceId);
+
+ FeatureInfo::Ref info(new FeatureInfo());
+ EXPECT_TRUE(program_info->Link(&shader_manager_, NULL, NULL, info.get()));
+}
+
+TEST_F(ProgramManagerWithCacheTest, CorrectCompileOnSourceChangeWithCompile) {
+ SetShadersNotCompiledButCached();
+ SetProgramCached();
+
+ const GLuint kNewShaderClientId = 4;
+ const GLuint kNewShaderServiceId = 40;
+ const GLuint kNewProgramClientId = 5;
+ const GLuint kNewProgramServiceId = 50;
+
+ ShaderManager::ShaderInfo* new_vertex_shader =
+ shader_manager_.CreateShaderInfo(kNewShaderClientId,
+ kNewShaderServiceId,
+ GL_VERTEX_SHADER);
+
+ new_vertex_shader->UpdateSource(vertex_shader_->source()->c_str());
+
+ ProgramManager::ProgramInfo* program_info = manager_.CreateProgramInfo(
+ kNewProgramClientId, kNewProgramServiceId);
+ ASSERT_TRUE(program_info != NULL);
+ program_info->AttachShader(&shader_manager_, new_vertex_shader);
+ program_info->AttachShader(&shader_manager_, fragment_shader_);
+
+ SetExpectationsForNoCompile(new_vertex_shader);
+
+ manager_.DoCompileShader(new_vertex_shader, NULL, NULL);
+
+ const std::string differentSource = "different!";
+ new_vertex_shader->UpdateSource(differentSource.c_str());
+ SetExpectationsForSuccessCompile(new_vertex_shader);
+
+ FeatureInfo::Ref info(new FeatureInfo());
+ manager_.DoCompileShader(new_vertex_shader, NULL, info.get());
+ EXPECT_EQ(differentSource,
+ *new_vertex_shader->deferred_compilation_source());
+
+ EXPECT_TRUE(new_vertex_shader->source_compiled());
+ EXPECT_FALSE(fragment_shader_->source_compiled());
+
+ // so we don't recompile because we were pending originally
+ SetExpectationsForNoCompile(new_vertex_shader);
+ SetExpectationsForSuccessCompile(fragment_shader_);
+ SetExpectationsForProgramCached(program_info,
+ new_vertex_shader,
+ fragment_shader_);
+ SetExpectationsForProgramLink(kNewProgramServiceId);
+
+ EXPECT_TRUE(program_info->Link(&shader_manager_, NULL, NULL, info.get()));
+}
+
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/shader_manager.cc b/gpu/command_buffer/service/shader_manager.cc
index 0d30f52..6a6c9ef 100644
--- a/gpu/command_buffer/service/shader_manager.cc
+++ b/gpu/command_buffer/service/shader_manager.cc
@@ -3,6 +3,9 @@
// found in the LICENSE file.
#include "gpu/command_buffer/service/shader_manager.h"
+
+#include <utility>
+
#include "base/logging.h"
#include "base/string_util.h"
@@ -13,7 +16,8 @@ ShaderManager::ShaderInfo::ShaderInfo(GLuint service_id, GLenum shader_type)
: use_count_(0),
service_id_(service_id),
shader_type_(shader_type),
- valid_(false) {
+ valid_(false),
+ source_compiled_(false) {
}
ShaderManager::ShaderInfo::~ShaderInfo() {
diff --git a/gpu/command_buffer/service/shader_manager.h b/gpu/command_buffer/service/shader_manager.h
index 9b6fad2..448dc5a 100644
--- a/gpu/command_buffer/service/shader_manager.h
+++ b/gpu/command_buffer/service/shader_manager.h
@@ -34,6 +34,11 @@ class GPU_EXPORT ShaderManager {
typedef ShaderTranslator::VariableInfo VariableInfo;
void UpdateSource(const char* source) {
+ // If the source is flagged as compiled, then store our previous source
+ // for deferred compile and caching.
+ if (!deferred_compilation_source_.get()) {
+ deferred_compilation_source_.reset(source_.release());
+ }
source_.reset(source ? new std::string(source) : NULL);
translated_source_.reset(NULL);
}
@@ -63,6 +68,26 @@ class GPU_EXPORT ShaderManager {
bool valid, const char* log,
ShaderTranslatorInterface* translator);
+ // If the source was actually compiled (compilation wasn't deferred)
+ bool source_compiled() const {
+ return source_compiled_;
+ }
+
+ // The source that was used when the user called CompileShader.
+ // This is used for a deferred compile and in the program cache
+ const std::string* deferred_compilation_source() const {
+ return deferred_compilation_source_.get() != NULL ?
+ deferred_compilation_source_.get() :
+ source_.get();
+ }
+
+ // Resets our deferred compilation source and stores if the source was
+ // actually compiled, or if we're expecting a cache hit
+ void FlagSourceAsCompiled(bool actually_compiled) {
+ source_compiled_ = actually_compiled;
+ deferred_compilation_source_.reset();
+ }
+
const VariableInfo* GetAttribInfo(const std::string& name) const;
const VariableInfo* GetUniformInfo(const std::string& name) const;
@@ -87,6 +112,28 @@ class GPU_EXPORT ShaderManager {
return use_count_ != 0;
}
+ // Used by program cache.
+ const ShaderTranslator::VariableMap& attrib_map() const {
+ return attrib_map_;
+ }
+
+ // Used by program cache.
+ const ShaderTranslator::VariableMap& uniform_map() const {
+ return uniform_map_;
+ }
+
+ // Used by program cache.
+ void set_attrib_map(const ShaderTranslator::VariableMap& attrib_map) {
+ // copied because cache might be cleared
+ attrib_map_ = ShaderTranslator::VariableMap(attrib_map);
+ }
+
+ // Used by program cache.
+ void set_uniform_map(const ShaderTranslator::VariableMap& uniform_map) {
+ // copied because cache might be cleared
+ uniform_map_ = ShaderTranslator::VariableMap(uniform_map);
+ }
+
private:
typedef ShaderTranslator::VariableMap VariableMap;
@@ -122,6 +169,13 @@ class GPU_EXPORT ShaderManager {
// The type info when the shader was last compiled.
VariableMap attrib_map_;
VariableMap uniform_map_;
+
+ // If the source was actually compiled (otherwise we're deferring
+ // compilation because of a possible cache hit)
+ bool source_compiled_;
+
+ // Holds on to the source for a deferred compile.
+ scoped_ptr<std::string> deferred_compilation_source_;
};
ShaderManager();
diff --git a/gpu/command_buffer/service/shader_manager_unittest.cc b/gpu/command_buffer/service/shader_manager_unittest.cc
index f927e26..fb1efe4 100644
--- a/gpu/command_buffer/service/shader_manager_unittest.cc
+++ b/gpu/command_buffer/service/shader_manager_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -245,7 +245,39 @@ TEST_F(ShaderManagerTest, ShaderInfoUseCount) {
EXPECT_TRUE(info2 == NULL);
}
-} // namespace gles2
-} // namespace gpu
+TEST_F(ShaderManagerTest, ShaderInfoStoreCompilationStatus) {
+ const GLuint kClientId = 1;
+ const GLuint kServiceId = 11;
+ const GLenum kShaderType = GL_VERTEX_SHADER;
+ ShaderManager::ShaderInfo* info = manager_.CreateShaderInfo(
+ kClientId, kServiceId, kShaderType);
+ ASSERT_TRUE(info != NULL);
+
+ info->UpdateSource("original source");
+ info->FlagSourceAsCompiled(false);
+ EXPECT_FALSE(info->source_compiled());
+ info->FlagSourceAsCompiled(true);
+ EXPECT_TRUE(info->source_compiled());
+}
+
+TEST_F(ShaderManagerTest, ShaderInfoStoreDeferredSource) {
+ const GLuint kClientId = 1;
+ const GLuint kServiceId = 11;
+ const GLenum kShaderType = GL_VERTEX_SHADER;
+ ShaderManager::ShaderInfo* info = manager_.CreateShaderInfo(
+ kClientId, kServiceId, kShaderType);
+ ASSERT_TRUE(info != NULL);
+
+ info->UpdateSource("original source");
+ info->FlagSourceAsCompiled(false);
+
+ EXPECT_EQ("original source", *info->deferred_compilation_source());
+ info->UpdateSource("different!");
+ EXPECT_EQ("original source", *info->deferred_compilation_source());
+ info->FlagSourceAsCompiled(true);
+ EXPECT_EQ("different!", *info->deferred_compilation_source());
+}
+} // namespace gles2
+} // namespace gpu
diff --git a/gpu/command_buffer/service/shader_translator.h b/gpu/command_buffer/service/shader_translator.h
index f82343e..be5b1f6 100644
--- a/gpu/command_buffer/service/shader_translator.h
+++ b/gpu/command_buffer/service/shader_translator.h
@@ -43,6 +43,12 @@ class ShaderTranslatorInterface {
size(_size),
name(_name) {
}
+ bool operator==(
+ const ShaderTranslatorInterface::VariableInfo& other) const {
+ return type == other.type &&
+ size == other.size &&
+ strcmp(name.c_str(), other.name.c_str()) == 0;
+ }
int type;
int size;
diff --git a/gpu/command_buffer/service/test_helper.cc b/gpu/command_buffer/service/test_helper.cc
index bd95c4f7..86eab65 100644
--- a/gpu/command_buffer/service/test_helper.cc
+++ b/gpu/command_buffer/service/test_helper.cc
@@ -4,6 +4,9 @@
#include "gpu/command_buffer/service/test_helper.h"
+#include <string>
+#include <algorithm>
+
#include "base/string_number_conversions.h"
#include "base/string_tokenizer.h"
#include "gpu/command_buffer/common/gl_mock.h"
@@ -12,8 +15,6 @@
#include "gpu/command_buffer/service/program_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include <string.h>
-
using ::testing::_;
using ::testing::DoAll;
using ::testing::InSequence;
@@ -368,17 +369,11 @@ void TestHelper::SetupExpectationsForClearingUniforms(
}
}
-void TestHelper::SetupShader(
+void TestHelper::SetupProgramSuccessExpectations(
::gfx::MockGLInterface* gl,
AttribInfo* attribs, size_t num_attribs,
UniformInfo* uniforms, size_t num_uniforms,
GLuint service_id) {
- InSequence s;
-
- EXPECT_CALL(*gl,
- LinkProgram(service_id))
- .Times(1)
- .RetiresOnSaturation();
EXPECT_CALL(*gl,
GetProgramiv(service_id, GL_LINK_STATUS, _))
.WillOnce(SetArgumentPointee<2>(1))
@@ -480,6 +475,22 @@ void TestHelper::SetupShader(
}
}
+void TestHelper::SetupShader(
+ ::gfx::MockGLInterface* gl,
+ AttribInfo* attribs, size_t num_attribs,
+ UniformInfo* uniforms, size_t num_uniforms,
+ GLuint service_id) {
+ InSequence s;
+
+ EXPECT_CALL(*gl,
+ LinkProgram(service_id))
+ .Times(1)
+ .RetiresOnSaturation();
+
+ SetupProgramSuccessExpectations(
+ gl, attribs, num_attribs, uniforms, num_uniforms, service_id);
+}
+
} // namespace gles2
} // namespace gpu
diff --git a/gpu/command_buffer/service/test_helper.h b/gpu/command_buffer/service/test_helper.h
index c0dd80f..9fc4550 100644
--- a/gpu/command_buffer/service/test_helper.h
+++ b/gpu/command_buffer/service/test_helper.h
@@ -81,6 +81,11 @@ class TestHelper {
UniformInfo* uniforms, size_t num_uniforms,
GLuint service_id);
+ static void SetupProgramSuccessExpectations(::gfx::MockGLInterface* gl,
+ AttribInfo* attribs, size_t num_attribs,
+ UniformInfo* uniforms, size_t num_uniforms,
+ GLuint service_id);
+
private:
static void SetupTextureInitializationExpectations(
::gfx::MockGLInterface* gl, GLenum target);
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 14db773..4fad23e 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "gpu/command_buffer/tests/gl_manager.h"
+
+#include <vector>
+
#include "base/at_exit.h"
#include "base/bind.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
@@ -12,7 +16,6 @@
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/gpu_scheduler.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
-#include "gpu/command_buffer/tests/gl_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_share_group.h"
@@ -94,8 +97,8 @@ void GLManager::Setup(
attribs.push_back(EGL_NONE);
if (!context_group) {
- context_group = new gles2::ContextGroup(
- mailbox_manager_.get(), kBindGeneratesResource);
+ context_group = new gles2::ContextGroup(mailbox_manager_.get(),
+ kBindGeneratesResource);
}
decoder_.reset(::gpu::gles2::GLES2Decoder::Create(context_group));
diff --git a/gpu/command_buffer_service.gypi b/gpu/command_buffer_service.gypi
index 4fbd06f..46ffcf0 100644
--- a/gpu/command_buffer_service.gypi
+++ b/gpu/command_buffer_service.gypi
@@ -57,6 +57,8 @@
'command_buffer/service/id_manager.cc',
'command_buffer/service/mailbox_manager.cc',
'command_buffer/service/mailbox_manager.h',
+ 'command_buffer/service/memory_program_cache.h',
+ 'command_buffer/service/memory_program_cache.cc',
'command_buffer/service/mocks.h',
'command_buffer/service/program_manager.h',
'command_buffer/service/program_manager.cc',
@@ -64,6 +66,10 @@
'command_buffer/service/query_manager.cc',
'command_buffer/service/renderbuffer_manager.h',
'command_buffer/service/renderbuffer_manager.cc',
+ 'command_buffer/service/program_cache.h',
+ 'command_buffer/service/program_cache.cc',
+ 'command_buffer/service/program_cache_lru_helper.h',
+ 'command_buffer/service/program_cache_lru_helper.cc',
'command_buffer/service/shader_manager.h',
'command_buffer/service/shader_manager.cc',
'command_buffer/service/shader_translator.h',
diff --git a/gpu/demos/framework/window.cc b/gpu/demos/framework/window.cc
index d30e427..1421aae 100644
--- a/gpu/demos/framework/window.cc
+++ b/gpu/demos/framework/window.cc
@@ -4,6 +4,8 @@
#include "gpu/demos/framework/window.h"
+#include <vector>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/ref_counted.h"
diff --git a/gpu/gpu_common.gypi b/gpu/gpu_common.gypi
index d117fc0..2546635 100644
--- a/gpu/gpu_common.gypi
+++ b/gpu/gpu_common.gypi
@@ -180,11 +180,14 @@
'command_buffer/service/gl_surface_mock.h',
'command_buffer/service/gpu_scheduler_unittest.cc',
'command_buffer/service/id_manager_unittest.cc',
+ 'command_buffer/service/memory_program_cache_unittest.cc',
'command_buffer/service/mocks.cc',
'command_buffer/service/mocks.h',
'command_buffer/service/program_manager_unittest.cc',
'command_buffer/service/query_manager_unittest.cc',
'command_buffer/service/renderbuffer_manager_unittest.cc',
+ 'command_buffer/service/program_cache_lru_helper_unittest.cc',
+ 'command_buffer/service/program_cache_unittest.cc',
'command_buffer/service/shader_manager_unittest.cc',
'command_buffer/service/shader_translator_unittest.cc',
'command_buffer/service/stream_texture_mock.cc',
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 5e7eccd..11ccff2 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -296,6 +296,12 @@ GL_FUNCTIONS = [
'names': ['glGetIntegerv'],
'arguments': 'GLenum pname, GLint* params', },
{ 'return_type': 'void',
+ 'names': ['glGetProgramBinary', 'glGetProgramBinaryOES'],
+ 'arguments': 'GLuint program, GLsizei bufSize, GLsizei* length, '
+ 'GLenum* binaryFormat, GLvoid* binary',
+ 'other_extensions': ['ARB_get_program_binary',
+ 'OES_get_program_binary'] },
+{ 'return_type': 'void',
'names': ['glGetProgramiv'],
'arguments': 'GLuint program, GLenum pname, GLint* params', },
{ 'return_type': 'void',
@@ -424,6 +430,16 @@ GL_FUNCTIONS = [
'names': ['glPolygonOffset'],
'arguments': 'GLfloat factor, GLfloat units', },
{ 'return_type': 'void',
+ 'names': ['glProgramBinary', 'glProgramBinaryOES'],
+ 'arguments': 'GLuint program, GLenum binaryFormat, '
+ 'const GLvoid* binary, GLsizei length',
+ 'other_extensions': ['ARB_get_program_binary',
+ 'OES_get_program_binary'] },
+{ 'return_type': 'void',
+ 'names': ['glProgramParameteri'],
+ 'arguments': 'GLuint program, GLenum pname, GLint value',
+ 'other_extensions': ['ARB_get_program_binary'] },
+{ 'return_type': 'void',
'names': ['glQueryCounter'],
'arguments': 'GLuint id, GLenum target', },
{ 'return_type': 'void',
diff --git a/ui/gl/gl_interface.h b/ui/gl/gl_interface.h
index af150de..6c6df36 100644
--- a/ui/gl/gl_interface.h
+++ b/ui/gl/gl_interface.h
@@ -272,6 +272,12 @@ class GL_EXPORT GLInterface {
virtual void GetIntegerv(GLenum pname, GLint* params) = 0;
+ virtual void GetProgramBinary(GLuint program,
+ GLsizei bufSize,
+ GLsizei* length,
+ GLenum* binaryFormat,
+ GLvoid* binary) = 0;
+
virtual void GetProgramiv(GLuint program, GLenum pname, GLint* params) = 0;
// TODO(gman): Implement this
@@ -398,6 +404,13 @@ class GL_EXPORT GLInterface {
virtual void PolygonOffset(GLfloat factor, GLfloat units) = 0;
+ virtual void ProgramBinary(GLuint program,
+ GLenum binaryFormat,
+ const GLvoid* binary,
+ GLsizei length) = 0;
+
+ virtual void ProgramParameteri(GLuint program, GLenum pname, GLint value) = 0;
+
virtual void QueryCounter(GLuint id, GLenum target) = 0;
virtual void ReadBuffer(GLenum src) = 0;
diff --git a/webkit/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc b/webkit/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc
index b7e93df..06ad53f 100644
--- a/webkit/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc
+++ b/webkit/gpu/webgraphicscontext3d_in_process_command_buffer_impl.cc
@@ -12,6 +12,7 @@
#include <algorithm>
#include <set>
+#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"