Bug 1646835 - add MapTile/UnmapTile hooks to the WR compositor. r?mstange,gw draft
authorLee Salzman <lsalzman@mozilla.com>
Tue, 23 Jun 2020 23:38:28 +0000
changeset 2999951 5e25bfad2e495f0c7da18ed3ca68f60630c0133d
parent 2999950 1d553e7c4a1b03e189b69b2e093dbc38b30d5ac9
child 2999952 8660d62b35e3d6c260dadf27a01e56714b528278
push id558588
push userreviewbot
push dateTue, 23 Jun 2020 23:38:48 +0000
treeherdertry@8660d62b35e3 [default view] [failures only]
reviewersmstange, gw
bugs1646835
milestone79.0a1
Bug 1646835 - add MapTile/UnmapTile hooks to the WR compositor. r?mstange,gw Summary: RenderCompositors for SWGL that wish to provide accelerated compositing need to subvert the existing Bind/Unbind hooks in the WR compositor. These compositors need to keep track of their HW tiles without actually binding an OpenGL FBO for WR to render picture cache tiles. SWGL needs to intervene and get a backing buffer for tile from the RenderCompositor so that it can create a SWGL FBO for WR to render the picture cache tile to. To that end, this adds MapTile/UnmapTile as a replacement for Bind/Unbind for those scenarios. This is done in a way that it affects only the WrCompositor of webrender_bindings without actually altering WR's Compositor interface. This is beneficial because WR does not have to understand the details of SWGL integration and also so as not to complicate other users of WR such as Servo who are not currently utilizing SWGL at all. RenderCompositorOGL is initially modified to use these hooks in this patch. It later became more convenient to restructure that in a follow-up patch. Differential Revision: https://phabricator.services.mozilla.com/D80269 Test Plan: Reviewers: mstange, gw Subscribers: Bug #: 1646835 Differential Diff: PHID-DIFF-hsxc6o5kmmmamde7bfcj
gfx/webrender_bindings/RenderCompositor.cpp
gfx/webrender_bindings/RenderCompositor.h
gfx/webrender_bindings/RenderCompositorOGL.cpp
gfx/webrender_bindings/RenderCompositorOGL.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/src/swgl_bindings.rs
--- a/gfx/webrender_bindings/RenderCompositor.cpp
+++ b/gfx/webrender_bindings/RenderCompositor.cpp
@@ -87,16 +87,29 @@ void wr_compositor_unbind(void* aComposi
   compositor->Unbind();
 }
 
 void wr_compositor_deinit(void* aCompositor) {
   RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
   compositor->DeInit();
 }
 
+void wr_compositor_map_tile(void* aCompositor, wr::NativeTileId aId,
+                            wr::DeviceIntRect aDirtyRect,
+                            wr::DeviceIntRect aValidRect, void** aData,
+                            int32_t* aStride) {
+  RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+  compositor->MapTile(aId, aDirtyRect, aValidRect, aData, aStride);
+}
+
+void wr_compositor_unmap_tile(void* aCompositor) {
+  RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+  compositor->UnmapTile();
+}
+
 /* static */
 UniquePtr<RenderCompositor> RenderCompositor::Create(
     RefPtr<widget::CompositorWidget>&& aWidget) {
 #ifdef XP_WIN
   if (gfx::gfxVars::UseWebRenderANGLE()) {
     return RenderCompositorANGLE::Create(std::move(aWidget));
   }
 #endif
--- a/gfx/webrender_bindings/RenderCompositor.h
+++ b/gfx/webrender_bindings/RenderCompositor.h
@@ -95,16 +95,22 @@ class RenderCompositor {
 
   // Interface for wr::Compositor
   virtual void CompositorBeginFrame() {}
   virtual void CompositorEndFrame() {}
   virtual void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
                     uint32_t* aFboId, wr::DeviceIntRect aDirtyRect,
                     wr::DeviceIntRect aValidRect) {}
   virtual void Unbind() {}
+  virtual bool MapTile(wr::NativeTileId aId, wr::DeviceIntRect aDirtyRect,
+                       wr::DeviceIntRect aValidRect, void** aData,
+                       int32_t* aStride) {
+    return false;
+  }
+  virtual void UnmapTile() {}
   virtual void CreateSurface(wr::NativeSurfaceId aId,
                              wr::DeviceIntPoint aVirtualOffset,
                              wr::DeviceIntSize aTileSize, bool aIsOpaque) {}
   virtual void DestroySurface(NativeSurfaceId aId) {}
   virtual void CreateTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) {}
   virtual void DestroyTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) {}
   virtual void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition,
                           wr::DeviceIntRect aClipRect) {}
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -40,17 +40,16 @@ UniquePtr<RenderCompositor> RenderCompos
 
 RenderCompositorOGL::RenderCompositorOGL(
     RefPtr<gl::GLContext>&& aGL, RefPtr<widget::CompositorWidget>&& aWidget)
     : RenderCompositor(std::move(aWidget)),
       mGL(aGL),
       mNativeLayerRoot(GetWidget()->GetNativeLayerRoot()),
       mPreviousFrameDoneSync(nullptr),
       mThisFrameDoneSync(nullptr) {
-  MOZ_ASSERT(mGL);
   if (mNativeLayerRoot) {
 #ifdef XP_MACOSX
     auto pool = RenderThread::Get()->SharedSurfacePool();
     if (pool) {
       mSurfacePoolHandle = pool->GetHandleForGL(mGL);
     }
 #endif
     MOZ_RELEASE_ASSERT(mSurfacePoolHandle);
@@ -60,17 +59,17 @@ RenderCompositorOGL::RenderCompositorOGL
 RenderCompositorOGL::~RenderCompositorOGL() {
   if (mNativeLayerRoot) {
     mNativeLayerRoot->SetLayers({});
     mNativeLayerForEntireWindow = nullptr;
     mNativeLayerRootSnapshotter = nullptr;
     mNativeLayerRoot = nullptr;
   }
 
-  if (!mGL->MakeCurrent()) {
+  if (mGL && !mGL->MakeCurrent()) {
     gfxCriticalNote
         << "Failed to make render context current during destroying.";
     // Leak resources!
     mPreviousFrameDoneSync = nullptr;
     mThisFrameDoneSync = nullptr;
     return;
   }
 
@@ -78,17 +77,17 @@ RenderCompositorOGL::~RenderCompositorOG
     mGL->fDeleteSync(mPreviousFrameDoneSync);
   }
   if (mThisFrameDoneSync) {
     mGL->fDeleteSync(mThisFrameDoneSync);
   }
 }
 
 bool RenderCompositorOGL::BeginFrame() {
-  if (!mGL->MakeCurrent()) {
+  if (mGL && !mGL->MakeCurrent()) {
     gfxCriticalNote << "Failed to make render context current, can't draw.";
     return false;
   }
 
   gfx::IntSize bufferSize = GetBufferSize().ToUnknownSize();
   if (mNativeLayerRoot && !ShouldUseNativeCompositor()) {
     if (mNativeLayerForEntireWindow &&
         mNativeLayerForEntireWindow->GetSize() != bufferSize) {
@@ -99,41 +98,55 @@ bool RenderCompositorOGL::BeginFrame() {
       mNativeLayerForEntireWindow =
           mNativeLayerRoot->CreateLayer(bufferSize, false, mSurfacePoolHandle);
       mNativeLayerForEntireWindow->SetSurfaceIsFlipped(true);
       mNativeLayerRoot->AppendLayer(mNativeLayerForEntireWindow);
     }
   }
   if (mNativeLayerForEntireWindow) {
     gfx::IntRect bounds({}, bufferSize);
-    Maybe<GLuint> fbo = mNativeLayerForEntireWindow->NextSurfaceAsFramebuffer(
-        bounds, bounds, true);
-    if (!fbo) {
+    if (mGL) {
+      Maybe<GLuint> fbo = mNativeLayerForEntireWindow->NextSurfaceAsFramebuffer(
+          bounds, bounds, true);
+      if (!fbo) {
+        return false;
+      }
+      mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, *fbo);
+    } else if (!MapNativeLayer(mNativeLayerForEntireWindow, bounds, bounds)) {
       return false;
     }
-    mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, *fbo);
-  } else {
+  } else if (mGL) {
     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGL->GetDefaultFramebuffer());
   }
 
   return true;
 }
 
+void RenderCompositorOGL::CancelFrame() {
+  if (mNativeLayerForEntireWindow && mLayerTarget) {
+    UnmapNativeLayer();
+  }
+}
+
 RenderedFrameId RenderCompositorOGL::EndFrame(
     const nsTArray<DeviceIntRect>& aDirtyRects) {
   RenderedFrameId frameId = GetNextRenderFrameId();
-  InsertFrameDoneSync();
 
-  if (!mNativeLayerRoot) {
-    mGL->SwapBuffers();
-    return frameId;
+  if (mGL) {
+    InsertFrameDoneSync();
+    if (!mNativeLayerRoot) {
+      mGL->SwapBuffers();
+    } else if (mNativeLayerForEntireWindow) {
+      mGL->fFlush();
+    }
+  } else if (mNativeLayerForEntireWindow && mLayerTarget) {
+    UnmapNativeLayer();
   }
 
   if (mNativeLayerForEntireWindow) {
-    mGL->fFlush();
     mNativeLayerForEntireWindow->NotifySurfaceReady();
     mNativeLayerRoot->CommitToScreen();
   }
 
   return frameId;
 }
 
 void RenderCompositorOGL::InsertFrameDoneSync() {
@@ -184,17 +197,19 @@ bool RenderCompositorOGL::MaybeReadback(
   if (!mNativeLayerRootSnapshotter) {
     mNativeLayerRootSnapshotter = mNativeLayerRoot->CreateSnapshotter();
   }
   bool success = mNativeLayerRootSnapshotter->ReadbackPixels(
       aReadbackSize, gfx::SurfaceFormat::B8G8R8A8, aReadbackBuffer);
 
   // ReadbackPixels might have changed the current context. Make sure mGL is
   // current again.
-  mGL->MakeCurrent();
+  if (mGL) {
+    mGL->MakeCurrent();
+  }
 
   return success;
 }
 
 uint32_t RenderCompositorOGL::GetMaxUpdateRects() {
   if (ShouldUseNativeCompositor() &&
       StaticPrefs::gfx_webrender_compositor_max_update_rects_AtStartup() > 0) {
     return 1;
@@ -231,58 +246,141 @@ void RenderCompositorOGL::CompositorEndF
                         int((mTotalPixelCount - mAddedPixelCount) * 100 /
                             windowPixelCount)),
         JS::ProfilingCategoryPair::GRAPHICS, mBeginFrameTimeStamp,
         TimeStamp::NowUnfuzzed());
   }
 #endif
   mDrawnPixelCount = 0;
 
+  if (mGL) {
+    mGL->fFlush();
+  }
+
   mNativeLayerRoot->SetLayers(mAddedLayers);
-  mGL->fFlush();
   mNativeLayerRoot->CommitToScreen();
   mSurfacePoolHandle->OnEndFrame();
 }
 
-void RenderCompositorOGL::Bind(wr::NativeTileId aId,
-                               wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
-                               wr::DeviceIntRect aDirtyRect,
-                               wr::DeviceIntRect aValidRect) {
+void RenderCompositorOGL::BindNativeLayer(wr::NativeTileId aId,
+                                          wr::DeviceIntRect aDirtyRect,
+                                          wr::DeviceIntRect aValidRect) {
   MOZ_RELEASE_ASSERT(!mCurrentlyBoundNativeLayer);
 
   auto surfaceCursor = mSurfaces.find(aId.surface_id);
   MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
   Surface& surface = surfaceCursor->second;
 
   auto layerCursor = surface.mNativeLayers.find(TileKey(aId.x, aId.y));
   MOZ_RELEASE_ASSERT(layerCursor != surface.mNativeLayers.end());
   RefPtr<layers::NativeLayer> layer = layerCursor->second;
 
+  gfx::IntRect dirtyRect(aDirtyRect.origin.x, aDirtyRect.origin.y,
+                         aDirtyRect.size.width, aDirtyRect.size.height);
+
+  mCurrentlyBoundNativeLayer = layer;
+
+  mDrawnPixelCount += dirtyRect.Area();
+}
+
+void RenderCompositorOGL::UnbindNativeLayer() {
+  MOZ_RELEASE_ASSERT(mCurrentlyBoundNativeLayer);
+
+  mCurrentlyBoundNativeLayer->NotifySurfaceReady();
+  mCurrentlyBoundNativeLayer = nullptr;
+}
+
+void RenderCompositorOGL::Bind(wr::NativeTileId aId,
+                               wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
+                               wr::DeviceIntRect aDirtyRect,
+                               wr::DeviceIntRect aValidRect) {
+  BindNativeLayer(aId, aDirtyRect, aValidRect);
+
   gfx::IntRect validRect(aValidRect.origin.x, aValidRect.origin.y,
                          aValidRect.size.width, aValidRect.size.height);
   gfx::IntRect dirtyRect(aDirtyRect.origin.x, aDirtyRect.origin.y,
                          aDirtyRect.size.width, aDirtyRect.size.height);
 
-  Maybe<GLuint> fbo =
-      layer->NextSurfaceAsFramebuffer(validRect, dirtyRect, true);
+  Maybe<GLuint> fbo = mCurrentlyBoundNativeLayer->NextSurfaceAsFramebuffer(
+      validRect, dirtyRect, true);
   MOZ_RELEASE_ASSERT(fbo);  // TODO: make fallible
-  mCurrentlyBoundNativeLayer = layer;
 
   *aFboId = *fbo;
   *aOffset = wr::DeviceIntPoint{0, 0};
-
-  mDrawnPixelCount += dirtyRect.Area();
 }
 
 void RenderCompositorOGL::Unbind() {
-  MOZ_RELEASE_ASSERT(mCurrentlyBoundNativeLayer);
+  mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+  UnbindNativeLayer();
+}
+
+bool RenderCompositorOGL::MapNativeLayer(layers::NativeLayer* aLayer,
+                                         const gfx::IntRect& aDirtyRect,
+                                         const gfx::IntRect& aValidRect) {
+  uint8_t* data = nullptr;
+  gfx::IntSize size;
+  int32_t stride = 0;
+  gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
+  RefPtr<gfx::DrawTarget> dt = aLayer->NextSurfaceAsDrawTarget(
+      aValidRect, gfx::IntRegion(aDirtyRect), gfx::BackendType::SKIA);
+  if (!dt || !dt->LockBits(&data, &size, &stride, &format)) {
+    return false;
+  }
+  MOZ_ASSERT(format == gfx::SurfaceFormat::B8G8R8A8 ||
+             format == gfx::SurfaceFormat::B8G8R8X8);
+  mLayerTarget = std::move(dt);
+  mLayerData = data;
+  mLayerStride = stride;
+  return true;
+}
+
+void RenderCompositorOGL::UnmapNativeLayer() {
+  MOZ_ASSERT(mLayerTarget && mLayerData);
+  mLayerTarget->ReleaseBits(mLayerData);
+  mLayerTarget = nullptr;
+  mLayerData = nullptr;
+  mLayerStride = 0;
+}
 
-  mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
-  mCurrentlyBoundNativeLayer->NotifySurfaceReady();
-  mCurrentlyBoundNativeLayer = nullptr;
+bool RenderCompositorOGL::GetMappedBuffer(uint8_t** aData, int32_t* aStride) {
+  if (mNativeLayerForEntireWindow && mLayerData) {
+    *aData = mLayerData;
+    *aStride = mLayerStride;
+    return true;
+  }
+  return false;
+}
+
+bool RenderCompositorOGL::MapTile(wr::NativeTileId aId,
+                                  wr::DeviceIntRect aDirtyRect,
+                                  wr::DeviceIntRect aValidRect,
+                                  void** aData, int32_t* aStride) {
+  if (!mNativeLayerRoot || mNativeLayerForEntireWindow) {
+    return false;
+  }
+  BindNativeLayer(aId, aDirtyRect, aValidRect);
+  gfx::IntRect dirtyRect(aDirtyRect.origin.x, aDirtyRect.origin.y,
+                         aDirtyRect.size.width, aDirtyRect.size.height);
+  gfx::IntRect validRect(aValidRect.origin.x, aValidRect.origin.y,
+                         aValidRect.size.width, aValidRect.size.height);
+  if (!MapNativeLayer(mCurrentlyBoundNativeLayer, dirtyRect, validRect)) {
+    UnbindNativeLayer();
+    return false;
+  }
+  *aData = mLayerData;
+  *aStride = mLayerStride;
+  return true;
+}
+
+void RenderCompositorOGL::UnmapTile() {
+  if (!mNativeLayerForEntireWindow && mCurrentlyBoundNativeLayer) {
+    UnmapNativeLayer();
+    UnbindNativeLayer();
+  }
 }
 
 void RenderCompositorOGL::CreateSurface(wr::NativeSurfaceId aId,
                                         wr::DeviceIntPoint aVirtualOffset,
                                         wr::DeviceIntSize aTileSize,
                                         bool aIsOpaque) {
   MOZ_RELEASE_ASSERT(mSurfaces.find(aId) == mSurfaces.end());
   mSurfaces.insert({aId, Surface{aTileSize, aIsOpaque}});
--- a/gfx/webrender_bindings/RenderCompositorOGL.h
+++ b/gfx/webrender_bindings/RenderCompositorOGL.h
@@ -27,21 +27,24 @@ class RenderCompositorOGL : public Rende
   static UniquePtr<RenderCompositor> Create(
       RefPtr<widget::CompositorWidget>&& aWidget);
 
   RenderCompositorOGL(RefPtr<gl::GLContext>&& aGL,
                       RefPtr<widget::CompositorWidget>&& aWidget);
   virtual ~RenderCompositorOGL();
 
   bool BeginFrame() override;
+  void CancelFrame() override;
   RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) final;
   bool WaitForGPU() override;
   void Pause() override;
   bool Resume() override;
 
+  bool GetMappedBuffer(uint8_t** aData, int32_t* aStride) override;
+
   gl::GLContext* gl() const override { return mGL; }
 
   bool UseANGLE() const override { return false; }
 
   LayoutDeviceIntSize GetBufferSize() override;
 
   bool ShouldUseNativeCompositor() override;
   uint32_t GetMaxUpdateRects() override;
@@ -53,16 +56,20 @@ class RenderCompositorOGL : public Rende
 
   // Interface for wr::Compositor
   void CompositorBeginFrame() override;
   void CompositorEndFrame() override;
   void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
             wr::DeviceIntRect aDirtyRect,
             wr::DeviceIntRect aValidRect) override;
   void Unbind() override;
+  bool MapTile(wr::NativeTileId aId, wr::DeviceIntRect aDirtyRect,
+               wr::DeviceIntRect aValidRect, void** aData,
+               int32_t* aStride) override;
+  void UnmapTile() override;
   void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset,
                      wr::DeviceIntSize aTileSize, bool aIsOpaque) override;
   void DestroySurface(NativeSurfaceId aId) override;
   void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
   void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
   void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition,
                   wr::DeviceIntRect aClipRect) override;
   CompositorCapabilities GetCompositorCapabilities() override;
@@ -72,16 +79,25 @@ class RenderCompositorOGL : public Rende
 
     int32_t mX;
     int32_t mY;
   };
 
  protected:
   void InsertFrameDoneSync();
 
+  void BindNativeLayer(wr::NativeTileId aId, wr::DeviceIntRect aDirtyRect,
+                       wr::DeviceIntRect aValidRect);
+  void UnbindNativeLayer();
+
+  bool MapNativeLayer(layers::NativeLayer* aLayer,
+                      const gfx::IntRect& aDirtyRect,
+                      const gfx::IntRect& aValidRect);
+  void UnmapNativeLayer();
+
   RefPtr<gl::GLContext> mGL;
 
   // Can be null.
   RefPtr<layers::NativeLayerRoot> mNativeLayerRoot;
   UniquePtr<layers::NativeLayerRootSnapshotter> mNativeLayerRootSnapshotter;
   RefPtr<layers::NativeLayer> mNativeLayerForEntireWindow;
   RefPtr<layers::SurfacePoolHandle> mSurfacePoolHandle;
 
@@ -107,16 +123,19 @@ class RenderCompositorOGL : public Rende
   struct SurfaceIdHashFn {
     std::size_t operator()(const wr::NativeSurfaceId& aId) const {
       return HashGeneric(wr::AsUint64(aId));
     }
   };
 
   // Used in native compositor mode:
   RefPtr<layers::NativeLayer> mCurrentlyBoundNativeLayer;
+  RefPtr<gfx::DrawTarget> mLayerTarget;
+  uint8_t* mLayerData = nullptr;
+  int32_t mLayerStride = 0;
   nsTArray<RefPtr<layers::NativeLayer>> mAddedLayers;
   uint64_t mTotalPixelCount = 0;
   uint64_t mAddedPixelCount = 0;
   uint64_t mAddedClippedPixelCount = 0;
   uint64_t mDrawnPixelCount = 0;
   gfx::IntRect mVisibleBounds;
   std::unordered_map<wr::NativeSurfaceId, Surface, SurfaceIdHashFn> mSurfaces;
   TimeStamp mBeginFrameTimeStamp;
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1214,16 +1214,25 @@ extern "C" {
         id: NativeSurfaceId,
         position: DeviceIntPoint,
         clip_rect: DeviceIntRect,
     );
     fn wr_compositor_end_frame(compositor: *mut c_void);
     fn wr_compositor_enable_native_compositor(compositor: *mut c_void, enable: bool);
     fn wr_compositor_deinit(compositor: *mut c_void);
     fn wr_compositor_get_capabilities(compositor: *mut c_void) -> CompositorCapabilities;
+    fn wr_compositor_map_tile(
+        compositor: *mut c_void,
+        id: NativeTileId,
+        dirty_rect: DeviceIntRect,
+        valid_rect: DeviceIntRect,
+        data: &mut *mut c_void,
+        stride: &mut i32,
+    );
+    fn wr_compositor_unmap_tile(compositor: *mut c_void);
 }
 
 pub struct WrCompositor(*mut c_void);
 
 impl Compositor for WrCompositor {
     fn create_surface(
         &mut self,
         id: NativeSurfaceId,
@@ -1310,16 +1319,68 @@ impl Compositor for WrCompositor {
         }
     }
 
     fn get_capabilities(&self) -> CompositorCapabilities {
         unsafe { wr_compositor_get_capabilities(self.0) }
     }
 }
 
+/// Information about the underlying data buffer of a mapped tile.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct MappedTileInfo {
+    pub data: *mut c_void,
+    pub stride: i32,
+}
+
+/// WrCompositor-specific extensions to the basic Compositor interface.
+impl WrCompositor {
+    /// Map a tile's underlying buffer so it can be used as the backing for
+    /// a SWGL framebuffer. This is intended to be a replacement for 'bind'
+    /// in any compositors that intend to directly interoperate with SWGL
+    /// while supporting some form of native layers.
+    pub fn map_tile(
+        &mut self,
+        id: NativeTileId,
+        dirty_rect: DeviceIntRect,
+        valid_rect: DeviceIntRect,
+    ) -> Option<MappedTileInfo> {
+        let mut tile_info = MappedTileInfo {
+            data: ptr::null_mut(),
+            stride: 0,
+        };
+
+        unsafe {
+            wr_compositor_map_tile(
+                self.0,
+                id,
+                dirty_rect,
+                valid_rect,
+                &mut tile_info.data,
+                &mut tile_info.stride,
+            );
+        }
+
+        if tile_info.data != ptr::null_mut() && tile_info.stride != 0 {
+            Some(tile_info)
+        } else {
+            None
+        }
+    }
+
+    /// Unmap a tile that was was previously mapped via map_tile to signal
+    /// that SWGL is done rendering to the buffer.
+    pub fn unmap_tile(&mut self) {
+        unsafe {
+            wr_compositor_unmap_tile(self.0);
+        }
+    }
+}
+
 // Call MakeCurrent before this.
 #[no_mangle]
 pub extern "C" fn wr_window_new(
     window_id: WrWindowId,
     window_width: i32,
     window_height: i32,
     support_low_priority_transactions: bool,
     support_low_priority_threadpool: bool,
@@ -1400,18 +1461,18 @@ pub extern "C" fn wr_window_new(
     let color = if cfg!(target_os = "android") {
         // The color is for avoiding black flash before receiving display list.
         ColorF::new(1.0, 1.0, 1.0, 1.0)
     } else {
         ColorF::new(0.0, 0.0, 0.0, 0.0)
     };
 
     let compositor_config = if software {
-        let wr_compositor: Option<Box<dyn Compositor>> = if compositor != ptr::null_mut() {
-            Some(Box::new(WrCompositor(compositor)))
+        let wr_compositor = if compositor != ptr::null_mut() {
+            Some(WrCompositor(compositor))
         } else {
             None
         };
         CompositorConfig::Native {
             max_update_rects: 1,
             compositor: Box::new(SwCompositor::new(sw_gl.unwrap(), native_gl, wr_compositor)),
         }
     } else if compositor != ptr::null_mut() {
--- a/gfx/webrender_bindings/src/swgl_bindings.rs
+++ b/gfx/webrender_bindings/src/swgl_bindings.rs
@@ -1,12 +1,13 @@
 /* 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/. */
 
+use bindings::WrCompositor;
 use gleam::{gl, gl::Gl};
 use std::collections::hash_map::HashMap;
 use std::os::raw::c_void;
 use std::ptr;
 use std::rc::Rc;
 use webrender::{api::units::*, Compositor, CompositorCapabilities, NativeSurfaceId, NativeSurfaceInfo, NativeTileId};
 
 #[no_mangle]
@@ -205,25 +206,25 @@ impl DrawTileHelper {
         self.gl.use_program(0);
         self.gl.bind_vertex_array(0);
     }
 }
 
 pub struct SwCompositor {
     gl: swgl::Context,
     native_gl: Option<Rc<dyn gl::Gl>>,
-    compositor: Option<Box<dyn Compositor>>,
+    compositor: Option<WrCompositor>,
     surfaces: HashMap<NativeSurfaceId, SwSurface>,
     frame_surfaces: Vec<(NativeSurfaceId, DeviceIntPoint, DeviceIntRect)>,
     cur_tile: NativeTileId,
     draw_tile: Option<DrawTileHelper>,
 }
 
 impl SwCompositor {
-    pub fn new(gl: swgl::Context, native_gl: Option<Rc<dyn gl::Gl>>, compositor: Option<Box<dyn Compositor>>) -> Self {
+    pub fn new(gl: swgl::Context, native_gl: Option<Rc<dyn gl::Gl>>, compositor: Option<WrCompositor>) -> Self {
         SwCompositor {
             gl,
             compositor,
             surfaces: HashMap::new(),
             frame_surfaces: Vec::new(),
             cur_tile: NativeTileId {
                 surface_id: NativeSurfaceId(0),
                 x: 0,
@@ -406,39 +407,47 @@ impl Compositor for SwCompositor {
         if let Some(surface) = self.surfaces.get_mut(&id.surface_id) {
             if let Some(tile) = surface.tiles.iter_mut().find(|t| t.x == id.x && t.y == id.y) {
                 tile.dirty_rect = dirty_rect;
                 tile.valid_rect = valid_rect;
                 if valid_rect.is_empty() {
                     return surface_info;
                 }
 
+                let mut stride = 0;
                 let mut buf = ptr::null_mut();
-                if let Some(native_gl) = &self.native_gl {
+                if let Some(compositor) = &mut self.compositor {
+                    if let Some(tile_info) = compositor.map_tile(id, dirty_rect, valid_rect) {
+                        stride = tile_info.stride;
+                        buf = tile_info.data;
+                    }
+                } else if let Some(native_gl) = &self.native_gl {
                     if tile.pbo_id != 0 {
                         native_gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, tile.pbo_id);
                         buf = native_gl.map_buffer_range(
                             gl::PIXEL_UNPACK_BUFFER,
                             0,
                             valid_rect.size.area() as isize * 4 + 16,
                             gl::MAP_WRITE_BIT | gl::MAP_INVALIDATE_BUFFER_BIT,
                         ); // | gl::MAP_UNSYNCHRONIZED_BIT);
-                        if buf == ptr::null_mut() {
+                        if buf != ptr::null_mut() {
+                            stride = valid_rect.size.width * 4;
+                        } else {
                             native_gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
                             native_gl.delete_buffers(&[tile.pbo_id]);
                             tile.pbo_id = 0;
                         }
                     }
                 }
                 self.gl.set_texture_buffer(
                     tile.color_id,
                     gl::RGBA8,
                     valid_rect.size.width,
                     valid_rect.size.height,
-                    valid_rect.size.width * 4,
+                    stride,
                     buf,
                     surface.tile_size.width,
                     surface.tile_size.height,
                 );
                 self.gl.set_texture_buffer(
                     tile.depth_id,
                     gl::DEPTH_COMPONENT16,
                     valid_rect.size.width,
@@ -452,27 +461,33 @@ impl Compositor for SwCompositor {
                 surface_info.origin -= valid_rect.origin.to_vector();
             }
         }
 
         surface_info
     }
 
     fn unbind(&mut self) {
-        let native_gl = match &self.native_gl {
-            Some(native_gl) => native_gl,
-            None => return,
-        };
-
         let id = self.cur_tile;
         if let Some(surface) = self.surfaces.get_mut(&id.surface_id) {
             if let Some(tile) = surface.tiles.iter().find(|t| t.x == id.x && t.y == id.y) {
                 if tile.valid_rect.is_empty() {
                     return;
                 }
+
+                if let Some(compositor) = &mut self.compositor {
+                    compositor.unmap_tile();
+                    return;
+                }
+
+                let native_gl = match &self.native_gl {
+                    Some(native_gl) => native_gl,
+                    None => return,
+                };
+
                 let (swbuf, _, _, stride) = self.gl.get_color_buffer(tile.fbo_id, true);
                 assert!(stride % 4 == 0);
                 let buf = if tile.pbo_id != 0 {
                     native_gl.unmap_buffer(gl::PIXEL_UNPACK_BUFFER);
                     0 as *mut c_void
                 } else {
                     swbuf
                 };