Bug 1464032 Part 10: Add a CanvasTranslator and canvas recorded events. r=jrmuizel
authorBob Owen <bobowencode@gmail.com>
Sun, 02 Dec 2018 14:17:12 +0000
changeset 477761 04067aec22bb2f8da87a34893c99e2195efd0b8c
parent 477760 0e6cf27e972802cd890667321ea8e37a043d8d38
child 477762 e32d6c3ba88775579562d9d1f27098306b7bfbb0
push id113373
push userbobowencode@gmail.com
push dateFri, 07 Jun 2019 11:10:59 +0000
treeherdermozilla-inbound@2195b79ea888 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1464032
milestone69.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 1464032 Part 10: Add a CanvasTranslator and canvas recorded events. r=jrmuizel These are extensions to the Moz2D RecordedEvents to record and play back canvas texture related functions in the GPU process. The CanvasTranslator handles the playback of these and the Moz2D ones.
gfx/2d/InlineTranslator.h
gfx/2d/Logging.h
gfx/2d/RecordedEvent.h
gfx/2d/RecordedEventImpl.h
gfx/layers/CanvasTranslator.cpp
gfx/layers/CanvasTranslator.h
gfx/layers/RecordedCanvasEventImpl.h
--- a/gfx/2d/InlineTranslator.h
+++ b/gfx/2d/InlineTranslator.h
@@ -121,23 +121,23 @@ class InlineTranslator : public Translat
     mUnscaledFonts.Put(aRefPtr, aUnscaledFont);
   }
 
   void AddNativeFontResource(uint64_t aKey,
                              NativeFontResource* aScaledFontResouce) final {
     mNativeFontResources.Put(aKey, aScaledFontResouce);
   }
 
-  void RemoveDrawTarget(ReferencePtr aRefPtr) final {
+  void RemoveDrawTarget(ReferencePtr aRefPtr) override {
     mDrawTargets.Remove(aRefPtr);
   }
 
   void RemovePath(ReferencePtr aRefPtr) final { mPaths.Remove(aRefPtr); }
 
-  void RemoveSourceSurface(ReferencePtr aRefPtr) final {
+  void RemoveSourceSurface(ReferencePtr aRefPtr) override {
     mSourceSurfaces.Remove(aRefPtr);
   }
 
   void RemoveFilterNode(ReferencePtr aRefPtr) final {
     mFilterNodes.Remove(aRefPtr);
   }
 
   void RemoveGradientStops(ReferencePtr aRefPtr) final {
@@ -149,17 +149,17 @@ class InlineTranslator : public Translat
   }
 
   void RemoveUnscaledFont(ReferencePtr aRefPtr) final {
     mUnscaledFonts.Remove(aRefPtr);
   }
 
   already_AddRefed<DrawTarget> CreateDrawTarget(
       ReferencePtr aRefPtr, const gfx::IntSize& aSize,
-      gfx::SurfaceFormat aFormat) final;
+      gfx::SurfaceFormat aFormat) override;
 
   mozilla::gfx::DrawTarget* GetReferenceDrawTarget() final { return mBaseDT; }
 
   void* GetFontContext() final { return mFontContext; }
   std::string GetError() { return mError; }
 
  private:
   RefPtr<DrawTarget> mBaseDT;
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -130,17 +130,18 @@ enum class LogReason : int {
   InvalidCacheSurface,
   AlphaWithBasicClient,
   UnbalancedClipStack,
   ProcessingError,
   InvalidDrawTarget,
   NativeFontResourceNotFound,
   UnscaledFontNotFound,
   ScaledFontNotFound,
-  InvalidLayerType,
+  InvalidLayerType,  // 40
+  PlayEventFailed,
   // End
   MustBeLessThanThis = 101,
 };
 
 struct BasicLogger {
   // For efficiency, this method exists and copies the logic of the
   // OutputMessage below.  If making any changes here, also make it
   // in the appropriate places in that method.
--- a/gfx/2d/RecordedEvent.h
+++ b/gfx/2d/RecordedEvent.h
@@ -8,16 +8,18 @@
 #define MOZILLA_GFX_RECORDEDEVENT_H_
 
 #include "2D.h"
 #include <ostream>
 #include <sstream>
 #include <cstring>
 #include <vector>
 
+#include "RecordingTypes.h"
+
 namespace mozilla {
 namespace gfx {
 
 struct PathOp;
 class PathRecording;
 
 const uint32_t kMagicInt = 0xc001feed;
 
@@ -387,12 +389,41 @@ class RecordedEvent {
   static void RecordUnscaledFontImpl(UnscaledFont* aUnscaledFont, S& aOutput);
 
   MOZ_IMPLICIT RecordedEvent(int32_t aType) : mType(aType) {}
 
   int32_t mType;
   std::vector<Float> mDashPatternStorage;
 };
 
+template <class Derived>
+class RecordedEventDerived : public RecordedEvent {
+  using RecordedEvent::RecordedEvent;
+
+ public:
+  void RecordToStream(std::ostream& aStream) const override {
+    WriteElement(aStream, this->mType);
+    static_cast<const Derived*>(this)->Record(aStream);
+  }
+  void RecordToStream(EventStream& aStream) const override {
+    WriteElement(aStream, this->mType);
+    static_cast<const Derived*>(this)->Record(aStream);
+  }
+  void RecordToStream(EventRingBuffer& aStream) const final {
+    aStream.RecordEvent(static_cast<const Derived*>(this));
+  }
+  void RecordToStream(MemStream& aStream) const override {
+    SizeCollector size;
+    WriteElement(size, this->mType);
+    static_cast<const Derived*>(this)->Record(size);
+
+    aStream.Resize(aStream.mLength + size.mTotalSize);
+
+    MemWriter writer(aStream.mData + aStream.mLength - size.mTotalSize);
+    WriteElement(writer, this->mType);
+    static_cast<const Derived*>(this)->Record(writer);
+  }
+};
+
 }  // namespace gfx
 }  // namespace mozilla
 
 #endif
--- a/gfx/2d/RecordedEventImpl.h
+++ b/gfx/2d/RecordedEventImpl.h
@@ -16,45 +16,16 @@
 #include "Logging.h"
 #include "ScaledFontBase.h"
 #include "SFNTData.h"
 
 namespace mozilla {
 namespace gfx {
 
 template <class Derived>
-class RecordedEventDerived : public RecordedEvent {
-  using RecordedEvent::RecordedEvent;
-
- public:
-  void RecordToStream(std::ostream& aStream) const override {
-    WriteElement(aStream, this->mType);
-    static_cast<const Derived*>(this)->Record(aStream);
-  }
-  void RecordToStream(EventStream& aStream) const override {
-    WriteElement(aStream, this->mType);
-    static_cast<const Derived*>(this)->Record(aStream);
-  }
-  void RecordToStream(EventRingBuffer& aStream) const final {
-    aStream.RecordEvent(static_cast<const Derived*>(this));
-  }
-  void RecordToStream(MemStream& aStream) const override {
-    SizeCollector size;
-    WriteElement(size, this->mType);
-    static_cast<const Derived*>(this)->Record(size);
-
-    aStream.Resize(aStream.mLength + size.mTotalSize);
-
-    MemWriter writer(aStream.mData + aStream.mLength - size.mTotalSize);
-    WriteElement(writer, this->mType);
-    static_cast<const Derived*>(this)->Record(writer);
-  }
-};
-
-template <class Derived>
 class RecordedDrawingEvent : public RecordedEventDerived<Derived> {
  public:
   ReferencePtr GetDestinedDT() override { return mDT; }
 
  protected:
   RecordedDrawingEvent(RecordedEvent::EventType aType, DrawTarget* aTarget)
       : RecordedEventDerived<Derived>(aType), mDT(aTarget) {}
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/CanvasTranslator.cpp
@@ -0,0 +1,238 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "CanvasTranslator.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "RecordedCanvasEventImpl.h"
+
+#if defined(XP_WIN)
+#  include "mozilla/gfx/DeviceManagerDx.h"
+#  include "mozilla/layers/TextureD3D11.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+// When in a transaction we wait for a short time because we're expecting more
+// events from the content process. We don't want to wait for too long in case
+// other content processes are waiting for events to process.
+static const TimeDuration kReadEventTimeout = TimeDuration::FromMilliseconds(5);
+
+static TextureData* CreateTextureData(TextureType aTextureType,
+                                      const gfx::IntSize& aSize,
+                                      gfx::SurfaceFormat aFormat) {
+  TextureData* textureData = nullptr;
+  switch (aTextureType) {
+#ifdef XP_WIN
+    case TextureType::D3D11: {
+      RefPtr<ID3D11Device> device =
+          gfx::DeviceManagerDx::Get()->GetCanvasDevice();
+      MOZ_RELEASE_ASSERT(device, "Failed to get a device for canvas drawing.");
+      textureData =
+          D3D11TextureData::Create(aSize, aFormat, ALLOC_CLEAR_BUFFER, device);
+      break;
+    }
+#endif
+    default:
+      MOZ_CRASH("Unsupported TextureType for CanvasTranslator.");
+  }
+
+  if (!textureData) {
+    MOZ_CRASH("Failed to create TextureData.");
+  }
+
+  return textureData;
+}
+
+/* static */
+UniquePtr<CanvasTranslator> CanvasTranslator::Create(
+    const TextureType& aTextureType,
+    const ipc::SharedMemoryBasic::Handle& aReadHandle,
+    const CrossProcessSemaphoreHandle& aReaderSem,
+    const CrossProcessSemaphoreHandle& aWriterSem) {
+  TextureData* textureData = CreateTextureData(aTextureType, gfx::IntSize(1, 1),
+                                               gfx::SurfaceFormat::B8G8R8A8);
+  textureData->Lock(OpenMode::OPEN_READ_WRITE);
+  RefPtr<gfx::DrawTarget> dt = textureData->BorrowDrawTarget();
+  return UniquePtr<CanvasTranslator>(new CanvasTranslator(
+      aTextureType, textureData, dt, aReadHandle, aReaderSem, aWriterSem));
+}
+
+CanvasTranslator::CanvasTranslator(
+    const TextureType& aTextureType, TextureData* aTextureData,
+    gfx::DrawTarget* aDT, const ipc::SharedMemoryBasic::Handle& aReadHandle,
+    const CrossProcessSemaphoreHandle& aReaderSem,
+    const CrossProcessSemaphoreHandle& aWriterSem)
+    : gfx::InlineTranslator(aDT),
+      mTextureType(aTextureType),
+      mReferenceTextureData(aTextureData) {
+  mStream.InitReader(aReadHandle, aReaderSem, aWriterSem);
+}
+
+CanvasTranslator::~CanvasTranslator() { mReferenceTextureData->Unlock(); }
+
+bool CanvasTranslator::TranslateRecording() {
+  int32_t eventType = mStream.ReadNextEvent();
+  while (mStream.good()) {
+    bool success = RecordedEvent::DoWithEventFromStream(
+        mStream, static_cast<RecordedEvent::EventType>(eventType),
+        [&](RecordedEvent* recordedEvent) -> bool {
+          // Make sure that the whole event was read from the stream.
+          if (!mStream.good()) {
+            return false;
+          }
+
+          return recordedEvent->PlayEvent(this);
+        });
+
+    if (!success && !HandleExtensionEvent(eventType)) {
+      gfxDevCrash(gfx::LogReason::PlayEventFailed)
+          << "Failed to play canvas event type: " << eventType;
+      mIsValid = false;
+      return true;
+    }
+
+    if (!mIsInTransaction) {
+      return mStream.StopIfEmpty();
+    }
+
+    if (!mStream.HasDataToRead()) {
+      // We're going to wait for the next event, so take the opportunity to
+      // flush the rendering.
+      Flush();
+      if (!mStream.WaitForDataToRead(kReadEventTimeout)) {
+        return true;
+      }
+    }
+
+    eventType = mStream.ReadNextEvent();
+  }
+
+  mIsValid = false;
+  return true;
+}
+
+#define READ_AND_PLAY_CANVAS_EVENT_TYPE(_typeenum, _class) \
+  case _typeenum: {                                        \
+    auto e = _class(mStream);                              \
+    if (!mStream.good()) {                                 \
+      return false;                                        \
+    }                                                      \
+    return e.PlayCanvasEvent(this);                        \
+  }
+
+bool CanvasTranslator::HandleExtensionEvent(int32_t aType) {
+  // This is where we handle extensions to the Moz2D Recording events to handle
+  // canvas specific things.
+  switch (aType) {
+    FOR_EACH_CANVAS_EVENT(READ_AND_PLAY_CANVAS_EVENT_TYPE)
+    default:
+      return false;
+  }
+}
+
+void CanvasTranslator::BeginTransaction() { mIsInTransaction = true; }
+
+void CanvasTranslator::Flush() {
+#if defined(XP_WIN)
+  RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetCanvasDevice();
+  RefPtr<ID3D11DeviceContext> deviceContext;
+  device->GetImmediateContext(getter_AddRefs(deviceContext));
+  deviceContext->Flush();
+#endif
+}
+
+void CanvasTranslator::EndTransaction() {
+  Flush();
+  mIsInTransaction = false;
+}
+
+void CanvasTranslator::AddSurfaceDescriptor(gfx::ReferencePtr aRefPtr,
+                                            TextureData* aTextureData) {
+  UniquePtr<SurfaceDescriptor> descriptor = MakeUnique<SurfaceDescriptor>();
+  if (!aTextureData->Serialize(*descriptor)) {
+    MOZ_CRASH("Failed to serialize");
+  }
+
+  MonitorAutoLock lock(mSurfaceDescriptorsMonitor);
+  mSurfaceDescriptors[aRefPtr] = std::move(descriptor);
+  mSurfaceDescriptorsMonitor.Notify();
+}
+
+already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget(
+    gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize,
+    gfx::SurfaceFormat aFormat) {
+  TextureData* textureData = CreateTextureData(mTextureType, aSize, aFormat);
+  textureData->Lock(OpenMode::OPEN_READ_WRITE);
+  mTextureDatas[aRefPtr] = UniquePtr<TextureData>(textureData);
+  AddSurfaceDescriptor(aRefPtr, textureData);
+  RefPtr<gfx::DrawTarget> dt = textureData->BorrowDrawTarget();
+  AddDrawTarget(aRefPtr, dt);
+
+  return dt.forget();
+}
+
+void CanvasTranslator::RemoveDrawTarget(gfx::ReferencePtr aDrawTarget) {
+  InlineTranslator::RemoveDrawTarget(aDrawTarget);
+  mTextureDatas.erase(aDrawTarget);
+}
+
+TextureData* CanvasTranslator::LookupTextureData(
+    gfx::ReferencePtr aDrawTarget) {
+  TextureMap::const_iterator result = mTextureDatas.find(aDrawTarget);
+  if (result == mTextureDatas.end()) {
+    return nullptr;
+  }
+  return result->second.get();
+}
+
+UniquePtr<SurfaceDescriptor> CanvasTranslator::WaitForSurfaceDescriptor(
+    gfx::ReferencePtr aDrawTarget) {
+  MonitorAutoLock lock(mSurfaceDescriptorsMonitor);
+  DescriptorMap::iterator result;
+  while ((result = mSurfaceDescriptors.find(aDrawTarget)) ==
+         mSurfaceDescriptors.end()) {
+    mSurfaceDescriptorsMonitor.Wait();
+  }
+
+  UniquePtr<SurfaceDescriptor> descriptor = std::move(result->second);
+  mSurfaceDescriptors.erase(aDrawTarget);
+  return descriptor;
+}
+
+gfx::DataSourceSurface* CanvasTranslator::LookupDataSurface(
+    gfx::ReferencePtr aRefPtr) {
+  return mDataSurfaces.GetWeak(aRefPtr);
+}
+
+void CanvasTranslator::AddDataSurface(
+    gfx::ReferencePtr aRefPtr, RefPtr<gfx::DataSourceSurface>&& aSurface) {
+  mDataSurfaces.Put(aRefPtr, aSurface);
+}
+
+void CanvasTranslator::RemoveDataSurface(gfx::ReferencePtr aRefPtr) {
+  mDataSurfaces.Remove(aRefPtr);
+}
+
+void CanvasTranslator::SetPreparedMap(
+    gfx::ReferencePtr aSurface,
+    UniquePtr<gfx::DataSourceSurface::ScopedMap> aMap) {
+  mMappedSurface = aSurface;
+  mPreparedMap = std::move(aMap);
+}
+
+UniquePtr<gfx::DataSourceSurface::ScopedMap> CanvasTranslator::GetPreparedMap(
+    gfx::ReferencePtr aSurface) {
+  MOZ_RELEASE_ASSERT(mMappedSurface == aSurface,
+                     "aSurface must match previously stored surface.");
+
+  mMappedSurface = nullptr;
+  return std::move(mPreparedMap);
+}
+
+}  // namespace layers
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/CanvasTranslator.h
@@ -0,0 +1,216 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CanvasTranslator_h
+#define mozilla_layers_CanvasTranslator_h
+
+#include <unordered_map>
+#include <vector>
+
+#include "mozilla/gfx/InlineTranslator.h"
+#include "mozilla/layers/CanvasDrawEventRecorder.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/ipc/CrossProcessSemaphore.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureData;
+
+class CanvasTranslator final : public gfx::InlineTranslator {
+ public:
+  /**
+   * Create a canvas translator for a particular TextureType, which translates
+   * events from a CanvasEventRingBuffer.
+   *
+   * @param aTextureType the TextureType the translator will create
+   * @param aReadHandle handle to the shared memory for the
+   * CanvasEventRingBuffer
+   * @param aReaderSem reading blocked semaphore for the CanvasEventRingBuffer
+   * @param aWriterSem writing blocked semaphore for the CanvasEventRingBuffer
+   * @returns the new CanvasTranslator
+   */
+  static UniquePtr<CanvasTranslator> Create(
+      const TextureType& aTextureType,
+      const ipc::SharedMemoryBasic::Handle& aReadHandle,
+      const CrossProcessSemaphoreHandle& aReaderSem,
+      const CrossProcessSemaphoreHandle& aWriterSem);
+
+  ~CanvasTranslator();
+
+  bool IsValid() { return mIsValid; }
+
+  /**
+   * Translates events until no more are available or the end of a transaction
+   * If this returns false the caller of this is responsible for re-calling
+   * this function.
+   *
+   * @returns true if all events are processed and false otherwise.
+   */
+  bool TranslateRecording();
+
+  /**
+   * Marks the beginning of rendering for a transaction. While in a transaction
+   * the translator will wait for a short time for events before returning.
+   * When not in a transaction the translator will only translate one event at a
+   * time.
+   */
+  void BeginTransaction();
+
+  /**
+   * Marks the end of a transaction.
+   */
+  void EndTransaction();
+
+  /**
+   * Flushes canvas drawing, for example to a device.
+   */
+  void Flush();
+
+  /**
+   * Used to send data back to the writer. This is done through the same shared
+   * memory so the writer must wait and read the response after it has submitted
+   * the event that uses this.
+   *
+   * @param aData the data to be written back to the writer
+   * @param aSize the number of chars to write
+   */
+  void ReturnWrite(const char* aData, size_t aSize) {
+    mStream.ReturnWrite(aData, aSize);
+  }
+
+  /**
+   * Used during playback of events to create DrawTargets. For the
+   * CanvasTranslator this means creating TextureDatas and getting the
+   * DrawTargets from those.
+   *
+   * @param aRefPtr the key to store the created DrawTarget against
+   * @param aSize the size of the DrawTarget
+   * @param aFormat the surface format for the DrawTarget
+   * @returns the new DrawTarget
+   */
+  already_AddRefed<gfx::DrawTarget> CreateDrawTarget(
+      gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize,
+      gfx::SurfaceFormat aFormat) final;
+
+  /**
+   * Get the TextureData associated with a DrawTarget from another process.
+   *
+   * @param aDrawTarget the key used to find the TextureData
+   * @returns the TextureData found
+   */
+  TextureData* LookupTextureData(gfx::ReferencePtr aDrawTarget);
+
+  /**
+   * Waits for the SurfaceDescriptor associated with a DrawTarget from another
+   * process to be created and then returns it.
+   *
+   * @param aDrawTarget the key used to find the TextureData
+   * @returns the SurfaceDescriptor found
+   */
+  UniquePtr<SurfaceDescriptor> WaitForSurfaceDescriptor(
+      gfx::ReferencePtr aDrawTarget);
+
+  /**
+   * Removes the DrawTarget and other objects associated with a DrawTarget from
+   * another process.
+   *
+   * @param aDrawTarget the key to the objects to remove
+   */
+  void RemoveDrawTarget(gfx::ReferencePtr aDrawTarget) final;
+
+  /**
+   * Removes the SourceSurface and other objects associated with a SourceSurface
+   * from another process.
+   *
+   * @param aRefPtr the key to the objects to remove
+   */
+  void RemoveSourceSurface(gfx::ReferencePtr aRefPtr) final {
+    RemoveDataSurface(aRefPtr);
+    InlineTranslator::RemoveSourceSurface(aRefPtr);
+  }
+
+  /**
+   * Gets the cached DataSourceSurface, if it exists, associated with a
+   * SourceSurface from another process.
+   *
+   * @param aRefPtr the key used to find the DataSourceSurface
+   * @returns the DataSourceSurface or nullptr if not found
+   */
+  gfx::DataSourceSurface* LookupDataSurface(gfx::ReferencePtr aRefPtr);
+
+  /**
+   * Used to cache the DataSourceSurface from a SourceSurface associated with a
+   * SourceSurface from another process. This is to improve performance if we
+   * require the data for that SourceSurface.
+   *
+   * @param aRefPtr the key used to store the DataSourceSurface
+   * @param aSurface the DataSourceSurface to store
+   */
+  void AddDataSurface(gfx::ReferencePtr aRefPtr,
+                      RefPtr<gfx::DataSourceSurface>&& aSurface);
+
+  /**
+   * Gets the cached DataSourceSurface, if it exists, associated with a
+   * SourceSurface from another process.
+   *
+   * @param aRefPtr the key used to find the DataSourceSurface
+   * @returns the DataSourceSurface or nullptr if not found
+   */
+  void RemoveDataSurface(gfx::ReferencePtr aRefPtr);
+
+  /**
+   * Sets a ScopedMap, to be used in a later event.
+   *
+   * @param aSurface the associated surface in the other process
+   * @param aMap the ScopedMap to store
+   */
+  void SetPreparedMap(gfx::ReferencePtr aSurface,
+                      UniquePtr<gfx::DataSourceSurface::ScopedMap> aMap);
+
+  /**
+   * Gets the ScopedMap stored using SetPreparedMap.
+   *
+   * @param aSurface must match the surface from the SetPreparedMap call
+   * @returns the ScopedMap if aSurface matches otherwise nullptr
+   */
+  UniquePtr<gfx::DataSourceSurface::ScopedMap> GetPreparedMap(
+      gfx::ReferencePtr aSurface);
+
+ private:
+  CanvasTranslator(const TextureType& aTextureType, TextureData* aTextureData,
+                   gfx::DrawTarget* aDrawTarget,
+                   const ipc::SharedMemoryBasic::Handle& aReadHandle,
+                   const CrossProcessSemaphoreHandle& aReaderSem,
+                   const CrossProcessSemaphoreHandle& aWriterSem);
+
+  void AddSurfaceDescriptor(gfx::ReferencePtr aRefPtr,
+                            TextureData* atextureData);
+
+  bool HandleExtensionEvent(int32_t aType);
+
+  CanvasEventRingBuffer mStream;
+  TextureType mTextureType = TextureType::Unknown;
+  UniquePtr<TextureData> mReferenceTextureData;
+  typedef std::unordered_map<void*, UniquePtr<TextureData>> TextureMap;
+  TextureMap mTextureDatas;
+  nsRefPtrHashtable<nsPtrHashKey<void>, gfx::DataSourceSurface> mDataSurfaces;
+  gfx::ReferencePtr mMappedSurface;
+  UniquePtr<gfx::DataSourceSurface::ScopedMap> mPreparedMap;
+  typedef std::unordered_map<void*, UniquePtr<SurfaceDescriptor>> DescriptorMap;
+  DescriptorMap mSurfaceDescriptors;
+  Monitor mSurfaceDescriptorsMonitor{
+      "CanvasTranslator::mSurfaceDescriptorsMonitor"};
+  bool mIsValid = true;
+  bool mIsInTransaction = false;
+};
+
+}  // namespace layers
+}  // namespace mozilla
+
+#endif  // mozilla_layers_CanvasTranslator_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/RecordedCanvasEventImpl.h
@@ -0,0 +1,380 @@
+/* -*- 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_layers_RecordedCanvasEventImpl_h
+#define mozilla_layers_RecordedCanvasEventImpl_h
+
+#include "mozilla/gfx/RecordedEvent.h"
+#include "mozilla/gfx/RecordingTypes.h"
+#include "mozilla/layers/CanvasTranslator.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace layers {
+
+using gfx::DrawTarget;
+using gfx::IntSize;
+using gfx::RecordedEvent;
+using gfx::RecordedEventDerived;
+using EventType = gfx::RecordedEvent::EventType;
+using gfx::ReadElement;
+using gfx::ReferencePtr;
+using gfx::SurfaceFormat;
+using gfx::WriteElement;
+using ipc::SharedMemoryBasic;
+
+const EventType CANVAS_BEGIN_TRANSACTION = EventType::LAST;
+const EventType CANVAS_END_TRANSACTION = EventType(EventType::LAST + 1);
+const EventType CANVAS_FLUSH = EventType(EventType::LAST + 2);
+const EventType TEXTURE_LOCK = EventType(EventType::LAST + 3);
+const EventType TEXTURE_UNLOCK = EventType(EventType::LAST + 4);
+const EventType CACHE_DATA_SURFACE = EventType(EventType::LAST + 5);
+const EventType PREPARE_DATA_FOR_SURFACE = EventType(EventType::LAST + 6);
+const EventType GET_DATA_FOR_SURFACE = EventType(EventType::LAST + 7);
+
+class RecordedCanvasBeginTransaction final
+    : public RecordedEventDerived<RecordedCanvasBeginTransaction> {
+ public:
+  RecordedCanvasBeginTransaction()
+      : RecordedEventDerived(CANVAS_BEGIN_TRANSACTION) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedCanvasBeginTransaction(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "RecordedCanvasBeginTransaction"; }
+};
+
+inline bool RecordedCanvasBeginTransaction::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  aTranslator->BeginTransaction();
+  return true;
+}
+
+template <class S>
+void RecordedCanvasBeginTransaction::Record(S& aStream) const {}
+
+template <class S>
+RecordedCanvasBeginTransaction::RecordedCanvasBeginTransaction(S& aStream)
+    : RecordedEventDerived(CANVAS_BEGIN_TRANSACTION) {}
+
+class RecordedCanvasEndTransaction final
+    : public RecordedEventDerived<RecordedCanvasEndTransaction> {
+ public:
+  RecordedCanvasEndTransaction()
+      : RecordedEventDerived(CANVAS_END_TRANSACTION) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedCanvasEndTransaction(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "RecordedCanvasEndTransaction"; }
+};
+
+inline bool RecordedCanvasEndTransaction::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  aTranslator->EndTransaction();
+  return true;
+}
+
+template <class S>
+void RecordedCanvasEndTransaction::Record(S& aStream) const {}
+
+template <class S>
+RecordedCanvasEndTransaction::RecordedCanvasEndTransaction(S& aStream)
+    : RecordedEventDerived(CANVAS_END_TRANSACTION) {}
+
+class RecordedCanvasFlush final
+    : public RecordedEventDerived<RecordedCanvasFlush> {
+ public:
+  RecordedCanvasFlush() : RecordedEventDerived(CANVAS_FLUSH) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedCanvasFlush(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "RecordedCanvasFlush"; }
+};
+
+inline bool RecordedCanvasFlush::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  aTranslator->Flush();
+  return true;
+}
+
+template <class S>
+void RecordedCanvasFlush::Record(S& aStream) const {}
+
+template <class S>
+RecordedCanvasFlush::RecordedCanvasFlush(S& aStream)
+    : RecordedEventDerived(CANVAS_FLUSH) {}
+
+class RecordedTextureLock final
+    : public RecordedEventDerived<RecordedTextureLock> {
+ public:
+  RecordedTextureLock(DrawTarget* aDT, const OpenMode aMode)
+      : RecordedEventDerived(TEXTURE_LOCK), mDT(aDT), mMode(aMode) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedTextureLock(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "TextureLock"; }
+
+ private:
+  ReferencePtr mDT;
+  OpenMode mMode;
+};
+
+inline bool RecordedTextureLock::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  TextureData* textureData = aTranslator->LookupTextureData(mDT);
+  if (!textureData) {
+    return false;
+  }
+
+  textureData->Lock(mMode);
+  return true;
+}
+
+template <class S>
+void RecordedTextureLock::Record(S& aStream) const {
+  WriteElement(aStream, mDT);
+  WriteElement(aStream, mMode);
+}
+
+template <class S>
+RecordedTextureLock::RecordedTextureLock(S& aStream)
+    : RecordedEventDerived(TEXTURE_LOCK) {
+  ReadElement(aStream, mDT);
+  ReadElement(aStream, mMode);
+}
+
+class RecordedTextureUnlock final
+    : public RecordedEventDerived<RecordedTextureUnlock> {
+ public:
+  explicit RecordedTextureUnlock(DrawTarget* aDT)
+      : RecordedEventDerived(TEXTURE_UNLOCK), mDT(aDT) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedTextureUnlock(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "TextureUnlock"; }
+
+ private:
+  ReferencePtr mDT;
+};
+
+inline bool RecordedTextureUnlock::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  TextureData* textureData = aTranslator->LookupTextureData(mDT);
+  if (!textureData) {
+    return false;
+  }
+
+  textureData->Unlock();
+  return true;
+}
+
+template <class S>
+void RecordedTextureUnlock::Record(S& aStream) const {
+  WriteElement(aStream, mDT);
+}
+
+template <class S>
+RecordedTextureUnlock::RecordedTextureUnlock(S& aStream)
+    : RecordedEventDerived(TEXTURE_UNLOCK) {
+  ReadElement(aStream, mDT);
+}
+
+class RecordedCacheDataSurface final
+    : public RecordedEventDerived<RecordedCacheDataSurface> {
+ public:
+  explicit RecordedCacheDataSurface(gfx::SourceSurface* aSurface)
+      : RecordedEventDerived(CACHE_DATA_SURFACE), mSurface(aSurface) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedCacheDataSurface(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "RecordedCacheDataSurface"; }
+
+ private:
+  ReferencePtr mSurface;
+};
+
+inline bool RecordedCacheDataSurface::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  RefPtr<gfx::SourceSurface> surface =
+      aTranslator->LookupSourceSurface(mSurface);
+
+  RefPtr<gfx::DataSourceSurface> dataSurface = surface->GetDataSurface();
+
+  aTranslator->AddDataSurface(mSurface, std::move(dataSurface));
+  return true;
+}
+
+template <class S>
+void RecordedCacheDataSurface::Record(S& aStream) const {
+  WriteElement(aStream, mSurface);
+}
+
+template <class S>
+RecordedCacheDataSurface::RecordedCacheDataSurface(S& aStream)
+    : RecordedEventDerived(CACHE_DATA_SURFACE) {
+  ReadElement(aStream, mSurface);
+}
+
+class RecordedPrepareDataForSurface final
+    : public RecordedEventDerived<RecordedPrepareDataForSurface> {
+ public:
+  explicit RecordedPrepareDataForSurface(const gfx::SourceSurface* aSurface)
+      : RecordedEventDerived(PREPARE_DATA_FOR_SURFACE), mSurface(aSurface) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedPrepareDataForSurface(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "RecordedPrepareDataForSurface"; }
+
+ private:
+  ReferencePtr mSurface;
+};
+
+inline bool RecordedPrepareDataForSurface::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  RefPtr<gfx::DataSourceSurface> dataSurface =
+      aTranslator->LookupDataSurface(mSurface);
+  if (!dataSurface) {
+    RefPtr<gfx::SourceSurface> surface =
+        aTranslator->LookupSourceSurface(mSurface);
+    if (!surface) {
+      return false;
+    }
+
+    dataSurface = surface->GetDataSurface();
+    if (!dataSurface) {
+      return false;
+    }
+  }
+
+  auto preparedMap = MakeUnique<gfx::DataSourceSurface::ScopedMap>(
+      dataSurface, gfx::DataSourceSurface::READ);
+  aTranslator->SetPreparedMap(mSurface, std::move(preparedMap));
+
+  return true;
+}
+
+template <class S>
+void RecordedPrepareDataForSurface::Record(S& aStream) const {
+  WriteElement(aStream, mSurface);
+}
+
+template <class S>
+RecordedPrepareDataForSurface::RecordedPrepareDataForSurface(S& aStream)
+    : RecordedEventDerived(PREPARE_DATA_FOR_SURFACE) {
+  ReadElement(aStream, mSurface);
+}
+
+class RecordedGetDataForSurface final
+    : public RecordedEventDerived<RecordedGetDataForSurface> {
+ public:
+  explicit RecordedGetDataForSurface(const gfx::SourceSurface* aSurface)
+      : RecordedEventDerived(GET_DATA_FOR_SURFACE), mSurface(aSurface) {}
+
+  template <class S>
+  MOZ_IMPLICIT RecordedGetDataForSurface(S& aStream);
+
+  bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
+
+  template <class S>
+  void Record(S& aStream) const;
+
+  std::string GetName() const final { return "RecordedGetDataForSurface"; }
+
+ private:
+  ReferencePtr mSurface;
+};
+
+inline bool RecordedGetDataForSurface::PlayCanvasEvent(
+    CanvasTranslator* aTranslator) const {
+  RefPtr<gfx::SourceSurface> surface =
+      aTranslator->LookupSourceSurface(mSurface);
+  if (!surface) {
+    return false;
+  }
+
+  UniquePtr<gfx::DataSourceSurface::ScopedMap> map =
+      aTranslator->GetPreparedMap(mSurface);
+
+  gfx::IntSize ssSize = surface->GetSize();
+  size_t dataFormatWidth = ssSize.width * BytesPerPixel(surface->GetFormat());
+  int32_t srcStride = map->GetStride();
+  char* src = reinterpret_cast<char*>(map->GetData());
+  char* endSrc = src + (ssSize.height * srcStride);
+  while (src < endSrc) {
+    aTranslator->ReturnWrite(src, dataFormatWidth);
+    src += srcStride;
+  }
+
+  return true;
+}
+
+template <class S>
+void RecordedGetDataForSurface::Record(S& aStream) const {
+  WriteElement(aStream, mSurface);
+}
+
+template <class S>
+RecordedGetDataForSurface::RecordedGetDataForSurface(S& aStream)
+    : RecordedEventDerived(GET_DATA_FOR_SURFACE) {
+  ReadElement(aStream, mSurface);
+}
+
+#define FOR_EACH_CANVAS_EVENT(f)                               \
+  f(CANVAS_BEGIN_TRANSACTION, RecordedCanvasBeginTransaction); \
+  f(CANVAS_END_TRANSACTION, RecordedCanvasEndTransaction);     \
+  f(CANVAS_FLUSH, RecordedCanvasFlush);                        \
+  f(TEXTURE_LOCK, RecordedTextureLock);                        \
+  f(TEXTURE_UNLOCK, RecordedTextureUnlock);                    \
+  f(CACHE_DATA_SURFACE, RecordedCacheDataSurface);             \
+  f(PREPARE_DATA_FOR_SURFACE, RecordedPrepareDataForSurface);  \
+  f(GET_DATA_FOR_SURFACE, RecordedGetDataForSurface);
+
+}  // namespace layers
+}  // namespace mozilla
+
+#endif  // mozilla_layers_RecordedCanvasEventImpl_h