diff options
author | dmurph@chromium.org <dmurph@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-23 20:39:39 +0000 |
---|---|---|
committer | dmurph@chromium.org <dmurph@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-23 20:39:39 +0000 |
commit | 65dfc60d7035875767bcddf62b9201f16126222a (patch) | |
tree | 8de0b9f97b48112b08b544d142ca7a46ca016768 /gpu/command_buffer/service/memory_program_cache_unittest.cc | |
parent | 51383d802b69967b462bf2eabd1d7b1ee879d7f7 (diff) | |
download | chromium_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.cc | 417 |
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 |