summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/service/memory_program_cache_unittest.cc
diff options
context:
space:
mode:
authordmurph@chromium.org <dmurph@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-23 20:39:39 +0000
committerdmurph@chromium.org <dmurph@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-23 20:39:39 +0000
commit65dfc60d7035875767bcddf62b9201f16126222a (patch)
tree8de0b9f97b48112b08b544d142ca7a46ca016768 /gpu/command_buffer/service/memory_program_cache_unittest.cc
parent51383d802b69967b462bf2eabd1d7b1ee879d7f7 (diff)
downloadchromium_src-65dfc60d7035875767bcddf62b9201f16126222a.zip
chromium_src-65dfc60d7035875767bcddf62b9201f16126222a.tar.gz
chromium_src-65dfc60d7035875767bcddf62b9201f16126222a.tar.bz2
gpu in-memory program cache implementation with a memory limit + lru eviction.
Wiring: - Added bindings for glProgramBinary, glGetProgramBinary, glProgramParameteri - Plumbed the shader cache from gl_channel_manager to program_manager - Program cache creation after first context is created Refactoring: - moved DoCompile to ProgramManager New: - added functionality to ShaderInfo to store if we have a possible pending cache compile - exposed attrib_map and uniform_map in ShaderInfo for the cache - program_cache base class with in-memory status storage - Simple memory_program_cache implementation, stores programs with lru eviction - Added caching logic to DoCompileShader and Link in ProgramMAnager - MemoryProgramCache, the in-memory cache implementation - ProgramCacheLruHelper, an O(1) lru implementation Misc: - A couple style fixes in modified files Design doc: https://docs.google.com/document/d/1Vceem-nF4TCICoeGSh7OMXxfGuJEJYblGXRgN9V9hcE/edit BUG=88572 Review URL: https://chromiumcodereview.appspot.com/10797055 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147932 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer/service/memory_program_cache_unittest.cc')
-rw-r--r--gpu/command_buffer/service/memory_program_cache_unittest.cc417
1 files changed, 417 insertions, 0 deletions
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