Bug 1345054 part 1 - Allocate pipleline for each video r=nical
authorsotaro <sotaro.ikeda.g@gmail.com>
Fri, 02 Jun 2017 16:11:34 +0900
changeset 361998 7c57e39a677a9def9f0ff3c7eecf407579a83e1e
parent 361997 0b3be4de7ac365ed6e79efb4046d013f7d020711
child 361999 5f944d9ffbe0080d9a1b7410224570c857a82993
push id31953
push usercbook@mozilla.com
push dateFri, 02 Jun 2017 12:22:33 +0000
treeherdermozilla-central@2a8478029a0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1345054
milestone55.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 1345054 part 1 - Allocate pipleline for each video r=nical
gfx/2d/Point.h
gfx/layers/LayersTypes.h
gfx/layers/ipc/PWebRenderBridge.ipdl
gfx/layers/ipc/WebRenderMessages.ipdlh
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
gfx/layers/wr/WebRenderCompositableHolder.cpp
gfx/layers/wr/WebRenderCompositableHolder.h
gfx/layers/wr/WebRenderImageHost.cpp
gfx/layers/wr/WebRenderImageHost.h
gfx/layers/wr/WebRenderImageLayer.cpp
gfx/layers/wr/WebRenderImageLayer.h
gfx/layers/wr/WebRenderMessageUtils.h
gfx/webrender_bindings/src/bindings.rs
--- a/gfx/2d/Point.h
+++ b/gfx/2d/Point.h
@@ -9,16 +9,17 @@
 #include "mozilla/Attributes.h"
 #include "Types.h"
 #include "Coord.h"
 #include "BaseCoord.h"
 #include "BasePoint.h"
 #include "BasePoint3D.h"
 #include "BasePoint4D.h"
 #include "BaseSize.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/TypeTraits.h"
 
 #include <cmath>
 
 namespace mozilla {
 
 template <typename> struct IsPixel;
 
@@ -287,16 +288,17 @@ struct IntSizeTyped :
     return IntSizeTyped<units>(aSize.width, aSize.height);
   }
 
   IntSizeTyped<UnknownUnits> ToUnknownSize() const {
     return IntSizeTyped<UnknownUnits>(this->width, this->height);
   }
 };
 typedef IntSizeTyped<UnknownUnits> IntSize;
+typedef Maybe<IntSize> MaybeIntSize;
 
 template<class units, class F = Float>
 struct SizeTyped :
   public BaseSize< F, SizeTyped<units, F> >,
   public units {
   static_assert(IsPixel<units>::value,
                 "'units' must be a coordinate system tag");
 
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -5,16 +5,17 @@
 
 #ifndef GFX_LAYERSTYPES_H
 #define GFX_LAYERSTYPES_H
 
 #include <stdint.h>                     // for uint32_t
 
 #include "Units.h"
 #include "mozilla/gfx/Point.h"          // for IntPoint
+#include "mozilla/Maybe.h"
 #include "mozilla/TypedEnumBits.h"
 #include "nsRegion.h"
 
 #include <stdio.h>            // FILE
 #include "mozilla/Logging.h"            // for PR_LOG
 
 #ifndef MOZ_LAYERS_HAVE_LOG
 #  define MOZ_LAYERS_HAVE_LOG
@@ -209,16 +210,18 @@ typedef gfx::Matrix4x4Typed<LayerPixel, 
 typedef gfx::Matrix4x4Typed<ParentLayerPixel, ParentLayerPixel> AsyncTransformComponentMatrix;
 typedef gfx::Matrix4x4Typed<CSSTransformedLayerPixel, ParentLayerPixel> AsyncTransformMatrix;
 
 typedef Array<gfx::Color, 4> BorderColors;
 typedef Array<LayerSize, 4> BorderCorners;
 typedef Array<LayerCoord, 4> BorderWidths;
 typedef Array<uint8_t, 4> BorderStyles;
 
+typedef Maybe<LayerRect> MaybeLayerRect;
+
 // This is used to communicate Layers across IPC channels. The Handle is valid
 // for layers in the same PLayerTransaction. Handles are created by ClientLayerManager,
 // and are cached in LayerTransactionParent on first use.
 class LayerHandle
 {
   friend struct IPC::ParamTraits<mozilla::layers::LayerHandle>;
 public:
   LayerHandle() : mHandle(0)
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -16,18 +16,18 @@ include protocol PTexture;
 
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
 using WrBuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
-using WrSize from "mozilla/webrender/webrender_ffi.h";
 using mozilla::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
 
 namespace mozilla {
 namespace layers {
 
 sync protocol PWebRenderBridge
 {
   manager PCompositorBridge;
@@ -55,17 +55,18 @@ parent:
   async DPBegin(IntSize aSize);
   async DPEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
               WrSize aContentSize, ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc,
               WebRenderScrollData aScrollData);
   sync DPSyncEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
                  WrSize aContentSize, ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc,
                  WebRenderScrollData aScrollData);
   sync DPGetSnapshot(PTexture texture);
-  async AddExternalImageId(ExternalImageId aImageId, CompositableHandle aHandle);
+  async AddPipelineIdForAsyncCompositable(PipelineId aImageId, CompositableHandle aHandle);
+  async RemovePipelineIdForAsyncCompositable(PipelineId aPipelineId);
   async AddExternalImageIdForCompositable(ExternalImageId aImageId, CompositableHandle aHandle);
   async RemoveExternalImageId(ExternalImageId aImageId);
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
   async ClearCachedResources();
   // Schedule a composite if one isn't already scheduled.
   async ForceComposite();
 
   // These correspond exactly to the equivalent APIs in PLayerTransaction -
--- a/gfx/layers/ipc/WebRenderMessages.ipdlh
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -4,18 +4,27 @@
 /* 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 LayersSurfaces;
 include LayersMessages;
 include protocol PTexture;
 
+using WrSize from "mozilla/webrender/webrender_ffi.h";
+using WrImageRendering from "mozilla/webrender/webrender_ffi.h";
+using WrMixBlendMode from "mozilla/webrender/webrender_ffi.h";
+using MaybeImageMask from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::gfx::MaybeIntSize from "mozilla/gfx/Point.h";
+using mozilla::LayerPoint from "Units.h";
+using mozilla::layers::MaybeLayerRect from "mozilla/layers/LayersTypes.h";
+using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace layers {
 
 union OptionalTransform {
   Matrix4x4;
   void_t;
@@ -26,28 +35,34 @@ union OptionalOpacity {
   void_t;
 };
 
 struct OpAddExternalImage {
   ExternalImageId externalImageId;
   ImageKey key;
 };
 
-struct OpAddExternalVideoImage {
-  ExternalImageId externalImageId;
-  ImageKey[] keys;
-};
-
 struct OpAddCompositorAnimations {
   CompositorAnimations data;
   OptionalTransform transform;
   OptionalOpacity opacity;
 };
 
+struct OpUpdateAsyncImagePipeline {
+  PipelineId pipelineId;
+  LayerRect scBounds;
+  Matrix4x4 scTransform;
+  MaybeIntSize scaleToSize;
+  MaybeLayerRect clipRect;
+  MaybeImageMask mask;
+  WrImageRendering filter;
+  WrMixBlendMode mixBlendMode;
+};
+
 union WebRenderParentCommand {
   OpAddExternalImage;
-  OpAddExternalVideoImage;
+  OpUpdateAsyncImagePipeline;
   CompositableOperation;
   OpAddCompositorAnimations;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -115,35 +115,38 @@ WebRenderBridgeChild::DPEnd(wr::DisplayL
                     contentSize, dlData, dl.dl_desc, aScrollData);
   }
 
   mParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
+void
+WebRenderBridgeChild::AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
+                                                        const CompositableHandle& aHandle)
+{
+  SendAddPipelineIdForAsyncCompositable(aPipelineId, aHandle);
+}
+
+void
+WebRenderBridgeChild::RemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId)
+{
+  SendRemovePipelineIdForAsyncCompositable(aPipelineId);
+}
+
 wr::ExternalImageId
 WebRenderBridgeChild::GetNextExternalImageId()
 {
   wr::MaybeExternalImageId id = GetCompositorBridgeChild()->GetNextExternalImageId();
   MOZ_RELEASE_ASSERT(id.isSome());
   return id.value();
 }
 
 wr::ExternalImageId
-WebRenderBridgeChild::AllocExternalImageId(const CompositableHandle& aHandle)
-{
-  MOZ_ASSERT(!mDestroyed);
-
-  wr::ExternalImageId imageId = GetNextExternalImageId();
-  SendAddExternalImageId(imageId, aHandle);
-  return imageId;
-}
-
-wr::ExternalImageId
 WebRenderBridgeChild::AllocExternalImageIdForCompositable(CompositableClient* aCompositable)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aCompositable->IsConnected());
 
   wr::ExternalImageId imageId = GetNextExternalImageId();
   SendAddExternalImageIdForCompositable(imageId, aCompositable->GetIPCHandle());
   return imageId;
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -68,17 +68,20 @@ public:
   CompositorBridgeChild* GetCompositorBridgeChild();
 
   wr::PipelineId GetPipeline() { return mPipelineId; }
 
   // KnowsCompositor
   TextureForwarder* GetTextureForwarder() override;
   LayersIPCActor* GetLayersIPCActor() override;
 
-  wr::ExternalImageId AllocExternalImageId(const CompositableHandle& aHandle);
+  void AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
+                                         const CompositableHandle& aHandlee);
+  void RemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId);
+
   wr::ExternalImageId AllocExternalImageIdForCompositable(CompositableClient* aCompositable);
   void DeallocExternalImageId(wr::ExternalImageId& aImageId);
 
   /**
    * Clean this up, finishing with SendShutDown() which will cause __delete__
    * to be sent from the parent side.
    */
   void Destroy();
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -128,16 +128,19 @@ WebRenderBridgeParent::WebRenderBridgePa
   MOZ_ASSERT(mCompositableHolder);
   mCompositableHolder->AddPipeline(mPipelineId);
   if (mWidget) {
     MOZ_ASSERT(!mCompositorScheduler);
     mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
   }
 }
 
+WebRenderBridgeParent::~WebRenderBridgeParent()
+{
+}
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvCreate(const gfx::IntSize& aSize)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
 
@@ -449,88 +452,56 @@ WebRenderBridgeParent::ProcessWebRenderC
         MOZ_ASSERT(!mActiveKeys.Get(wr::AsUint64(keys[0]), nullptr));
         mActiveKeys.Put(wr::AsUint64(keys[0]), keys[0]);
 
         RefPtr<WebRenderImageHost> host = mExternalImageIds.Get(wr::AsUint64(op.externalImageId()));
         if (!host) {
           NS_ERROR("CompositableHost does not exist");
           break;
         }
-        // XXX select Texture for video in CompositeToTarget().
         TextureHost* texture = host->GetAsTextureHostForComposite();
         if (!texture) {
           NS_ERROR("TextureHost does not exist");
           break;
         }
         WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
         if (wrTexture) {
           wrTexture->AddWRImage(mApi, keys, wrTexture->GetExternalImageKey());
           break;
         }
         RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
         if (!dSurf) {
+          NS_ERROR("TextureHost does not return DataSourceSurface");
           break;
         }
 
         DataSourceSurface::MappedSurface map;
         if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
+          NS_ERROR("DataSourceSurface failed to map");
           break;
         }
 
         IntSize size = dSurf->GetSize();
         wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
         auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
         mApi->AddImage(keys[0], descriptor, slice);
 
         dSurf->Unmap();
         break;
       }
-      case WebRenderParentCommand::TOpAddExternalVideoImage: {
-        const OpAddExternalVideoImage& op = cmd.get_OpAddExternalVideoImage();
-        MOZ_ASSERT(mExternalImageIds.Get(wr::AsUint64(op.externalImageId())).get());
-        MOZ_ASSERT(op.keys().Length() > 0);
-        Range<const wr::ImageKey> keys(&(op.keys())[0], op.keys().Length());
-        for (auto key : keys) {
-          MOZ_ASSERT(!mActiveKeys.Get(wr::AsUint64(key), nullptr));
-          mActiveKeys.Put(wr::AsUint64(key), key);
-        }
-
-        RefPtr<WebRenderImageHost> host = mExternalImageIds.Get(wr::AsUint64(op.externalImageId()));
-        if (!host) {
-          NS_ERROR("CompositableHost does not exist");
-          break;
-        }
-        // XXX select Texture for video in CompositeToTarget().
-        TextureHost* texture = host->GetAsTextureHostForComposite();
-        if (!texture) {
-          NS_ERROR("TextureHost does not exist");
-          break;
-        }
-        WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
-        if (wrTexture) {
-          wrTexture->AddWRImage(mApi, keys, wrTexture->GetExternalImageKey());
-          break;
-        }
-
-        MOZ_ASSERT(keys.length() == 1);
-        RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
-        if (!dSurf) {
-          break;
-        }
-        DataSourceSurface::MappedSurface map;
-        if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
-          break;
-        }
-
-        IntSize size = dSurf->GetSize();
-        wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
-        auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
-        mApi->AddImage(keys[0], descriptor, slice);
-
-        dSurf->Unmap();
+      case WebRenderParentCommand::TOpUpdateAsyncImagePipeline: {
+        const OpUpdateAsyncImagePipeline& op = cmd.get_OpUpdateAsyncImagePipeline();
+        mCompositableHolder->UpdateAsyncImagePipeline(op.pipelineId(),
+                                                      op.scBounds(),
+                                                      op.scTransform(),
+                                                      op.scaleToSize(),
+                                                      op.clipRect(),
+                                                      op.mask(),
+                                                      op.filter(),
+                                                      op.mixBlendMode());
         break;
       }
       case WebRenderParentCommand::TCompositableOperation: {
         if (!ReceiveCompositableUpdate(cmd.get_CompositableOperation())) {
           NS_ERROR("ReceiveCompositableUpdate failed");
         }
         break;
       }
@@ -628,24 +599,24 @@ WebRenderBridgeParent::RecvDPGetSnapshot
   mApi->Readback(size, buffer, buffer_size);
 
   mForceRendering = false;
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvAddExternalImageId(const ExternalImageId& aImageId,
-                                              const CompositableHandle& aHandle)
+WebRenderBridgeParent::RecvAddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
+                                                             const CompositableHandle& aHandle)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
 
-  MOZ_ASSERT(!mExternalImageIds.Get(wr::AsUint64(aImageId)).get());
+  MOZ_ASSERT(!mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get());
 
   RefPtr<ImageBridgeParent> imageBridge = ImageBridgeParent::GetInstance(OtherPid());
   if (!imageBridge) {
      return IPC_FAIL_NO_REASON(this);
   }
   RefPtr<CompositableHost> host = imageBridge->FindCompositable(aHandle);
   if (!host) {
     NS_ERROR("CompositableHost not found in the map!");
@@ -654,18 +625,36 @@ WebRenderBridgeParent::RecvAddExternalIm
   MOZ_ASSERT(host->AsWebRenderImageHost());
   WebRenderImageHost* wrHost = host->AsWebRenderImageHost();
   if (!wrHost) {
     NS_ERROR("Incompatible CompositableHost");
     return IPC_OK();
   }
 
   wrHost->SetWrBridge(this);
-  mExternalImageIds.Put(wr::AsUint64(aImageId), wrHost);
+  mAsyncCompositables.Put(wr::AsUint64(aPipelineId), wrHost);
+  mCompositableHolder->AddAsyncImagePipeline(aPipelineId, wrHost);
+
+  return IPC_OK();
+}
 
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvRemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+
+  MOZ_ASSERT(mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get());
+  WebRenderImageHost* wrHost = mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get();
+  if (wrHost) {
+    wrHost->ClearWrBridge();
+    mCompositableHolder->RemoveAsyncImagePipeline(mApi, aPipelineId);
+  }
+  mAsyncCompositables.Remove(wr::AsUint64(aPipelineId));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
                                                              const CompositableHandle& aHandle)
 {
   if (mDestroyed) {
@@ -828,16 +817,19 @@ WebRenderBridgeParent::CompositeToTarget
     ScheduleComposition();
     return;
   }
 
   bool scheduleComposite = false;
   nsTArray<WrOpacityProperty> opacityArray;
   nsTArray<WrTransformProperty> transformArray;
 
+  mCompositableHolder->SetCompositionTime(TimeStamp::Now());
+  mCompositableHolder->ApplyAsyncImages(mApi);
+
   if (gfxPrefs::WebRenderOMTAEnabled()) {
     SampleAnimations(opacityArray, transformArray);
 
     if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
       scheduleComposite = true;
     }
   }
 
@@ -846,20 +838,19 @@ WebRenderBridgeParent::CompositeToTarget
   }
 
   if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
     mApi->GenerateFrame(opacityArray, transformArray);
   } else {
     mApi->GenerateFrame();
   }
 
-  // XXX Enable it when async video is supported.
-  // if (!mCompositableHolder->GetCompositeUntilTime().IsNull()) {
-  //   scheduleComposite = true;
-  // }
+  if (!mCompositableHolder->GetCompositeUntilTime().IsNull()) {
+    scheduleComposite = true;
+  }
 
   if (scheduleComposite) {
     ScheduleComposition();
   }
 }
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(uint32_t aWrEpoch, uint64_t aTransactionId)
@@ -906,20 +897,16 @@ WebRenderBridgeParent::FlushTransactionI
       mPendingTransactionIds.pop();
       break;
     }
     mPendingTransactionIds.pop();
   }
   return id;
 }
 
-WebRenderBridgeParent::~WebRenderBridgeParent()
-{
-}
-
 uint64_t
 WebRenderBridgeParent::GetLayersId() const
 {
   return wr::AsUint64(mPipelineId);
 }
 
 void
 WebRenderBridgeParent::DeleteOldImages()
@@ -1003,16 +990,25 @@ WebRenderBridgeParent::ClearResources()
     mKeysToDelete.push_back(iter.Data());
     iter.Remove();
   }
   DeleteOldImages();
   for (auto iter = mExternalImageIds.Iter(); !iter.Done(); iter.Next()) {
     iter.Data()->ClearWrBridge();
   }
   mExternalImageIds.Clear();
+  for (auto iter = mAsyncCompositables.Iter(); !iter.Done(); iter.Next()) {
+    wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
+    RefPtr<WebRenderImageHost> host = iter.Data();
+    MOZ_ASSERT(host->GetAsyncRef());
+    host->ClearWrBridge();
+    mCompositableHolder->RemoveAsyncImagePipeline(mApi, pipelineId);
+  }
+  mAsyncCompositables.Clear();
+
   mCompositableHolder->RemovePipeline(mPipelineId, wr::NewEpoch(mWrEpoch));
 
   if (mWidget) {
     mCompositorScheduler->Destroy();
   }
   mCompositorScheduler = nullptr;
   mApi = nullptr;
   mCompositorBridge = nullptr;
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -101,18 +101,20 @@ public:
                                         const uint64_t& aFwdTransactionId,
                                         const uint64_t& aTransactionId,
                                         const WrSize& aContentSize,
                                         const ByteBuffer& dl,
                                         const WrBuiltDisplayListDescriptor& dlDesc,
                                         const WebRenderScrollData& aScrollData) override;
   mozilla::ipc::IPCResult RecvDPGetSnapshot(PTextureParent* aTexture) override;
 
-  mozilla::ipc::IPCResult RecvAddExternalImageId(const ExternalImageId& aImageId,
-                                                 const CompositableHandle& aHandle) override;
+  mozilla::ipc::IPCResult RecvAddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineIds,
+                                                                const CompositableHandle& aHandle) override;
+  mozilla::ipc::IPCResult RecvRemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId) override;
+
   mozilla::ipc::IPCResult RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
                                                                 const CompositableHandle& aHandle) override;
   mozilla::ipc::IPCResult RecvRemoveExternalImageId(const ExternalImageId& aImageId) override;
   mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
 
   mozilla::ipc::IPCResult RecvClearCachedResources() override;
   mozilla::ipc::IPCResult RecvForceComposite() override;
 
@@ -174,28 +176,29 @@ public:
   const WebRenderScrollData& GetScrollData() const;
 
   static uint32_t AllocIdNameSpace() {
     return ++sIdNameSpace;
   }
 
   void FlushRendering(bool aIsSync);
 
+  void ScheduleComposition();
+
 private:
   virtual ~WebRenderBridgeParent();
 
   uint64_t GetLayersId() const;
   void DeleteOldImages();
   void ProcessWebRenderCommands(const gfx::IntSize &aSize,
                                 InfallibleTArray<WebRenderParentCommand>& commands,
                                 const wr::Epoch& aEpoch,
                                 const WrSize& aContentSize,
                                 const ByteBuffer& dl,
                                 const WrBuiltDisplayListDescriptor& dlDesc);
-  void ScheduleComposition();
   void ClearResources();
   uint64_t GetChildLayerObserverEpoch() const { return mChildLayerObserverEpoch; }
   bool ShouldParentObserveEpoch();
   void HandleDPEnd(const gfx::IntSize& aSize,
                    InfallibleTArray<WebRenderParentCommand>&& aCommands,
                    InfallibleTArray<OpDestroy>&& aToDestroy,
                    const uint64_t& aFwdTransactionId,
                    const uint64_t& aTransactionId,
@@ -233,16 +236,17 @@ private:
   wr::PipelineId mPipelineId;
   RefPtr<widget::CompositorWidget> mWidget;
   RefPtr<wr::WebRenderAPI> mApi;
   RefPtr<WebRenderCompositableHolder> mCompositableHolder;
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   std::vector<wr::ImageKey> mKeysToDelete;
   // XXX How to handle active keys of non-ExternalImages?
   nsDataHashtable<nsUint64HashKey, wr::ImageKey> mActiveKeys;
+  nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mAsyncCompositables;
   nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mExternalImageIds;
   nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
 
   // These fields keep track of the latest layer observer epoch values in the child and the
   // parent. mChildLayerObserverEpoch is the latest epoch value received from the child.
   // mParentLayerObserverEpoch is the latest epoch value that we have told TabParent about
   // (via ObserveLayerUpdate).
   uint64_t mChildLayerObserverEpoch;
--- a/gfx/layers/wr/WebRenderCompositableHolder.cpp
+++ b/gfx/layers/wr/WebRenderCompositableHolder.cpp
@@ -1,77 +1,358 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "WebRenderCompositableHolder.h"
 
 #include "CompositableHost.h"
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/WebRenderImageHost.h"
 #include "mozilla/layers/WebRenderTextureHost.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 WebRenderCompositableHolder::WebRenderCompositableHolder(uint32_t aIdNamespace)
  : mIdNamespace(aIdNamespace)
  , mResourceId(0)
+ , mAsyncImageEpoch(0)
+ , mDestroyed(false)
 {
   MOZ_COUNT_CTOR(WebRenderCompositableHolder);
 }
 
 WebRenderCompositableHolder::~WebRenderCompositableHolder()
 {
   MOZ_COUNT_DTOR(WebRenderCompositableHolder);
 }
 
 void
+WebRenderCompositableHolder::Destroy(wr::WebRenderAPI* aApi)
+{
+  DeleteOldAsyncImages(aApi);
+  mDestroyed = true;
+}
+
+bool
+WebRenderCompositableHolder::HasKeysToDelete()
+{
+  return !mKeysToDelete.IsEmpty();
+}
+
+void
+WebRenderCompositableHolder::DeleteOldAsyncImages(wr::WebRenderAPI* aApi)
+{
+  for (wr::ImageKey key : mKeysToDelete) {
+    aApi->DeleteImage(key);
+  }
+  mKeysToDelete.Clear();
+}
+
+void
 WebRenderCompositableHolder::AddPipeline(const wr::PipelineId& aPipelineId)
 {
+  if (mDestroyed) {
+    return;
+  }
   uint64_t id = wr::AsUint64(aPipelineId);
 
   MOZ_ASSERT(!mPipelineTexturesHolders.Get(id));
   PipelineTexturesHolder* holder = new PipelineTexturesHolder();
   mPipelineTexturesHolders.Put(id, holder);
 }
 
 void
 WebRenderCompositableHolder::RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
 {
+  if (mDestroyed) {
+    return;
+  }
+
   PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
   MOZ_ASSERT(holder);
   if (!holder) {
     return;
   }
   MOZ_ASSERT(holder->mDestroyedEpoch.isNothing());
   holder->mDestroyedEpoch = Some(aEpoch);
 }
 
 void
+WebRenderCompositableHolder::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost)
+{
+  if (mDestroyed) {
+    return;
+  }
+  MOZ_ASSERT(aImageHost);
+  uint64_t id = wr::AsUint64(aPipelineId);
+
+  MOZ_ASSERT(!mAsyncImagePipelineHolders.Get(id));
+  AsyncImagePipelineHolder* holder = new AsyncImagePipelineHolder();
+  holder->mImageHost = aImageHost;
+  mAsyncImagePipelineHolders.Put(id, holder);
+  AddPipeline(aPipelineId);
+}
+
+void
+WebRenderCompositableHolder::RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId)
+{
+  if (mDestroyed) {
+    return;
+  }
+
+  uint64_t id = wr::AsUint64(aPipelineId);
+  if (!mAsyncImagePipelineHolders.Get(id)) {
+    return;
+  }
+
+  ++mAsyncImageEpoch; // Update webrender epoch
+  aApi->ClearRootDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
+  mAsyncImagePipelineHolders.Remove(id);
+  RemovePipeline(aPipelineId, wr::NewEpoch(mAsyncImageEpoch));
+}
+
+void
+WebRenderCompositableHolder::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
+                                                      const LayerRect& aScBounds,
+                                                      const gfx::Matrix4x4& aScTransform,
+                                                      const gfx::MaybeIntSize& aScaleToSize,
+                                                      const MaybeLayerRect& aClipRect,
+                                                      const MaybeImageMask& aMask,
+                                                      const WrImageRendering& aFilter,
+                                                      const WrMixBlendMode& aMixBlendMode)
+{
+  if (mDestroyed) {
+    return;
+  }
+  AsyncImagePipelineHolder* holder = mAsyncImagePipelineHolders.Get(wr::AsUint64(aPipelineId));
+  MOZ_ASSERT(holder);
+  if (!holder) {
+    return;
+  }
+  holder->mScBounds = aScBounds;
+  holder->mScTransform = aScTransform;
+  holder->mScaleToSize = aScaleToSize;
+  holder->mClipRect = aClipRect;
+  holder->mMask = aMask;
+  holder->mFilter = aFilter;
+  holder->mMixBlendMode = aMixBlendMode;
+}
+
+void
+WebRenderCompositableHolder::GetImageKeys(nsTArray<wr::ImageKey>& aKeys, size_t aChannelNumber)
+{
+  MOZ_ASSERT(aChannelNumber > 0);
+  for (size_t i = 0; i < aChannelNumber; ++i) {
+    wr::ImageKey key = GetImageKey();
+    aKeys.AppendElement(key);
+  }
+}
+
+void
+WebRenderCompositableHolder::GetImageKeysForExternalImage(nsTArray<wr::ImageKey>& aKeys)
+{
+  MOZ_ASSERT(aKeys.IsEmpty());
+
+  // XXX (Jerry): Remove the hardcode image format setting.
+#if defined(XP_WIN)
+  // Use libyuv to convert the buffer to rgba format. So, use 1 image key here.
+  GetImageKeys(aKeys, 1);
+#elif defined(XP_MACOSX)
+  if (gfxVars::CanUseHardwareVideoDecoding()) {
+    // Use the hardware MacIOSurface with YCbCr interleaved format. It uses 1
+    // image key.
+    GetImageKeys(aKeys, 1);
+  } else {
+    // Use libyuv.
+    GetImageKeys(aKeys, 1);
+  }
+#elif defined(MOZ_WIDGET_GTK)
+  // Use libyuv.
+  GetImageKeys(aKeys, 1);
+#elif defined(ANDROID)
+  // Use libyuv.
+  GetImageKeys(aKeys, 1);
+#endif
+
+  MOZ_ASSERT(!aKeys.IsEmpty());
+}
+
+bool
+WebRenderCompositableHolder::GetImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys)
+{
+  MOZ_ASSERT(aKeys.IsEmpty());
+
+  if (!aTexture) {
+    return false;
+  }
+
+  WebRenderTextureHost* wrTexture = aTexture->AsWebRenderTextureHost();
+
+  if (wrTexture) {
+    GetImageKeysForExternalImage(aKeys);
+    MOZ_ASSERT(!aKeys.IsEmpty());
+    Range<const wr::ImageKey> keys(&aKeys[0], aKeys.Length());
+    wrTexture->AddWRImage(aApi, keys, wrTexture->GetExternalImageKey());
+    return true;
+  } else {
+    RefPtr<DataSourceSurface> dSurf = aTexture->GetAsSurface();
+    if (!dSurf) {
+      NS_ERROR("TextureHost does not return DataSourceSurface");
+      return false;
+    }
+    DataSourceSurface::MappedSurface map;
+    if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
+      NS_ERROR("DataSourceSurface failed to map");
+      return false;
+    }
+    IntSize size = dSurf->GetSize();
+    wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
+    auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
+
+    wr::ImageKey key = GetImageKey();
+    aKeys.AppendElement(key);
+    aApi->AddImage(key, descriptor, slice);
+    dSurf->Unmap();
+  }
+  return false;
+}
+
+void
+WebRenderCompositableHolder::PushExternalImage(wr::DisplayListBuilder& aBuilder,
+                                               const WrRect& aBounds,
+                                               const WrClipRegionToken aClip,
+                                               wr::ImageRendering aFilter,
+                                               nsTArray<wr::ImageKey>& aKeys)
+{
+  // XXX (Jerry): Remove the hardcode image format setting. The format of
+  // textureClient could change from time to time. So, we just set the most
+  // usable format here.
+#if defined(XP_WIN)
+  // Use libyuv to convert the buffer to rgba format.
+  MOZ_ASSERT(aKeys.Length() == 1);
+  aBuilder.PushImage(aBounds, aClip, aFilter, aKeys[0]);
+#elif defined(XP_MACOSX)
+  if (gfx::gfxVars::CanUseHardwareVideoDecoding()) {
+    // Use the hardware MacIOSurface with YCbCr interleaved format.
+    MOZ_ASSERT(aKeys.Length() == 1);
+    aBuilder.PushYCbCrInterleavedImage(aBounds, aClip, aKeys[0], WrYuvColorSpace::Rec601, aFilter);
+  } else {
+    // Use libyuv to convert the buffer to rgba format.
+    MOZ_ASSERT(aKeys.Length() == 1);
+    aBuilder.PushImage(aBounds, aClip, aFilter, aKeys[0]);
+  }
+#elif defined(MOZ_WIDGET_GTK)
+  // Use libyuv to convert the buffer to rgba format.
+  MOZ_ASSERT(aKeys.Length() == 1);
+  aBuilder.PushImage(aBounds, aClip, aFilter, aKeys[0]);
+#elif defined(ANDROID)
+  // Use libyuv to convert the buffer to rgba format.
+  MOZ_ASSERT(aKeys.Length() == 1);
+  aBuilder.PushImage(aBounds, aClip, aFilter, aKeys[0]);
+#endif
+}
+
+void
+WebRenderCompositableHolder::ApplyAsyncImages(wr::WebRenderAPI* aApi)
+{
+  if (mDestroyed) {
+    return;
+  }
+  ++mAsyncImageEpoch; // Update webrender epoch
+  wr::Epoch epoch = wr::NewEpoch(mAsyncImageEpoch);
+  nsTArray<wr::ImageKey> keysToDelete;
+
+  for (auto iter = mAsyncImagePipelineHolders.Iter(); !iter.Done(); iter.Next()) {
+    wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
+    AsyncImagePipelineHolder* holder = iter.Data();
+
+    WrSize contentSize { holder->mScBounds.width, holder->mScBounds.height };
+    wr::DisplayListBuilder builder(pipelineId, contentSize);
+    float opacity = 1.0f;
+    builder.PushStackingContext(wr::ToWrRect(holder->mScBounds),
+                                0,
+                                &opacity,
+                                holder->mScTransform.IsIdentity() ? nullptr : &holder->mScTransform,
+                                holder->mMixBlendMode);
+
+    TextureHost* texture = holder->mImageHost->GetAsTextureHostForComposite();
+    nsTArray<wr::ImageKey> keys;
+    bool useExternalImage = GetImageKeyForTextureHost(aApi, texture, keys);
+
+    if (!keys.IsEmpty()) {
+      LayerRect rect(0, 0, texture->GetSize().width, texture->GetSize().height);
+      if (holder->mScaleToSize.isSome()) {
+        rect = LayerRect(0, 0, holder->mScaleToSize.value().width, holder->mScaleToSize.value().height);
+      }
+      LayerRect clipRect = holder->mClipRect.valueOr(rect);
+      WrClipRegionToken clip = builder.PushClipRegion(
+        wr::ToWrRect(clipRect),
+        holder->mMask.ptrOr(nullptr));
+
+      if (useExternalImage) {
+        MOZ_ASSERT(texture->AsWebRenderTextureHost());
+        PushExternalImage(builder,
+                          wr::ToWrRect(rect),
+                          clip,
+                          holder->mFilter,
+                          keys);
+        HoldExternalImage(pipelineId, epoch, texture->AsWebRenderTextureHost());
+      } else {
+        MOZ_ASSERT(keys.Length() == 1);
+        builder.PushImage(wr::ToWrRect(rect),
+                          clip,
+                          holder->mFilter,
+                          keys[0]);
+      }
+      // XXX do not delete ImageKey for every rendering.
+      keysToDelete.AppendElements(keys);
+    }
+    builder.PopStackingContext();
+
+    wr::BuiltDisplayList dl;
+    WrSize builderContentSize;
+    builder.Finalize(builderContentSize, dl);
+    aApi->SetRootDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(holder->mScBounds.width, holder->mScBounds.height),
+                             pipelineId, builderContentSize,
+                             dl.dl_desc, dl.dl.inner.data, dl.dl.inner.length);
+  }
+  DeleteOldAsyncImages(aApi);
+  mKeysToDelete.SwapElements(keysToDelete);
+}
+
+void
 WebRenderCompositableHolder::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
 {
+  if (mDestroyed) {
+    return;
+  }
   MOZ_ASSERT(aTexture);
 
   PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
   MOZ_ASSERT(holder);
   if (!holder) {
     return;
   }
   // Hold WebRenderTextureHost until end of its usage on RenderThread
   holder->mTextureHosts.push(ForwardingTextureHost(aEpoch, aTexture));
 }
 
 void
 WebRenderCompositableHolder::Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
 {
+  if (mDestroyed) {
+    return;
+  }
   PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
   if (!holder) {
     return;
   }
 
   // Remove Pipeline
   if (holder->mDestroyedEpoch.isSome() && holder->mDestroyedEpoch.ref() <= aEpoch) {
     mPipelineTexturesHolders.Remove(wr::AsUint64(aPipelineId));
--- a/gfx/layers/wr/WebRenderCompositableHolder.h
+++ b/gfx/layers/wr/WebRenderCompositableHolder.h
@@ -3,45 +3,53 @@
  * 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_WEBRENDERCOMPOSITABLE_HOLDER_H
 #define MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H
 
 #include <queue>
 
+#include "mozilla/gfx/Point.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "nsClassHashtable.h"
 
 namespace mozilla {
 
 namespace wr {
+class DisplayListBuilder;
 class WebRenderAPI;
 }
 
 namespace layers {
 
 class CompositableHost;
+class CompositorVsyncScheduler;
+class WebRenderImageHost;
 class WebRenderTextureHost;
 
 class WebRenderCompositableHolder final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderCompositableHolder)
 
   explicit WebRenderCompositableHolder(uint32_t aIdNamespace);
 
 protected:
   ~WebRenderCompositableHolder();
 
 public:
+  void Destroy(wr::WebRenderAPI* aApi);
+  bool HasKeysToDelete();
+
   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 Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
 
   TimeStamp GetCompositionTime() const {
     return mCompositionTime;
   }
   void SetCompositionTime(TimeStamp aTimeStamp) {
     mCompositionTime = aTimeStamp;
@@ -55,46 +63,85 @@ public:
         mCompositeUntilTime < aTimeStamp) {
       mCompositeUntilTime = aTimeStamp;
     }
   }
   TimeStamp GetCompositeUntilTime() const {
     return mCompositeUntilTime;
   }
 
+  void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost);
+  void RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId);
+
+  void UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
+                                const LayerRect& aScBounds,
+                                const gfx::Matrix4x4& aScTransform,
+                                const gfx::MaybeIntSize& aScaleToSize,
+                                const MaybeLayerRect& aClipRect,
+                                const MaybeImageMask& aMask,
+                                const WrImageRendering& aFilter,
+                                const WrMixBlendMode& aMixBlendMode);
+  void ApplyAsyncImages(wr::WebRenderAPI* aApi);
+
+private:
+  void DeleteOldAsyncImages(wr::WebRenderAPI* aApi);
+
   uint32_t GetNextResourceId() { return ++mResourceId; }
   uint32_t GetNamespace() { return mIdNamespace; }
   wr::ImageKey GetImageKey()
   {
     wr::ImageKey key;
     key.mNamespace = GetNamespace();
     key.mHandle = GetNextResourceId();
     return key;
   }
-
-private:
+  void GetImageKeys(nsTArray<wr::ImageKey>& aKeys, size_t aChannelNumber);
+  void GetImageKeysForExternalImage(nsTArray<wr::ImageKey>& aKeys);
+  bool GetImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys);
+  void PushExternalImage(wr::DisplayListBuilder& aBuilder,
+                         const WrRect& aBounds,
+                         const WrClipRegionToken aClip,
+                         wr::ImageRendering aFilter,
+                         nsTArray<wr::ImageKey>& aKeys);
 
   struct ForwardingTextureHost {
     ForwardingTextureHost(const wr::Epoch& aEpoch, TextureHost* aTexture)
       : mEpoch(aEpoch)
       , mTexture(aTexture)
     {}
     wr::Epoch mEpoch;
     CompositableTextureHostRef mTexture;
   };
 
   struct PipelineTexturesHolder {
     // Holds forwarding WebRenderTextureHosts.
     std::queue<ForwardingTextureHost> mTextureHosts;
     Maybe<wr::Epoch> mDestroyedEpoch;
   };
 
+  struct AsyncImagePipelineHolder {
+    LayerRect mScBounds;
+    gfx::Matrix4x4 mScTransform;
+    gfx::MaybeIntSize mScaleToSize;
+    MaybeLayerRect mClipRect;
+    MaybeImageMask mMask;
+    WrImageRendering mFilter;
+    WrMixBlendMode mMixBlendMode;
+    RefPtr<WebRenderImageHost> mImageHost;
+    CompositableTextureHostRef mCurrentTextureHost;
+  };
+
   uint32_t mIdNamespace;
   uint32_t mResourceId;
+
   nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder> mPipelineTexturesHolders;
+  nsClassHashtable<nsUint64HashKey, AsyncImagePipelineHolder> mAsyncImagePipelineHolders;
+  uint32_t mAsyncImageEpoch;
+  nsTArray<wr::ImageKey> mKeysToDelete;
+  bool mDestroyed;
 
   // Render time for the current composition.
   TimeStamp mCompositionTime;
 
   // When nonnull, during rendering, some compositable indicated that it will
   // change its rendering at this time. In order not to miss it, we composite
   // on every vsync until this time occurs (this is the latest such time).
   TimeStamp mCompositeUntilTime;
--- a/gfx/layers/wr/WebRenderImageHost.cpp
+++ b/gfx/layers/wr/WebRenderImageHost.cpp
@@ -63,16 +63,20 @@ WebRenderImageHost::UseTextureHost(const
     img.mProducerID = t.mProducerID;
     img.mTextureHost->SetCropRect(img.mPictureRect);
     img.mTextureHost->Updated();
   }
 
   mImages.SwapElements(newImages);
   newImages.Clear();
 
+  if (mWrBridge && GetAsyncRef()) {
+    mWrBridge->ScheduleComposition();
+  }
+
   // Video producers generally send replacement images with the same frameID but
   // slightly different timestamps in order to sync with the audio clock. This
   // means that any CompositeUntil() call we made in Composite() may no longer
   // guarantee that we'll composite until the next frame is ready. Fix that here.
   if (mWrBridge && mLastFrameID >= 0) {
     MOZ_ASSERT(mWrBridge->CompositableHolder());
     for (size_t i = 0; i < mImages.Length(); ++i) {
       bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
--- a/gfx/layers/wr/WebRenderImageHost.h
+++ b/gfx/layers/wr/WebRenderImageHost.h
@@ -68,16 +68,18 @@ public:
   virtual WebRenderImageHost* AsWebRenderImageHost() override { return this; }
 
   TextureHost* GetAsTextureHostForComposite();
 
   void SetWrBridge(WebRenderBridgeParent* aWrBridge);
 
   void ClearWrBridge();
 
+  TextureHost* GetCurrentTextureHost() { return mCurrentTextureHost; }
+
 protected:
   // ImageComposite
   virtual TimeStamp GetCompositionTime() const override;
 
   void SetCurrentTextureHost(TextureHost* aTexture);
 
   WebRenderBridgeParent* MOZ_NON_OWNING_REF mWrBridge;
 
--- a/gfx/layers/wr/WebRenderImageLayer.cpp
+++ b/gfx/layers/wr/WebRenderImageLayer.cpp
@@ -28,26 +28,26 @@ WebRenderImageLayer::WebRenderImageLayer
 {
   MOZ_COUNT_CTOR(WebRenderImageLayer);
 }
 
 WebRenderImageLayer::~WebRenderImageLayer()
 {
   MOZ_COUNT_DTOR(WebRenderImageLayer);
 
-  for (auto key : mVideoKeys) {
-    WrManager()->AddImageKeyForDiscard(key);
-  }
   if (mKey.isSome()) {
     WrManager()->AddImageKeyForDiscard(mKey.value());
   }
 
   if (mExternalImageId.isSome()) {
     WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
   }
+  if (mPipelineId.isSome()) {
+    WrBridge()->RemovePipelineIdForAsyncCompositable(mPipelineId.ref());
+  }
 }
 
 CompositableType
 WebRenderImageLayer::GetImageClientType()
 {
   if (mImageClientTypeContainer != CompositableType::UNKNOWN) {
     return mImageClientTypeContainer;
   }
@@ -86,27 +86,16 @@ void
 WebRenderImageLayer::ClearCachedResources()
 {
   if (mImageClient) {
     mImageClient->ClearCachedResources();
   }
 }
 
 void
-WebRenderImageLayer::AddWRVideoImage(size_t aChannelNumber)
-{
-  for (size_t i = 0; i < aChannelNumber; ++i) {
-    WrImageKey key = GetImageKey();
-    WrManager()->AddImageKeyForDiscard(key);
-    mVideoKeys.AppendElement(key);
-  }
-  WrBridge()->AddWebRenderParentCommand(OpAddExternalVideoImage(mExternalImageId.value(), mVideoKeys));
-}
-
-void
 WebRenderImageLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
                                  const StackingContextHelper& aSc)
 {
   if (!mContainer) {
      return;
   }
 
   CompositableType type = GetImageClientType();
@@ -121,72 +110,99 @@ WebRenderImageLayer::RenderLayer(wr::Dis
                                                   WrBridge(),
                                                   TextureFlags::DEFAULT);
     if (!mImageClient) {
       return;
     }
     mImageClient->Connect();
   }
 
-  if (mExternalImageId.isNothing()) {
-    if (GetImageClientType() == CompositableType::IMAGE_BRIDGE) {
-      MOZ_ASSERT(!mImageClient);
-      mExternalImageId = Some(WrBridge()->AllocExternalImageId(mContainer->GetAsyncContainerHandle()));
-      // Alloc async image pipeline id.
-      mPipelineId = Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
-    } else {
-      // Handle CompositableType::IMAGE case
-      MOZ_ASSERT(mImageClient);
-      mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
+  if (GetImageClientType() == CompositableType::IMAGE_BRIDGE && mPipelineId.isNothing()) {
+    MOZ_ASSERT(!mImageClient);
+    // Alloc async image pipeline id.
+    mPipelineId = Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
+    WrBridge()->AddPipelineIdForAsyncCompositable(mPipelineId.ref(),
+                                                  mContainer->GetAsyncContainerHandle());
+  } else if (GetImageClientType() == CompositableType::IMAGE && mExternalImageId.isNothing())  {
+    MOZ_ASSERT(mImageClient);
+    mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
+    MOZ_ASSERT(mExternalImageId.isSome());
+  }
+
+  if (GetImageClientType() == CompositableType::IMAGE_BRIDGE) {
+    MOZ_ASSERT(!mImageClient);
+    MOZ_ASSERT(mExternalImageId.isNothing());
+
+    // Push IFrame for async image pipeline.
+
+    ParentLayerRect bounds = GetLocalTransformTyped().TransformBounds(Bounds());
+
+    // As with WebRenderTextLayer, because we don't push a stacking context for
+    // this async image pipeline, WR doesn't know about the transform on this layer.
+    // Therefore we need to apply that transform to the bounds before we pass it on to WR.
+    // The conversion from ParentLayerPixel to LayerPixel below is a result of
+    // changing the reference layer from "this layer" to the "the layer that
+    // created aSc".
+    LayerRect rect = ViewAs<LayerPixel>(bounds,
+        PixelCastJustification::MovingDownToChildren);
+    DumpLayerInfo("Image Layer async", rect);
+
+    // XXX Remove IFrame for async image pipeline when partial display list update is supported.
+    WrClipRegionToken clipRegion = aBuilder.PushClipRegion(aSc.ToRelativeWrRect(rect));
+    aBuilder.PushIFrame(aSc.ToRelativeWrRect(rect), clipRegion, mPipelineId.ref());
+
+    // Prepare data that are necessary for async image pipelin.
+    // They are used within WebRenderCompositableHolder
+
+    gfx::Matrix4x4 scTransform = GetTransform();
+    // Translate is applied as part of PushIFrame()
+    scTransform.PostTranslate(-rect.x, -rect.y, 0);
+    // Adjust transform as to apply origin
+    LayerPoint scOrigin = Bounds().TopLeft();
+    scTransform.PreTranslate(-scOrigin.x, -scOrigin.y, 0);
+
+    MaybeIntSize scaleToSize;
+    if (mScaleMode != ScaleMode::SCALE_NONE) {
+      NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
+                   "No other scalemodes than stretch and none supported yet.");
+      scaleToSize = Some(mScaleToSize);
     }
+    LayerRect scBounds = BoundsForStackingContext();
+    wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
+    wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+
+    StackingContextHelper sc(aSc, aBuilder, this);
+    Maybe<WrImageMask> mask = BuildWrMaskLayer(&sc);
+
+    WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(mPipelineId.value(),
+                                                                     scBounds,
+                                                                     scTransform,
+                                                                     scaleToSize,
+                                                                     ClipRect(),
+                                                                     mask,
+                                                                     filter,
+                                                                     mixBlendMode));
+    return;
   }
-  MOZ_ASSERT(mExternalImageId.isSome());
 
-  // XXX Not good for async ImageContainer case.
+  MOZ_ASSERT(GetImageClientType() == CompositableType::IMAGE);
+  MOZ_ASSERT(mImageClient->AsImageClientSingle());
+
   AutoLockImage autoLock(mContainer);
   Image* image = autoLock.GetImage();
   if (!image) {
     return;
   }
   gfx::IntSize size = image->GetSize();
-
-  if (GetImageClientType() != CompositableType::IMAGE_BRIDGE) {
-    // Handle CompositableType::IMAGE case
-    MOZ_ASSERT(mImageClient->AsImageClientSingle());
-    mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
-                          mContainer,
-                          mKey,
-                          mExternalImageId.ref());
-    if (mKey.isNothing()) {
-      return;
-    }
-  } else {
-    // Always allocate key.
-    mVideoKeys.Clear();
-
-    // XXX (Jerry): Remove the hardcode image format setting.
-#if defined(XP_WIN)
-    // Use libyuv to convert the buffer to rgba format. So, use 1 image key here.
-    AddWRVideoImage(1);
-#elif defined(XP_MACOSX)
-    if (gfx::gfxVars::CanUseHardwareVideoDecoding()) {
-      // Use the hardware MacIOSurface with YCbCr interleaved format. It uses 1
-      // image key.
-      AddWRVideoImage(1);
-    } else {
-      // Use libyuv.
-      AddWRVideoImage(1);
-    }
-#elif defined(MOZ_WIDGET_GTK)
-    // Use libyuv.
-    AddWRVideoImage(1);
-#elif defined(ANDROID)
-    // Use libyuv.
-    AddWRVideoImage(1);
-#endif
+  mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
+                        mContainer,
+                        mKey,
+                        mExternalImageId.ref());
+  if (mKey.isNothing()) {
+    return;
   }
 
   ScrollingLayersHelper scroller(this, aBuilder, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
 
   LayerRect rect(0, 0, size.width, size.height);
   if (mScaleMode != ScaleMode::SCALE_NONE) {
     NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
@@ -203,47 +219,17 @@ WebRenderImageLayer::RenderLayer(wr::Dis
   wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
 
   DumpLayerInfo("Image Layer", rect);
   if (gfxPrefs::LayersDump()) {
     printf_stderr("ImageLayer %p texture-filter=%s \n",
                   GetLayer(),
                   Stringify(filter).c_str());
   }
-
-  if (GetImageClientType() != CompositableType::IMAGE_BRIDGE) {
-    aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mKey.value());
-  } else {
-    // XXX (Jerry): Remove the hardcode image format setting. The format of
-    // textureClient could change from time to time. So, we just set the most
-    // usable format here.
-#if defined(XP_WIN)
-    // Use libyuv to convert the buffer to rgba format.
-    MOZ_ASSERT(mVideoKeys.Length() == 1);
-    aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mVideoKeys[0]);
-#elif defined(XP_MACOSX)
-    if (gfx::gfxVars::CanUseHardwareVideoDecoding()) {
-      // Use the hardware MacIOSurface with YCbCr interleaved format.
-      MOZ_ASSERT(mVideoKeys.Length() == 1);
-      aBuilder.PushYCbCrInterleavedImage(sc.ToRelativeWrRect(rect), clip, mVideoKeys[0], WrYuvColorSpace::Rec601, filter);
-    } else {
-      // Use libyuv to convert the buffer to rgba format.
-      MOZ_ASSERT(mVideoKeys.Length() == 1);
-      aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mVideoKeys[0]);
-    }
-#elif defined(MOZ_WIDGET_GTK)
-    // Use libyuv to convert the buffer to rgba format.
-    MOZ_ASSERT(mVideoKeys.Length() == 1);
-    aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mVideoKeys[0]);
-#elif defined(ANDROID)
-    // Use libyuv to convert the buffer to rgba format.
-    MOZ_ASSERT(mVideoKeys.Length() == 1);
-    aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mVideoKeys[0]);
-#endif
-  }
+  aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mKey.value());
 }
 
 Maybe<WrImageMask>
 WebRenderImageLayer::RenderMaskLayer(const gfx::Matrix4x4& aTransform)
 {
   if (!mContainer) {
      return Nothing();
   }
--- a/gfx/layers/wr/WebRenderImageLayer.h
+++ b/gfx/layers/wr/WebRenderImageLayer.h
@@ -34,19 +34,16 @@ public:
   Maybe<WrImageMask> RenderMaskLayer(const gfx::Matrix4x4& aTransform) override;
 
 protected:
   CompositableType GetImageClientType();
 
   void AddWRVideoImage(size_t aChannelNumber);
 
   wr::MaybeExternalImageId mExternalImageId;
-  // Some video image format contains multiple channel data.
-  nsTArray<wr::ImageKey> mVideoKeys;
-  // The regular single channel image.
   Maybe<wr::ImageKey> mKey;
   RefPtr<ImageClient> mImageClient;
   CompositableType mImageClientTypeContainer;
   Maybe<wr::PipelineId> mPipelineId;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -168,16 +168,54 @@ struct ParamTraits<WrPoint>
   Read(const Message* aMsg, PickleIterator* aIter, WrPoint* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->x) &&
            ReadParam(aMsg, aIter, &aResult->y);
   }
 };
 
 template<>
+struct ParamTraits<WrImageMask>
+{
+  static void
+  Write(Message* aMsg, const WrImageMask& aParam)
+  {
+    WriteParam(aMsg, aParam.image);
+    WriteParam(aMsg, aParam.rect);
+    WriteParam(aMsg, aParam.repeat);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, WrImageMask* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->image)
+        && ReadParam(aMsg, aIter, &aResult->rect)
+        && ReadParam(aMsg, aIter, &aResult->repeat);
+  }
+};
+
+template<>
+struct ParamTraits<WrImageRendering>
+  : public ContiguousEnumSerializer<
+        WrImageRendering,
+        WrImageRendering::Auto,
+        WrImageRendering::Sentinel>
+{
+};
+
+template<>
+struct ParamTraits<WrMixBlendMode>
+  : public ContiguousEnumSerializer<
+        WrMixBlendMode,
+        WrMixBlendMode::Normal,
+        WrMixBlendMode::Sentinel>
+{
+};
+
+template<>
 struct ParamTraits<WrBuiltDisplayListDescriptor>
 {
   static void
   Write(Message* aMsg, const WrBuiltDisplayListDescriptor& aParam)
   {
     WriteParam(aMsg, aParam.display_list_items_size);
     WriteParam(aMsg, aParam.builder_start_time);
     WriteParam(aMsg, aParam.builder_finish_time);
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1167,42 +1167,42 @@ impl WebRenderFrameBuilder {
 pub struct WrState {
     pipeline_id: WrPipelineId,
     frame_builder: WebRenderFrameBuilder,
 }
 
 #[no_mangle]
 pub extern "C" fn wr_state_new(pipeline_id: WrPipelineId,
                                content_size: WrSize) -> *mut WrState {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
 
     let state = Box::new(WrState {
                              pipeline_id: pipeline_id,
                              frame_builder: WebRenderFrameBuilder::new(pipeline_id,
                                                                        content_size),
                          });
 
     Box::into_raw(state)
 }
 
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
 #[no_mangle]
 pub extern "C" fn wr_state_delete(state: *mut WrState) {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
 
     unsafe {
         Box::from_raw(state);
     }
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_begin(state: &mut WrState,
                               width: u32,
                               height: u32) {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
     state.frame_builder.dl_builder.data.clear();
 
     let bounds = LayoutRect::new(LayoutPoint::new(0.0, 0.0),
                                  LayoutSize::new(width as f32, height as f32));
 
     state.frame_builder
          .dl_builder
          .push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
@@ -1211,28 +1211,28 @@ pub extern "C" fn wr_dp_begin(state: &mu
                                 TransformStyle::Flat,
                                 None,
                                 MixBlendMode::Normal,
                                 Vec::new());
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_end(state: &mut WrState) {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
     state.frame_builder.dl_builder.pop_stacking_context();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_clip_region(state: &mut WrState,
                                          main: WrRect,
                                          complex: *const WrComplexClipRegion,
                                          complex_count: usize,
                                          image_mask: *const WrImageMask)
                                          -> WrClipRegionToken {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
 
     let main = main.into();
     let complex_slice = make_slice(complex, complex_count);
     let complex_iter = complex_slice.iter().map(|x| x.into());
     let mask = unsafe { image_mask.as_ref() }.map(|x| x.into());
 
     let clip_region = state.frame_builder.dl_builder.push_clip_region(&main, complex_iter, mask);
 
@@ -1241,17 +1241,17 @@ pub extern "C" fn wr_dp_push_clip_region
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_stacking_context(state: &mut WrState,
                                               bounds: WrRect,
                                               animation_id: u64,
                                               opacity: *const f32,
                                               transform: *const WrMatrix,
                                               mix_blend_mode: WrMixBlendMode) {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
 
     let bounds = bounds.into();
 
     let mut filters: Vec<FilterOp> = Vec::new();
     let opacity = unsafe { opacity.as_ref() };
     if let Some(opacity) = opacity {
         if *opacity < 1.0 {
             filters.push(FilterOp::Opacity(PropertyBinding::Value(*opacity)));
@@ -1277,34 +1277,34 @@ pub extern "C" fn wr_dp_push_stacking_co
                                 TransformStyle::Flat,
                                 None,
                                 mix_blend_mode,
                                 filters);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState) {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
     state.frame_builder.dl_builder.pop_stacking_context();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_clip(state: &mut WrState,
                                   clip_rect: WrRect,
                                   mask: *const WrImageMask) {
     assert!(unsafe { is_in_main_thread() });
     let clip_rect = clip_rect.into();
     let mask = unsafe { mask.as_ref() }.map(|x| x.into());
     let clip_region = state.frame_builder.dl_builder.push_clip_region(&clip_rect, vec![], mask);
     state.frame_builder.dl_builder.push_clip_node(clip_rect, clip_region, None);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_clip(state: &mut WrState) {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
     state.frame_builder.dl_builder.pop_clip_node();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_scroll_layer(state: &mut WrState,
                                           scroll_id: u64,
                                           content_rect: WrRect,
                                           clip_rect: WrRect) {
@@ -1348,30 +1348,30 @@ pub extern "C" fn wr_dp_push_iframe(stat
     state.frame_builder.dl_builder.push_iframe(rect.into(), clip.into(), pipeline_id);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_rect(state: &mut WrState,
                                   rect: WrRect,
                                   clip: WrClipRegionToken,
                                   color: WrColor) {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
 
     state.frame_builder.dl_builder.push_rect(rect.into(), clip.into(), color.into());
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_image(state: &mut WrState,
                                    bounds: WrRect,
                                    clip: WrClipRegionToken,
                                    stretch_size: WrSize,
                                    tile_spacing: WrSize,
                                    image_rendering: WrImageRendering,
                                    key: WrImageKey) {
-    assert!(unsafe { is_in_main_thread() });
+    assert!(unsafe { !is_in_render_thread() });
 
     state.frame_builder
          .dl_builder
          .push_image(bounds.into(),
                      clip.into(),
                      stretch_size.into(),
                      tile_spacing.into(),
                      image_rendering,