Bug 1425484 - Part 3. Integrate shared surfaces with the blob image recordings. r=jrmuizel
authorAndrew Osmond <aosmond@mozilla.com>
Thu, 26 Apr 2018 19:00:16 -0400
changeset 472072 b2a3cbc041dd835893b261ff64caa8120468f887
parent 472071 fae8c28c3966314bd80d40444b6cf756ba143301
child 472073 2321cd280739952a9481a1ad96008aeb3d0a0bd7
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1425484
milestone61.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 1425484 - Part 3. Integrate shared surfaces with the blob image recordings. r=jrmuizel
gfx/2d/DrawEventRecorder.cpp
gfx/2d/DrawEventRecorder.h
gfx/2d/DrawTargetRecording.cpp
gfx/2d/InlineTranslator.h
gfx/2d/RecordedEvent.cpp
gfx/2d/RecordedEvent.h
gfx/2d/RecordedEventImpl.h
gfx/layers/moz.build
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderDrawEventRecorder.cpp
gfx/layers/wr/WebRenderDrawEventRecorder.h
gfx/layers/wr/WebRenderUserData.h
gfx/webrender_bindings/Moz2DImageRenderer.cpp
--- a/gfx/2d/DrawEventRecorder.cpp
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -2,27 +2,58 @@
 /* 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 "DrawEventRecorder.h"
 #include "PathRecording.h"
 #include "RecordingTypes.h"
+#include "RecordedEventImpl.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace std;
 
 DrawEventRecorderPrivate::DrawEventRecorderPrivate() : mExternalFonts(false)
 {
 }
 
 void
+DrawEventRecorderPrivate::StoreExternalSurfaceRecording(SourceSurface* aSurface,
+                                                        uint64_t aKey)
+{
+  RecordEvent(RecordedExternalSurfaceCreation(aSurface, aKey));
+  mExternalSurfaces.push_back(aSurface);
+}
+
+void
+DrawEventRecorderPrivate::StoreSourceSurfaceRecording(SourceSurface *aSurface,
+                                                      const char *aReason)
+{
+  RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
+  if (dataSurf) {
+    DataSourceSurface::ScopedMap map(dataSurf, DataSourceSurface::READ);
+    RecordEvent(
+      RecordedSourceSurfaceCreation(aSurface, map.GetData(), map.GetStride(),
+                                    dataSurf->GetSize(), dataSurf->GetFormat()));
+    return;
+  }
+
+  gfxWarning() << "Recording failed to record SourceSurface for " << aReason;
+  // Insert a bogus source surface.
+  int32_t stride = aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat());
+  UniquePtr<uint8_t[]> sourceData(new uint8_t[stride * aSurface->GetSize().height]());
+  RecordEvent(
+    RecordedSourceSurfaceCreation(aSurface, sourceData.get(), stride,
+                                  aSurface->GetSize(), aSurface->GetFormat()));
+}
+
+void
 DrawEventRecorderFile::RecordEvent(const RecordedEvent &aEvent)
 {
   WriteElement(mOutputStream, aEvent.mType);
 
   aEvent.RecordToStream(mOutputStream);
 
   Flush();
 }
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -107,27 +107,39 @@ public:
       index = mUnscaledFonts.size() - 1;
       mUnscaledFontMap.insert({{aFont, index}});
     } else {
       index = i->second;
     }
     return index;
   }
 
-  bool WantsExternalFonts() { return mExternalFonts; }
+  bool WantsExternalFonts() const { return mExternalFonts; }
+
+  void TakeExternalSurfaces(std::vector<RefPtr<SourceSurface>>& aSurfaces)
+  {
+    aSurfaces = Move(mExternalSurfaces);
+  }
+
+  virtual void StoreSourceSurfaceRecording(SourceSurface *aSurface,
+                                           const char *aReason);
 
 protected:
+  void StoreExternalSurfaceRecording(SourceSurface* aSurface,
+                                     uint64_t aKey);
+
   virtual void Flush() = 0;
 
   std::unordered_set<const void*> mStoredObjects;
   std::unordered_set<uint64_t> mStoredFontData;
   std::unordered_set<ScaledFont*> mStoredFonts;
   std::unordered_set<SourceSurface*> mStoredSurfaces;
   std::vector<RefPtr<UnscaledFont>> mUnscaledFonts;
   std::unordered_map<UnscaledFont*, size_t> mUnscaledFontMap;
+  std::vector<RefPtr<SourceSurface>> mExternalSurfaces;
   bool mExternalFonts;
 };
 
 class DrawEventRecorderFile : public DrawEventRecorderPrivate
 {
   using char_type = filesystem::Path::value_type;
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile, override)
@@ -160,17 +172,17 @@ private:
 
   mozilla::OFStream mOutputStream;
 };
 
 typedef std::function<void(MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts)> SerializeResourcesFn;
 
 // WARNING: This should not be used in its existing state because
 // it is likely to OOM because of large continguous allocations.
-class DrawEventRecorderMemory final : public DrawEventRecorderPrivate
+class DrawEventRecorderMemory : public DrawEventRecorderPrivate
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory, override)
 
   /**
    * Constructs a DrawEventRecorder that stores the recording in memory.
    */
   DrawEventRecorderMemory();
@@ -195,19 +207,22 @@ public:
   MemStream mOutputStream;
   /* The index stream is of the form:
    * ItemIndex { size_t dataEnd; size_t extraDataEnd; }
    * It gets concatenated to the end of mOutputStream in Finish()
    * The last size_t in the stream is offset of the begining of the
    * index.
    */
   MemStream mIndex;
+
+protected:
+  ~DrawEventRecorderMemory() {};
+
 private:
   SerializeResourcesFn mSerializeCallback;
-  ~DrawEventRecorderMemory() {};
 
   void Flush() override;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_DRAWEVENTRECORDER_H_ */
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -33,45 +33,24 @@ void RecordingSourceSurfaceUserDataFunc(
   userData->recorder->RemoveStoredObject(userData->refPtr);
   userData->recorder->RecordEvent(
     RecordedSourceSurfaceDestruction(ReferencePtr(userData->refPtr)));
 
   delete userData;
 }
 
 static void
-StoreSourceSurfaceRecording(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface,
-                   DataSourceSurface *aDataSurf, const char *reason)
-{
-  if (!aDataSurf) {
-    gfxWarning() << "Recording failed to record SourceSurface for " << reason;
-    // Insert a bogus source surface.
-    int32_t stride = aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat());
-    UniquePtr<uint8_t[]> sourceData(new uint8_t[stride * aSurface->GetSize().height]());
-    aRecorder->RecordEvent(
-      RecordedSourceSurfaceCreation(aSurface, sourceData.get(), stride,
-                                    aSurface->GetSize(), aSurface->GetFormat()));
-  } else {
-    DataSourceSurface::ScopedMap map(aDataSurf, DataSourceSurface::READ);
-    aRecorder->RecordEvent(
-      RecordedSourceSurfaceCreation(aSurface, map.GetData(), map.GetStride(),
-                                    aDataSurf->GetSize(), aDataSurf->GetFormat()));
-  }
-}
-
-static void
 EnsureSurfaceStoredRecording(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface,
                     const char *reason)
 {
   if (aRecorder->HasStoredObject(aSurface)) {
     return;
   }
 
-  RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
-  StoreSourceSurfaceRecording(aRecorder, aSurface, dataSurf, reason);
+  aRecorder->StoreSourceSurfaceRecording(aSurface, reason);
   aRecorder->AddStoredObject(aSurface);
   aRecorder->AddSourceSurface(aSurface);
 
   RecordingSourceSurfaceUserData *userData = new RecordingSourceSurfaceUserData;
   userData->refPtr = aSurface;
   userData->recorder = aRecorder;
   aSurface->AddUserData(reinterpret_cast<UserDataKey*>(aRecorder),
                         userData, &RecordingSourceSurfaceUserDataFunc);
--- a/gfx/2d/InlineTranslator.h
+++ b/gfx/2d/InlineTranslator.h
@@ -22,17 +22,17 @@ using gfx::ReferencePtr;
 using gfx::DrawTarget;
 using gfx::Path;
 using gfx::SourceSurface;
 using gfx::FilterNode;
 using gfx::GradientStops;
 using gfx::ScaledFont;
 using gfx::NativeFontResource;
 
-class InlineTranslator final : public Translator
+class InlineTranslator : public Translator
 {
 public:
   explicit InlineTranslator(DrawTarget* aDT, void* aFontContext = nullptr);
 
   bool TranslateRecording(char *, size_t len);
 
   DrawTarget* LookupDrawTarget(ReferencePtr aRefPtr) final
   {
--- a/gfx/2d/RecordedEvent.cpp
+++ b/gfx/2d/RecordedEvent.cpp
@@ -108,16 +108,18 @@ RecordedEvent::GetEventName(EventType aT
   case PUSHLAYER:
     return "PushLayer";
   case POPLAYER:
     return "PopLayer";
   case UNSCALEDFONTCREATION:
     return "UnscaledFontCreation";
   case UNSCALEDFONTDESTRUCTION:
     return "UnscaledFontDestruction";
+  case EXTERNALSURFACECREATION:
+    return "ExternalSourceSurfaceCreation";
   default:
     return "Unknown";
   }
 }
 
 template<class S>
 void RecordedEvent::RecordUnscaledFontImpl(UnscaledFont *aUnscaledFont, S& aOutput) {
   RecordedFontData fontData(aUnscaledFont);
--- a/gfx/2d/RecordedEvent.h
+++ b/gfx/2d/RecordedEvent.h
@@ -87,16 +87,17 @@ public:
   virtual Path *LookupPath(ReferencePtr aRefPtr) = 0;
   virtual SourceSurface *LookupSourceSurface(ReferencePtr aRefPtr) = 0;
   virtual FilterNode *LookupFilterNode(ReferencePtr aRefPtr) = 0;
   virtual GradientStops *LookupGradientStops(ReferencePtr aRefPtr) = 0;
   virtual ScaledFont *LookupScaledFont(ReferencePtr aRefPtr) = 0;
   virtual UnscaledFont* LookupUnscaledFont(ReferencePtr aRefPtr) = 0;
   virtual UnscaledFont* LookupUnscaledFontByIndex(size_t aIndex) { return nullptr; }
   virtual NativeFontResource *LookupNativeFontResource(uint64_t aKey) = 0;
+  virtual already_AddRefed<SourceSurface> LookupExternalSurface(uint64_t aKey) { return nullptr; }
   virtual void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) = 0;
   virtual void RemoveDrawTarget(ReferencePtr aRefPtr) = 0;
   virtual void AddPath(ReferencePtr aRefPtr, Path *aPath) = 0;
   virtual void RemovePath(ReferencePtr aRefPtr) = 0;
   virtual void AddSourceSurface(ReferencePtr aRefPtr, SourceSurface *aPath) = 0;
   virtual void RemoveSourceSurface(ReferencePtr aRefPtr) = 0;
   virtual void AddFilterNode(mozilla::gfx::ReferencePtr aRefPtr, FilterNode *aSurface) = 0;
   virtual void RemoveFilterNode(mozilla::gfx::ReferencePtr aRefPtr) = 0;
@@ -256,16 +257,17 @@ public:
     FONTDATA,
     FONTDESC,
     PUSHLAYER,
     PUSHLAYERWITHBLEND,
     POPLAYER,
     UNSCALEDFONTCREATION,
     UNSCALEDFONTDESTRUCTION,
     INTOLUMINANCE,
+    EXTERNALSURFACECREATION,
   };
   static const uint32_t kTotalEventTypes = RecordedEvent::FILTERNODESETINPUT + 1;
 
   virtual ~RecordedEvent() {}
 
   static std::string GetEventName(EventType aType);
 
   /**
--- a/gfx/2d/RecordedEventImpl.h
+++ b/gfx/2d/RecordedEventImpl.h
@@ -800,16 +800,44 @@ private:
   friend class RecordedEvent;
 
   ReferencePtr mRefPtr;
 
   template<class S>
   MOZ_IMPLICIT RecordedSourceSurfaceDestruction(S &aStream);
 };
 
+class RecordedExternalSurfaceCreation : public RecordedEventDerived<RecordedExternalSurfaceCreation> {
+public:
+  RecordedExternalSurfaceCreation(ReferencePtr aRefPtr, const uint64_t aKey)
+    : RecordedEventDerived(EXTERNALSURFACECREATION), mRefPtr(aRefPtr), mKey(aKey)
+  {
+  }
+
+  ~RecordedExternalSurfaceCreation()
+  {
+  }
+
+  virtual bool PlayEvent(Translator *aTranslator) const;
+
+  template<class S> void Record(S &aStream) const;
+  virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const;
+
+  virtual std::string GetName() const { return "SourceSurfaceSharedData Creation"; }
+  virtual ReferencePtr GetObjectRef() const { return mRefPtr; }
+private:
+  friend class RecordedEvent;
+
+  ReferencePtr mRefPtr;
+  uint64_t mKey;
+
+  template<class S>
+  MOZ_IMPLICIT RecordedExternalSurfaceCreation(S &aStream);
+};
+
 class RecordedFilterNodeCreation : public RecordedEventDerived<RecordedFilterNodeCreation> {
 public:
   RecordedFilterNodeCreation(ReferencePtr aRefPtr, FilterType aType)
     : RecordedEventDerived(FILTERNODECREATION), mRefPtr(aRefPtr), mType(aType)
   {
   }
 
   ~RecordedFilterNodeCreation();
@@ -2722,16 +2750,50 @@ RecordedSourceSurfaceDestruction::Record
 }
 
 inline void
 RecordedSourceSurfaceDestruction::OutputSimpleEventInfo(std::stringstream &aStringStream) const
 {
   aStringStream << "[" << mRefPtr << "] SourceSurface Destroyed";
 }
 
+inline bool
+RecordedExternalSurfaceCreation::PlayEvent(Translator *aTranslator) const
+{
+  RefPtr<SourceSurface> surface = aTranslator->LookupExternalSurface(mKey);
+  if (!surface) {
+    return false;
+  }
+
+  aTranslator->AddSourceSurface(mRefPtr, surface);
+  return true;
+}
+
+template<class S>
+void
+RecordedExternalSurfaceCreation::Record(S &aStream) const
+{
+  WriteElement(aStream, mRefPtr);
+  WriteElement(aStream, mKey);
+}
+
+template<class S>
+RecordedExternalSurfaceCreation::RecordedExternalSurfaceCreation(S &aStream)
+  : RecordedEventDerived(EXTERNALSURFACECREATION)
+{
+  ReadElement(aStream, mRefPtr);
+  ReadElement(aStream, mKey);
+}
+
+inline void
+RecordedExternalSurfaceCreation::OutputSimpleEventInfo(std::stringstream &aStringStream) const
+{
+  aStringStream << "[" << mRefPtr << "] SourceSurfaceSharedData created (Key: " << mKey << ")";
+}
+
 inline
 RecordedFilterNodeCreation::~RecordedFilterNodeCreation()
 {
 }
 
 inline bool
 RecordedFilterNodeCreation::PlayEvent(Translator *aTranslator) const
 {
@@ -3504,17 +3566,18 @@ RecordedFilterNodeSetInput::OutputSimple
     f(CREATECLIPPEDDRAWTARGET, RecordedCreateClippedDrawTarget); \
     f(FONTDATA, RecordedFontData); \
     f(FONTDESC, RecordedFontDescriptor); \
     f(PUSHLAYER, RecordedPushLayer); \
     f(PUSHLAYERWITHBLEND, RecordedPushLayerWithBlend); \
     f(POPLAYER, RecordedPopLayer); \
     f(UNSCALEDFONTCREATION, RecordedUnscaledFontCreation); \
     f(UNSCALEDFONTDESTRUCTION, RecordedUnscaledFontDestruction); \
-    f(INTOLUMINANCE, RecordedIntoLuminanceSource);
+    f(INTOLUMINANCE, RecordedIntoLuminanceSource); \
+    f(EXTERNALSURFACECREATION, RecordedExternalSurfaceCreation);
 
 template<class S>
 RecordedEvent *
 RecordedEvent::LoadEvent(S &aStream, EventType aType)
 {
   switch (aType) {
     FOR_EACH_EVENT(LOAD_EVENT_TYPE)
   default:
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -236,16 +236,17 @@ EXPORTS.mozilla.layers += [
     'wr/AsyncImagePipelineManager.h',
     'wr/IpcResourceUpdateQueue.h',
     'wr/ScrollingLayersHelper.h',
     'wr/StackingContextHelper.h',
     'wr/WebRenderBridgeChild.h',
     'wr/WebRenderBridgeParent.h',
     'wr/WebRenderCanvasRenderer.h',
     'wr/WebRenderCommandBuilder.h',
+    '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/WebRenderUserData.h',
@@ -473,16 +474,17 @@ UNIFIED_SOURCES += [
     'wr/AsyncImagePipelineManager.cpp',
     'wr/IpcResourceUpdateQueue.cpp',
     'wr/ScrollingLayersHelper.cpp',
     'wr/StackingContextHelper.cpp',
     'wr/WebRenderBridgeChild.cpp',
     '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/WebRenderUserData.cpp',
     # XXX here are some unified build error.
     #'wr/WebRenderTextureHost.cpp'
 ]
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -5,24 +5,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderCommandBuilder.h"
 
 #include "BasicLayers.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Types.h"
-#include "mozilla/gfx/DrawEventRecorder.h"
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/ScrollingLayersHelper.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/UpdateImageHelper.h"
+#include "mozilla/layers/WebRenderDrawEventRecorder.h"
 #include "UnitTransforms.h"
 #include "gfxEnv.h"
 #include "nsDisplayListInvalidation.h"
 #include "WebRenderCanvasRenderer.h"
 #include "LayersLogging.h"
 #include "LayerTreeInvalidation.h"
 
 namespace mozilla {
@@ -186,17 +186,17 @@ struct Grouper
   std::vector<nsDisplayItem*> mItemStack;
   nsDisplayListBuilder* mDisplayListBuilder;
   ScrollingLayersHelper& mScrollingHelper;
   Matrix mTransform;
 
   // Paint the list of aChildren display items.
   void PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, const IntRect& aItemBounds,
                           nsDisplayList* aChildren, gfxContext* aContext,
-                          gfx::DrawEventRecorderMemory* aRecorder);
+                          WebRenderDrawEventRecorder* aRecorder);
 
   // Builds groups of display items split based on 'layer activity'
   void ConstructGroups(WebRenderCommandBuilder* aCommandBuilder,
                        wr::DisplayListBuilder& aBuilder,
                        wr::IpcResourceUpdateQueue& aResources,
                        DIGroup* aGroup, nsDisplayList* aList,
                        const StackingContextHelper& aSc);
   // Builds a group of display items without promoting anything to active.
@@ -286,16 +286,17 @@ struct DIGroup
   nsPoint mAnimatedGeometryRootOrigin;
   nsPoint mLastAnimatedGeometryRootOrigin;
   IntRect mInvalidRect;
   nsRect mGroupBounds;
   int32_t mAppUnitsPerDevPixel;
   gfx::Size mScale;
   LayerIntRect mLayerBounds;
   Maybe<wr::ImageKey> mKey;
+  std::vector<RefPtr<SourceSurface>> mExternalSurfaces;
 
   DIGroup() : mAppUnitsPerDevPixel(0) {}
 
   void InvalidateRect(const IntRect& aRect)
   {
     // Empty rects get dropped
     mInvalidRect = mInvalidRect.Union(aRect);
   }
@@ -547,18 +548,18 @@ struct DIGroup
       GP("End EndGroup\n");
       if (mKey) {
         PushImage(aBuilder, bounds);
       }
       return;
     }
 
     gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
-    RefPtr<gfx::DrawEventRecorderMemory> recorder =
-      MakeAndAddRef<gfx::DrawEventRecorderMemory>(
+    RefPtr<WebRenderDrawEventRecorder> recorder =
+      MakeAndAddRef<WebRenderDrawEventRecorder>(
         [&](MemStream& aStream, std::vector<RefPtr<UnscaledFont>>& aUnscaledFonts) {
           size_t count = aUnscaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
           for (auto unscaled : aUnscaledFonts) {
             wr::FontKey key = aWrManager->WrBridge()->GetFontKeyForUnscaledFont(unscaled);
             aStream.write((const char*)&key, sizeof(key));
           }
         });
@@ -583,16 +584,17 @@ struct DIGroup
       return;
     }
 
     PaintItemRange(aGrouper, aStartItem, aEndItem, context, recorder);
 
     // XXX: set this correctly perhaps using aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);?
     bool isOpaque = false;
 
+    recorder->TakeExternalSurfaces(mExternalSurfaces);
     bool hasItems = recorder->Finish();
     GP("%d Finish\n", hasItems);
     Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
     if (!mKey) {
       if (!hasItems) // we don't want to send a new image that doesn't have any items in it
         return;
       wr::ImageKey key = aWrManager->WrBridge()->GetNextImageKey();
       GP("No previous key making new one %d\n", key.mHandle);
@@ -627,17 +629,17 @@ struct DIGroup
                        wr::ToImageRendering(sampleFilter),
                        mKey.value());
   }
 
   void PaintItemRange(Grouper* aGrouper,
                       nsDisplayItem* aStartItem,
                       nsDisplayItem* aEndItem,
                       gfxContext* aContext,
-                      gfx::DrawEventRecorderMemory* aRecorder) {
+                      WebRenderDrawEventRecorder* aRecorder) {
     LayerIntSize size = mLayerBounds.Size();
     for (nsDisplayItem* item = aStartItem; item != aEndItem; item = item->GetAbove()) {
       IntRect bounds = ItemBounds(item);
       auto bottomRight = bounds.BottomRight();
 
       GP("Trying %s %p-%d %d %d %d %d\n", item->Name(), item->Frame(), item->GetPerFrameKey(), bounds.x, bounds.y, bounds.XMost(), bounds.YMost());
       GP("paint check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, size.width, size.height);
       // skip empty items
@@ -698,17 +700,17 @@ struct DIGroup
       delete data;
     }
   }
 };
 
 void
 Grouper::PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, const IntRect& aItemBounds,
                             nsDisplayList* aChildren, gfxContext* aContext,
-                            gfx::DrawEventRecorderMemory* aRecorder)
+                            WebRenderDrawEventRecorder* aRecorder)
 {
   mItemStack.push_back(aItem);
   switch (aItem->GetType()) {
     case DisplayItemType::TYPE_TRANSFORM: {
       DisplayItemClip currentClip = aItem->GetClip();
 
       gfx::Matrix matrix;
       if (currentClip.HasClip()) {
@@ -1649,33 +1651,35 @@ WebRenderCommandBuilder::GenerateFallbac
     fallbackData->SetGeometry(Move(newGeometry));
 
     gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ?
                                                       gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8;
     if (useBlobImage) {
       bool snapped;
       bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);
 
-      RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>([&] (MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts) {
+      RefPtr<WebRenderDrawEventRecorder> recorder =
+        MakeAndAddRef<WebRenderDrawEventRecorder>([&] (MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts) {
           size_t count = aUnscaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
           for (auto unscaled : aUnscaledFonts) {
             wr::FontKey key = mManager->WrBridge()->GetFontKeyForUnscaledFont(unscaled);
             aStream.write((const char*)&key, sizeof(key));
           }
         });
       RefPtr<gfx::DrawTarget> dummyDt =
         gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
       RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, dtSize.ToUnknownSize());
       if (!fallbackData->mBasicLayerManager) {
         fallbackData->mBasicLayerManager = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
       }
       bool isInvalidated = PaintItemByDrawTarget(aItem, dt, paintRect, offset, aDisplayListBuilder,
                                                  fallbackData->mBasicLayerManager, scale, highlight);
       recorder->FlushItem(IntRect(0, 0, paintSize.width, paintSize.height));
+      recorder->TakeExternalSurfaces(fallbackData->mExternalSurfaces);
       recorder->Finish();
 
       if (isInvalidated) {
         Range<uint8_t> bytes((uint8_t *)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
         wr::ImageKey key = mManager->WrBridge()->GetNextImageKey();
         wr::ImageDescriptor descriptor(dtSize.ToUnknownSize(), 0, dt->GetFormat(), isOpaque);
         if (!aResources.AddBlobImage(key, descriptor, bytes)) {
           return nullptr;
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderDrawEventRecorder.cpp
@@ -0,0 +1,36 @@
+/* -*- 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 "WebRenderDrawEventRecorder.h"
+#include "mozilla/layers/SharedSurfacesChild.h"
+#include "mozilla/layers/SharedSurfacesParent.h"
+
+namespace mozilla {
+using namespace gfx;
+
+namespace layers {
+
+void
+WebRenderDrawEventRecorder::StoreSourceSurfaceRecording(SourceSurface* aSurface,
+                                                        const char* aReason)
+{
+  wr::ExternalImageId extId;
+  nsresult rv = layers::SharedSurfacesChild::Share(aSurface, extId);
+  if (NS_FAILED(rv)) {
+    DrawEventRecorderMemory::StoreSourceSurfaceRecording(aSurface, aReason);
+    return;
+  }
+
+  StoreExternalSurfaceRecording(aSurface, wr::AsUint64(extId));
+}
+
+already_AddRefed<SourceSurface>
+WebRenderTranslator::LookupExternalSurface(uint64_t aKey)
+{
+  return SharedSurfacesParent::Get(wr::ToExternalImageId(aKey));
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderDrawEventRecorder.h
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_LAYERS_WEBRENDERDRAWTARGETRECORDER_H
+#define MOZILLA_LAYERS_WEBRENDERDRAWTARGETRECORDER_H
+
+#include "mozilla/gfx/DrawEventRecorder.h"
+#include "mozilla/gfx/InlineTranslator.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderDrawEventRecorder final : public gfx::DrawEventRecorderMemory
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(WebRenderDrawEventRecorder, final)
+
+  explicit WebRenderDrawEventRecorder(const gfx::SerializeResourcesFn &aSerialize)
+    : DrawEventRecorderMemory(aSerialize)
+  { }
+
+  void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface,
+                                   const char* aReason) final;
+
+private:
+  ~WebRenderDrawEventRecorder() override
+  { }
+};
+
+class WebRenderTranslator final : public gfx::InlineTranslator
+{
+public:
+  explicit WebRenderTranslator(gfx::DrawTarget* aDT, void* aFontContext = nullptr)
+    : InlineTranslator(aDT, aFontContext)
+  { }
+
+  already_AddRefed<gfx::SourceSurface>
+    LookupExternalSurface(uint64_t aKey) final;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -2,29 +2,34 @@
 /* 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 GFX_WEBRENDERUSERDATA_H
 #define GFX_WEBRENDERUSERDATA_H
 
+#include <vector>
 #include "BasicLayers.h"                // for BasicLayerManager
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/layers/AnimationInfo.h"
 #include "nsIFrame.h"
 
 class nsDisplayItemGeometry;
 
 namespace mozilla {
 namespace wr {
 class IpcResourceUpdateQueue;
 }
 
+namespace gfx {
+class SourceSurface;
+}
+
 namespace layers {
 class CanvasLayer;
 class ImageClient;
 class ImageContainer;
 class WebRenderBridgeChild;
 class WebRenderCanvasData;
 class WebRenderCanvasRendererAsync;
 class WebRenderImageData;
@@ -157,16 +162,17 @@ public:
   nsRect GetBounds() { return mBounds; }
   void SetBounds(const nsRect& aRect) { mBounds = aRect; }
   void SetInvalid(bool aInvalid) { mInvalid = aInvalid; }
   void SetScale(gfx::Size aScale) { mScale = aScale; }
   gfx::Size GetScale() { return mScale; }
   bool IsInvalid() { return mInvalid; }
 
   RefPtr<BasicLayerManager> mBasicLayerManager;
+  std::vector<RefPtr<gfx::SourceSurface>> mExternalSurfaces;
 protected:
   nsAutoPtr<nsDisplayItemGeometry> mGeometry;
   nsRect mBounds;
   bool mInvalid;
   gfx::Size mScale;
 };
 
 class WebRenderAnimationData : public WebRenderUserData
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -3,19 +3,19 @@
 /* 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 "gfxUtils.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Range.h"
 #include "mozilla/gfx/2D.h"
-#include "mozilla/gfx/InlineTranslator.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/RecordedEvent.h"
+#include "mozilla/layers/WebRenderDrawEventRecorder.h"
 #include "WebRenderTypes.h"
 #include "webrender_ffi.h"
 
 #include <unordered_map>
 
 #ifdef XP_MACOSX
 #include "mozilla/gfx/UnscaledFontMac.h"
 #elif defined(XP_WIN)
@@ -306,17 +306,17 @@ static bool Moz2DRenderCallback(const Ra
 
   bool ret;
   size_t offset = 0;
   while (reader.pos < reader.len) {
     size_t end = reader.ReadSize();
     size_t extra_end = reader.ReadSize();
     reader.SkipBounds();
 
-    gfx::InlineTranslator translator(dt);
+    layers::WebRenderTranslator translator(dt);
 
     size_t count = *(size_t*)(aBlob.begin().get() + end);
     for (size_t i = 0; i < count; i++) {
       wr::FontKey key = *(wr::FontKey*)(aBlob.begin() + end + sizeof(count) + sizeof(wr::FontKey)*i).get();
       RefPtr<UnscaledFont> font = GetUnscaledFont(&translator, key);
       translator.AddUnscaledFont(0, font);
     }
     Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);