Bug 1477608 - Reuse same ExternalImageId for different video frames if possible r=jrmuizel
authorsotaro <sotaro.ikeda.g@gmail.com>
Wed, 25 Jul 2018 23:30:08 +0900
changeset 428284 7bb705198ab362f036c9da9fae69f97d7df907e1
parent 428283 4157a638f0a23b182decff28c035a6aece693707
child 428327 13c6faaf92d341470e11fbdf8a46c2831c035a96
push id105669
push usersikeda@mozilla.com
push dateWed, 25 Jul 2018 14:30:36 +0000
treeherdermozilla-inbound@7bb705198ab3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1477608
milestone63.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 1477608 - Reuse same ExternalImageId for different video frames if possible r=jrmuizel
gfx/layers/composite/GPUVideoTextureHost.cpp
gfx/layers/composite/GPUVideoTextureHost.h
gfx/layers/composite/TextureHost.h
gfx/layers/d3d11/TextureD3D11.h
gfx/layers/moz.build
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/AsyncImagePipelineManager.h
gfx/layers/wr/WebRenderTextureHost.cpp
gfx/layers/wr/WebRenderTextureHost.h
gfx/layers/wr/WebRenderTextureHostWrapper.cpp
gfx/layers/wr/WebRenderTextureHostWrapper.h
gfx/webrender_bindings/RenderTextureHost.h
gfx/webrender_bindings/RenderTextureHostWrapper.cpp
gfx/webrender_bindings/RenderTextureHostWrapper.h
gfx/webrender_bindings/RenderThread.cpp
gfx/webrender_bindings/RenderThread.h
gfx/webrender_bindings/moz.build
--- a/gfx/layers/composite/GPUVideoTextureHost.cpp
+++ b/gfx/layers/composite/GPUVideoTextureHost.cpp
@@ -141,10 +141,17 @@ GPUVideoTextureHost::PushDisplayItems(wr
 
   mWrappedTextureHost->PushDisplayItems(aBuilder,
                                          aBounds,
                                          aClip,
                                          aFilter,
                                          aImageKeys);
 }
 
+bool
+GPUVideoTextureHost::SupportsWrNativeTexture()
+{
+  MOZ_ASSERT(mWrappedTextureHost);
+  return mWrappedTextureHost->SupportsWrNativeTexture();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/GPUVideoTextureHost.h
+++ b/gfx/layers/composite/GPUVideoTextureHost.h
@@ -57,16 +57,18 @@ public:
                                    const wr::ExternalImageId& aExtID) override;
 
   virtual void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                                 const wr::LayoutRect& aBounds,
                                 const wr::LayoutRect& aClip,
                                 wr::ImageRendering aFilter,
                                 const Range<wr::ImageKey>& aImageKeys) override;
 
+  virtual bool SupportsWrNativeTexture() override;
+
 protected:
   RefPtr<TextureHost> mWrappedTextureHost;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -669,16 +669,18 @@ public:
 
   /**
    * Some API's can use the cross-process IOSurface directly, such as OpenVR
    */
   virtual MacIOSurface* GetMacIOSurface() { return nullptr; }
 
   virtual bool IsDirectMap() { return false; }
 
+  virtual bool SupportsWrNativeTexture() { return false; }
+
 protected:
   virtual void ReadUnlock();
 
   void RecycleTexture(TextureFlags aFlags);
 
   virtual void MaybeNotifyUnlocked() {}
 
   virtual void UpdatedInternal(const nsIntRegion *Region) {}
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -366,16 +366,18 @@ public:
                                    const wr::ExternalImageId& aExtID) override;
 
   virtual void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                                 const wr::LayoutRect& aBounds,
                                 const wr::LayoutRect& aClip,
                                 wr::ImageRendering aFilter,
                                 const Range<wr::ImageKey>& aImageKeys) override;
 
+  virtual bool SupportsWrNativeTexture() override { return true; }
+
 protected:
   bool LockInternal();
   void UnlockInternal();
 
   bool EnsureTextureSource();
 
   RefPtr<ID3D11Device> GetDevice();
 
@@ -428,16 +430,18 @@ public:
                                    const wr::ExternalImageId& aExtID) override;
 
   virtual void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                                 const wr::LayoutRect& aBounds,
                                 const wr::LayoutRect& aClip,
                                 wr::ImageRendering aFilter,
                                 const Range<wr::ImageKey>& aImageKeys) override;
 
+  virtual bool SupportsWrNativeTexture() override { return true; }
+
 private:
   bool EnsureTextureSource();
 
 protected:
   RefPtr<ID3D11Device> GetDevice();
 
   bool EnsureTexture();
 
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -246,16 +246,17 @@ EXPORTS.mozilla.layers += [
     'wr/WebRenderDrawEventRecorder.h',
     'wr/WebRenderImageHost.h',
     'wr/WebRenderLayerManager.h',
     'wr/WebRenderLayersLogging.h',
     'wr/WebRenderMessageUtils.h',
     'wr/WebRenderScrollData.h',
     'wr/WebRenderScrollDataWrapper.h',
     'wr/WebRenderTextureHost.h',
+    'wr/WebRenderTextureHostWrapper.h',
     'wr/WebRenderUserData.h',
 ]
 
 if CONFIG['MOZ_X11']:
     EXPORTS.mozilla.layers += [
         'basic/TextureClientX11.h',
         'basic/X11TextureSourceBasic.h',
         'composite/X11TextureHost.h',
@@ -484,16 +485,17 @@ UNIFIED_SOURCES += [
     'wr/WebRenderBridgeParent.cpp',
     'wr/WebRenderCanvasRenderer.cpp',
     'wr/WebRenderCommandBuilder.cpp',
     'wr/WebRenderDrawEventRecorder.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderLayerManager.cpp',
     'wr/WebRenderLayersLogging.cpp',
     'wr/WebRenderScrollData.cpp',
+    'wr/WebRenderTextureHostWrapper.cpp',
     'wr/WebRenderUserData.cpp',
     # XXX here are some unified build error.
     #'wr/WebRenderTextureHost.cpp'
 ]
 
 SOURCES += [
     'basic/BasicImageLayer.cpp',
     'ImageContainer.cpp',
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -161,56 +161,79 @@ AsyncImagePipelineManager::UpdateAsyncIm
   pipeline->Update(aScBounds,
                    aScTransform,
                    aScaleToSize,
                    aFilter,
                    aMixBlendMode);
 }
 
 Maybe<TextureHost::ResourceUpdateOp>
-AsyncImagePipelineManager::UpdateImageKeys(wr::TransactionBuilder& aResources,
+AsyncImagePipelineManager::UpdateImageKeys(const wr::Epoch& aEpoch,
+                                           const wr::PipelineId& aPipelineId,
+                                           wr::TransactionBuilder& aResources,
                                            AsyncImagePipeline* aPipeline,
                                            nsTArray<wr::ImageKey>& aKeys)
 {
   MOZ_ASSERT(aKeys.IsEmpty());
   MOZ_ASSERT(aPipeline);
 
   TextureHost* texture = aPipeline->mImageHost->GetAsTextureHostForComposite();
   TextureHost* previousTexture = aPipeline->mCurrentTexture.get();
 
   if (texture == previousTexture) {
     // The texture has not changed, just reuse previous ImageKeys.
     aKeys = aPipeline->mKeys;
+    if (aPipeline->mWrTextureWrapper) {
+      HoldExternalImage(aPipelineId, aEpoch, aPipeline->mWrTextureWrapper);
+    }
     return Nothing();
   }
 
   if (!texture) {
     // We don't have a new texture, there isn't much we can do.
     aKeys = aPipeline->mKeys;
+    if (aPipeline->mWrTextureWrapper) {
+      HoldExternalImage(aPipelineId, aEpoch, aPipeline->mWrTextureWrapper);
+    }
     return Nothing();
   }
 
   aPipeline->mCurrentTexture = texture;
 
   WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
 
   bool useExternalImage = !gfxEnv::EnableWebRenderRecording() && wrTexture;
   aPipeline->mUseExternalImage = useExternalImage;
 
+  // Use WebRenderTextureHostWrapper only for video.
+  // And WebRenderTextureHostWrapper could be used only with WebRenderTextureHost
+  // that supports NativeTexture
+  bool useWrTextureWrapper = aPipeline->mImageHost->GetAsyncRef() &&
+                             useExternalImage &&
+                             wrTexture &&
+                             wrTexture->SupportsWrNativeTexture();
+
   // The non-external image code path falls back to converting the texture into
   // an rgb image.
   auto numKeys = useExternalImage ? texture->NumSubTextures() : 1;
 
   // If we already had a texture and the format hasn't changed, better to reuse the image keys
   // than create new ones.
   bool canUpdate = !!previousTexture
                    && previousTexture->GetSize() == texture->GetSize()
                    && previousTexture->GetFormat() == texture->GetFormat()
                    && aPipeline->mKeys.Length() == numKeys;
 
+  // Check if WebRenderTextureHostWrapper could be reused.
+  if (aPipeline->mWrTextureWrapper &&
+      (!useWrTextureWrapper || !canUpdate)) {
+    aPipeline->mWrTextureWrapper = nullptr;
+    canUpdate = false;
+  }
+
   if (!canUpdate) {
     for (auto key : aPipeline->mKeys) {
       aResources.DeleteImage(key);
     }
     aPipeline->mKeys.Clear();
     for (uint32_t i = 0; i < numKeys; ++i) {
       aPipeline->mKeys.AppendElement(GenerateImageKey());
     }
@@ -219,18 +242,37 @@ AsyncImagePipelineManager::UpdateImageKe
   aKeys = aPipeline->mKeys;
 
   auto op = canUpdate ? TextureHost::UPDATE_IMAGE : TextureHost::ADD_IMAGE;
 
   if (!useExternalImage) {
     return UpdateWithoutExternalImage(aResources, texture, aKeys[0], op);
   }
 
-  Range<wr::ImageKey> keys(&aKeys[0], aKeys.Length());
-  wrTexture->PushResourceUpdates(aResources, op, keys, wrTexture->GetExternalImageKey());
+  if (useWrTextureWrapper && aPipeline->mWrTextureWrapper) {
+    MOZ_ASSERT(canUpdate);
+    // Reuse WebRenderTextureHostWrapper. With it, rendered frame could be updated
+    // without batch re-creation.
+    aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(wrTexture);
+    // Ensure frame generation.
+    SetWillGenerateFrame();
+  } else {
+    if (useWrTextureWrapper) {
+      aPipeline->mWrTextureWrapper = new WebRenderTextureHostWrapper(this);
+      aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(wrTexture);
+    }
+    Range<wr::ImageKey> keys(&aKeys[0], aKeys.Length());
+    auto externalImageKey =
+      aPipeline->mWrTextureWrapper ? aPipeline->mWrTextureWrapper->GetExternalImageKey() : wrTexture->GetExternalImageKey();
+    wrTexture->PushResourceUpdates(aResources, op, keys, externalImageKey);
+  }
+
+  if (aPipeline->mWrTextureWrapper) {
+    HoldExternalImage(aPipelineId, aEpoch, aPipeline->mWrTextureWrapper);
+  }
 
   return Some(op);
 }
 
 Maybe<TextureHost::ResourceUpdateOp>
 AsyncImagePipelineManager::UpdateWithoutExternalImage(wr::TransactionBuilder& aResources,
                                                       TextureHost* aTexture,
                                                       wr::ImageKey aKey,
@@ -291,17 +333,17 @@ AsyncImagePipelineManager::ApplyAsyncIma
 
 void
 AsyncImagePipelineManager::ApplyAsyncImageForPipeline(const wr::Epoch& aEpoch,
                                                       const wr::PipelineId& aPipelineId,
                                                       AsyncImagePipeline* aPipeline,
                                                       wr::TransactionBuilder& aTxn)
 {
   nsTArray<wr::ImageKey> keys;
-  auto op = UpdateImageKeys(aTxn, aPipeline, keys);
+  auto op = UpdateImageKeys(aEpoch, aPipelineId, aTxn, aPipeline, keys);
 
   bool updateDisplayList = aPipeline->mInitialised &&
                            (aPipeline->mIsChanged || op == Some(TextureHost::ADD_IMAGE)) &&
                            !!aPipeline->mCurrentTexture;
 
   // We will schedule generating a frame after the scene
   // build is done or resource update is done, so we don't need to do it here.
 
@@ -420,16 +462,33 @@ AsyncImagePipelineManager::HoldExternalI
   if (!holder) {
     return;
   }
   // Hold WebRenderTextureHost until end of its usage on RenderThread
   holder->mTextureHosts.push(ForwardingTextureHost(aEpoch, aTexture));
 }
 
 void
+AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHostWrapper* aWrTextureWrapper)
+{
+  if (mDestroyed) {
+    return;
+  }
+  MOZ_ASSERT(aWrTextureWrapper);
+
+  PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
+  MOZ_ASSERT(holder);
+  if (!holder) {
+    return;
+  }
+  // Hold WebRenderTextureHostWrapper until end of its usage on RenderThread
+  holder->mTextureHostWrappers.push(ForwardingTextureHostWrapper(aEpoch, aWrTextureWrapper));
+}
+
+void
 AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const wr::ExternalImageId& aImageId)
 {
   if (mDestroyed) {
     SharedSurfacesParent::Release(aImageId);
     return;
   }
 
   PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
@@ -505,16 +564,22 @@ AsyncImagePipelineManager::ProcessPipeli
     PipelineTexturesHolder* holder = entry.Data();
     // Release TextureHosts based on Epoch
     while (!holder->mTextureHosts.empty()) {
       if (aEpoch <= holder->mTextureHosts.front().mEpoch) {
         break;
       }
       holder->mTextureHosts.pop();
     }
+    while (!holder->mTextureHostWrappers.empty()) {
+      if (aEpoch <= holder->mTextureHostWrappers.front().mEpoch) {
+        break;
+      }
+      holder->mTextureHostWrappers.pop();
+    }
     while (!holder->mExternalImages.empty()) {
       if (aEpoch <= holder->mExternalImages.front().mEpoch) {
         break;
       }
       DebugOnly<bool> released =
         SharedSurfacesParent::Release(holder->mExternalImages.front().mImageId);
       MOZ_ASSERT(released);
       holder->mExternalImages.pop();
--- a/gfx/layers/wr/AsyncImagePipelineManager.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -7,16 +7,17 @@
 #ifndef MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H
 #define MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H
 
 #include <queue>
 
 #include "CompositableHost.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/WebRenderTextureHostWrapper.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "nsClassHashtable.h"
 
 namespace mozilla {
 
 namespace wr {
@@ -25,16 +26,17 @@ class WebRenderAPI;
 }
 
 namespace layers {
 
 class CompositableHost;
 class CompositorVsyncScheduler;
 class WebRenderImageHost;
 class WebRenderTextureHost;
+class WebRenderTextureHostWrapper;
 
 class AsyncImagePipelineManager final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncImagePipelineManager)
 
   explicit AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi);
 
@@ -43,16 +45,17 @@ protected:
 
 public:
   void Destroy();
 
   void AddPipeline(const wr::PipelineId& aPipelineId);
   void RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
 
   void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture);
+  void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHostWrapper* aWrTextureWrapper);
   void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const wr::ExternalImageId& aImageId);
 
   // This is called from the Renderer thread to notify this class about the
   // pipelines in the most recently completed render. A copy of the update
   // information is put into mUpdatesQueue.
   void NotifyPipelinesUpdated(wr::WrPipelineInfo aInfo);
 
   // This is run on the compositor thread to process mUpdatesQueue. We make
@@ -101,16 +104,20 @@ public:
   void FlushImageNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications)
   {
     aNotifications->AppendElements(std::move(mImageCompositeNotifications));
   }
 
   void SetWillGenerateFrame();
   bool GetAndResetWillGenerateFrame();
 
+  wr::ExternalImageId GetNextExternalImageId() {
+    return wr::ToExternalImageId(GetNextResourceId());
+  }
+
 private:
   void ProcessPipelineRendered(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
   void ProcessPipelineRemoved(const wr::PipelineId& aPipelineId);
 
   wr::Epoch GetNextImageEpoch();
   uint32_t GetNextResourceId() { return ++mResourceId; }
   wr::IdNamespace GetNamespace() { return mIdNamespace; }
   wr::ImageKey GenerateImageKey()
@@ -125,28 +132,38 @@ private:
     ForwardingTextureHost(const wr::Epoch& aEpoch, TextureHost* aTexture)
       : mEpoch(aEpoch)
       , mTexture(aTexture)
     {}
     wr::Epoch mEpoch;
     CompositableTextureHostRef mTexture;
   };
 
+  struct ForwardingTextureHostWrapper {
+    ForwardingTextureHostWrapper(const wr::Epoch& aEpoch, WebRenderTextureHostWrapper* aWrTextureWrapper)
+      : mEpoch(aEpoch)
+      , mWrTextureWrapper(aWrTextureWrapper)
+    {}
+    wr::Epoch mEpoch;
+    RefPtr<WebRenderTextureHostWrapper> mWrTextureWrapper;
+  };
+
   struct ForwardingExternalImage {
     ForwardingExternalImage(const wr::Epoch& aEpoch, const wr::ExternalImageId& aImageId)
       : mEpoch(aEpoch)
       , mImageId(aImageId)
     {}
     wr::Epoch mEpoch;
     wr::ExternalImageId mImageId;
   };
 
   struct PipelineTexturesHolder {
     // Holds forwarding WebRenderTextureHosts.
     std::queue<ForwardingTextureHost> mTextureHosts;
+    std::queue<ForwardingTextureHostWrapper> mTextureHostWrappers;
     std::queue<ForwardingExternalImage> mExternalImages;
     Maybe<wr::Epoch> mDestroyedEpoch;
   };
 
   struct AsyncImagePipeline {
     AsyncImagePipeline();
     void Update(const LayoutDeviceRect& aScBounds,
                 const gfx::Matrix4x4& aScTransform,
@@ -171,25 +188,28 @@ private:
     bool mUseExternalImage;
     LayoutDeviceRect mScBounds;
     gfx::Matrix4x4 mScTransform;
     gfx::MaybeIntSize mScaleToSize;
     wr::ImageRendering mFilter;
     wr::MixBlendMode mMixBlendMode;
     RefPtr<WebRenderImageHost> mImageHost;
     CompositableTextureHostRef mCurrentTexture;
+    RefPtr<WebRenderTextureHostWrapper> mWrTextureWrapper;
     nsTArray<wr::ImageKey> mKeys;
   };
 
   void ApplyAsyncImageForPipeline(const wr::Epoch& aEpoch,
                                   const wr::PipelineId& aPipelineId,
                                   AsyncImagePipeline* aPipeline,
                                   wr::TransactionBuilder& aTxn);
   Maybe<TextureHost::ResourceUpdateOp>
-  UpdateImageKeys(wr::TransactionBuilder& aResourceUpdates,
+  UpdateImageKeys(const wr::Epoch& aEpoch,
+                  const wr::PipelineId& aPipelineId,
+                  wr::TransactionBuilder& aResourceUpdates,
                   AsyncImagePipeline* aPipeline,
                   nsTArray<wr::ImageKey>& aKeys);
   Maybe<TextureHost::ResourceUpdateOp>
   UpdateWithoutExternalImage(wr::TransactionBuilder& aResources,
                              TextureHost* aTexture,
                              wr::ImageKey aKey,
                              TextureHost::ResourceUpdateOp);
 
--- a/gfx/layers/wr/WebRenderTextureHost.cpp
+++ b/gfx/layers/wr/WebRenderTextureHost.cpp
@@ -144,17 +144,17 @@ WebRenderTextureHost::NumSubTextures() c
 
 void
 WebRenderTextureHost::PushResourceUpdates(wr::TransactionBuilder& aResources,
                                           ResourceUpdateOp aOp,
                                           const Range<wr::ImageKey>& aImageKeys,
                                           const wr::ExternalImageId& aExtID)
 {
   MOZ_ASSERT(mWrappedTextureHost);
-  MOZ_ASSERT(mExternalImageId == aExtID);
+  MOZ_ASSERT(mExternalImageId == aExtID || SupportsWrNativeTexture());
 
   mWrappedTextureHost->PushResourceUpdates(aResources, aOp, aImageKeys, aExtID);
 }
 
 void
 WebRenderTextureHost::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                                        const wr::LayoutRect& aBounds,
                                        const wr::LayoutRect& aClip,
@@ -166,10 +166,16 @@ WebRenderTextureHost::PushDisplayItems(w
 
   mWrappedTextureHost->PushDisplayItems(aBuilder,
                                          aBounds,
                                          aClip,
                                          aFilter,
                                          aImageKeys);
 }
 
+bool
+WebRenderTextureHost::SupportsWrNativeTexture()
+{
+  return mWrappedTextureHost->SupportsWrNativeTexture();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderTextureHost.h
+++ b/gfx/layers/wr/WebRenderTextureHost.h
@@ -72,16 +72,18 @@ public:
                                    const wr::ExternalImageId& aExtID) override;
 
   virtual void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                                 const wr::LayoutRect& aBounds,
                                 const wr::LayoutRect& aClip,
                                 wr::ImageRendering aFilter,
                                 const Range<wr::ImageKey>& aImageKeys) override;
 
+  virtual bool SupportsWrNativeTexture() override;
+
 protected:
   void CreateRenderTextureHost(const SurfaceDescriptor& aDesc, TextureHost* aTexture);
 
   RefPtr<TextureHost> mWrappedTextureHost;
   wr::ExternalImageId mExternalImageId;
 };
 
 } // namespace layers
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderTextureHostWrapper.cpp
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderTextureHostWrapper.h"
+
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/layers/WebRenderTextureHost.h"
+#include "mozilla/webrender/RenderTextureHostWrapper.h"
+#include "mozilla/webrender/RenderThread.h"
+
+namespace mozilla {
+namespace layers {
+
+WebRenderTextureHostWrapper::WebRenderTextureHostWrapper(AsyncImagePipelineManager* aManager)
+  : mExternalImageId(aManager->GetNextExternalImageId())
+{
+  MOZ_ASSERT(aManager);
+  MOZ_COUNT_CTOR(WebRenderTextureHostWrapper);
+
+  RefPtr<wr::RenderTextureHost> texture = new wr::RenderTextureHostWrapper();
+  wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(mExternalImageId), texture.forget());
+}
+
+WebRenderTextureHostWrapper::~WebRenderTextureHostWrapper()
+{
+  MOZ_COUNT_DTOR(WebRenderTextureHostWrapper);
+  wr::RenderThread::Get()->UnregisterExternalImage(wr::AsUint64(mExternalImageId));
+}
+
+void
+WebRenderTextureHostWrapper::UpdateWebRenderTextureHost(WebRenderTextureHost* aTextureHost) {
+  MOZ_ASSERT(aTextureHost);
+  mWrTextureHost = aTextureHost;
+  wr::RenderThread::Get()->UpdateRenderTextureHost(wr::AsUint64(mExternalImageId), wr::AsUint64(aTextureHost->GetExternalImageKey()));
+}
+
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderTextureHostWrapper.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_WEBRENDERTEXTUREHOSTWRAPPER_H
+#define MOZILLA_GFX_WEBRENDERTEXTUREHOSTWRAPPER_H
+
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderTextureHost;
+class AsyncImagePipelineManager;
+
+class WebRenderTextureHostWrapper
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderTextureHostWrapper)
+
+public:
+  explicit WebRenderTextureHostWrapper(AsyncImagePipelineManager* aManager);
+
+  void UpdateWebRenderTextureHost(WebRenderTextureHost* aTextureHost);
+
+  wr::ExternalImageId GetExternalImageKey() { return mExternalImageId; }
+
+protected:
+  virtual ~WebRenderTextureHostWrapper();
+
+  RefPtr<WebRenderTextureHost> mWrTextureHost;
+  wr::ExternalImageId mExternalImageId;
+};
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_WEBRENDERTEXTUREHOSTWRAPPER_H
--- a/gfx/webrender_bindings/RenderTextureHost.h
+++ b/gfx/webrender_bindings/RenderTextureHost.h
@@ -17,27 +17,30 @@ namespace mozilla {
 namespace gl {
 class GLContext;
 }
 
 namespace wr {
 
 class RenderBufferTextureHost;
 class RenderTextureHostOGL;
+class RenderTextureHostWrapper;
 
 class RenderTextureHost
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RenderTextureHost)
 
 public:
   RenderTextureHost();
 
   virtual wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) = 0;
   virtual void Unlock() = 0;
   virtual void ClearCachedResources() {}
+
+  virtual RenderTextureHostWrapper* AsRenderTextureHostWrapper() { return nullptr; }
 protected:
   virtual ~RenderTextureHost();
 };
 
 } // namespace wr
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_RENDERTEXTUREHOST_H
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHostWrapper.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RenderTextureHostWrapper.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/SourceSurfaceSharedData.h"
+#include "mozilla/webrender/RenderThread.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderTextureHostWrapper::RenderTextureHostWrapper()
+  : mInited(false)
+  , mLocked(false)
+{
+  MOZ_COUNT_CTOR_INHERITED(RenderTextureHostWrapper, RenderTextureHost);
+}
+
+RenderTextureHostWrapper::~RenderTextureHostWrapper()
+{
+  MOZ_COUNT_DTOR_INHERITED(RenderTextureHostWrapper, RenderTextureHost);
+}
+
+wr::WrExternalImage
+RenderTextureHostWrapper::Lock(uint8_t aChannelIndex, gl::GLContext* aGL)
+{
+  if (!mTextureHost) {
+    MOZ_ASSERT_UNREACHABLE("unexpected to happen");
+    return InvalidToWrExternalImage();
+  }
+
+  mLocked = true;
+  return mTextureHost->Lock(aChannelIndex, aGL);
+}
+
+void
+RenderTextureHostWrapper::Unlock()
+{
+  if (mTextureHost) {
+    mTextureHost->Unlock();
+  }
+  mLocked = false;
+}
+
+void
+RenderTextureHostWrapper::ClearCachedResources()
+{
+  if (mTextureHost) {
+    mTextureHost->ClearCachedResources();
+  }
+}
+
+void
+RenderTextureHostWrapper::UpdateRenderTextureHost(RenderTextureHost* aTextureHost)
+{
+  MOZ_ASSERT(!mInited || RenderThread::IsInRenderThread());
+  MOZ_RELEASE_ASSERT(!mLocked);
+
+  mInited = true;
+  mTextureHost = aTextureHost;
+}
+
+} // namespace wr
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHostWrapper.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_RENDERTEXTUREHOSTWRAPPER_H
+#define MOZILLA_GFX_RENDERTEXTUREHOSTWRAPPER_H
+
+#include "RenderTextureHost.h"
+
+namespace mozilla {
+
+namespace wr {
+
+class RenderTextureHostWrapper final : public RenderTextureHost
+{
+public:
+  explicit RenderTextureHostWrapper();
+
+  wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+  void Unlock() override;
+  void ClearCachedResources() override;
+
+  RenderTextureHostWrapper* AsRenderTextureHostWrapper() override { return this; }
+
+  void UpdateRenderTextureHost(RenderTextureHost* aTextureHost);
+  bool IsInited() { return mInited; }
+
+private:
+  ~RenderTextureHostWrapper() override;
+
+  bool mInited;
+  bool mLocked;
+  RefPtr<RenderTextureHost> mTextureHost;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERTEXTUREHOSTWRAPPER_H
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -537,16 +537,48 @@ RenderThread::UnregisterExternalImage(ui
       this, &RenderThread::DeferredRenderTextureHostDestroy
     ));
   } else {
     mRenderTextures.erase(it);
   }
 }
 
 void
+RenderThread::UpdateRenderTextureHost(uint64_t aSrcExternalImageId, uint64_t aWrappedExternalImageId)
+{
+  MOZ_ASSERT(aSrcExternalImageId != aWrappedExternalImageId);
+
+  MutexAutoLock lock(mRenderTextureMapLock);
+  if (mHasShutdown) {
+    return;
+  }
+  auto src = mRenderTextures.find(aSrcExternalImageId);
+  auto wrapped = mRenderTextures.find(aWrappedExternalImageId);
+  if (src == mRenderTextures.end() || wrapped == mRenderTextures.end()) {
+    return;
+  }
+  MOZ_ASSERT(src->second->AsRenderTextureHostWrapper());
+  MOZ_ASSERT(!wrapped->second->AsRenderTextureHostWrapper());
+  RenderTextureHostWrapper* wrapper = src->second->AsRenderTextureHostWrapper();
+  if (!wrapper) {
+    MOZ_ASSERT_UNREACHABLE("unexpected to happen");
+    return;
+  }
+  if (!wrapper->IsInited()) {
+    wrapper->UpdateRenderTextureHost(wrapped->second);
+    MOZ_ASSERT(wrapper->IsInited());
+  } else {
+    Loop()->PostTask(NewRunnableMethod<RenderTextureHost*>(
+      "RenderThread::DeferredRenderTextureHostDestroy",
+      wrapper, &RenderTextureHostWrapper::UpdateRenderTextureHost, wrapped->second
+    ));
+  }
+}
+
+void
 RenderThread::UnregisterExternalImageDuringShutdown(uint64_t aExternalImageId)
 {
   MOZ_ASSERT(IsInRenderThread());
   MutexAutoLock lock(mRenderTextureMapLock);
   MOZ_ASSERT(mHasShutdown);
   MOZ_ASSERT(mRenderTextures.find(aExternalImageId) != mRenderTextures.end());
   mRenderTextures.erase(aExternalImageId);
 }
--- a/gfx/webrender_bindings/RenderThread.h
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -136,16 +136,19 @@ public:
   bool Resume(wr::WindowId aWindowId);
 
   /// Can be called from any thread.
   void RegisterExternalImage(uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture);
 
   /// Can be called from any thread.
   void UnregisterExternalImage(uint64_t aExternalImageId);
 
+  /// Can be called from any thread.
+  void UpdateRenderTextureHost(uint64_t aSrcExternalImageId, uint64_t aWrappedExternalImageId);
+
   /// Can only be called from the render thread.
   void UnregisterExternalImageDuringShutdown(uint64_t aExternalImageId);
 
   /// Can only be called from the render thread.
   RenderTextureHost* GetRenderTexture(WrExternalImageId aExternalImageId);
 
   /// Can be called from any thread.
   bool IsDestroyed(wr::WindowId aWindowId);
--- a/gfx/webrender_bindings/moz.build
+++ b/gfx/webrender_bindings/moz.build
@@ -10,32 +10,34 @@ with Files('**'):
 EXPORTS.mozilla.webrender += [
     'RenderBufferTextureHost.h',
     'RenderCompositor.h',
     'RenderCompositorOGL.h',
     'RendererOGL.h',
     'RenderSharedSurfaceTextureHost.h',
     'RenderTextureHost.h',
     'RenderTextureHostOGL.h',
+    'RenderTextureHostWrapper.h',
     'RenderThread.h',
     'webrender_ffi.h',
     'webrender_ffi_generated.h',
     'WebRenderAPI.h',
     'WebRenderTypes.h',
 ]
 
 UNIFIED_SOURCES += [
     'Moz2DImageRenderer.cpp',
     'RenderBufferTextureHost.cpp',
     'RenderCompositor.cpp',
     'RenderCompositorOGL.cpp',
     'RendererOGL.cpp',
     'RenderSharedSurfaceTextureHost.cpp',
     'RenderTextureHost.cpp',
     'RenderTextureHostOGL.cpp',
+    'RenderTextureHostWrapper.cpp',
     'RenderThread.cpp',
     'WebRenderAPI.cpp',
     'WebRenderTypes.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     EXPORTS.mozilla.webrender += [
         'RenderMacIOSurfaceTextureHostOGL.h',