// 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/bind.h" #include "gpu/command_buffer/common/gles2_cmd_format.h" #include "gpu/command_buffer/service/gl_utils.h" #include "gpu/command_buffer/service/gpu_service_test.h" #include "gpu/command_buffer/service/shader_manager.h" #include "gpu/command_buffer/service/shader_translator.h" #include "gpu/command_buffer/service/test_helper.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_mock.h" using ::testing::_; using ::testing::Invoke; using ::testing::SetArgPointee; 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) { if (length) { *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 GpuServiceTest { 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), shader_cache_count_(0) { } ~MemoryProgramCacheTest() override { shader_manager_.Destroy(false); } void ShaderCacheCb(const std::string& key, const std::string& shader) { shader_cache_count_++; shader_cache_shader_ = shader; } int32 shader_cache_count() { return shader_cache_count_; } const std::string& shader_cache_shader() { return shader_cache_shader_; } protected: void SetUp() override { GpuServiceTest::SetUpWithGLVersion("3.0", "GL_ARB_get_program_binary"); vertex_shader_ = shader_manager_.CreateShader(kVertexShaderClientId, kVertexShaderServiceId, GL_VERTEX_SHADER); fragment_shader_ = shader_manager_.CreateShader( kFragmentShaderClientId, kFragmentShaderServiceId, GL_FRAGMENT_SHADER); ASSERT_TRUE(vertex_shader_ != NULL); ASSERT_TRUE(fragment_shader_ != NULL); AttributeMap vertex_attrib_map; UniformMap vertex_uniform_map; VaryingMap vertex_varying_map; AttributeMap fragment_attrib_map; UniformMap fragment_uniform_map; VaryingMap fragment_varying_map; vertex_attrib_map["a"] = TestHelper::ConstructAttribute( GL_FLOAT_VEC2, 34, GL_LOW_FLOAT, false, "a"); vertex_uniform_map["a"] = TestHelper::ConstructUniform( GL_FLOAT, 10, GL_MEDIUM_FLOAT, true, "a"); vertex_uniform_map["b"] = TestHelper::ConstructUniform( GL_FLOAT_VEC3, 3114, GL_HIGH_FLOAT, true, "b"); vertex_varying_map["c"] = TestHelper::ConstructVarying( GL_FLOAT_VEC4, 2, GL_HIGH_FLOAT, true, "c"); fragment_attrib_map["jjjbb"] = TestHelper::ConstructAttribute( GL_FLOAT_MAT4, 1114, GL_MEDIUM_FLOAT, false, "jjjbb"); fragment_uniform_map["k"] = TestHelper::ConstructUniform( GL_FLOAT_MAT2, 34413, GL_MEDIUM_FLOAT, true, "k"); fragment_varying_map["c"] = TestHelper::ConstructVarying( GL_FLOAT_VEC4, 2, GL_HIGH_FLOAT, true, "c"); vertex_shader_->set_source("bbbalsldkdkdkd"); fragment_shader_->set_source("bbbal sldkdkdkas 134 ad"); TestHelper::SetShaderStates( gl_.get(), vertex_shader_, true, NULL, NULL, NULL, &vertex_attrib_map, &vertex_uniform_map, &vertex_varying_map, NULL); TestHelper::SetShaderStates( gl_.get(), fragment_shader_, true, NULL, NULL, NULL, &fragment_attrib_map, &fragment_uniform_map, &fragment_varying_map, NULL); } 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)); } scoped_ptr cache_; ShaderManager shader_manager_; Shader* vertex_shader_; Shader* fragment_shader_; int32 shader_cache_count_; std::string shader_cache_shader_; std::vector varyings_; }; 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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus( vertex_shader_->last_compiled_signature(), fragment_shader_->last_compiled_signature(), NULL, varyings_, GL_NONE)); EXPECT_EQ(1, shader_cache_count()); } TEST_F(MemoryProgramCacheTest, LoadProgram) { const GLenum kFormat = 1; const int kProgramId = 10; const int kBinaryLength = 20; char test_binary[kBinaryLength]; for (int i = 0; i < kBinaryLength; ++i) { test_binary[i] = i; } ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary); SetExpectationsForSaveLinkedProgram(kProgramId, &emulator); cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus( vertex_shader_->last_compiled_signature(), fragment_shader_->last_compiled_signature(), NULL, varyings_, GL_NONE)); EXPECT_EQ(1, shader_cache_count()); cache_->Clear(); cache_->LoadProgram(shader_cache_shader()); EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus( vertex_shader_->last_compiled_signature(), fragment_shader_->last_compiled_signature(), NULL, varyings_, GL_NONE)); } 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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); EXPECT_EQ(1, shader_cache_count()); AttributeMap vertex_attrib_map = vertex_shader_->attrib_map(); UniformMap vertex_uniform_map = vertex_shader_->uniform_map(); VaryingMap vertex_varying_map = vertex_shader_->varying_map(); AttributeMap fragment_attrib_map = fragment_shader_->attrib_map(); UniformMap fragment_uniform_map = fragment_shader_->uniform_map(); VaryingMap fragment_varying_map = fragment_shader_->varying_map(); vertex_shader_->set_attrib_map(AttributeMap()); vertex_shader_->set_uniform_map(UniformMap()); vertex_shader_->set_varying_map(VaryingMap()); fragment_shader_->set_attrib_map(AttributeMap()); fragment_shader_->set_uniform_map(UniformMap()); fragment_shader_->set_varying_map(VaryingMap()); SetExpectationsForLoadLinkedProgram(kProgramId, &emulator); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); // 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_uniform_map, vertex_shader_->uniform_map()); EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map()); EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map()); EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map()); EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_map()); #endif } TEST_F(MemoryProgramCacheTest, LoadProgramMatchesSave) { const GLenum kFormat = 1; const int kProgramId = 10; const int kBinaryLength = 20; char test_binary[kBinaryLength]; for (int i = 0; i < kBinaryLength; ++i) { test_binary[i] = i; } ProgramBinaryEmulator emulator(kBinaryLength, kFormat, test_binary); SetExpectationsForSaveLinkedProgram(kProgramId, &emulator); cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); EXPECT_EQ(1, shader_cache_count()); AttributeMap vertex_attrib_map = vertex_shader_->attrib_map(); UniformMap vertex_uniform_map = vertex_shader_->uniform_map(); VaryingMap vertex_varying_map = vertex_shader_->varying_map(); AttributeMap fragment_attrib_map = fragment_shader_->attrib_map(); UniformMap fragment_uniform_map = fragment_shader_->uniform_map(); VaryingMap fragment_varying_map = fragment_shader_->varying_map(); vertex_shader_->set_attrib_map(AttributeMap()); vertex_shader_->set_uniform_map(UniformMap()); vertex_shader_->set_varying_map(VaryingMap()); fragment_shader_->set_attrib_map(AttributeMap()); fragment_shader_->set_uniform_map(UniformMap()); fragment_shader_->set_varying_map(VaryingMap()); SetExpectationsForLoadLinkedProgram(kProgramId, &emulator); cache_->Clear(); cache_->LoadProgram(shader_cache_shader()); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); // 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_uniform_map, vertex_shader_->uniform_map()); EXPECT_EQ(vertex_varying_map, vertex_shader_->varying_map()); EXPECT_EQ(fragment_attrib_map, fragment_shader_->attrib_map()); EXPECT_EQ(fragment_uniform_map, fragment_shader_->uniform_map()); EXPECT_EQ(fragment_varying_map, fragment_shader_->varying_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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); SetExpectationsForLoadLinkedProgramFailure(kProgramId, &emulator); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); } 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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); const std::string vertex_orig_source = vertex_shader_->last_compiled_source(); vertex_shader_->set_source("different!"); TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); vertex_shader_->set_source(vertex_orig_source); TestHelper::SetShaderStates(gl_.get(), vertex_shader_, true); fragment_shader_->set_source("different!"); TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); } 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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); binding_map["different!"] = 59; EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, &binding_map, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); } TEST_F(MemoryProgramCacheTest, LoadFailOnDifferentTransformFeedbackVaryings) { 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); varyings_.push_back("test"); cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_INTERLEAVED_ATTRIBS, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_SEPARATE_ATTRIBS, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); varyings_.push_back("different!"); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_FAILURE, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_INTERLEAVED_ATTRIBS, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); } TEST_F(MemoryProgramCacheTest, MemoryProgramCacheEviction) { 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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); const int kEvictingProgramId = 11; const GLuint kEvictingBinaryLength = kCacheSizeBytes - kBinaryLength + 1; // save old source and modify for new program const std::string& old_sig = fragment_shader_->last_compiled_signature(); fragment_shader_->set_source("al sdfkjdk"); TestHelper::SetShaderStates(gl_.get(), fragment_shader_, true); scoped_ptr bigTestBinary = scoped_ptr(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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus( vertex_shader_->last_compiled_signature(), fragment_shader_->last_compiled_signature(), NULL, varyings_, GL_NONE)); EXPECT_EQ(ProgramCache::LINK_UNKNOWN, cache_->GetLinkedProgramStatus( old_sig, fragment_shader_->last_compiled_signature(), NULL, varyings_, GL_NONE)); } 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_->set_source("different!"); SetExpectationsForSaveLinkedProgram(kProgramId, &emulator1); cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus( vertex_shader_->last_compiled_signature(), fragment_shader_->last_compiled_signature(), NULL, varyings_, GL_NONE)); } 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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); EXPECT_EQ(ProgramCache::LINK_SUCCEEDED, cache_->GetLinkedProgramStatus( vertex_shader_->last_compiled_signature(), fragment_shader_->last_compiled_signature(), NULL, varyings_, GL_NONE)); SetExpectationsForLoadLinkedProgram(kProgramId, &emulator); fragment_shader_->set_source("different!"); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); } TEST_F(MemoryProgramCacheTest, OverwriteOnNewSave) { 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, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); char test_binary2[kBinaryLength]; for (int i = 0; i < kBinaryLength; ++i) { test_binary2[i] = (i*2) % 250; } ProgramBinaryEmulator emulator2(kBinaryLength, kFormat, test_binary2); SetExpectationsForSaveLinkedProgram(kProgramId, &emulator2); cache_->SaveLinkedProgram(kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this))); SetExpectationsForLoadLinkedProgram(kProgramId, &emulator2); EXPECT_EQ(ProgramCache::PROGRAM_LOAD_SUCCESS, cache_->LoadLinkedProgram( kProgramId, vertex_shader_, fragment_shader_, NULL, varyings_, GL_NONE, base::Bind(&MemoryProgramCacheTest::ShaderCacheCb, base::Unretained(this)))); } } // namespace gles2 } // namespace gpu