| // Copyright 2013 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "gpu/command_buffer/service/feature_info.h" | 
 | #include "gpu/command_buffer/service/gpu_service_test.h" | 
 | #include "gpu/command_buffer/service/mailbox_manager_impl.h" | 
 | #include "gpu/command_buffer/service/mailbox_manager_sync.h" | 
 | #include "gpu/command_buffer/service/texture_manager.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "ui/gl/gl_context_stub.h" | 
 | #include "ui/gl/gl_mock.h" | 
 | #include "ui/gl/gl_surface_stub.h" | 
 |  | 
 | namespace gpu { | 
 | namespace gles2 { | 
 |  | 
 | using namespace ::testing; | 
 |  | 
 | class MailboxManagerTest : public GpuServiceTest { | 
 |  public: | 
 |   MailboxManagerTest() {} | 
 |   ~MailboxManagerTest() override {} | 
 |  | 
 |  protected: | 
 |   void SetUp() override { | 
 |     GpuServiceTest::SetUp(); | 
 |     feature_info_ = new FeatureInfo; | 
 |     manager_ = new MailboxManagerImpl; | 
 |     DCHECK(!manager_->UsesSync()); | 
 |   } | 
 |  | 
 |   virtual void SetUpWithSynchronizer() { | 
 |     GpuServiceTest::SetUp(); | 
 |     feature_info_ = new FeatureInfo; | 
 |     manager_ = new MailboxManagerSync(); | 
 |     DCHECK(manager_->UsesSync()); | 
 |   } | 
 |  | 
 |   void TearDown() override { GpuServiceTest::TearDown(); } | 
 |  | 
 |   Texture* CreateTexture() { | 
 |     return new Texture(1); | 
 |   } | 
 |  | 
 |   void SetTarget(Texture* texture, GLenum target, GLuint max_level) { | 
 |     texture->SetTarget(NULL, target, max_level); | 
 |   } | 
 |  | 
 |   void SetLevelInfo( | 
 |       Texture* texture, | 
 |       GLenum target, | 
 |       GLint level, | 
 |       GLenum internal_format, | 
 |       GLsizei width, | 
 |       GLsizei height, | 
 |       GLsizei depth, | 
 |       GLint border, | 
 |       GLenum format, | 
 |       GLenum type, | 
 |       bool cleared) { | 
 |     texture->SetLevelInfo(NULL, | 
 |                           target, | 
 |                           level, | 
 |                           internal_format, | 
 |                           width, | 
 |                           height, | 
 |                           depth, | 
 |                           border, | 
 |                           format, | 
 |                           type, | 
 |                           cleared); | 
 |   } | 
 |  | 
 |   GLenum SetParameter(Texture* texture, GLenum pname, GLint param) { | 
 |     return texture->SetParameteri(feature_info_.get(), pname, param); | 
 |   } | 
 |  | 
 |   void DestroyTexture(Texture* texture) { | 
 |     delete texture; | 
 |   } | 
 |  | 
 |   scoped_refptr<MailboxManager> manager_; | 
 |  | 
 |  private: | 
 |   scoped_refptr<FeatureInfo> feature_info_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MailboxManagerTest); | 
 | }; | 
 |  | 
 | // Tests basic produce/consume behavior. | 
 | TEST_F(MailboxManagerTest, Basic) { | 
 |   Texture* texture = CreateTexture(); | 
 |  | 
 |   Mailbox name = Mailbox::Generate(); | 
 |   manager_->ProduceTexture(name, texture); | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name)); | 
 |  | 
 |   // We can consume multiple times. | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name)); | 
 |  | 
 |   // Destroy should cleanup the mailbox. | 
 |   DestroyTexture(texture); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name)); | 
 | } | 
 |  | 
 | // Tests behavior with multiple produce on the same texture. | 
 | TEST_F(MailboxManagerTest, ProduceMultipleMailbox) { | 
 |   Texture* texture = CreateTexture(); | 
 |  | 
 |   Mailbox name1 = Mailbox::Generate(); | 
 |  | 
 |   manager_->ProduceTexture(name1, texture); | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name1)); | 
 |  | 
 |   // Can produce a second time with the same mailbox. | 
 |   manager_->ProduceTexture(name1, texture); | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name1)); | 
 |  | 
 |   // Can produce again, with a different mailbox. | 
 |   Mailbox name2 = Mailbox::Generate(); | 
 |   manager_->ProduceTexture(name2, texture); | 
 |  | 
 |   // Still available under all mailboxes. | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name1)); | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name2)); | 
 |  | 
 |   // Destroy should cleanup all mailboxes. | 
 |   DestroyTexture(texture); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name1)); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name2)); | 
 | } | 
 |  | 
 | // Tests behavior with multiple produce on the same mailbox with different | 
 | // textures. | 
 | TEST_F(MailboxManagerTest, ProduceMultipleTexture) { | 
 |   Texture* texture1 = CreateTexture(); | 
 |   Texture* texture2 = CreateTexture(); | 
 |  | 
 |   Mailbox name = Mailbox::Generate(); | 
 |  | 
 |   manager_->ProduceTexture(name, texture1); | 
 |   EXPECT_EQ(texture1, manager_->ConsumeTexture(name)); | 
 |  | 
 |   // Can produce a second time with the same mailbox, but different texture. | 
 |   manager_->ProduceTexture(name, texture2); | 
 |   EXPECT_EQ(texture2, manager_->ConsumeTexture(name)); | 
 |  | 
 |   // Destroying the texture that's under no mailbox shouldn't have an effect. | 
 |   DestroyTexture(texture1); | 
 |   EXPECT_EQ(texture2, manager_->ConsumeTexture(name)); | 
 |  | 
 |   // Destroying the texture that's bound should clean up. | 
 |   DestroyTexture(texture2); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name)); | 
 | } | 
 |  | 
 | TEST_F(MailboxManagerTest, ProduceMultipleTextureMailbox) { | 
 |   Texture* texture1 = CreateTexture(); | 
 |   Texture* texture2 = CreateTexture(); | 
 |   Mailbox name1 = Mailbox::Generate(); | 
 |   Mailbox name2 = Mailbox::Generate(); | 
 |  | 
 |   // Put texture1 on name1 and name2. | 
 |   manager_->ProduceTexture(name1, texture1); | 
 |   manager_->ProduceTexture(name2, texture1); | 
 |   EXPECT_EQ(texture1, manager_->ConsumeTexture(name1)); | 
 |   EXPECT_EQ(texture1, manager_->ConsumeTexture(name2)); | 
 |  | 
 |   // Put texture2 on name2. | 
 |   manager_->ProduceTexture(name2, texture2); | 
 |   EXPECT_EQ(texture1, manager_->ConsumeTexture(name1)); | 
 |   EXPECT_EQ(texture2, manager_->ConsumeTexture(name2)); | 
 |  | 
 |   // Destroy texture1, shouldn't affect name2. | 
 |   DestroyTexture(texture1); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name1)); | 
 |   EXPECT_EQ(texture2, manager_->ConsumeTexture(name2)); | 
 |  | 
 |   DestroyTexture(texture2); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name2)); | 
 | } | 
 |  | 
 | const GLsizei kMaxTextureWidth = 64; | 
 | const GLsizei kMaxTextureHeight = 64; | 
 | const GLsizei kMaxTextureDepth = 1; | 
 |  | 
 | class MailboxManagerSyncTest : public MailboxManagerTest { | 
 |  public: | 
 |   MailboxManagerSyncTest() {} | 
 |   ~MailboxManagerSyncTest() override {} | 
 |  | 
 |  protected: | 
 |   void SetUp() override { | 
 |     MailboxManagerTest::SetUpWithSynchronizer(); | 
 |     manager2_ = new MailboxManagerSync(); | 
 |     context_ = new gfx::GLContextStub(); | 
 |     surface_ = new gfx::GLSurfaceStub(); | 
 |     context_->MakeCurrent(surface_.get()); | 
 |   } | 
 |  | 
 |   Texture* DefineTexture() { | 
 |     Texture* texture = CreateTexture(); | 
 |     const GLsizei levels_needed = TextureManager::ComputeMipMapCount( | 
 |         GL_TEXTURE_2D, kMaxTextureWidth, kMaxTextureHeight, kMaxTextureDepth); | 
 |     SetTarget(texture, GL_TEXTURE_2D, levels_needed); | 
 |     SetLevelInfo(texture, | 
 |                  GL_TEXTURE_2D, | 
 |                  0, | 
 |                  GL_RGBA, | 
 |                  1, | 
 |                  1, | 
 |                  1, | 
 |                  0, | 
 |                  GL_RGBA, | 
 |                  GL_UNSIGNED_BYTE, | 
 |                  true); | 
 |     SetParameter(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 
 |     SetParameter(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 
 |     return texture; | 
 |   } | 
 |  | 
 |   void SetupUpdateTexParamExpectations(GLuint texture_id, | 
 |                                        GLenum min, | 
 |                                        GLenum mag, | 
 |                                        GLenum wrap_s, | 
 |                                        GLenum wrap_t) { | 
 |     DCHECK(texture_id); | 
 |     const GLuint kCurrentTexture = 0; | 
 |     EXPECT_CALL(*gl_, GetIntegerv(GL_TEXTURE_BINDING_2D, _)) | 
 |         .WillOnce(SetArgPointee<1>(kCurrentTexture)) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, texture_id)) | 
 |         .Times(1) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*gl_, | 
 |                 TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min)) | 
 |         .Times(1) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*gl_, | 
 |                 TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag)) | 
 |         .Times(1) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s)) | 
 |         .Times(1) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t)) | 
 |         .Times(1) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*gl_, Flush()) | 
 |         .Times(1) | 
 |         .RetiresOnSaturation(); | 
 |     EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kCurrentTexture)) | 
 |         .Times(1) | 
 |         .RetiresOnSaturation(); | 
 |   } | 
 |  | 
 |   void TearDown() override { | 
 |     context_->ReleaseCurrent(NULL); | 
 |     MailboxManagerTest::TearDown(); | 
 |   } | 
 |  | 
 |   scoped_refptr<MailboxManager> manager2_; | 
 |   scoped_refptr<gfx::GLContext> context_; | 
 |   scoped_refptr<gfx::GLSurface> surface_; | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(MailboxManagerSyncTest); | 
 | }; | 
 |  | 
 | TEST_F(MailboxManagerSyncTest, ProduceDestroy) { | 
 |   Texture* texture = DefineTexture(); | 
 |   Mailbox name = Mailbox::Generate(); | 
 |  | 
 |   InSequence sequence; | 
 |   manager_->ProduceTexture(name, texture); | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name)); | 
 |  | 
 |   DestroyTexture(texture); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name)); | 
 |   EXPECT_EQ(NULL, manager2_->ConsumeTexture(name)); | 
 | } | 
 |  | 
 | TEST_F(MailboxManagerSyncTest, ProduceSyncDestroy) { | 
 |   InSequence sequence; | 
 |  | 
 |   Texture* texture = DefineTexture(); | 
 |   Mailbox name = Mailbox::Generate(); | 
 |  | 
 |   manager_->ProduceTexture(name, texture); | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name)); | 
 |  | 
 |   // Synchronize | 
 |   manager_->PushTextureUpdates(0); | 
 |   manager2_->PullTextureUpdates(0); | 
 |  | 
 |   DestroyTexture(texture); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name)); | 
 |   EXPECT_EQ(NULL, manager2_->ConsumeTexture(name)); | 
 | } | 
 |  | 
 | TEST_F(MailboxManagerSyncTest, ProduceSyncClobberDestroy) { | 
 |   InSequence sequence; | 
 |  | 
 |   Texture* texture = DefineTexture(); | 
 |   Mailbox name = Mailbox::Generate(); | 
 |  | 
 |   manager_->ProduceTexture(name, texture); | 
 |   manager_->PushTextureUpdates(0); | 
 |  | 
 |   // Clobber | 
 |   Texture* old_texture = texture; | 
 |   texture = DefineTexture(); | 
 |   manager_->ProduceTexture(name, texture); | 
 |  | 
 |   DestroyTexture(old_texture); | 
 |   DestroyTexture(texture); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name)); | 
 |   EXPECT_EQ(NULL, manager2_->ConsumeTexture(name)); | 
 | } | 
 |  | 
 | // Duplicates a texture into a second manager instance, and then | 
 | // makes sure a redefinition becomes visible there too. | 
 | TEST_F(MailboxManagerSyncTest, ProduceConsumeResize) { | 
 |   const GLuint kNewTextureId = 1234; | 
 |   InSequence sequence; | 
 |  | 
 |   Texture* texture = DefineTexture(); | 
 |   Mailbox name = Mailbox::Generate(); | 
 |  | 
 |   manager_->ProduceTexture(name, texture); | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name)); | 
 |  | 
 |   // Synchronize | 
 |   manager_->PushTextureUpdates(0); | 
 |   manager2_->PullTextureUpdates(0); | 
 |  | 
 |   EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |       .WillOnce(SetArgPointee<1>(kNewTextureId)); | 
 |   SetupUpdateTexParamExpectations( | 
 |       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |   Texture* new_texture = manager2_->ConsumeTexture(name); | 
 |   EXPECT_FALSE(new_texture == NULL); | 
 |   EXPECT_NE(texture, new_texture); | 
 |   EXPECT_EQ(kNewTextureId, new_texture->service_id()); | 
 |  | 
 |   // Resize original texture | 
 |   SetLevelInfo(texture, | 
 |                GL_TEXTURE_2D, | 
 |                0, | 
 |                GL_RGBA, | 
 |                16, | 
 |                32, | 
 |                1, | 
 |                0, | 
 |                GL_RGBA, | 
 |                GL_UNSIGNED_BYTE, | 
 |                true); | 
 |   // Should have been orphaned | 
 |   EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) == NULL); | 
 |  | 
 |   // Synchronize again | 
 |   manager_->PushTextureUpdates(0); | 
 |   SetupUpdateTexParamExpectations( | 
 |       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |   manager2_->PullTextureUpdates(0); | 
 |   GLsizei width, height; | 
 |   new_texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height); | 
 |   EXPECT_EQ(16, width); | 
 |   EXPECT_EQ(32, height); | 
 |  | 
 |   // Should have gotten a new attachment | 
 |   EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) != NULL); | 
 |   // Resize original texture again.... | 
 |   SetLevelInfo(texture, | 
 |                GL_TEXTURE_2D, | 
 |                0, | 
 |                GL_RGBA, | 
 |                64, | 
 |                64, | 
 |                1, | 
 |                0, | 
 |                GL_RGBA, | 
 |                GL_UNSIGNED_BYTE, | 
 |                true); | 
 |   // ...and immediately delete the texture which should save the changes. | 
 |   SetupUpdateTexParamExpectations( | 
 |       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |   DestroyTexture(texture); | 
 |  | 
 |   // Should be still around since there is a ref from manager2 | 
 |   EXPECT_EQ(new_texture, manager2_->ConsumeTexture(name)); | 
 |  | 
 |   // The last change to the texture should be visible without a sync point (i.e. | 
 |   // push). | 
 |   manager2_->PullTextureUpdates(0); | 
 |   new_texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height); | 
 |   EXPECT_EQ(64, width); | 
 |   EXPECT_EQ(64, height); | 
 |  | 
 |   DestroyTexture(new_texture); | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name)); | 
 |   EXPECT_EQ(NULL, manager2_->ConsumeTexture(name)); | 
 | } | 
 |  | 
 | // Makes sure changes are correctly published even when updates are | 
 | // pushed in both directions, i.e. makes sure we don't clobber a shared | 
 | // texture definition with an older version. | 
 | TEST_F(MailboxManagerSyncTest, ProduceConsumeBidirectional) { | 
 |   const GLuint kNewTextureId1 = 1234; | 
 |   const GLuint kNewTextureId2 = 4321; | 
 |  | 
 |   Texture* texture1 = DefineTexture(); | 
 |   Mailbox name1 = Mailbox::Generate(); | 
 |   Texture* texture2 = DefineTexture(); | 
 |   Mailbox name2 = Mailbox::Generate(); | 
 |   Texture* new_texture1 = NULL; | 
 |   Texture* new_texture2 = NULL; | 
 |  | 
 |   manager_->ProduceTexture(name1, texture1); | 
 |   manager2_->ProduceTexture(name2, texture2); | 
 |  | 
 |   // Make visible. | 
 |   manager_->PushTextureUpdates(0); | 
 |   manager2_->PushTextureUpdates(0); | 
 |  | 
 |   // Create textures in the other manager instances for texture1 and texture2, | 
 |   // respectively to create a real sharing scenario. Otherwise, there would | 
 |   // never be conflicting updates/pushes. | 
 |   { | 
 |     InSequence sequence; | 
 |     EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |         .WillOnce(SetArgPointee<1>(kNewTextureId1)); | 
 |     SetupUpdateTexParamExpectations( | 
 |         kNewTextureId1, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |     new_texture1 = manager2_->ConsumeTexture(name1); | 
 |     EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |         .WillOnce(SetArgPointee<1>(kNewTextureId2)); | 
 |     SetupUpdateTexParamExpectations( | 
 |         kNewTextureId2, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |     new_texture2 = manager_->ConsumeTexture(name2); | 
 |   } | 
 |   EXPECT_EQ(kNewTextureId1, new_texture1->service_id()); | 
 |   EXPECT_EQ(kNewTextureId2, new_texture2->service_id()); | 
 |  | 
 |   // Make a change to texture1 | 
 |   DCHECK_EQ(static_cast<GLuint>(GL_LINEAR), texture1->min_filter()); | 
 |   EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), | 
 |             SetParameter(texture1, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); | 
 |  | 
 |   // Make sure this does not clobber it with the previous version we pushed. | 
 |   manager_->PullTextureUpdates(0); | 
 |  | 
 |   // Make a change to texture2 | 
 |   DCHECK_EQ(static_cast<GLuint>(GL_LINEAR), texture2->mag_filter()); | 
 |   EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), | 
 |             SetParameter(texture2, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); | 
 |  | 
 |   Mock::VerifyAndClearExpectations(gl_.get()); | 
 |  | 
 |   // Synchronize in both directions | 
 |   manager_->PushTextureUpdates(0); | 
 |   manager2_->PushTextureUpdates(0); | 
 |   // manager1 should see the change to texture2 mag_filter being applied. | 
 |   SetupUpdateTexParamExpectations( | 
 |       new_texture2->service_id(), GL_LINEAR, GL_NEAREST, GL_REPEAT, GL_REPEAT); | 
 |   manager_->PullTextureUpdates(0); | 
 |   // manager2 should see the change to texture1 min_filter being applied. | 
 |   SetupUpdateTexParamExpectations( | 
 |       new_texture1->service_id(), GL_NEAREST, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |   manager2_->PullTextureUpdates(0); | 
 |  | 
 |   DestroyTexture(texture1); | 
 |   DestroyTexture(texture2); | 
 |   DestroyTexture(new_texture1); | 
 |   DestroyTexture(new_texture2); | 
 | } | 
 |  | 
 | // If a texture is shared with another manager instance, but the mailbox | 
 | // is then clobbered with a different texture in the source context, this should | 
 | // disconnect the earlier texture from updates. | 
 | TEST_F(MailboxManagerSyncTest, ProduceAndClobber) { | 
 |   const GLuint kNewTextureId = 1234; | 
 |   InSequence sequence; | 
 |  | 
 |   Texture* texture = DefineTexture(); | 
 |   Mailbox name = Mailbox::Generate(); | 
 |  | 
 |   manager_->ProduceTexture(name, texture); | 
 |   EXPECT_EQ(texture, manager_->ConsumeTexture(name)); | 
 |  | 
 |   // Synchronize | 
 |   manager_->PushTextureUpdates(0); | 
 |   manager2_->PullTextureUpdates(0); | 
 |  | 
 |   EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |       .WillOnce(SetArgPointee<1>(kNewTextureId)); | 
 |   SetupUpdateTexParamExpectations( | 
 |       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |   Texture* new_texture = manager2_->ConsumeTexture(name); | 
 |   EXPECT_FALSE(new_texture == NULL); | 
 |   EXPECT_NE(texture, new_texture); | 
 |   EXPECT_EQ(kNewTextureId, new_texture->service_id()); | 
 |  | 
 |   Texture* old_texture = texture; | 
 |   texture = DefineTexture(); | 
 |   manager_->ProduceTexture(name, texture); | 
 |  | 
 |   // Make a change to the new texture | 
 |   DCHECK_EQ(static_cast<GLuint>(GL_LINEAR), texture->min_filter()); | 
 |   EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), | 
 |             SetParameter(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); | 
 |  | 
 |   // Synchronize in both directions - no changes, since it's not shared | 
 |   manager_->PushTextureUpdates(0); | 
 |   manager2_->PullTextureUpdates(0); | 
 |   EXPECT_EQ(static_cast<GLuint>(GL_LINEAR), new_texture->min_filter()); | 
 |  | 
 |   // Make a change to the previously shared texture | 
 |   DCHECK_EQ(static_cast<GLuint>(GL_LINEAR), old_texture->mag_filter()); | 
 |   EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), | 
 |             SetParameter(old_texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); | 
 |  | 
 |   // Synchronize and expect update | 
 |   manager_->PushTextureUpdates(0); | 
 |   SetupUpdateTexParamExpectations( | 
 |       new_texture->service_id(), GL_LINEAR, GL_NEAREST, GL_REPEAT, GL_REPEAT); | 
 |   manager2_->PullTextureUpdates(0); | 
 |  | 
 |   EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |       .WillOnce(SetArgPointee<1>(kNewTextureId)); | 
 |   SetupUpdateTexParamExpectations( | 
 |       kNewTextureId, GL_NEAREST, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |   Texture* tmp_texture = manager2_->ConsumeTexture(name); | 
 |   EXPECT_NE(new_texture, tmp_texture); | 
 |   DestroyTexture(tmp_texture); | 
 |  | 
 |   DestroyTexture(old_texture); | 
 |   DestroyTexture(texture); | 
 |   DestroyTexture(new_texture); | 
 |  | 
 |   EXPECT_EQ(NULL, manager_->ConsumeTexture(name)); | 
 |   EXPECT_EQ(NULL, manager2_->ConsumeTexture(name)); | 
 | } | 
 |  | 
 | // Putting the same texture into multiple mailboxes should result in sharing | 
 | // only a single texture also within a synchronized manager instance. | 
 | TEST_F(MailboxManagerSyncTest, SharedThroughMultipleMailboxes) { | 
 |   const GLuint kNewTextureId = 1234; | 
 |   InSequence sequence; | 
 |  | 
 |   Texture* texture = DefineTexture(); | 
 |   Mailbox name1 = Mailbox::Generate(); | 
 |   Mailbox name2 = Mailbox::Generate(); | 
 |  | 
 |   manager_->ProduceTexture(name1, texture); | 
 |  | 
 |   // Share | 
 |   manager_->PushTextureUpdates(0); | 
 |   EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |       .WillOnce(SetArgPointee<1>(kNewTextureId)); | 
 |   manager2_->PullTextureUpdates(0); | 
 |   SetupUpdateTexParamExpectations( | 
 |       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |   Texture* new_texture = manager2_->ConsumeTexture(name1); | 
 |   EXPECT_EQ(kNewTextureId, new_texture->service_id()); | 
 |  | 
 |   manager_->ProduceTexture(name2, texture); | 
 |  | 
 |   // Synchronize | 
 |   manager_->PushTextureUpdates(0); | 
 |   manager2_->PullTextureUpdates(0); | 
 |  | 
 |   // name2 should return the same texture | 
 |   EXPECT_EQ(new_texture, manager2_->ConsumeTexture(name2)); | 
 |  | 
 |   // Even after destroying the source texture, the original mailbox should | 
 |   // still exist. | 
 |   DestroyTexture(texture); | 
 |   EXPECT_EQ(new_texture, manager2_->ConsumeTexture(name1)); | 
 |   DestroyTexture(new_texture); | 
 | } | 
 |  | 
 | // A: produce texture1 into M, B: consume into new_texture | 
 | // B: produce texture2 into M, A: produce texture1 into M | 
 | // B: consume M should return new_texture | 
 | TEST_F(MailboxManagerSyncTest, ProduceBothWays) { | 
 |   const GLuint kNewTextureId = 1234; | 
 |   InSequence sequence; | 
 |  | 
 |   Texture* texture1 = DefineTexture(); | 
 |   Texture* texture2 = DefineTexture(); | 
 |   Mailbox name = Mailbox::Generate(); | 
 |  | 
 |   manager_->ProduceTexture(name, texture1); | 
 |  | 
 |   // Share | 
 |   manager_->PushTextureUpdates(0); | 
 |   EXPECT_CALL(*gl_, GenTextures(1, _)) | 
 |       .WillOnce(SetArgPointee<1>(kNewTextureId)); | 
 |   SetupUpdateTexParamExpectations( | 
 |       kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT); | 
 |   Texture* new_texture = manager2_->ConsumeTexture(name); | 
 |   EXPECT_EQ(kNewTextureId, new_texture->service_id()); | 
 |  | 
 |   // Clobber | 
 |   manager2_->ProduceTexture(name, texture2); | 
 |   manager_->ProduceTexture(name, texture1); | 
 |  | 
 |   // Synchronize manager -> manager2 | 
 |   manager_->PushTextureUpdates(0); | 
 |   manager2_->PullTextureUpdates(0); | 
 |  | 
 |   // name should return the original texture, and not texture2 or a new one. | 
 |   EXPECT_EQ(new_texture, manager2_->ConsumeTexture(name)); | 
 |  | 
 |   DestroyTexture(texture1); | 
 |   DestroyTexture(texture2); | 
 |   DestroyTexture(new_texture); | 
 | } | 
 |  | 
 | // TODO: Produce incomplete texture | 
 |  | 
 | // TODO: Texture::level_infos_[][].size() | 
 |  | 
 | // TODO: unsupported targets and formats | 
 |  | 
 | }  // namespace gles2 | 
 | }  // namespace gpu |