Bug 1176363 - Part 1: Stop using DrawTargets off the main thread. r=mattwoodrow
☠☠ backed out by ad59ccd84735 ☠ ☠
authorBas Schouten <bschouten@mozilla.com>
Mon, 27 Jul 2015 15:47:29 +0000
changeset 286439 adbd8a7608e4721c88c6e2217a933399b817241b
parent 286438 b6ebb4cba9e153f835937208312c4db707bad7b4
child 286440 34ade6cc41e13782ff04460b45a2755c83d88446
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1176363
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1176363 - Part 1: Stop using DrawTargets off the main thread. r=mattwoodrow
gfx/layers/ImageContainer.cpp
gfx/layers/TextureDIB.cpp
gfx/layers/TextureDIB.h
gfx/layers/basic/TextureClientX11.cpp
gfx/layers/basic/TextureClientX11.h
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/d3d11/TextureD3D11.cpp
gfx/layers/d3d11/TextureD3D11.h
gfx/layers/d3d9/TextureD3D9.cpp
gfx/layers/d3d9/TextureD3D9.h
gfx/layers/opengl/GrallocTextureClient.cpp
gfx/layers/opengl/GrallocTextureClient.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -634,30 +634,27 @@ CairoImage::GetTextureClient(Compositabl
     textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(),
                                                            surface->GetSize(),
                                                            gfx::BackendType::NONE,
                                                            TextureFlags::DEFAULT);
   }
   if (!textureClient) {
     return nullptr;
   }
-  MOZ_ASSERT(textureClient->CanExposeDrawTarget());
+
   if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
     return nullptr;
   }
 
-  TextureClientAutoUnlock autoUnolck(textureClient);
-  {
-    // We must not keep a reference to the DrawTarget after it has been unlocked.
-    DrawTarget* dt = textureClient->BorrowDrawTarget();
-    if (!dt) {
-      return nullptr;
-    }
-    dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
-  }
+  TextureClientAutoUnlock autoUnlock(textureClient);
+
+  RefPtr<DataSourceSurface> dataSurf = surface->GetDataSurface();
+  textureClient->UpdateFromSurface(dataSurf);
+
+  textureClient->SyncWithObject(forwarder->GetSyncObject());
 
   mTextureClients.Put(forwarder->GetSerial(), textureClient);
   return textureClient;
 }
 
 PImageContainerChild*
 ImageContainer::GetPImageContainerChild()
 {
--- a/gfx/layers/TextureDIB.cpp
+++ b/gfx/layers/TextureDIB.cpp
@@ -51,16 +51,35 @@ TextureClientDIB::BorrowDrawTarget()
   if (!mDrawTarget) {
     mDrawTarget =
       gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mSurface, mSize);
   }
 
   return mDrawTarget;
 }
 
+void
+TextureClientDIB::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  MOZ_ASSERT(mIsLocked && IsAllocated());
+
+  nsRefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
+
+  DataSourceSurface::MappedSurface sourceMap;
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+
+  for (int y = 0; y < aSurface->GetSize().height; y++) {
+    memcpy(imgSurf->Data() + imgSurf->Stride() * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
+  }
+
+  aSurface->Unmap();
+}
+
 TextureClientMemoryDIB::TextureClientMemoryDIB(ISurfaceAllocator* aAllocator,
                                    gfx::SurfaceFormat aFormat,
                                    TextureFlags aFlags)
   : TextureClientDIB(aAllocator, aFormat, aFlags)
 {
   MOZ_COUNT_CTOR(TextureClientMemoryDIB);
 }
 
--- a/gfx/layers/TextureDIB.h
+++ b/gfx/layers/TextureDIB.h
@@ -29,16 +29,18 @@ public:
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool HasInternalBuffer() const override { return true; }
 
 protected:
   TextureClientDIB(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat, TextureFlags aFlags)
     : TextureClient(aAllocator, aFlags)
     , mFormat(aFormat)
     , mIsLocked(false)
   { }
--- a/gfx/layers/basic/TextureClientX11.cpp
+++ b/gfx/layers/basic/TextureClientX11.cpp
@@ -147,8 +147,23 @@ TextureClientX11::BorrowDrawTarget()
 
   if (!mDrawTarget) {
     IntSize size = mSurface->GetSize();
     mDrawTarget = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
   }
 
   return mDrawTarget;
 }
+
+void
+TextureClientX11::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  MOZ_ASSERT(CanExposeDrawTarget());
+
+  DrawTarget* dt = BorrowDrawTarget();
+
+  if (!dt) {
+    gfxCriticalError() << "Failed to borrow drawtarget for TextureClientX11::UpdateFromSurface";
+    return;
+  }
+
+  dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint());
+}
\ No newline at end of file
--- a/gfx/layers/basic/TextureClientX11.h
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -38,16 +38,18 @@ class TextureClientX11 : public TextureC
   virtual bool IsLocked() const override { return mLocked; }
 
   virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags flags) override;
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool HasInternalBuffer() const override { return false; }
 
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -348,25 +348,27 @@ TextureClient::CreateForDrawing(ISurface
       aSize.width <= maxTextureSize &&
       aSize.height <= maxTextureSize) {
     texture = new TextureClientD3D11(aAllocator, aFormat, aTextureFlags);
   }
   if (parentBackend == LayersBackend::LAYERS_D3D9 &&
       aMoz2DBackend == gfx::BackendType::CAIRO &&
       aAllocator->IsSameProcess() &&
       aSize.width <= maxTextureSize &&
-      aSize.height <= maxTextureSize) {
+      aSize.height <= maxTextureSize &&
+      NS_IsMainThread()) {
     if (gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) {
       texture = new TextureClientD3D9(aAllocator, aFormat, aTextureFlags);
     }
   }
 
   if (!texture && aFormat == SurfaceFormat::B8G8R8X8 &&
       aAllocator->IsSameProcess() &&
-      aMoz2DBackend == gfx::BackendType::CAIRO) {
+      aMoz2DBackend == gfx::BackendType::CAIRO &&
+      NS_IsMainThread()) {
     if (aAllocator->IsSameProcess()) {
       texture = new TextureClientMemoryDIB(aAllocator, aFormat, aTextureFlags);
     } else {
       texture = new TextureClientShmemDIB(aAllocator, aFormat, aTextureFlags);
     }
   }
 #endif
 
@@ -796,16 +798,17 @@ BufferTextureClient::AllocateForSurface(
   mSize = aSize;
   return true;
 }
 
 gfx::DrawTarget*
 BufferTextureClient::BorrowDrawTarget()
 {
   MOZ_ASSERT(IsValid());
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mLocked, "BorrowDrawTarget should be called on locked textures only");
   if (!mLocked) {
     return nullptr;
   }
 
   if (mDrawTarget) {
     mDrawTarget->SetTransform(Matrix());
     return mDrawTarget;
@@ -821,16 +824,43 @@ BufferTextureClient::BorrowDrawTarget()
     return mDrawTarget;
   }
 
   mDrawTarget = serializer.GetAsDrawTarget(BackendType::CAIRO);
 
   return mDrawTarget;
 }
 
+void
+BufferTextureClient::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
+
+  RefPtr<DataSourceSurface> surface = serializer.GetAsSurface();
+
+  if (surface->GetSize() != aSurface->GetSize() || surface->GetFormat() != aSurface->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+    return;
+  }
+
+  DataSourceSurface::MappedSurface sourceMap;
+  DataSourceSurface::MappedSurface destMap;
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+  surface->Map(DataSourceSurface::WRITE, &destMap);
+
+  for (int y = 0; y < aSurface->GetSize().height; y++) {
+    memcpy(destMap.mData + destMap.mStride * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
+  }
+
+  aSurface->Unmap();
+  surface->Unmap();
+}
+
 bool
 BufferTextureClient::Lock(OpenMode aMode)
 {
   MOZ_ASSERT(!mLocked, "The TextureClient is already Locked!");
   mOpenMode = aMode;
   mLocked = IsValid() && IsAllocated();;
   return mLocked;
 }
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -238,16 +238,17 @@ public:
   virtual void Unlock() {}
 
   virtual bool IsLocked() const = 0;
 
   virtual bool CanExposeDrawTarget() const { return false; }
 
   /**
    * Returns a DrawTarget to draw into the TextureClient.
+   * This function should never be called when not on the main thread!
    *
    * This must never be called on a TextureClient that is not sucessfully locked.
    * When called several times within one Lock/Unlock pair, this method should
    * return the same DrawTarget.
    * The DrawTarget is automatically flushed by the TextureClient when the latter
    * is unlocked, and the DrawTarget that will be returned within the next
    * lock/unlock pair may or may not be the same object.
    * Do not keep references to the DrawTarget outside of the lock/unlock pair.
@@ -263,16 +264,22 @@ public:
    *   DrawTarget* dt = texture->BorrowDrawTarget();
    *   // use the draw target ...
    * }
    * texture->Unlock();
    *
    */
   virtual gfx::DrawTarget* BorrowDrawTarget() { return nullptr; }
 
+  /**
+   * This function can be used to update the contents of the TextureClient
+   * off the main thread.
+   */
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) { MOZ_CRASH(); }
+
   // TextureClients that can expose a DrawTarget should override this method.
   virtual gfx::SurfaceFormat GetFormat() const
   {
     return gfx::SurfaceFormat::UNKNOWN;
   }
 
   /**
    * This method is strictly for debugging. It causes locking and
@@ -581,16 +588,18 @@ public:
   virtual bool IsLocked() const override { return mLocked; }
 
   uint8_t* GetLockedData() const;
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   // TextureClientYCbCr
 
   virtual TextureClientYCbCr* AsTextureClientYCbCr() override { return this; }
 
   virtual bool UpdateYCbCr(const PlanarYCbCrData& aData) override;
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -239,17 +239,18 @@ TextureClientD3D11::CreateSimilar(Textur
   }
 
   return tex.forget();
 }
 
 void
 TextureClientD3D11::SyncWithObject(SyncObject* aSyncObject)
 {
-  if (!aSyncObject) {
+  if (!aSyncObject || !NS_IsMainThread()) {
+    // When off the main thread we sync using a keyed mutex per texture.
     return;
   }
 
   MOZ_ASSERT(aSyncObject->GetSyncType() == SyncObject::SyncType::D3D11);
 
   SyncObjectD3D11* sync = static_cast<SyncObjectD3D11*>(aSyncObject);
 
   if (mTexture) {
@@ -273,43 +274,45 @@ TextureClientD3D11::Lock(OpenMode aMode)
   } else {
     MOZ_ASSERT(!mTexture);
     mIsLocked = LockD3DTexture(mTexture10.get());
   }
   if (!mIsLocked) {
     return false;
   }
 
-  // Make sure that successful write-lock means we will have a DrawTarget to
-  // write into.
-  if (aMode & OpenMode::OPEN_WRITE) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
-      Unlock();
-      return false;
-    }
-  }
-
-  if (mNeedsClear) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
+  if (NS_IsMainThread()) {
+    // Make sure that successful write-lock means we will have a DrawTarget to
+    // write into.
+    if (aMode & OpenMode::OPEN_WRITE) {
+      mDrawTarget = BorrowDrawTarget();
+      if (!mDrawTarget) {
         Unlock();
         return false;
+      }
     }
-    mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
-    mNeedsClear = false;
-  }
-  if (mNeedsClearWhite) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
+
+    if (mNeedsClear) {
+      mDrawTarget = BorrowDrawTarget();
+      if (!mDrawTarget) {
         Unlock();
         return false;
+      }
+      mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
+      mNeedsClear = false;
     }
-    mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
-    mNeedsClearWhite = false;
+    if (mNeedsClearWhite) {
+      mDrawTarget = BorrowDrawTarget();
+      if (!mDrawTarget) {
+        Unlock();
+        return false;
+      }
+      mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+      mNeedsClearWhite = false;
+    }
   }
 
   return true;
 }
 
 void
 TextureClientD3D11::Unlock()
 {
@@ -322,17 +325,17 @@ TextureClientD3D11::Unlock()
     // see the comment on TextureClient::BorrowDrawTarget.
     // This DrawTarget is internal to the TextureClient and is only exposed to the
     // outside world between Lock() and Unlock(). This assertion checks that no outside
     // reference remains by the time Unlock() is called.
     MOZ_ASSERT(mDrawTarget->refCount() == 1);
     mDrawTarget->Flush();
   }
 
-  if (mReadbackSink && mTexture10) {
+  if (NS_IsMainThread() && mReadbackSink && mTexture10) {
     ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
 
     D3D10_TEXTURE2D_DESC desc;
     mTexture10->GetDesc(&desc);
     desc.BindFlags = 0;
     desc.Usage = D3D10_USAGE_STAGING;
     desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
     desc.MiscFlags = 0;
@@ -358,16 +361,17 @@ TextureClientD3D11::Unlock()
   }
   mIsLocked = false;
 }
 
 DrawTarget*
 TextureClientD3D11::BorrowDrawTarget()
 {
   MOZ_ASSERT(mIsLocked, "Calling TextureClient::BorrowDrawTarget without locking :(");
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!mIsLocked || (!mTexture && !mTexture10)) {
     gfxCriticalError() << "Attempted to borrow a DrawTarget without locking the texture.";
     return nullptr;
   }
 
   if (mDrawTarget) {
     return mDrawTarget;
@@ -382,16 +386,61 @@ TextureClientD3D11::BorrowDrawTarget()
     mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture10, mFormat);
   }
   if (!mDrawTarget) {
       gfxWarning() << "Invalid draw target for borrowing";
   }
   return mDrawTarget;
 }
 
+void
+TextureClientD3D11::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  DataSourceSurface::MappedSurface sourceMap;
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+
+  // Ensure unflushed work from our outstanding drawtarget won't override this
+  // update later.
+  mDrawTarget->Flush();
+
+  if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format!";
+    return;
+  }
+
+  if (mTexture) {
+    RefPtr<ID3D11Device> device;
+    mTexture->GetDevice(byRef(device));
+    RefPtr<ID3D11DeviceContext> ctx;
+    device->GetImmediateContext(byRef(ctx));
+
+    D3D11_BOX box;
+    box.front = 0;
+    box.back = 1;
+    box.top = box.left = 0;
+    box.right = aSurface->GetSize().width;
+    box.bottom = aSurface->GetSize().height;
+
+    ctx->UpdateSubresource(mTexture, 0, &box, sourceMap.mData, sourceMap.mStride, 0);
+  } else {
+    RefPtr<ID3D10Device> device;
+    mTexture10->GetDevice(byRef(device));
+
+    D3D10_BOX box;
+    box.front = 0;
+    box.back = 1;
+    box.top = box.left = 0;
+    box.right = aSurface->GetSize().width;
+    box.bottom = aSurface->GetSize().height;
+
+    device->UpdateSubresource(mTexture10, 0, &box, sourceMap.mData, sourceMap.mStride, 0);
+  }
+  aSurface->Unmap();
+}
+
 static const GUID sD3D11TextureUsage =
 { 0xd89275b0, 0x6c7d, 0x4038, { 0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e } };
 
 /* This class gets its lifetime tied to a D3D texture
  * and increments memory usage on construction and decrements
  * on destruction */
 class TextureMemoryMeasurer : public IUnknown
 {
@@ -442,28 +491,36 @@ TextureClientD3D11::AllocateForSurface(g
   HRESULT hr;
 
   if (mFormat == SurfaceFormat::A8) {
     // Currently TextureClientD3D11 does not support A8 surfaces. Fallback.
     return false;
   }
 
   gfxWindowsPlatform* windowsPlatform = gfxWindowsPlatform::GetPlatform();
-  ID3D11Device* d3d11device = windowsPlatform->GetD3D11ContentDevice();
-  bool haveD3d11Backend = windowsPlatform->GetContentBackend() == BackendType::DIRECT2D1_1;
+  ID3D11Device* d3d11device = windowsPlatform->GetD3D11DeviceForCurrentThread();
+
+  // When we're not on the main thread we're not going to be using Direct2D
+  // to access the contents of this texture client so we will always use D3D11.
+  bool haveD3d11Backend = windowsPlatform->GetContentBackend() == BackendType::DIRECT2D1_1 || !NS_IsMainThread();
 
   if (haveD3d11Backend) {
     MOZ_ASSERT(d3d11device != nullptr);
 
     CD3D11_TEXTURE2D_DESC newDesc(mFormat == SurfaceFormat::A8 ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM,
                                   aSize.width, aSize.height, 1, 1,
                                   D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
 
     newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
 
+    if (!NS_IsMainThread()) {
+      // On the main thread we use the syncobject to handle synchronization.
+      newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+    }
+
     hr = d3d11device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
     if (FAILED(hr)) {
       gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
       return false;
     }
     mTexture->SetPrivateDataInterface(sD3D11TextureUsage,
                                       new TextureMemoryMeasurer(newDesc.Width * newDesc.Height *
                                                                 (mFormat == SurfaceFormat::A8 ?
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -59,16 +59,18 @@ public:
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
 
   virtual void SyncWithObject(SyncObject* aSyncObject) override;
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -493,45 +493,16 @@ TextureClientD3D9::Lock(OpenMode aMode)
     if (FAILED(hr)) {
       NS_WARNING("Failed to get texture surface level.");
       return false;
     }
   }
 
   mIsLocked = true;
 
-  // Make sure that successful write-lock means we will have a DrawTarget to
-  // write into.
-  if (aMode & OpenMode::OPEN_WRITE) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
-      Unlock();
-      return false;
-    }
-  }
-
-  if (mNeedsClear) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
-      Unlock();
-      return false;
-    }
-    mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
-    mNeedsClear = false;
-  }
-  if (mNeedsClearWhite) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
-      Unlock();
-      return false;
-    }
-    mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
-    mNeedsClearWhite = false;
-  }
-
   return true;
 }
 
 void
 TextureClientD3D9::Unlock()
 {
   MOZ_ASSERT(mIsLocked, "Unlocked called while the texture is not locked!");
   if (!mIsLocked) {
@@ -596,19 +567,61 @@ TextureClientD3D9::BorrowDrawTarget()
       return nullptr;
     }
     mDrawTarget =
      gfxPlatform::GetPlatform()->CreateDrawTargetForData((uint8_t*)rect.pBits, mSize,
                                                          rect.Pitch, mFormat);
     mLockRect = true;
   }
 
+  if (mNeedsClear) {
+    mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
+    mNeedsClear = false;
+  }
+  if (mNeedsClearWhite) {
+    mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+    mNeedsClearWhite = false;
+  }
+
   return mDrawTarget;
 }
 
+void
+TextureClientD3D9::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  MOZ_ASSERT(mIsLocked && mD3D9Surface);
+
+  // gfxWindowsSurface don't support transparency so we can't use the d3d9
+  // windows surface optimization.
+  // Instead we have to use a gfxImageSurface and fallback for font drawing.
+  D3DLOCKED_RECT rect;
+  HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0);
+  if (FAILED(hr) || !rect.pBits) {
+    gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 " << hexa(hr);
+    return;
+  }
+
+  if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << mFormat << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+    return;
+  }
+
+  DataSourceSurface::MappedSurface sourceMap;
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+
+  for (int y = 0; y < aSurface->GetSize().height; y++) {
+    memcpy((uint8_t*)rect.pBits + rect.Pitch * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
+  }
+
+  aSurface->Unmap();
+  mD3D9Surface->UnlockRect();
+}
+
 bool
 TextureClientD3D9::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
 {
   MOZ_ASSERT(!IsAllocated());
   mSize = aSize;
   _D3DFORMAT format = SurfaceFormatToD3D9Format(mFormat);
 
   DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -197,16 +197,18 @@ public:
   virtual gfx::IntSize GetSize() const { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   virtual bool HasInternalBuffer() const override { return true; }
 
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -209,16 +209,50 @@ GrallocTextureClientOGL::BorrowDrawTarge
   long byteStride = pixelStride * BytesPerPixel(format);
   mDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForData(GetBuffer(),
                                                                     mSize,
                                                                     byteStride,
                                                                     mFormat);
   return mDrawTarget;
 }
 
+void
+GrallocTextureClientOGL::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  MOZ_ASSERT(IsValid());
+  MOZ_ASSERT(mMappedBuffer, "Calling TextureClient::BorrowDrawTarget without locking :(");
+
+  if (!IsValid() || !IsAllocated() || !mMappedBuffer) {
+    return;
+  }
+
+  gfx::SurfaceFormat format = SurfaceFormatForPixelFormat(mGraphicBuffer->getPixelFormat());
+  if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << format << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+    return;
+  }
+
+  long pixelStride = mGraphicBuffer->getStride();
+  long byteStride = pixelStride * BytesPerPixel(format);
+
+  DataSourceSurface::MappedSurface sourceMap;
+
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+
+  uint8_t* buffer = GetBuffer();
+
+  for (int y = 0; y < aSurface->GetSize().height; y++) {
+    memcpy(buffer + byteStride * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
+  }
+
+  aSurface->Unmap();
+}
+
 bool
 GrallocTextureClientOGL::AllocateForSurface(gfx::IntSize aSize,
                                             TextureAllocationFlags)
 {
   MOZ_ASSERT(IsValid());
 
   uint32_t format;
   uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN |
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -77,16 +77,18 @@ public:
   {
     return mGraphicBuffer->getPixelFormat();
   }
 
   virtual uint8_t* GetBuffer() const override;
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   virtual bool AllocateForYCbCr(gfx::IntSize aYSize,
                                 gfx::IntSize aCbCrSize,
                                 StereoMode aStereoMode) override;
 
   bool AllocateForGLRendering(gfx::IntSize aSize);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1583,16 +1583,25 @@ gfxWindowsPlatform::GetD3D11ImageBridgeD
     return mD3D11ImageBridgeDevice;
   }
 
   InitD3D11Devices();
 
   return mD3D11ImageBridgeDevice;
 }
 
+ID3D11Device*
+gfxWindowsPlatform::GetD3D11DeviceForCurrentThread()
+{
+  if (NS_IsMainThread()) {
+    return GetD3D11ContentDevice();
+  } else {
+    return GetD3D11ImageBridgeDevice();
+  }
+}
 
 ReadbackManagerD3D11*
 gfxWindowsPlatform::GetReadbackManager()
 {
   if (!mD3D11ReadbackManager) {
     mD3D11ReadbackManager = new ReadbackManagerD3D11();
   }
 
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -239,16 +239,17 @@ public:
     inline bool DWriteEnabled() { return false; }
 #endif
     void OnDeviceManagerDestroy(mozilla::layers::DeviceManagerD3D9* aDeviceManager);
     mozilla::layers::DeviceManagerD3D9* GetD3D9DeviceManager();
     IDirect3DDevice9* GetD3D9Device();
     ID3D10Device1 *GetD3D10Device() { return mD3D10Device; }
     ID3D11Device *GetD3D11Device();
     ID3D11Device *GetD3D11ContentDevice();
+    ID3D11Device* GetD3D11DeviceForCurrentThread();
     // Device to be used on the ImageBridge thread
     ID3D11Device *GetD3D11ImageBridgeDevice();
 
     // Create a D3D11 device to be used for DXVA decoding.
     already_AddRefed<ID3D11Device> CreateD3D11DecoderDevice();
 
     mozilla::layers::ReadbackManagerD3D11* GetReadbackManager();