GPU: Make AsyncPixelTransferState per context rather than per context group.

Introduces a per context AsyncPixelTransferManager that owns the 
AsyncPixelTransferStates. In order to scope these to the lifetime of a Texture,
a gles::gpu::TextureManager::DestructionObserver interface was added.

BUG=240504

Review URL: https://chromiumcodereview.appspot.com/16126004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203305 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate.h b/gpu/command_buffer/service/async_pixel_transfer_delegate.h
index b21a63b..f90e2af 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate.h
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate.h
@@ -7,6 +7,7 @@
 
 #include "base/basictypes.h"
 #include "base/callback.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
@@ -74,18 +75,20 @@
 // AsyncPixelTransferState holds the resources required to do async
 // transfers on one texture. It should stay alive for the lifetime
 // of the texture to allow multiple transfers.
-class GPU_EXPORT AsyncPixelTransferState :
-    public base::SupportsWeakPtr<AsyncPixelTransferState> {
+class GPU_EXPORT AsyncPixelTransferState
+    : public base::RefCounted<AsyncPixelTransferState>,
+      public base::SupportsWeakPtr<AsyncPixelTransferState> {
  public:
-  virtual ~AsyncPixelTransferState();
-
   // Returns true if there is a transfer in progress.
   virtual bool TransferIsInProgress() = 0;
 
  protected:
   AsyncPixelTransferState();
+  virtual ~AsyncPixelTransferState();
 
  private:
+  friend class base::RefCounted<AsyncPixelTransferState>;
+
   DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferState);
 };
 
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc
index c56f72a..246acb4b 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_egl.cc
@@ -393,7 +393,6 @@
                                             wait_for_creation,
                                             use_image_preserved)) {
   }
-  virtual ~AsyncTransferStateImpl() {}
 
   virtual bool TransferIsInProgress() OVERRIDE {
       return internal_->TransferIsInProgress();
@@ -404,6 +403,9 @@
   }
 
   scoped_refptr<TransferStateInternal> internal_;
+
+ private:
+  virtual ~AsyncTransferStateImpl() {}
 };
 
 }  // namespace
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_idle.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_idle.cc
index f4a3e481..d8bf916db 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_idle.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_idle.cc
@@ -32,7 +32,6 @@
         texture_id_(texture_id),
         transfer_in_progress_(false) {
   }
-  virtual ~AsyncPixelTransferStateImpl() {}
 
   // Implement AsyncPixelTransferState:
   virtual bool TransferIsInProgress() OVERRIDE {
@@ -53,6 +52,8 @@
   }
 
  private:
+  virtual ~AsyncPixelTransferStateImpl() {}
+
   uint64 id_;
   GLuint texture_id_;
   bool transfer_in_progress_;
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.cc
index 2affd24..17d2b652 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.cc
@@ -10,6 +10,7 @@
 }
 
 MockAsyncPixelTransferState::~MockAsyncPixelTransferState() {
+  Destroy();
 }
 
 MockAsyncPixelTransferDelegate::MockAsyncPixelTransferDelegate() {
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h
index 146eaa46..f16934c 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h
@@ -15,6 +15,9 @@
  public:
   MockAsyncPixelTransferState();
 
+  // Called in ~MockAsyncPixelTransferState.
+  MOCK_METHOD0(Destroy, void());
+
   // Implement AsyncPixelTransferState.
   MOCK_METHOD0(TransferIsInProgress, bool());
   MOCK_METHOD1(BindTransfer, void(AsyncTexImage2DParams* level_params));
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc
index a71bd66..7a9c1a9 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_share_group.cc
@@ -305,12 +305,6 @@
                          const AsyncTexImage2DParams& define_params)
       : internal_(new TransferStateInternal(texture_id, define_params)) {}
 
-  virtual ~AsyncTransferStateImpl() {
-    TRACE_EVENT0("gpu", " ~AsyncTransferStateImpl");
-    base::AutoLock locked(*internal_->upload_lock());
-    internal_->cancel_upload_flag()->Set();
-  }
-
   virtual bool TransferIsInProgress() OVERRIDE {
       return internal_->TransferIsInProgress();
   }
@@ -318,6 +312,12 @@
   TransferStateInternal* internal() { return internal_.get(); }
 
  private:
+  virtual ~AsyncTransferStateImpl() {
+    TRACE_EVENT0("gpu", " ~AsyncTransferStateImpl");
+    base::AutoLock locked(*internal_->upload_lock());
+    internal_->cancel_upload_flag()->Set();
+  }
+
   scoped_refptr<TransferStateInternal> internal_;
 };
 
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_stub.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_stub.cc
index 474e245..5bd7db9 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_stub.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_stub.cc
@@ -11,12 +11,14 @@
 class AsyncPixelTransferStateImpl : public AsyncPixelTransferState {
  public:
   AsyncPixelTransferStateImpl() {}
-  virtual ~AsyncPixelTransferStateImpl() {}
 
   // Implement AsyncPixelTransferState:
   virtual bool TransferIsInProgress() OVERRIDE {
     return false;
   }
+
+ private:
+  virtual ~AsyncPixelTransferStateImpl() {}
 };
 
 }  // namespace
diff --git a/gpu/command_buffer/service/async_pixel_transfer_delegate_sync.cc b/gpu/command_buffer/service/async_pixel_transfer_delegate_sync.cc
index dd132a6..6eb58f2 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_delegate_sync.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_delegate_sync.cc
@@ -11,12 +11,14 @@
 class AsyncPixelTransferStateImpl : public AsyncPixelTransferState {
  public:
   AsyncPixelTransferStateImpl() {}
-  virtual ~AsyncPixelTransferStateImpl() {}
 
   // Implement AsyncPixelTransferState:
   virtual bool TransferIsInProgress() OVERRIDE {
     return false;
   }
+
+ private:
+  virtual ~AsyncPixelTransferStateImpl() {}
 };
 
 }  // namespace
diff --git a/gpu/command_buffer/service/async_pixel_transfer_manager.cc b/gpu/command_buffer/service/async_pixel_transfer_manager.cc
new file mode 100644
index 0000000..92ecfd6
--- /dev/null
+++ b/gpu/command_buffer/service/async_pixel_transfer_manager.cc
@@ -0,0 +1,72 @@
+// 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/async_pixel_transfer_manager.h"
+
+#include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
+
+namespace gpu {
+
+AsyncPixelTransferManager::AsyncPixelTransferManager(
+    gles2::TextureManager* manager,
+    gfx::GLContext* context)
+    : manager_(manager),
+      delegate_(AsyncPixelTransferDelegate::Create(context)) {
+  manager_->AddObserver(this);
+}
+
+AsyncPixelTransferManager::~AsyncPixelTransferManager() {
+  if (manager_)
+    manager_->RemoveObserver(this);
+}
+
+AsyncPixelTransferState* AsyncPixelTransferManager::CreatePixelTransferState(
+    gles2::TextureRef* ref,
+    const AsyncTexImage2DParams& define_params) {
+  DCHECK(!GetPixelTransferState(ref));
+  AsyncPixelTransferState* state = delegate_->CreatePixelTransferState(
+      ref->texture()->service_id(), define_params);
+  state_map_[ref] = state;
+  return state;
+}
+
+AsyncPixelTransferState*
+AsyncPixelTransferManager::GetPixelTransferState(
+    gles2::TextureRef* ref) {
+  TextureToStateMap::iterator it = state_map_.find(ref);
+  if (it == state_map_.end()) {
+    return NULL;
+  } else {
+    return it->second;
+  }
+}
+
+void AsyncPixelTransferManager::ClearPixelTransferStateForTest(
+    gles2::TextureRef* ref) {
+  TextureToStateMap::iterator it = state_map_.find(ref);
+  if (it != state_map_.end())
+    state_map_.erase(it);
+}
+
+bool AsyncPixelTransferManager::AsyncTransferIsInProgress(
+    gles2::TextureRef* ref) {
+  AsyncPixelTransferState* state = GetPixelTransferState(ref);
+  return state && state->TransferIsInProgress();
+}
+
+void AsyncPixelTransferManager::OnTextureManagerDestroying(
+    gles2::TextureManager* manager) {
+  // TextureManager should outlive AsyncPixelTransferManager.
+  NOTREACHED();
+  manager_ = NULL;
+}
+
+void AsyncPixelTransferManager::OnTextureRefDestroying(
+    gles2::TextureRef* texture) {
+  TextureToStateMap::iterator it = state_map_.find(texture);
+  if (it != state_map_.end())
+    state_map_.erase(it);
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/async_pixel_transfer_manager.h b/gpu/command_buffer/service/async_pixel_transfer_manager.h
new file mode 100644
index 0000000..2f61099
--- /dev/null
+++ b/gpu/command_buffer/service/async_pixel_transfer_manager.h
@@ -0,0 +1,83 @@
+// 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.
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_MANAGER_H_
+#define GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_MANAGER_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "gpu/command_buffer/service/texture_manager.h"
+#include "gpu/gpu_export.h"
+
+#if defined(COMPILER_GCC)
+namespace BASE_HASH_NAMESPACE {
+template <>
+  struct hash<gpu::gles2::TextureRef*> {
+  size_t operator()(gpu::gles2::TextureRef* ptr) const {
+    return hash<size_t>()(reinterpret_cast<size_t>(ptr));
+  }
+};
+}  // namespace BASE_HASH_NAMESPACE
+#endif  // COMPILER
+
+namespace gfx {
+class GLContext;
+}
+
+namespace gpu {
+class AsyncPixelTransferDelegate;
+class AsyncPixelTransferState;
+struct AsyncTexImage2DParams;
+
+class GPU_EXPORT AsyncPixelTransferManager
+    : public gles2::TextureManager::DestructionObserver {
+ public:
+  AsyncPixelTransferManager(gles2::TextureManager* texture_manager_,
+                            gfx::GLContext* context);
+  virtual ~AsyncPixelTransferManager();
+
+  AsyncPixelTransferDelegate* GetAsyncPixelTransferDelegate() {
+    return delegate_.get();
+  }
+
+  void SetAsyncPixelTransferDelegateForTest(
+      AsyncPixelTransferDelegate* delegate) {
+    delegate_ = make_scoped_ptr(delegate);
+  }
+
+  AsyncPixelTransferState* CreatePixelTransferState(
+      gles2::TextureRef* ref,
+      const AsyncTexImage2DParams& define_params);
+
+  AsyncPixelTransferState* GetPixelTransferState(
+      gles2::TextureRef* ref);
+
+  void ClearPixelTransferStateForTest(gles2::TextureRef* ref);
+
+  bool AsyncTransferIsInProgress(gles2::TextureRef* ref);
+
+  // gles2::TextureRef::DestructionObserver implementation:
+  virtual void OnTextureManagerDestroying(gles2::TextureManager* manager)
+      OVERRIDE;
+  virtual void OnTextureRefDestroying(gles2::TextureRef* texture) OVERRIDE;
+
+ private:
+  gles2::TextureManager* manager_;
+
+  scoped_ptr<AsyncPixelTransferDelegate> delegate_;
+
+  typedef base::hash_map<gles2::TextureRef*,
+                         scoped_refptr<AsyncPixelTransferState> >
+      TextureToStateMap;
+  TextureToStateMap state_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferManager);
+};
+
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_ASYNC_PIXEL_TRANSFER_MANAGER_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 28d3b57..6bfcc18 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -56,6 +56,7 @@
 #include "gpu/command_buffer/service/vertex_attrib_manager.h"
 #include "gpu/command_buffer/service/vertex_array_manager.h"
 #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
+#include "gpu/command_buffer/service/async_pixel_transfer_manager.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_image.h"
 #include "ui/gl/gl_implementation.h"
@@ -574,8 +575,11 @@
 
   virtual AsyncPixelTransferDelegate*
       GetAsyncPixelTransferDelegate() OVERRIDE;
-  virtual void SetAsyncPixelTransferDelegate(
+  virtual void SetAsyncPixelTransferDelegateForTest(
       AsyncPixelTransferDelegate* delegate) OVERRIDE;
+  virtual AsyncPixelTransferManager*
+      GetAsyncPixelTransferManager() OVERRIDE;
+  virtual void ResetAsyncPixelTransferManagerForTest() OVERRIDE;
   void ProcessFinishedAsyncTransfers();
 
   virtual bool GetServiceTextureId(uint32 client_texture_id,
@@ -820,7 +824,7 @@
   // Extra validation for async tex(Sub)Image2D.
   bool ValidateAsyncTransfer(
       const char* function_name,
-      Texture* texture,
+      TextureRef* texture_ref,
       GLenum target,
       GLint level,
       const void * data);
@@ -1632,7 +1636,7 @@
   ShaderCacheCallback shader_cache_callback_;
 
   StreamTextureManager* stream_texture_manager_;
-  scoped_ptr<AsyncPixelTransferDelegate> async_pixel_transfer_delegate_;
+  scoped_ptr<AsyncPixelTransferManager> async_pixel_transfer_manager_;
 
   // The format of the back buffer_
   GLenum back_buffer_color_format_;
@@ -2471,9 +2475,8 @@
   if (!offscreen)
     context_->SetSafeToForceGpuSwitch();
 
-  // Create a delegate to perform async pixel transfers.
-  async_pixel_transfer_delegate_.reset(
-      AsyncPixelTransferDelegate::Create(context.get()));
+  async_pixel_transfer_manager_.reset(
+      new AsyncPixelTransferManager(texture_manager(), context.get()));
 
   return true;
 }
@@ -2789,7 +2792,7 @@
   // from the client, as the client may have recieved an async
   // completion while issuing those commands.
   // "DidFlushStart" would be ideal if we had such a callback.
-  async_pixel_transfer_delegate_->BindCompletedAsyncTransfers();
+  GetAsyncPixelTransferDelegate()->BindCompletedAsyncTransfers();
 }
 
 void GLES2DecoderImpl::ReleaseCurrent() {
@@ -3053,12 +3056,21 @@
 
 AsyncPixelTransferDelegate*
     GLES2DecoderImpl::GetAsyncPixelTransferDelegate() {
-  return async_pixel_transfer_delegate_.get();
+  return async_pixel_transfer_manager_->GetAsyncPixelTransferDelegate();
 }
 
-void GLES2DecoderImpl::SetAsyncPixelTransferDelegate(
+void GLES2DecoderImpl::SetAsyncPixelTransferDelegateForTest(
     AsyncPixelTransferDelegate* delegate) {
-  async_pixel_transfer_delegate_ = make_scoped_ptr(delegate);
+  async_pixel_transfer_manager_->SetAsyncPixelTransferDelegateForTest(delegate);
+}
+
+AsyncPixelTransferManager*
+    GLES2DecoderImpl::GetAsyncPixelTransferManager() {
+  return async_pixel_transfer_manager_.get();
+}
+
+void GLES2DecoderImpl::ResetAsyncPixelTransferManagerForTest() {
+  async_pixel_transfer_manager_.reset();
 }
 
 bool GLES2DecoderImpl::GetServiceTextureId(uint32 client_texture_id,
@@ -3073,12 +3085,12 @@
 
 uint32 GLES2DecoderImpl::GetTextureUploadCount() {
   return texture_upload_count_ +
-      async_pixel_transfer_delegate_->GetTextureUploadCount();
+         GetAsyncPixelTransferDelegate()->GetTextureUploadCount();
 }
 
 base::TimeDelta GLES2DecoderImpl::GetTotalTextureUploadTime() {
   return total_texture_upload_time_ +
-      async_pixel_transfer_delegate_->GetTotalTextureUploadTime();
+         GetAsyncPixelTransferDelegate()->GetTotalTextureUploadTime();
 }
 
 base::TimeDelta GLES2DecoderImpl::GetTotalProcessingCommandsTime() {
@@ -3190,6 +3202,10 @@
   offscreen_resolved_frame_buffer_.reset();
   offscreen_resolved_color_texture_.reset();
 
+  // Should destroy the transfer manager before the texture manager held
+  // by the context group.
+  async_pixel_transfer_manager_.reset();
+
   if (group_) {
     group_->Destroy(this, have_context);
     group_ = NULL;
@@ -8095,7 +8111,7 @@
         GL_INVALID_VALUE, "glCopyTexSubImage2D", "bad dimensions.");
     return;
   }
-  if (texture->AsyncTransferIsInProgress()) {
+  if (async_pixel_transfer_manager_->AsyncTransferIsInProgress(texture_ref)) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         "glCopyTexSubImage2D", "async upload pending for texture");
@@ -8231,7 +8247,7 @@
         function_name, "type does not match type of texture.");
     return false;
   }
-  if (texture->AsyncTransferIsInProgress()) {
+  if (async_pixel_transfer_manager_->AsyncTransferIsInProgress(texture_ref)) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         function_name, "async upload pending for texture");
@@ -9096,13 +9112,13 @@
 }
 
 bool GLES2DecoderImpl::HasMoreIdleWork() {
-  return async_pixel_transfer_delegate_->NeedsProcessMorePendingTransfers();
+  return GetAsyncPixelTransferDelegate()->NeedsProcessMorePendingTransfers();
 }
 
 void GLES2DecoderImpl::PerformIdleWork() {
-  if (!async_pixel_transfer_delegate_->NeedsProcessMorePendingTransfers())
+  if (!GetAsyncPixelTransferDelegate()->NeedsProcessMorePendingTransfers())
     return;
-  async_pixel_transfer_delegate_->ProcessMorePendingTransfers();
+  GetAsyncPixelTransferDelegate()->ProcessMorePendingTransfers();
   ProcessFinishedAsyncTransfers();
 }
 
@@ -10099,7 +10115,7 @@
 
 bool GLES2DecoderImpl::ValidateAsyncTransfer(
     const char* function_name,
-    Texture* texture,
+    TextureRef* texture_ref,
     GLenum target,
     GLint level,
     const void * data) {
@@ -10119,7 +10135,8 @@
     return false;
   }
   // We only support one async transfer in progress.
-  if (!texture || texture->AsyncTransferIsInProgress()) {
+  if (!texture_ref ||
+      async_pixel_transfer_manager_->AsyncTransferIsInProgress(texture_ref)) {
     LOCAL_SET_GL_ERROR(
         GL_INVALID_OPERATION,
         function_name, "transfer already in progress");
@@ -10170,7 +10187,7 @@
   TextureRef* texture_ref = GetTextureInfoForTarget(target);
   Texture* texture = texture_ref->texture();
   if (!ValidateAsyncTransfer(
-      "glAsyncTexImage2DCHROMIUM", texture, target, level, pixels))
+      "glAsyncTexImage2DCHROMIUM", texture_ref, target, level, pixels))
     return error::kNoError;
 
   // Don't allow async redefinition of a textures.
@@ -10205,16 +10222,13 @@
   // Set up the async state if needed, and make the texture
   // immutable so the async state stays valid. The level info
   // is set up lazily when the transfer completes.
-  DCHECK(!texture->GetAsyncTransferState());
-  texture_ref->SetAsyncTransferState(
-      make_scoped_ptr(
-          async_pixel_transfer_delegate_->CreatePixelTransferState(
-              texture->service_id(),
-              tex_params)));
+  AsyncPixelTransferState* state =
+      async_pixel_transfer_manager_->CreatePixelTransferState(texture_ref,
+                                                              tex_params);
   texture->SetImmutable(true);
 
-  async_pixel_transfer_delegate_->AsyncTexImage2D(
-      texture->GetAsyncTransferState(),
+  GetAsyncPixelTransferDelegate()->AsyncTexImage2D(
+      state,
       tex_params,
       mem_params,
       base::Bind(&TextureManager::SetLevelInfoFromParams,
@@ -10261,7 +10275,7 @@
   TextureRef* texture_ref = GetTextureInfoForTarget(target);
   Texture* texture = texture_ref->texture();
   if (!ValidateAsyncTransfer(
-         "glAsyncTexSubImage2DCHROMIUM", texture, target, level, pixels))
+         "glAsyncTexSubImage2DCHROMIUM", texture_ref, target, level, pixels))
     return error::kNoError;
 
   // Guarantee async textures are always 'cleared' as follows:
@@ -10293,7 +10307,8 @@
                                               width, height, format, type};
   AsyncMemoryParams mem_params = {shared_memory, shm_size,
                                        shm_data_offset, shm_data_size};
-  AsyncPixelTransferState* state = texture->GetAsyncTransferState();
+  AsyncPixelTransferState* state =
+      async_pixel_transfer_manager_->GetPixelTransferState(texture_ref);
   if (!state) {
     // TODO(epenner): We may want to enforce exclusive use
     // of async APIs in which case this should become an error,
@@ -10306,14 +10321,12 @@
                                          &define_params.internal_format);
     // Set up the async state if needed, and make the texture
     // immutable so the async state stays valid.
-    state = async_pixel_transfer_delegate_->CreatePixelTransferState(
-        texture->service_id(),
-        define_params);
-    texture_ref->SetAsyncTransferState(make_scoped_ptr(state));
+    state = async_pixel_transfer_manager_->CreatePixelTransferState(
+        texture_ref, define_params);
     texture->SetImmutable(true);
   }
 
-  async_pixel_transfer_delegate_->AsyncTexSubImage2D(
+  GetAsyncPixelTransferDelegate()->AsyncTexSubImage2D(
       state, tex_params, mem_params);
   return error::kNoError;
 }
@@ -10336,14 +10349,14 @@
     return error::kNoError;
   }
   AsyncPixelTransferState* state =
-      texture_ref->texture()->GetAsyncTransferState();
+      async_pixel_transfer_manager_->GetPixelTransferState(texture_ref);
   if (!state) {
       LOCAL_SET_GL_ERROR(
           GL_INVALID_OPERATION,
           "glWaitAsyncTexImage2DCHROMIUM", "No async transfer started");
     return error::kNoError;
   }
-  async_pixel_transfer_delegate_->WaitForTransferCompletion(state);
+  GetAsyncPixelTransferDelegate()->WaitForTransferCompletion(state);
   ProcessFinishedAsyncTransfers();
   return error::kNoError;
 }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h
index 86d4801..65ebe76 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.h
@@ -26,6 +26,7 @@
 namespace gpu {
 
 class AsyncPixelTransferDelegate;
+class AsyncPixelTransferManager;
 class StreamTextureManager;
 
 namespace gles2 {
@@ -179,9 +180,12 @@
 
   // Interface to performing async pixel transfers.
   virtual AsyncPixelTransferDelegate* GetAsyncPixelTransferDelegate() = 0;
-  virtual void SetAsyncPixelTransferDelegate(
+  virtual void SetAsyncPixelTransferDelegateForTest(
       AsyncPixelTransferDelegate* delegate) = 0;
 
+  virtual AsyncPixelTransferManager* GetAsyncPixelTransferManager() = 0;
+  virtual void ResetAsyncPixelTransferManagerForTest() = 0;
+
   // Get the service texture ID corresponding to a client texture ID.
   // If no such record is found then return false.
   virtual bool GetServiceTextureId(uint32 client_texture_id,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
index 3f714bdf..ded5e4b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
@@ -73,8 +73,11 @@
   MOCK_METHOD1(SetStreamTextureManager, void(StreamTextureManager*));
   MOCK_METHOD0(GetAsyncPixelTransferDelegate,
       AsyncPixelTransferDelegate*());
-  MOCK_METHOD1(SetAsyncPixelTransferDelegate,
+  MOCK_METHOD1(SetAsyncPixelTransferDelegateForTest,
       void(AsyncPixelTransferDelegate*));
+  MOCK_METHOD0(GetAsyncPixelTransferManager,
+      AsyncPixelTransferManager*());
+  MOCK_METHOD0(ResetAsyncPixelTransferManagerForTest, void());
   MOCK_METHOD3(DoCommand, error::Error(unsigned int command,
                                        unsigned int arg_count,
                                        const void* cmd_data));
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index c2413d1..b4baf8b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -9,6 +9,7 @@
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/common/id_allocator.h"
 #include "gpu/command_buffer/service/async_pixel_transfer_delegate_mock.h"
+#include "gpu/command_buffer/service/async_pixel_transfer_manager.h"
 #include "gpu/command_buffer/service/cmd_buffer_engine.h"
 #include "gpu/command_buffer/service/context_group.h"
 #include "gpu/command_buffer/service/gl_surface_mock.h"
@@ -8082,7 +8083,7 @@
   // Set a mock Async delegate
   StrictMock<gpu::MockAsyncPixelTransferDelegate>* delegate =
       new StrictMock<gpu::MockAsyncPixelTransferDelegate>;
-  decoder_->SetAsyncPixelTransferDelegate(delegate);
+  decoder_->SetAsyncPixelTransferDelegateForTest(delegate);
   StrictMock<gpu::MockAsyncPixelTransferState>* state = NULL;
 
   // Tex(Sub)Image2D upload commands.
@@ -8096,7 +8097,8 @@
   wait_cmd.Init(GL_TEXTURE_2D);
 
   // No transfer state exists initially.
-  EXPECT_FALSE(texture->GetAsyncTransferState());
+  EXPECT_FALSE(decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+      texture_ref));
 
   base::Closure bind_callback;
 
@@ -8113,7 +8115,8 @@
     // Command succeeds.
     EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd));
     EXPECT_EQ(GL_NO_ERROR, GetGLError());
-    EXPECT_TRUE(texture->GetAsyncTransferState());
+    EXPECT_TRUE(decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+        texture_ref));
     EXPECT_TRUE(texture->IsImmutable());
     // The texture is safe but the level has not been defined yet.
     EXPECT_TRUE(texture->SafeToRenderFrom());
@@ -8125,7 +8128,8 @@
     // Command fails.
     EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd));
     EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
-    EXPECT_TRUE(texture->GetAsyncTransferState());
+    EXPECT_TRUE(decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+        texture_ref));
     EXPECT_TRUE(texture->IsImmutable());
     EXPECT_TRUE(texture->SafeToRenderFrom());
   }
@@ -8149,7 +8153,8 @@
   }
 
   // AsyncTexSubImage2D
-  texture_ref->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
+  decoder_->GetAsyncPixelTransferManager()
+      ->ClearPixelTransferStateForTest(texture_ref);
   texture->SetImmutable(false);
   {
     // Create transfer state since it doesn't exist.
@@ -8162,7 +8167,8 @@
     // Command succeeds.
     EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd));
     EXPECT_EQ(GL_NO_ERROR, GetGLError());
-    EXPECT_TRUE(texture->GetAsyncTransferState());
+    EXPECT_TRUE(decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+        texture_ref));
     EXPECT_TRUE(texture->IsImmutable());
     EXPECT_TRUE(texture->SafeToRenderFrom());
   }
@@ -8177,7 +8183,8 @@
     // Command succeeds.
     EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd));
     EXPECT_EQ(GL_NO_ERROR, GetGLError());
-    EXPECT_TRUE(texture->GetAsyncTransferState());
+    EXPECT_TRUE(decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+        texture_ref));
     EXPECT_TRUE(texture->IsImmutable());
     EXPECT_TRUE(texture->SafeToRenderFrom());
   }
@@ -8189,23 +8196,33 @@
     // No async call, command fails.
     EXPECT_EQ(error::kNoError, ExecuteCmd(texsubimage_cmd));
     EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
-    EXPECT_TRUE(texture->GetAsyncTransferState());
+    EXPECT_TRUE(decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+        texture_ref));
     EXPECT_TRUE(texture->IsImmutable());
     EXPECT_TRUE(texture->SafeToRenderFrom());
   }
 
+  // Delete state on DeleteTexture.
+  {
+    EXPECT_CALL(*state, Destroy()).RetiresOnSaturation();
+    DoDeleteTexture(client_texture_id_, kServiceTextureId);
+    EXPECT_FALSE(
+        decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+            texture_ref));
+    state = NULL;
+  }
+
   // WaitAsyncTexImage2D
   {
     // Get a fresh texture since the existing texture cannot be respecified
     // asynchronously and AsyncTexSubImage2D does not involved binding.
     EXPECT_CALL(*gl_, GenTextures(1, _))
         .WillOnce(SetArgumentPointee<1>(kServiceTextureId));
-    texture_ref->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
-    DoDeleteTexture(client_texture_id_, kServiceTextureId);
     DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
     texture_ref = GetTexture(client_texture_id_);
     texture = texture_ref->texture();
-    texture_ref->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
+    decoder_->GetAsyncPixelTransferManager()
+        ->ClearPixelTransferStateForTest(texture_ref);
     texture->SetImmutable(false);
     // Create transfer state since it doesn't exist.
     EXPECT_CALL(*delegate, CreatePixelTransferState(kServiceTextureId, _))
@@ -8217,7 +8234,9 @@
     // Start async transfer.
     EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd));
     EXPECT_EQ(GL_NO_ERROR, GetGLError());
-    EXPECT_TRUE(texture->GetAsyncTransferState());
+    EXPECT_TRUE(decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+        texture_ref));
+
     EXPECT_TRUE(texture->IsImmutable());
     // Wait for completion.
     EXPECT_CALL(*delegate, WaitForTransferCompletion(state));
@@ -8226,8 +8245,63 @@
     EXPECT_EQ(GL_NO_ERROR, GetGLError());
   }
 
-  decoder_->SetAsyncPixelTransferDelegate(NULL);
-  texture_ref->SetAsyncTransferState(scoped_ptr<AsyncPixelTransferState>());
+  decoder_->SetAsyncPixelTransferDelegateForTest(NULL);
+  decoder_->GetAsyncPixelTransferManager()
+      ->ClearPixelTransferStateForTest(texture_ref);
+}
+
+TEST_F(GLES2DecoderManualInitTest, AsyncPixelTransferManager) {
+  InitDecoder(
+      "GL_CHROMIUM_async_pixel_transfers",  // extensions
+      false, false, false,  // has alpha/depth/stencil
+      false, false, false,  // request alpha/depth/stencil
+      true);   // bind generates resource
+
+  // Set up the texture.
+  DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
+  TextureRef* texture_ref = GetTexture(client_texture_id_);
+
+  // Set a mock Async delegate.
+  StrictMock<gpu::MockAsyncPixelTransferDelegate>* delegate =
+      new StrictMock<gpu::MockAsyncPixelTransferDelegate>;
+  decoder_->SetAsyncPixelTransferDelegateForTest(delegate);
+  StrictMock<gpu::MockAsyncPixelTransferState>* state = NULL;
+
+  AsyncTexImage2DCHROMIUM teximage_cmd;
+  teximage_cmd.Init(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA,
+                    GL_UNSIGNED_BYTE, kSharedMemoryId, kSharedMemoryOffset);
+
+  // No transfer state exists initially.
+  EXPECT_FALSE(decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+      texture_ref));
+
+  // Create state on AsyncTexImage2D.
+  {
+    EXPECT_CALL(*delegate, CreatePixelTransferState(kServiceTextureId, _))
+        .WillOnce(
+             Return(state = new StrictMock<gpu::MockAsyncPixelTransferState>))
+        .RetiresOnSaturation();
+    EXPECT_CALL(*delegate, AsyncTexImage2D(state, _, _, _))
+        .RetiresOnSaturation();
+
+    // Command succeeds.
+    EXPECT_EQ(error::kNoError, ExecuteCmd(teximage_cmd));
+    EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  }
+
+  // State is cached.
+  EXPECT_EQ(state,
+            decoder_->GetAsyncPixelTransferManager()->GetPixelTransferState(
+                texture_ref));
+
+  // Delete state on manager teardown.
+  {
+    EXPECT_CALL(*state, Destroy()).RetiresOnSaturation();
+    decoder_->ResetAsyncPixelTransferManagerForTest();
+
+    // Texture ref still valid.
+    EXPECT_EQ(texture_ref, GetTexture(client_texture_id_));
+  }
 }
 
 namespace {
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 9e67849a..f878df3f 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -60,7 +60,15 @@
   }
 }
 
+TextureManager::DestructionObserver::DestructionObserver() {}
+
+TextureManager::DestructionObserver::~DestructionObserver() {}
+
 TextureManager::~TextureManager() {
+  FOR_EACH_OBSERVER(DestructionObserver,
+                    destruction_observers_,
+                    OnTextureManagerDestroying(this));
+
   DCHECK(textures_.empty());
 
   // If this triggers, that means something is keeping a reference to
@@ -432,15 +440,6 @@
     (*it)->manager()->IncFramebufferStateChangeCount();
 }
 
-AsyncPixelTransferState* Texture::GetAsyncTransferState() const {
-  for (RefSet::const_iterator it = refs_.begin(); it != refs_.end(); ++it) {
-    AsyncPixelTransferState* state = (*it)->async_transfer_state();
-    if (state)
-      return state;
-  }
-  return NULL;
-}
-
 void Texture::SetLevelInfo(
     const FeatureInfo* feature_info,
     GLenum target,
@@ -1097,6 +1096,10 @@
 }
 
 void TextureManager::StopTracking(TextureRef* ref) {
+  FOR_EACH_OBSERVER(DestructionObserver,
+                    destruction_observers_,
+                    OnTextureRefDestroying(ref));
+
   Texture* texture = ref->texture();
   --texture_count_;
   if (!texture->CanRender(feature_info_)) {
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index ec0b5a6..2d4640e 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -13,6 +13,7 @@
 #include "base/hash_tables.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
 #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
 #include "gpu/command_buffer/service/gl_utils.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
@@ -144,15 +145,6 @@
     return stream_texture_;
   }
 
-  // Gets the async transfer state for this texture. Note: the transfer state is
-  // owned by a single TextureRef.
-  AsyncPixelTransferState* GetAsyncTransferState() const;
-
-  bool AsyncTransferIsInProgress() {
-    AsyncPixelTransferState* state = GetAsyncTransferState();
-    return state && state->TransferIsInProgress();
-  }
-
   void SetImmutable(bool immutable) {
     immutable_ = immutable;
   }
@@ -411,15 +403,6 @@
   GLuint client_id() const { return client_id_; }
   GLuint service_id() const { return texture_->service_id(); }
 
-  // Sets the async transfer state for this texture. Only a single TextureRef
-  // can set this on a given texture at any time.
-  // NOTE: this should be per-context rather than per-texture. crbug.com/240504
-  void SetAsyncTransferState(
-      scoped_ptr<AsyncPixelTransferState> state) {
-    DCHECK(!state || !texture_->GetAsyncTransferState());
-    async_transfer_state_ = state.Pass();
-  }
-
  private:
   friend class base::RefCounted<TextureRef>;
   friend class Texture;
@@ -428,18 +411,12 @@
   ~TextureRef();
   const TextureManager* manager() const { return manager_; }
   TextureManager* manager() { return manager_; }
-  AsyncPixelTransferState* async_transfer_state() const {
-    return async_transfer_state_.get();
-  }
   void reset_client_id() { client_id_ = 0; }
 
   TextureManager* manager_;
   Texture* texture_;
   GLuint client_id_;
 
-  // State to facilitate async transfers on this texture.
-  scoped_ptr<AsyncPixelTransferState> async_transfer_state_;
-
   DISALLOW_COPY_AND_ASSIGN(TextureRef);
 };
 
@@ -450,6 +427,21 @@
 // shared by multiple GLES2Decoders.
 class GPU_EXPORT TextureManager {
  public:
+  class GPU_EXPORT DestructionObserver {
+   public:
+    DestructionObserver();
+    virtual ~DestructionObserver();
+
+    // Called in ~TextureManager.
+    virtual void OnTextureManagerDestroying(TextureManager* manager) = 0;
+
+    // Called via ~TextureRef.
+    virtual void OnTextureRefDestroying(TextureRef* texture) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
+  };
+
   enum DefaultAndBlackTextures {
     kTexture2D,
     kCubeMap,
@@ -654,6 +646,14 @@
       GLint level,
       std::string* signature) const;
 
+  void AddObserver(DestructionObserver* observer) {
+    destruction_observers_.AddObserver(observer);
+  }
+
+  void RemoveObserver(DestructionObserver* observer) {
+    destruction_observers_.RemoveObserver(observer);
+  }
+
  private:
   friend class Texture;
   friend class TextureRef;
@@ -707,6 +707,8 @@
   // The default textures for each target (texture name = 0)
   scoped_refptr<TextureRef> default_textures_[kNumDefaultTextures];
 
+  ObserverList<DestructionObserver> destruction_observers_;
+
   DISALLOW_COPY_AND_ASSIGN(TextureManager);
 };