Cherry-pick: gpu: Add Will/DidUseTexImage to GLImage API.

Cherry-pick of Chromium crrev.com/r230093

BUG: 11392857

Conflicts:
	cc/resources/resource_provider.cc
	gpu/command_buffer/service/gles2_cmd_decoder.cc
	ui/gl/gl_image_egl.cc

Original description:

WillUseTexImage/DidUseTexImage is called before/after the image is
used for sampling. The result is that the client only has to call
bind/releaseTexImage2D when contents have changed, which allows
for more efficient GLImage implementations as work required before
use can be separated from work required when contents have changed.

Change-Id: I3bfa9cdd894fcdd60b7d10ff70afde349cfdd476
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 11eaab0..573fe30 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -86,6 +86,8 @@
       format(0),
       filter(0),
       image_id(0),
+      bound_image_id(0),
+      dirty_image(false),
       texture_pool(0),
       hint(TextureUsageAny),
       type(static_cast<ResourceType>(0)) {}
@@ -118,6 +120,8 @@
       format(format),
       filter(filter),
       image_id(0),
+      bound_image_id(0),
+      dirty_image(false),
       texture_pool(texture_pool),
       hint(hint),
       type(GLTexture) {}
@@ -143,6 +147,8 @@
       format(format),
       filter(filter),
       image_id(0),
+      bound_image_id(0),
+      dirty_image(false),
       texture_pool(0),
       hint(TextureUsageAny),
       type(Bitmap) {}
@@ -620,7 +626,6 @@
 }
 
 ResourceProvider::ScopedSamplerGL::~ScopedSamplerGL() {
-  resource_provider_->UnbindForSampling(resource_id_, target_, unit_);
 }
 
 ResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL(
@@ -1116,32 +1121,14 @@
     resource->filter = filter;
   }
 
-  if (resource->image_id)
+  if (resource->image_id && resource->dirty_image) {
+    // Release image currently bound to texture.
+    if (resource->bound_image_id)
+      context3d->releaseTexImage2DCHROMIUM(target, resource->bound_image_id);
     context3d->bindTexImage2DCHROMIUM(target, resource->image_id);
-
-  // Active unit being GL_TEXTURE0 is effectively the ground state.
-  if (unit != GL_TEXTURE0)
-    GLC(context3d, context3d->activeTexture(GL_TEXTURE0));
-}
-
-void ResourceProvider::UnbindForSampling(
-    ResourceProvider::ResourceId resource_id, GLenum target, GLenum unit) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  ResourceMap::iterator it = resources_.find(resource_id);
-  DCHECK(it != resources_.end());
-  Resource* resource = &it->second;
-
-  if (!resource->image_id)
-    return;
-
-  WebGraphicsContext3D* context3d = output_surface_->context3d();
-  DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(context3d));
-  if (unit != GL_TEXTURE0)
-    GLC(context3d, context3d->activeTexture(unit));
-  context3d->releaseTexImage2DCHROMIUM(target, resource->image_id);
-  // Active unit being GL_TEXTURE0 is effectively the ground state.
-  if (unit != GL_TEXTURE0)
-    GLC(context3d, context3d->activeTexture(GL_TEXTURE0));
+    resource->bound_image_id = resource->image_id;
+    resource->dirty_image = false;
+  }
 }
 
 void ResourceProvider::BeginSetPixels(ResourceId id) {
@@ -1374,6 +1361,8 @@
   DCHECK(context3d);
   context3d->destroyImageCHROMIUM(resource->image_id);
   resource->image_id = 0;
+  resource->bound_image_id = 0;
+  resource->dirty_image = false;
   resource->allocated = false;
 }
 
@@ -1413,6 +1402,7 @@
     WebGraphicsContext3D* context3d = output_surface_->context3d();
     DCHECK(context3d);
     context3d->unmapImageCHROMIUM(resource->image_id);
+    resource->dirty_image = true;
   }
 }
 
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index b5dccc8..f1b5dad 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -368,6 +368,8 @@
     // TODO(skyostil): Use a separate sampler object for filter state.
     GLenum filter;
     unsigned image_id;
+    unsigned bound_image_id;
+    bool dirty_image;
     GLenum texture_pool;
     TextureUsageHint hint;
     ResourceType type;
@@ -417,9 +419,6 @@
                        GLenum target,
                        GLenum unit,
                        GLenum filter);
-  void UnbindForSampling(ResourceProvider::ResourceId resource_id,
-                         GLenum target,
-                         GLenum unit);
 
   OutputSurface* output_surface_;
   bool lost_output_surface_;
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc
index a23af0b..32c5cf6 100644
--- a/cc/resources/resource_provider_unittest.cc
+++ b/cc/resources/resource_provider_unittest.cc
@@ -1638,9 +1638,30 @@
   EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
       .Times(1)
       .RetiresOnSaturation();
+  {
+    ResourceProvider::ScopedSamplerGL lock_gl(
+        resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
+    EXPECT_EQ(kTextureId, lock_gl.texture_id());
+  }
+
+  EXPECT_CALL(*context, mapImageCHROMIUM(kImageId, GL_READ_WRITE))
+      .WillOnce(Return(dummy_mapped_buffer_address))
+      .RetiresOnSaturation();
+  resource_provider->MapImage(id);
+
+  EXPECT_CALL(*context, unmapImageCHROMIUM(kImageId))
+      .Times(1)
+      .RetiresOnSaturation();
+  resource_provider->UnmapImage(id);
+
+  EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId)).Times(1)
+      .RetiresOnSaturation();
   EXPECT_CALL(*context, releaseTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
       .Times(1)
       .RetiresOnSaturation();
+  EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
+      .Times(1)
+      .RetiresOnSaturation();
   EXPECT_CALL(*context, deleteTexture(kTextureId))
       .Times(1)
       .RetiresOnSaturation();
diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc
index b468262..c118af2 100644
--- a/gpu/command_buffer/service/framebuffer_manager.cc
+++ b/gpu/command_buffer/service/framebuffer_manager.cc
@@ -84,7 +84,7 @@
     return true;
   }
 
-  virtual void DetachFromFramebuffer() const OVERRIDE {
+  virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const OVERRIDE {
     // Nothing to do for renderbuffers.
   }
 
@@ -188,8 +188,10 @@
     return texture_ref_->texture()->CanRenderTo();
   }
 
-  virtual void DetachFromFramebuffer() const OVERRIDE {
+  virtual void DetachFromFramebuffer(Framebuffer* framebuffer)
+      const OVERRIDE {
     texture_ref_->texture()->DetachFromFramebuffer();
+    framebuffer->OnTextureRefDetached(texture_ref_.get());
   }
 
   virtual bool ValidForAttachmentType(
@@ -225,6 +227,10 @@
   DISALLOW_COPY_AND_ASSIGN(TextureAttachment);
 };
 
+FramebufferManager::TextureDetachObserver::TextureDetachObserver() {}
+
+FramebufferManager::TextureDetachObserver::~TextureDetachObserver() {}
+
 FramebufferManager::FramebufferManager(
     uint32 max_draw_buffers, uint32 max_color_attachments)
     : framebuffer_state_change_count_(1),
@@ -247,7 +253,7 @@
   deleted_ = true;
   while (!attachments_.empty()) {
     Attachment* attachment = attachments_.begin()->second.get();
-    attachment->DetachFromFramebuffer();
+    attachment->DetachFromFramebuffer(this);
     attachments_.erase(attachments_.begin());
   }
 }
@@ -532,7 +538,7 @@
     GLenum attachment, Renderbuffer* renderbuffer) {
   const Attachment* a = GetAttachment(attachment);
   if (a)
-    a->DetachFromFramebuffer();
+    a->DetachFromFramebuffer(this);
   if (renderbuffer) {
     attachments_[attachment] = scoped_refptr<Attachment>(
         new RenderbufferAttachment(renderbuffer));
@@ -547,7 +553,7 @@
     GLint level, GLsizei samples) {
   const Attachment* a = GetAttachment(attachment);
   if (a)
-    a->DetachFromFramebuffer();
+    a->DetachFromFramebuffer(this);
   if (texture_ref) {
     attachments_[attachment] = scoped_refptr<Attachment>(
         new TextureAttachment(texture_ref, target, level, samples));
@@ -568,6 +574,10 @@
   return NULL;
 }
 
+void Framebuffer::OnTextureRefDetached(TextureRef* texture) {
+  manager_->OnTextureRefDetached(texture);
+}
+
 bool FramebufferManager::GetClientId(
     GLuint service_id, GLuint* client_id) const {
   // This doesn't need to be fast. It's only used during slow queries.
@@ -605,6 +615,12 @@
       framebuffer_state_change_count_;
 }
 
+void FramebufferManager::OnTextureRefDetached(TextureRef* texture) {
+  FOR_EACH_OBSERVER(TextureDetachObserver,
+                    texture_detach_observers_,
+                    OnTextureRefDetachedFromFramebuffer(texture));
+}
+
 }  // namespace gles2
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h
index 176e3e2..850e702 100644
--- a/gpu/command_buffer/service/framebuffer_manager.h
+++ b/gpu/command_buffer/service/framebuffer_manager.h
@@ -9,6 +9,7 @@
 #include "base/containers/hash_tables.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
 #include "gpu/command_buffer/service/gl_utils.h"
 #include "gpu/gpu_export.h"
 
@@ -41,7 +42,7 @@
     virtual bool IsRenderbuffer(
         Renderbuffer* renderbuffer) const = 0;
     virtual bool CanRenderTo() const = 0;
-    virtual void DetachFromFramebuffer() const = 0;
+    virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const = 0;
     virtual bool ValidForAttachmentType(
         GLenum attachment_type, uint32 max_color_attachments) = 0;
     virtual void AddToSignature(
@@ -130,6 +131,8 @@
     return allow_framebuffer_combo_complete_map_;
   }
 
+  void OnTextureRefDetached(TextureRef* texture);
+
  private:
   friend class FramebufferManager;
   friend class base::RefCounted<Framebuffer>;
@@ -184,6 +187,17 @@
 // so we can correctly clear them.
 class GPU_EXPORT FramebufferManager {
  public:
+  class GPU_EXPORT TextureDetachObserver {
+   public:
+    TextureDetachObserver();
+    virtual ~TextureDetachObserver();
+
+    virtual void OnTextureRefDetachedFromFramebuffer(TextureRef* texture) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(TextureDetachObserver);
+  };
+
   FramebufferManager(uint32 max_draw_buffers, uint32 max_color_attachments);
   ~FramebufferManager();
 
@@ -217,12 +231,22 @@
         (framebuffer_state_change_count_ + 1) | 0x80000000U;
   }
 
+  void AddObserver(TextureDetachObserver* observer) {
+    texture_detach_observers_.AddObserver(observer);
+  }
+
+  void RemoveObserver(TextureDetachObserver* observer) {
+    texture_detach_observers_.RemoveObserver(observer);
+  }
+
  private:
   friend class Framebuffer;
 
   void StartTracking(Framebuffer* framebuffer);
   void StopTracking(Framebuffer* framebuffer);
 
+  void OnTextureRefDetached(TextureRef* texture);
+
   // Info for each framebuffer in the system.
   typedef base::hash_map<GLuint, scoped_refptr<Framebuffer> >
       FramebufferMap;
@@ -241,6 +265,8 @@
   uint32 max_draw_buffers_;
   uint32 max_color_attachments_;
 
+  ObserverList<TextureDetachObserver> texture_detach_observers_;
+
   DISALLOW_COPY_AND_ASSIGN(FramebufferManager);
 };
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 20c75d4..85ecf2c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -524,7 +524,8 @@
 
 // This class implements GLES2Decoder so we don't have to expose all the GLES2
 // cmd stuff to outside this class.
-class GLES2DecoderImpl : public GLES2Decoder {
+class GLES2DecoderImpl : public GLES2Decoder,
+                         public FramebufferManager::TextureDetachObserver {
  public:
   // Used by PrepForSetUniformByLocation to validate types.
   struct BaseUniformInfo {
@@ -647,6 +648,10 @@
 
   virtual error::ContextLostReason GetContextLostReason() OVERRIDE;
 
+  // Overridden from FramebufferManager::TextureDetachObserver:
+  virtual void OnTextureRefDetachedFromFramebuffer(
+      TextureRef* texture) OVERRIDE;
+
  private:
   friend class ScopedFrameBufferBinder;
   friend class ScopedGLErrorSuppressor;
@@ -1406,9 +1411,14 @@
   // buffer and bind the texture implicitly.
   void UpdateStreamTextureIfNeeded(Texture* texture, GLuint texture_unit_index);
 
-  // Returns false if unrenderable textures were replaced.
+  // If an image is bound to texture, this will call Will/DidUseTexImage
+  // if needed.
+  void DoWillUseTexImageIfNeeded(Texture* texture, GLenum textarget);
+  void DoDidUseTexImageIfNeeded(Texture* texture, GLenum textarget);
+
+  // Returns false if textures were replaced.
   bool PrepareTexturesForRender();
-  void RestoreStateForNonRenderableTextures();
+  void RestoreStateForTextures();
 
   // Returns true if GL_FIXED attribs were simulated.
   bool SimulateFixedAttribs(
@@ -2537,6 +2547,8 @@
       AsyncPixelTransferManager::Create(context.get()));
   async_pixel_transfer_manager_->Initialize(texture_manager());
 
+  framebuffer_manager()->AddObserver(this);
+
   return true;
 }
 
@@ -3257,6 +3269,8 @@
   // by the context group.
   async_pixel_transfer_manager_.reset();
 
+  framebuffer_manager()->RemoveObserver(this);
+
   if (group_.get()) {
     group_->Destroy(this, have_context);
     group_ = NULL;
@@ -4924,6 +4938,9 @@
     return;
   }
 
+  if (texture_ref)
+    DoWillUseTexImageIfNeeded(texture_ref->texture(), textarget);
+
   LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(name);
   if (0 == samples) {
     glFramebufferTexture2DEXT(target, attachment, textarget, service_id, level);
@@ -4944,6 +4961,10 @@
   if (framebuffer == state_.bound_draw_framebuffer.get()) {
     clear_state_dirty_ = true;
   }
+
+  if (texture_ref)
+    DoDidUseTexImageIfNeeded(texture_ref->texture(), textarget);
+
   OnFboChanged();
 }
 
@@ -5663,11 +5684,49 @@
   }
 }
 
+void GLES2DecoderImpl::DoWillUseTexImageIfNeeded(
+    Texture* texture, GLenum textarget) {
+  // This might be supported in the future.
+  if (textarget != GL_TEXTURE_2D)
+    return;
+  // Image is already in use if texture is attached to a framebuffer.
+  if (texture && !texture->IsAttachedToFramebuffer()) {
+    gfx::GLImage* image = texture->GetLevelImage(textarget, 0);
+    if (image) {
+      ScopedGLErrorSuppressor suppressor(
+          "GLES2DecoderImpl::DoWillUseTexImageIfNeeded",
+          this);
+      glBindTexture(textarget, texture->service_id());
+      image->WillUseTexImage();
+      RestoreCurrentTexture2DBindings();
+    }
+  }
+}
+
+void GLES2DecoderImpl::DoDidUseTexImageIfNeeded(
+    Texture* texture, GLenum textarget) {
+  // This might be supported in the future.
+  if (textarget != GL_TEXTURE_2D)
+    return;
+  // Image is still in use if texture is attached to a framebuffer.
+  if (texture && !texture->IsAttachedToFramebuffer()) {
+    gfx::GLImage* image = texture->GetLevelImage(textarget, 0);
+    if (image) {
+      ScopedGLErrorSuppressor suppressor(
+          "GLES2DecoderImpl::DoDidUseTexImageIfNeeded",
+          this);
+      glBindTexture(textarget, texture->service_id());
+      image->DidUseTexImage();
+      RestoreCurrentTexture2DBindings();
+    }
+  }
+}
+
 bool GLES2DecoderImpl::PrepareTexturesForRender() {
   DCHECK(state_.current_program.get());
-  bool have_unrenderable_textures =
-      texture_manager()->HaveUnrenderableTextures();
-  if (!have_unrenderable_textures && !features().oes_egl_image_external) {
+  if (!texture_manager()->HaveUnrenderableTextures() &&
+      !texture_manager()->HaveImages() &&
+      !features().oes_egl_image_external) {
     return true;
   }
 
@@ -5682,16 +5741,14 @@
       GLuint texture_unit_index = uniform_info->texture_units[jj];
       if (texture_unit_index < state_.texture_units.size()) {
         TextureUnit& texture_unit = state_.texture_units[texture_unit_index];
-        TextureRef* texture =
+        TextureRef* texture_ref =
             texture_unit.GetInfoForSamplerType(uniform_info->type).get();
-        if (texture)
-          UpdateStreamTextureIfNeeded(texture->texture(), texture_unit_index);
-        if (have_unrenderable_textures &&
-            (!texture || !texture_manager()->CanRender(texture))) {
+        GLenum textarget = GetBindTargetForSamplerType(uniform_info->type);
+        if (!texture_ref || !texture_manager()->CanRender(texture_ref)) {
           textures_set = true;
           glActiveTexture(GL_TEXTURE0 + texture_unit_index);
           glBindTexture(
-              GetBindTargetForSamplerType(uniform_info->type),
+              textarget,
               texture_manager()->black_texture_id(uniform_info->type));
           LOCAL_RENDER_WARNING(
               std::string("texture bound to texture unit ") +
@@ -5699,7 +5756,23 @@
               " is not renderable. It maybe non-power-of-2 and have"
               " incompatible texture filtering or is not"
               " 'texture complete'");
+          continue;
         }
+
+        Texture* texture = texture_ref->texture();
+        if (textarget == GL_TEXTURE_2D) {
+          gfx::GLImage* image = texture->GetLevelImage(textarget, 0);
+          if (image && !texture->IsAttachedToFramebuffer()) {
+            ScopedGLErrorSuppressor suppressor(
+                "GLES2DecoderImpl::PrepareTexturesForRender", this);
+            textures_set = true;
+            glActiveTexture(GL_TEXTURE0 + texture_unit_index);
+            image->WillUseTexImage();
+            continue;
+          }
+        }
+
+        UpdateStreamTextureIfNeeded(texture, texture_unit_index);
       }
       // else: should this be an error?
     }
@@ -5707,7 +5780,7 @@
   return !textures_set;
 }
 
-void GLES2DecoderImpl::RestoreStateForNonRenderableTextures() {
+void GLES2DecoderImpl::RestoreStateForTextures() {
   DCHECK(state_.current_program.get());
   const Program::SamplerIndices& sampler_indices =
       state_.current_program->sampler_indices();
@@ -5720,9 +5793,7 @@
       if (texture_unit_index < state_.texture_units.size()) {
         TextureUnit& texture_unit = state_.texture_units[texture_unit_index];
         TextureRef* texture_ref =
-            uniform_info->type == GL_SAMPLER_2D
-                ? texture_unit.bound_texture_2d.get()
-                : texture_unit.bound_texture_cube_map.get();
+            texture_unit.GetInfoForSamplerType(uniform_info->type).get();
         if (!texture_ref || !texture_manager()->CanRender(texture_ref)) {
           glActiveTexture(GL_TEXTURE0 + texture_unit_index);
           // Get the texture_ref info that was previously bound here.
@@ -5731,6 +5802,20 @@
                             : texture_unit.bound_texture_cube_map.get();
           glBindTexture(texture_unit.bind_target,
                         texture_ref ? texture_ref->service_id() : 0);
+          continue;
+        }
+
+        Texture* texture = texture_ref->texture();
+        if (texture_unit.bind_target == GL_TEXTURE_2D) {
+          gfx::GLImage* image = texture->GetLevelImage(
+              texture_unit.bind_target, 0);
+          if (image && !texture->IsAttachedToFramebuffer()) {
+            ScopedGLErrorSuppressor suppressor(
+                "GLES2DecoderImpl::RestoreStateForTextures", this);
+            glActiveTexture(GL_TEXTURE0 + texture_unit_index);
+            image->DidUseTexImage();
+            continue;
+          }
         }
       }
     }
@@ -6073,7 +6158,7 @@
       }
       ProcessPendingQueries();
       if (textures_set) {
-        RestoreStateForNonRenderableTextures();
+        RestoreStateForTextures();
       }
       if (simulated_fixed_attribs) {
         RestoreStateForSimulatedFixedAttribs();
@@ -6207,7 +6292,7 @@
 
       ProcessPendingQueries();
       if (textures_set) {
-        RestoreStateForNonRenderableTextures();
+        RestoreStateForTextures();
       }
       if (simulated_fixed_attribs) {
         RestoreStateForSimulatedFixedAttribs();
@@ -9789,6 +9874,8 @@
         dest_texture_ref, GL_TEXTURE_2D, level, true);
   }
 
+  DoWillUseTexImageIfNeeded(source_texture, source_texture->target());
+
   // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix
   // before presenting.
   if (source_texture->target() == GL_TEXTURE_EXTERNAL_OES) {
@@ -9821,6 +9908,8 @@
         unpack_premultiply_alpha_,
         unpack_unpremultiply_alpha_);
   }
+
+  DoDidUseTexImageIfNeeded(source_texture, source_texture->target());
 }
 
 static GLenum ExtractTypeFromStorageFormat(GLenum internalformat) {
@@ -10484,6 +10573,12 @@
   return error::kNoError;
 }
 
+void GLES2DecoderImpl::OnTextureRefDetachedFromFramebuffer(
+    TextureRef* texture_ref) {
+  Texture* texture = texture_ref->texture();
+  DoDidUseTexImageIfNeeded(texture, texture->target());
+}
+
 // Include the auto-generated part of this file. We split this because it means
 // we can easily edit the non-auto generated parts right here in this file
 // instead of having to edit some template or the code generator.
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 14acb40..52caaa0 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -8051,6 +8051,146 @@
   EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 0) == NULL);
 }
 
+class MockGLImage : public gfx::GLImage {
+ public:
+  MockGLImage() {}
+
+  // Overridden from gfx::GLImage:
+  MOCK_METHOD0(Destroy, void());
+  MOCK_METHOD0(GetSize, gfx::Size());
+  MOCK_METHOD0(BindTexImage, bool());
+  MOCK_METHOD0(ReleaseTexImage, void());
+  MOCK_METHOD0(WillUseTexImage, void());
+  MOCK_METHOD0(DidUseTexImage, void());
+
+ protected:
+  virtual ~MockGLImage() {}
+};
+
+TEST_F(GLES2DecoderWithShaderTest, UseTexImage) {
+  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
+  DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+               kSharedMemoryId, kSharedMemoryOffset);
+
+  TextureRef* texture_ref = group().texture_manager()->GetTexture(
+      client_texture_id_);
+  ASSERT_TRUE(texture_ref != NULL);
+  Texture* texture = texture_ref->texture();
+  EXPECT_EQ(kServiceTextureId, texture->service_id());
+
+  const int32 kImageId = 1;
+  scoped_refptr<MockGLImage> image(new MockGLImage);
+  group().image_manager()->AddImage(image.get(), kImageId);
+
+  // Bind image to texture.
+  EXPECT_CALL(*image, BindTexImage())
+      .Times(1)
+      .WillOnce(Return(true))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*image, GetSize())
+      .Times(1)
+      .WillOnce(Return(gfx::Size(1, 1)))
+      .RetiresOnSaturation();
+  // ScopedGLErrorSuppressor calls GetError on its constructor and destructor.
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  BindTexImage2DCHROMIUM bind_tex_image_2d_cmd;
+  bind_tex_image_2d_cmd.Init(GL_TEXTURE_2D, kImageId);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(bind_tex_image_2d_cmd));
+
+  AddExpectationsForSimulatedAttrib0(kNumVertices, 0);
+  SetupExpectationsForApplyingDefaultDirtyState();
+
+  // ScopedGLErrorSuppressor calls GetError on its constructor and destructor.
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
+      .Times(3)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*image, WillUseTexImage())
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*image, DidUseTexImage())
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
+      .Times(1)
+      .RetiresOnSaturation();
+  DrawArrays cmd;
+  cmd.Init(GL_TRIANGLES, 0, kNumVertices);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+  DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_,
+                    kServiceFramebufferId);
+  // ScopedGLErrorSuppressor calls GetError on its constructor and destructor.
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kServiceTextureId))
+      .Times(2)
+      .RetiresOnSaturation();
+  // Image will be 'in use' as long as bound to a framebuffer.
+  EXPECT_CALL(*image, WillUseTexImage())
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, FramebufferTexture2DEXT(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+      kServiceTextureId, 0))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  FramebufferTexture2D fbtex_cmd;
+  fbtex_cmd.Init(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, client_texture_id_,
+      0);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(fbtex_cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+
+  // ScopedGLErrorSuppressor calls GetError on its constructor and destructor.
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+      kServiceRenderbufferId))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, BindTexture(GL_TEXTURE_2D, kServiceTextureId))
+      .Times(2)
+      .RetiresOnSaturation();
+  // Image should no longer be 'in use' after being unbound from framebuffer.
+  EXPECT_CALL(*image, DidUseTexImage())
+      .Times(1)
+      .RetiresOnSaturation();
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
+  FramebufferRenderbuffer fbrb_cmd;
+  fbrb_cmd.Init(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+      client_renderbuffer_id_);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd));
+}
+
 TEST_F(GLES2DecoderManualInitTest, GpuMemoryManagerCHROMIUM) {
   InitDecoder(
       "GL_ARB_texture_rectangle",  // extensions
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 486bdac..9f4c1d9 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -79,6 +79,7 @@
   DCHECK_EQ(0, num_unrenderable_textures_);
   DCHECK_EQ(0, num_unsafe_textures_);
   DCHECK_EQ(0, num_uncleared_mips_);
+  DCHECK_EQ(0, num_images_);
 }
 
 void TextureManager::Destroy(bool have_context) {
@@ -117,6 +118,7 @@
       framebuffer_attachment_count_(0),
       stream_texture_(false),
       immutable_(false),
+      has_images_(false),
       estimated_size_(0),
       can_render_condition_(CAN_RENDER_ALWAYS) {
 }
@@ -433,6 +435,29 @@
   can_render_condition_ = can_render_condition;
 }
 
+void Texture::UpdateHasImages() {
+  if (level_infos_.empty())
+    return;
+
+  bool has_images = false;
+  for (size_t ii = 0; ii < level_infos_.size(); ++ii) {
+    for (size_t jj = 0; jj < level_infos_[ii].size(); ++jj) {
+      const Texture::LevelInfo& info = level_infos_[ii][jj];
+      if (info.image.get() != NULL) {
+        has_images = true;
+        break;
+      }
+    }
+  }
+
+  if (has_images_ == has_images)
+    return;
+  has_images_ = has_images;
+  int delta = has_images ? +1 : -1;
+  for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it)
+    (*it)->manager()->UpdateNumImages(delta);
+}
+
 void Texture::IncAllFramebufferStateChangeCount() {
   for (RefSet::iterator it = refs_.begin(); it != refs_.end(); ++it)
     (*it)->manager()->IncFramebufferStateChangeCount();
@@ -481,6 +506,7 @@
   Update(feature_info);
   UpdateCleared();
   UpdateCanRenderCondition();
+  UpdateHasImages();
   if (IsAttachedToFramebuffer()) {
     // TODO(gman): If textures tracked which framebuffers they were attached to
     // we could just mark those framebuffers as not complete.
@@ -782,10 +808,10 @@
   DCHECK_EQ(info.level, level);
   info.image = image;
   UpdateCanRenderCondition();
+  UpdateHasImages();
 }
 
-gfx::GLImage* Texture::GetLevelImage(
-  GLint target, GLint level) const {
+gfx::GLImage* Texture::GetLevelImage(GLint target, GLint level) const {
   size_t face_index = GLTargetToFaceIndex(target);
   if (level >= 0 && face_index < level_infos_.size() &&
       static_cast<size_t>(level) < level_infos_[face_index].size()) {
@@ -846,6 +872,7 @@
       num_unrenderable_textures_(0),
       num_unsafe_textures_(0),
       num_uncleared_mips_(0),
+      num_images_(0),
       texture_count_(0),
       have_context_(true) {
   for (int ii = 0; ii < kNumDefaultTextures; ++ii) {
@@ -1131,6 +1158,8 @@
     ++num_unsafe_textures_;
   if (!texture->CanRender(feature_info_.get()))
     ++num_unrenderable_textures_;
+  if (texture->HasImages())
+    ++num_images_;
 }
 
 void TextureManager::StopTracking(TextureRef* ref) {
@@ -1145,6 +1174,10 @@
   }
 
   --texture_count_;
+  if (texture->HasImages()) {
+    DCHECK_NE(0, num_images_);
+    --num_images_;
+  }
   if (!texture->CanRender(feature_info_.get())) {
     DCHECK_NE(0, num_unrenderable_textures_);
     --num_unrenderable_textures_;
@@ -1230,10 +1263,14 @@
     ++num_unrenderable_textures_;
 }
 
+void TextureManager::UpdateNumImages(int delta) {
+  num_images_ += delta;
+  DCHECK_GE(num_images_, 0);
+}
+
 void TextureManager::IncFramebufferStateChangeCount() {
   if (framebuffer_manager_)
     framebuffer_manager_->IncFramebufferStateChangeCount();
-
 }
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index cbc659c..dea11a6 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -112,6 +112,10 @@
   // does not exist.
   gfx::GLImage* GetLevelImage(GLint target, GLint level) const;
 
+  bool HasImages() const {
+    return has_images_;
+  }
+
   // Returns true of the given dimensions are inside the dimensions of the
   // level and if the format and type match the level.
   bool ValidForTexture(
@@ -315,6 +319,10 @@
   // texture.
   void UpdateCanRenderCondition();
 
+  // Updates the images count in all the managers referencing this
+  // texture.
+  void UpdateHasImages();
+
   // Increment the framebuffer state change count in all the managers
   // referencing this texture.
   void IncAllFramebufferStateChangeCount();
@@ -376,6 +384,9 @@
   // or dimensions of the texture object can be made.
   bool immutable_;
 
+  // Whether or not this texture has images.
+  bool has_images_;
+
   // Size in bytes this texture is assumed to take in memory.
   uint32 estimated_size_;
 
@@ -629,6 +640,10 @@
     return num_uncleared_mips_ > 0;
   }
 
+  bool HaveImages() const {
+    return num_images_ > 0;
+  }
+
   GLuint black_texture_id(GLenum target) const {
     switch (target) {
       case GL_SAMPLER_2D:
@@ -687,6 +702,7 @@
   void UpdateUnclearedMips(int delta);
   void UpdateCanRenderCondition(Texture::CanRenderCondition old_condition,
                                 Texture::CanRenderCondition new_condition);
+  void UpdateNumImages(int delta);
   void IncFramebufferStateChangeCount();
 
   MemoryTypeTracker* GetMemTracker(GLenum texture_pool);
@@ -710,6 +726,7 @@
   int num_unrenderable_textures_;
   int num_unsafe_textures_;
   int num_uncleared_mips_;
+  int num_images_;
 
   // Counts the number of Textures allocated with 'this' as its manager.
   // Allows to check no Texture will outlive this.
diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc
index 402cc8d..ec5c919 100644
--- a/gpu/command_buffer/service/texture_manager_unittest.cc
+++ b/gpu/command_buffer/service/texture_manager_unittest.cc
@@ -2349,5 +2349,65 @@
             memory_tracker2_->GetSize(MemoryTracker::kUnmanaged));
 }
 
+TEST_F(SharedTextureTest, Images) {
+  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
+  scoped_refptr<TextureRef> ref2 =
+      texture_manager2_->Consume(20, ref1->texture());
+
+  texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D);
+  texture_manager1_->SetLevelInfo(ref1.get(),
+                                  GL_TEXTURE_2D,
+                                  1,
+                                  GL_RGBA,
+                                  2,
+                                  2,
+                                  1,
+                                  0,
+                                  GL_RGBA,
+                                  GL_UNSIGNED_BYTE,
+                                  true);
+  EXPECT_FALSE(ref1->texture()->HasImages());
+  EXPECT_FALSE(ref2->texture()->HasImages());
+  EXPECT_FALSE(texture_manager1_->HaveImages());
+  EXPECT_FALSE(texture_manager2_->HaveImages());
+  texture_manager1_->SetLevelImage(ref1.get(),
+                                   GL_TEXTURE_2D,
+                                   1,
+                                   gfx::GLImage::CreateGLImage(0).get());
+  EXPECT_TRUE(ref1->texture()->HasImages());
+  EXPECT_TRUE(ref2->texture()->HasImages());
+  EXPECT_TRUE(texture_manager1_->HaveImages());
+  EXPECT_TRUE(texture_manager2_->HaveImages());
+  texture_manager1_->SetLevelImage(ref1.get(),
+                                   GL_TEXTURE_2D,
+                                   1,
+                                   gfx::GLImage::CreateGLImage(0).get());
+  EXPECT_TRUE(ref1->texture()->HasImages());
+  EXPECT_TRUE(ref2->texture()->HasImages());
+  EXPECT_TRUE(texture_manager1_->HaveImages());
+  EXPECT_TRUE(texture_manager2_->HaveImages());
+  texture_manager1_->SetLevelInfo(ref1.get(),
+                                  GL_TEXTURE_2D,
+                                  1,
+                                  GL_RGBA,
+                                  2,
+                                  2,
+                                  1,
+                                  0,
+                                  GL_RGBA,
+                                  GL_UNSIGNED_BYTE,
+                                  true);
+  EXPECT_FALSE(ref1->texture()->HasImages());
+  EXPECT_FALSE(ref2->texture()->HasImages());
+  EXPECT_FALSE(texture_manager1_->HaveImages());
+  EXPECT_FALSE(texture_manager1_->HaveImages());
+
+  EXPECT_CALL(*gl_, DeleteTextures(1, _))
+      .Times(1)
+      .RetiresOnSaturation();
+  texture_manager1_->RemoveTexture(10);
+  texture_manager2_->RemoveTexture(20);
+}
+
 }  // namespace gles2
 }  // namespace gpu
diff --git a/ui/gl/gl_image.cc b/ui/gl/gl_image.cc
index aaefb94..ff7eb56 100644
--- a/ui/gl/gl_image.cc
+++ b/ui/gl/gl_image.cc
@@ -19,6 +19,14 @@
   NOTIMPLEMENTED();
 }
 
+void GLImage::WillUseTexImage() {
+  NOTIMPLEMENTED();
+}
+
+void GLImage::DidUseTexImage() {
+  NOTIMPLEMENTED();
+}
+
 GLImage::~GLImage() {}
 
 }  // namespace gfx
diff --git a/ui/gl/gl_image.h b/ui/gl/gl_image.h
index f1d0537..36ba904 100644
--- a/ui/gl/gl_image.h
+++ b/ui/gl/gl_image.h
@@ -33,6 +33,12 @@
   // Release image from texture currently bound to GL_TEXTURE_2D target.
   virtual void ReleaseTexImage();
 
+  // Called before the texture is used for drawing.
+  virtual void WillUseTexImage();
+
+  // Called after the texture has been used for drawing.
+  virtual void DidUseTexImage();
+
   // Create a GL image for a window.
   static scoped_refptr<GLImage> CreateGLImage(gfx::PluginWindowHandle window);
 
diff --git a/ui/gl/gl_image_egl.cc b/ui/gl/gl_image_egl.cc
index 4b8a356..cb8a44c 100644
--- a/ui/gl/gl_image_egl.cc
+++ b/ui/gl/gl_image_egl.cc
@@ -33,7 +33,8 @@
 
 GLImageEGL::GLImageEGL(gfx::Size size)
     : egl_image_(EGL_NO_IMAGE_KHR),
-      size_(size) {
+      size_(size),
+      in_use_(false) {
 }
 
 GLImageEGL::~GLImageEGL() {
@@ -62,25 +63,6 @@
   return true;
 }
 
-bool GLImageEGL::BindTexImage() {
-  if (egl_image_ == EGL_NO_IMAGE_KHR) {
-    LOG(ERROR) << "NULL EGLImage in BindTexImage";
-    return false;
-  }
-
-  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
-
-  if (glGetError() != GL_NO_ERROR) {
-    return false;
-  }
-
-  return true;
-}
-
-gfx::Size GLImageEGL::GetSize() {
-  return size_;
-}
-
 void GLImageEGL::Destroy() {
   if (egl_image_ == EGL_NO_IMAGE_KHR)
     return;
@@ -96,7 +78,40 @@
   egl_image_ = EGL_NO_IMAGE_KHR;
 }
 
+gfx::Size GLImageEGL::GetSize() {
+  return size_;
+}
+
+bool GLImageEGL::BindTexImage() {
+  if (egl_image_ == EGL_NO_IMAGE_KHR) {
+    LOG(ERROR) << "NULL EGLImage in BindTexImage";
+    return false;
+  }
+
+  // Defer ImageTargetTexture2D if not currently in use.
+  if (!in_use_)
+    return true;
+
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
+  DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+  return true;
+}
+
 void GLImageEGL::ReleaseTexImage() {
+  // Nothing to do here as image is released after each use.
+}
+
+void GLImageEGL::WillUseTexImage() {
+  DCHECK(egl_image_);
+  DCHECK(!in_use_);
+  in_use_ = true;
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
+  DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+}
+
+void GLImageEGL::DidUseTexImage() {
+  DCHECK(in_use_);
+  in_use_ = false;
   // === START ANDROID WORKAROUND b/11392857
   static bool requires_gralloc_unbind = RequiresGrallocUnbind();
   if (!requires_gralloc_unbind) return;
diff --git a/ui/gl/gl_image_egl.h b/ui/gl/gl_image_egl.h
index 1c66bb9..2c64f4d 100644
--- a/ui/gl/gl_image_egl.h
+++ b/ui/gl/gl_image_egl.h
@@ -21,6 +21,8 @@
   virtual gfx::Size GetSize() OVERRIDE;
   virtual bool BindTexImage() OVERRIDE;
   virtual void ReleaseTexImage() OVERRIDE;
+  virtual void WillUseTexImage() OVERRIDE;
+  virtual void DidUseTexImage() OVERRIDE;
 
  protected:
   virtual ~GLImageEGL();
@@ -28,6 +30,7 @@
  private:
   EGLImageKHR egl_image_;
   gfx::Size size_;
+  bool in_use_;
 
   DISALLOW_COPY_AND_ASSIGN(GLImageEGL);
 };
diff --git a/ui/gl/gl_image_glx.cc b/ui/gl/gl_image_glx.cc
index 85254f3..739b4d4 100644
--- a/ui/gl/gl_image_glx.cc
+++ b/ui/gl/gl_image_glx.cc
@@ -52,6 +52,10 @@
     glx_pixmap_(0) {
 }
 
+GLImageGLX::~GLImageGLX() {
+  Destroy();
+}
+
 bool GLImageGLX::Initialize() {
   if (!GLSurfaceGLX::IsTextureFromPixmapSupported()) {
     LOG(ERROR) << "GLX_EXT_texture_from_pixmap not supported.";
@@ -170,8 +174,10 @@
   glXReleaseTexImageEXT(display_, glx_pixmap_, GLX_FRONT_LEFT_EXT);
 }
 
-GLImageGLX::~GLImageGLX() {
-  Destroy();
+void GLImageGLX::WillUseTexImage() {
+}
+
+void GLImageGLX::DidUseTexImage() {
 }
 
 }  // namespace gfx
diff --git a/ui/gl/gl_image_glx.h b/ui/gl/gl_image_glx.h
index a3cec2d..8632934 100644
--- a/ui/gl/gl_image_glx.h
+++ b/ui/gl/gl_image_glx.h
@@ -23,6 +23,8 @@
   virtual gfx::Size GetSize() OVERRIDE;
   virtual bool BindTexImage() OVERRIDE;
   virtual void ReleaseTexImage() OVERRIDE;
+  virtual void WillUseTexImage() OVERRIDE;
+  virtual void DidUseTexImage() OVERRIDE;
 
  protected:
   virtual ~GLImageGLX();
diff --git a/ui/gl/gl_image_shm.cc b/ui/gl/gl_image_shm.cc
index 14ed836..ccee11b 100644
--- a/ui/gl/gl_image_shm.cc
+++ b/ui/gl/gl_image_shm.cc
@@ -36,6 +36,13 @@
   return true;
 }
 
+void GLImageShm::Destroy() {
+}
+
+gfx::Size GLImageShm::GetSize() {
+  return size_;
+}
+
 bool GLImageShm::BindTexImage() {
   TRACE_EVENT0("gpu", "GLImageShm::BindTexImage");
   DCHECK(shared_memory_);
@@ -63,14 +70,13 @@
   return true;
 }
 
-gfx::Size GLImageShm::GetSize() {
-  return size_;
-}
-
-void GLImageShm::Destroy() {
-}
-
 void GLImageShm::ReleaseTexImage() {
 }
 
+void GLImageShm::WillUseTexImage() {
+}
+
+void GLImageShm::DidUseTexImage() {
+}
+
 }  // namespace gfx
diff --git a/ui/gl/gl_image_shm.h b/ui/gl/gl_image_shm.h
index 70233f9..c94ca85 100644
--- a/ui/gl/gl_image_shm.h
+++ b/ui/gl/gl_image_shm.h
@@ -21,6 +21,8 @@
   virtual gfx::Size GetSize() OVERRIDE;
   virtual bool BindTexImage() OVERRIDE;
   virtual void ReleaseTexImage() OVERRIDE;
+  virtual void WillUseTexImage() OVERRIDE;
+  virtual void DidUseTexImage() OVERRIDE;
 
  protected:
   virtual ~GLImageShm();
diff --git a/ui/gl/gl_image_stub.cc b/ui/gl/gl_image_stub.cc
index 86efe6e..a1a8a85 100644
--- a/ui/gl/gl_image_stub.cc
+++ b/ui/gl/gl_image_stub.cc
@@ -6,6 +6,13 @@
 
 namespace gfx {
 
+GLImageStub::GLImageStub() {
+}
+
+GLImageStub::~GLImageStub() {
+  Destroy();
+}
+
 void GLImageStub::Destroy() {
 }
 
@@ -20,6 +27,10 @@
 void GLImageStub::ReleaseTexImage() {
 }
 
-GLImageStub::~GLImageStub() {}
+void GLImageStub::WillUseTexImage() {
+}
+
+void GLImageStub::DidUseTexImage() {
+}
 
 }  // namespace gfx
diff --git a/ui/gl/gl_image_stub.h b/ui/gl/gl_image_stub.h
index 8b47358..ec232fa 100644
--- a/ui/gl/gl_image_stub.h
+++ b/ui/gl/gl_image_stub.h
@@ -12,11 +12,15 @@
 // A GLImage that does nothing for unit tests.
 class GL_EXPORT GLImageStub : public GLImage {
  public:
+  GLImageStub();
+
   // Implement GLImage.
   virtual void Destroy() OVERRIDE;
   virtual gfx::Size GetSize() OVERRIDE;
   virtual bool BindTexImage() OVERRIDE;
   virtual void ReleaseTexImage() OVERRIDE;
+  virtual void WillUseTexImage() OVERRIDE;
+  virtual void DidUseTexImage() OVERRIDE;
 
  protected:
   virtual ~GLImageStub();