Bug 1285561 - Refactor surface blitting on X11. r?jrmuizel draft
authorAndrew Comminos <andrew@comminos.com>
Mon, 11 Jul 2016 16:08:43 -0400
changeset 386672 43d9bf11ad5ba62b1bdd21f632f16495976d2576
parent 386555 aac8ff1024c553d9c92b85b8b6ba90f65de2ed08
child 525171 ecfe768ab45e3639cb96cc77fff0319988acc6df
push id22768
push userbmo:andrew@comminos.com
push dateTue, 12 Jul 2016 14:29:27 +0000
reviewersjrmuizel
bugs1285561
milestone50.0a1
Bug 1285561 - Refactor surface blitting on X11. r?jrmuizel MozReview-Commit-ID: LHbVK8SYGSJ
widget/WindowSurface.h
widget/WindowSurfaceX11SHM.cpp
widget/WindowSurfaceX11SHM.h
widget/gtk/WindowSurfaceX11.cpp
widget/gtk/WindowSurfaceX11.h
widget/gtk/WindowSurfaceX11Image.cpp
widget/gtk/WindowSurfaceX11Image.h
widget/gtk/WindowSurfaceXRender.cpp
widget/gtk/WindowSurfaceXRender.h
widget/gtk/moz.build
widget/gtk/nsDeviceContextSpecG.cpp
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/moz.build
new file mode 100644
--- /dev/null
+++ b/widget/WindowSurface.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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_WIDGET_WINDOW_SURFACE_H
+#define _MOZILLA_WIDGET_WINDOW_SURFACE_H
+
+#include "mozilla/gfx/2D.h"
+#include "Units.h"
+
+namespace mozilla {
+namespace widget {
+
+// A class for drawing to double-buffered windows.
+class WindowSurface {
+public:
+  virtual ~WindowSurface() {}
+
+  // Locks a region of the window for drawing, returning a draw target
+  // capturing the bounds of the provided region.
+  // Implementations must permit invocation from any thread.
+  virtual already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) = 0;
+
+  // Swaps the provided invalid region from the back buffer to the window.
+  // Implementations must permit invocation from any thread.
+  virtual void Commit(const LayoutDeviceIntRegion& aInvalidRegion) = 0;
+};
+
+}  // namespace widget
+}  // namespace mozilla
+
+#endif // _MOZILLA_WIDGET_WINDOW_SURFACE_H
new file mode 100644
--- /dev/null
+++ b/widget/WindowSurfaceX11SHM.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "WindowSurfaceX11SHM.h"
+
+namespace mozilla {
+namespace widget {
+
+WindowSurfaceX11SHM::WindowSurfaceX11SHM(Display* aDisplay, Drawable aWindow,
+                                         Visual* aVisual, unsigned int aDepth)
+{
+  mFrontImage = new nsShmImage(aDisplay, aWindow, aVisual, aDepth);
+  mBackImage = new nsShmImage(aDisplay, aWindow, aVisual, aDepth);
+}
+
+already_AddRefed<gfx::DrawTarget>
+WindowSurfaceX11SHM::Lock(const LayoutDeviceIntRegion& aRegion)
+{
+  mBackImage.swap(mFrontImage);
+  return mBackImage->CreateDrawTarget(aRegion);
+}
+
+void
+WindowSurfaceX11SHM::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
+{
+  mBackImage->Put(aInvalidRegion);
+}
+
+} // namespace widget
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/WindowSurfaceX11SHM.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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_WIDGET_WINDOW_SURFACE_X11_SHM_H
+#define _MOZILLA_WIDGET_WINDOW_SURFACE_X11_SHM_H
+
+#ifdef MOZ_X11
+
+#include "mozilla/widget/WindowSurface.h"
+#include "nsShmImage.h"
+
+namespace mozilla {
+namespace widget {
+
+class WindowSurfaceX11SHM : public WindowSurface {
+public:
+  WindowSurfaceX11SHM(Display* aDisplay, Drawable aWindow, Visual* aVisual,
+                      unsigned int aDepth);
+
+  already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
+  void Commit(const LayoutDeviceIntRegion& aInvalidRegion) override;
+
+private:
+  RefPtr<nsShmImage> mFrontImage;
+  RefPtr<nsShmImage> mBackImage;
+};
+
+}  // namespace widget
+}  // namespace mozilla
+
+#endif // MOZ_X11
+#endif // _MOZILLA_WIDGET_WINDOW_SURFACE_X11_SHM_H
new file mode 100644
--- /dev/null
+++ b/widget/gtk/WindowSurfaceX11.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "WindowSurfaceX11.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+namespace widget {
+
+WindowSurfaceX11::WindowSurfaceX11(Display* aDisplay,
+                                   Window aWindow,
+                                   Visual* aVisual,
+                                   unsigned int aDepth)
+  : mDisplay(aDisplay)
+  , mWindow(aWindow)
+  , mVisual(aVisual)
+  , mDepth(aDepth)
+  , mFormat(GetVisualFormat(aVisual, aDepth))
+  , mGC(None)
+{
+  MOZ_ASSERT(mFormat != gfx::SurfaceFormat::UNKNOWN,
+             "Could not find SurfaceFormat for visual!");
+}
+
+WindowSurfaceX11::~WindowSurfaceX11()
+{
+  if (mGC != None)
+    XFreeGC(mDisplay, mGC);
+}
+
+void
+WindowSurfaceX11::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
+{
+  AutoTArray<XRectangle, 32> xrects;
+  xrects.SetCapacity(aInvalidRegion.GetNumRects());
+
+  for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+    const mozilla::LayoutDeviceIntRect &r = iter.Get();
+    XRectangle xrect = { (short)r.x, (short)r.y, (unsigned short)r.width, (unsigned short)r.height };
+    xrects.AppendElement(xrect);
+  }
+
+  if (!mGC) {
+    mGC = XCreateGC(mDisplay, mWindow, 0, nullptr);
+    if (!mGC) {
+      NS_WARNING("Couldn't create X11 graphics context for window!");
+      return;
+    }
+  }
+
+  XSetClipRectangles(mDisplay, mGC, 0, 0, xrects.Elements(), xrects.Length(), YXBanded);
+  CommitToDrawable(mWindow, mGC, aInvalidRegion);
+}
+
+/* static */
+gfx::SurfaceFormat
+WindowSurfaceX11::GetVisualFormat(const Visual* aVisual, unsigned int aDepth)
+{
+  switch (aDepth) {
+  case 32:
+    if (aVisual->red_mask == 0xff0000 &&
+        aVisual->green_mask == 0xff00 &&
+        aVisual->blue_mask == 0xff) {
+      return gfx::SurfaceFormat::B8G8R8A8;
+    }
+    break;
+  case 24:
+    // Only support the BGRX layout, and report it as BGRA to the compositor.
+    // The alpha channel will be discarded when we put the image.
+    // Cairo/pixman lacks some fast paths for compositing BGRX onto BGRA, so
+    // just report it as BGRX directly in that case.
+    if (aVisual->red_mask == 0xff0000 &&
+        aVisual->green_mask == 0xff00 &&
+        aVisual->blue_mask == 0xff) {
+      gfx::BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
+      return backend == gfx::BackendType::CAIRO ? gfx::SurfaceFormat::B8G8R8X8
+                                                : gfx::SurfaceFormat::B8G8R8A8;
+    }
+    break;
+  case 16:
+    if (aVisual->red_mask == 0xf800 &&
+        aVisual->green_mask == 0x07e0 &&
+        aVisual->blue_mask == 0x1f) {
+      return gfx::SurfaceFormat::R5G6B5_UINT16;
+    }
+    break;
+  }
+
+  return gfx::SurfaceFormat::UNKNOWN;
+}
+
+}  // namespace widget
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/gtk/WindowSurfaceX11.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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_WIDGET_GTK_WINDOW_SURFACE_X11_H
+#define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_H
+
+#ifdef MOZ_X11
+
+#include "mozilla/widget/WindowSurface.h"
+#include "mozilla/gfx/Types.h"
+
+#include <X11/Xlib.h>
+
+namespace mozilla {
+namespace widget {
+
+class WindowSurfaceX11 : public WindowSurface {
+public:
+  WindowSurfaceX11(Display* aDisplay, Window aWindow, Visual* aVisual,
+                   unsigned int aDepth);
+  ~WindowSurfaceX11();
+
+  void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final;
+
+  virtual already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) = 0;
+
+  // Draws the buffered image onto aDest using the given GC.
+  // The GC provided has been clipped to aInvalidRegion.
+  virtual void CommitToDrawable(Drawable aDest, GC aGC,
+                                const LayoutDeviceIntRegion& aInvalidRegion) = 0;
+
+protected:
+  static gfx::SurfaceFormat GetVisualFormat(const Visual* aVisual, unsigned int aDepth);
+
+  Display* const mDisplay;
+  const Window mWindow;
+  Visual* const mVisual;
+  const unsigned int mDepth;
+  const gfx::SurfaceFormat mFormat;
+
+  GC mGC;
+};
+
+}  // namespace widget
+}  // namespace mozilla
+
+#endif // MOZ_X11
+#endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_H
new file mode 100644
--- /dev/null
+++ b/widget/gtk/WindowSurfaceX11Image.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "WindowSurfaceX11Image.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Tools.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+namespace widget {
+
+WindowSurfaceX11Image::WindowSurfaceX11Image(Display* aDisplay,
+                                             Window aWindow,
+                                             Visual* aVisual,
+                                             unsigned int aDepth)
+  : WindowSurfaceX11(aDisplay, aWindow, aVisual, aDepth)
+  , mImage(nullptr)
+{
+}
+
+WindowSurfaceX11Image::~WindowSurfaceX11Image()
+{
+  if (mImage)
+    XDestroyImage(mImage);
+}
+
+already_AddRefed<gfx::DrawTarget>
+WindowSurfaceX11Image::Lock(const LayoutDeviceIntRegion& aRegion)
+{
+  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+  gfx::IntSize size(bounds.XMost(), bounds.YMost());
+
+  if (!mImage || mImage->width < size.width || mImage->height < size.height) {
+    if (mImage)
+      XDestroyImage(mImage);
+
+    int stride = gfx::GetAlignedStride<16>(gfx::BytesPerPixel(mFormat) * size.width);
+    char* data = static_cast<char*>(malloc(stride * size.height));
+    if (!data)
+      return nullptr;
+
+    mImage = XCreateImage(mDisplay, mVisual, mDepth, ZPixmap, 0,
+                          data, size.width, size.height,
+                          8 * gfx::BytesPerPixel(mFormat), stride);
+  }
+
+  if (!mImage)
+    return nullptr;
+
+  unsigned char* data = (unsigned char*) mImage->data;
+  return gfxPlatform::GetPlatform()->CreateDrawTargetForData(data,
+                                                             size,
+                                                             mImage->bytes_per_line,
+                                                             mFormat);
+}
+
+void
+WindowSurfaceX11Image::CommitToDrawable(Drawable aDest, GC aGC,
+                                        const LayoutDeviceIntRegion& aInvalidRegion)
+{
+  MOZ_ASSERT(mImage, "Attempted to commit invalid surface!");
+
+  gfx::IntRect bounds = aInvalidRegion.GetBounds().ToUnknownRect();
+  gfx::IntSize size(bounds.XMost(), bounds.YMost());
+  XPutImage(mDisplay, aDest, aGC, mImage, bounds.x, bounds.y,
+            bounds.x, bounds.y, size.width, size.height);
+}
+
+}  // namespace widget
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/gtk/WindowSurfaceX11Image.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
+#define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
+
+#ifdef MOZ_X11
+
+#include "WindowSurfaceX11.h"
+
+namespace mozilla {
+namespace widget {
+
+class WindowSurfaceX11Image : public WindowSurfaceX11 {
+public:
+  WindowSurfaceX11Image(Display* aDisplay, Window aWindow, Visual* aVisual,
+                        unsigned int aDepth);
+  ~WindowSurfaceX11Image();
+
+  already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
+  void CommitToDrawable(Drawable aDest, GC aGC,
+                        const LayoutDeviceIntRegion& aInvalidRegion) override;
+
+private:
+  XImage* mImage;
+};
+
+}  // namespace widget
+}  // namespace mozilla
+
+#endif // MOZ_X11
+#endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
new file mode 100644
--- /dev/null
+++ b/widget/gtk/WindowSurfaceXRender.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "WindowSurfaceXRender.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Types.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+namespace widget {
+
+WindowSurfaceXRender::WindowSurfaceXRender(Display* aDisplay,
+                                           Window aWindow,
+                                           Visual* aVisual,
+                                           unsigned int aDepth)
+  : WindowSurfaceX11(aDisplay, aWindow, aVisual, aDepth)
+  , mXlibSurface(nullptr)
+{
+}
+
+already_AddRefed<gfx::DrawTarget>
+WindowSurfaceXRender::Lock(const LayoutDeviceIntRegion& aRegion)
+{
+  gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
+  gfx::IntSize size(bounds.XMost(), bounds.YMost());
+  if (!mXlibSurface || mXlibSurface->CairoStatus() != 0 ||
+      mXlibSurface->GetSize().width < size.width ||
+      mXlibSurface->GetSize().height < size.height)
+  {
+    mXlibSurface = gfxXlibSurface::Create(DefaultScreenOfDisplay(mDisplay),
+                                          mVisual,
+                                          size,
+                                          mWindow);
+  }
+
+  if (mXlibSurface && mXlibSurface->CairoStatus() == 0) {
+    return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mXlibSurface.get(), size);
+  }
+  return nullptr;
+}
+
+void
+WindowSurfaceXRender::CommitToDrawable(Drawable aDest, GC aGC,
+                                       const LayoutDeviceIntRegion& aInvalidRegion)
+{
+  MOZ_ASSERT(mXlibSurface && mXlibSurface->CairoStatus() == 0,
+             "Attempted to commit invalid surface!");
+  gfx::IntRect bounds = aInvalidRegion.GetBounds().ToUnknownRect();
+  gfx::IntSize size(bounds.XMost(), bounds.YMost());
+  XCopyArea(mDisplay, mXlibSurface->XDrawable(), aDest, aGC, bounds.x, bounds.y,
+            size.width, size.height, bounds.x, bounds.y);
+}
+
+}  // namespace widget
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/gtk/WindowSurfaceXRender.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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_WIDGET_GTK_WINDOW_SURFACE_XRENDER_H
+#define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_XRENDER_H
+
+#ifdef MOZ_X11
+
+#include "WindowSurfaceX11.h"
+#include "gfxXlibSurface.h"
+
+namespace mozilla {
+namespace widget {
+
+class WindowSurfaceXRender : public WindowSurfaceX11 {
+public:
+  WindowSurfaceXRender(Display* aDisplay, Window aWindow, Visual* aVisual,
+                       unsigned int aDepth);
+
+  already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
+  void CommitToDrawable(Drawable aDest, GC aGC,
+                        const LayoutDeviceIntRegion& aInvalidRegion) override;
+
+private:
+  RefPtr<gfxXlibSurface> mXlibSurface;
+};
+
+}  // namespace widget
+}  // namespace mozilla
+
+#endif // MOZ_X11
+#endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_XRENDER_H
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -58,16 +58,19 @@ if CONFIG['NS_PRINTING']:
         'nsPrintSettingsGTK.cpp',
         'nsPSPrinters.cpp',
     ]
 
 if CONFIG['MOZ_X11']:
     UNIFIED_SOURCES += [
         'nsClipboard.cpp',
         'nsDragService.cpp',
+        'WindowSurfaceX11.cpp',
+        'WindowSurfaceX11Image.cpp',
+        'WindowSurfaceXRender.cpp',
     ]
 
 if CONFIG['ACCESSIBILITY']:
     UNIFIED_SOURCES += [
         'maiRedundantObjectFactory.c',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk2':
--- a/widget/gtk/nsDeviceContextSpecG.cpp
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -20,16 +20,17 @@
 #include "nsPSPrinters.h"
 #include "nsPaperPS.h"  /* Paper size list */
 
 #include "nsPrintSettingsGTK.h"
 
 #include "nsIFileStreams.h"
 #include "nsIFile.h"
 #include "nsTArray.h"
+#include "nsThreadUtils.h"
 
 #include "mozilla/Preferences.h"
 
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 using namespace mozilla;
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -112,18 +112,21 @@ using namespace mozilla::widget;
 #include "Layers.h"
 #include "GLContextProvider.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/HelpersCairo.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 
 #ifdef MOZ_X11
 #include "gfxXlibSurface.h"
-#endif
-  
+#include "WindowSurfaceX11Image.h"
+#include "WindowSurfaceX11SHM.h"
+#include "WindowSurfaceXRender.h"
+#endif // MOZ_X11
+
 #include "nsShmImage.h"
 
 #include "nsIDOMWheelEvent.h"
 
 #include "NativeKeyBindings.h"
 
 #include <dlfcn.h>
 
@@ -471,18 +474,16 @@ nsWindow::nsWindow()
     mTransparencyBitmap = nullptr;
 
     mTransparencyBitmapWidth  = 0;
     mTransparencyBitmapHeight = 0;
 
 #if GTK_CHECK_VERSION(3,4,0)
     mLastScrollEventTime = GDK_CURRENT_TIME;
 #endif
-
-    mFallbackSurface = nullptr;
     mPendingConfigures = 0;
 }
 
 nsWindow::~nsWindow()
 {
     LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
 
     delete[] mTransparencyBitmap;
@@ -795,21 +796,16 @@ nsWindow::Destroy(void)
     }
 
 #ifdef ACCESSIBILITY
      if (mRootAccessible) {
          mRootAccessible = nullptr;
      }
 #endif
 
-    if (mFallbackSurface) {
-        cairo_surface_destroy(mFallbackSurface);
-        mFallbackSurface = nullptr;
-    }
-
     // Save until last because OnDestroy() may cause us to be deleted.
     OnDestroy();
 
     return NS_OK;
 }
 
 nsIWidget *
 nsWindow::GetParent(void)
@@ -6552,143 +6548,46 @@ nsWindow::GetDrawTargetForGdkDrawable(Gd
         return nullptr;
     }
 
     return dt.forget();
 }
 #endif
 
 already_AddRefed<DrawTarget>
-nsWindow::GetDrawTarget(const LayoutDeviceIntRegion& aRegion, BufferMode* aBufferMode)
-{
-  if (!mGdkWindow || aRegion.IsEmpty()) {
+nsWindow::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, BufferMode* aBufferMode)
+{
+  if (aInvalidRegion.IsEmpty())
     return nullptr;
+
+  if (!mWindowSurface) {
+    mWindowSurface = CreateWindowSurface();
+    if (!mWindowSurface)
+      return nullptr;
   }
 
-  RefPtr<DrawTarget> dt;
-
+  *aBufferMode = BufferMode::BUFFER_NONE;
+  RefPtr<DrawTarget> dt = nullptr;
+  if (!(dt = mWindowSurface->Lock(aInvalidRegion))) {
 #ifdef MOZ_X11
-  bool useXRender = false;
-#ifdef MOZ_WIDGET_GTK
-  useXRender = gfxPlatformGtk::GetPlatform()->UseXRender();
-#endif
-
-  if (useXRender) {
-    LayoutDeviceIntRect bounds = aRegion.GetBounds();
-    LayoutDeviceIntSize size(bounds.XMost(), bounds.YMost());
-    RefPtr<gfxXlibSurface> surf = new gfxXlibSurface(mXDisplay, mXWindow, mXVisual, size.ToUnknownSize());
-    if (!surf->CairoStatus()) {
-      dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf.get(), surf->GetSize());
-      *aBufferMode = BufferMode::BUFFERED;
-    }
-  }
-
-#  ifdef MOZ_HAVE_SHMIMAGE
-  if (!dt && mIsX11Display && nsShmImage::UseShm()) {
-    mBackShmImage.swap(mFrontShmImage);
-    if (!mBackShmImage) {
-      mBackShmImage = new nsShmImage(mXDisplay, mXWindow, mXVisual, mXDepth);
-    }
-    dt = mBackShmImage->CreateDrawTarget(aRegion);
-    *aBufferMode = BufferMode::BUFFER_NONE;
-    if (!dt) {
-      mBackShmImage = nullptr;
-    }
-  }
-#  endif  // MOZ_HAVE_SHMIMAGE
+    if (mIsX11Display) {
+      gfxWarningOnce() << "Failed to lock WindowSurface, falling back to XPutImage backend.";
+      mWindowSurface = MakeUnique<WindowSurfaceX11Image>(mXDisplay, mXWindow, mXVisual, mXDepth);
+    }
 #endif // MOZ_X11
-
-  // If MIT-SHM and XRender are unavailable, buffer to an image surface.
-  if (!dt) {
-    IntRect bounds = aRegion.GetBounds().ToUnknownRect();
-    IntSize size(bounds.XMost(), bounds.YMost());
-
-    // Recreate the fallback surface if there is unsufficient space to render.
-    if (!mFallbackSurface ||
-        cairo_image_surface_get_width(mFallbackSurface) < size.width ||
-        cairo_image_surface_get_height(mFallbackSurface) < size.height)
-    {
-      if (mFallbackSurface)
-        cairo_surface_destroy(mFallbackSurface);
-
-      GdkScreen* screen = gdk_screen_get_default();
-      bool argb = gdk_window_get_visual(mGdkWindow) ==
-                  gdk_screen_get_rgba_visual(screen);
-      cairo_format_t cairo_format = argb ? CAIRO_FORMAT_ARGB32
-                                         : CAIRO_FORMAT_RGB24;
-      mFallbackSurface = cairo_image_surface_create(cairo_format,
-                                                    bounds.XMost(),
-                                                    bounds.YMost());
-
-      // Set the appropriate device scale so that our surface can be used
-      // as a source without transforming to the GDK window's scale.
-      static auto sCairoSurfaceSetDeviceScale =
-          (void (*)(cairo_surface_t*, double, double))
-          dlsym(RTLD_DEFAULT, "cairo_surface_set_device_scale");
-      if (sCairoSurfaceSetDeviceScale) {
-        gint scale = GdkScaleFactor();
-        sCairoSurfaceSetDeviceScale(mFallbackSurface, scale, scale);
-      }
-
-      cairo_surface_flush(mFallbackSurface);
-    }
-
-    SurfaceFormat format = CairoFormatToGfxFormat(
-            cairo_image_surface_get_format(mFallbackSurface));
-    dt = gfxPlatform::GetPlatform()->CreateDrawTargetForData(
-            cairo_image_surface_get_data(mFallbackSurface)
-            + bounds.x * BytesPerPixel(format)
-            + bounds.y * cairo_image_surface_get_stride(mFallbackSurface),
-            bounds.Size(),
-            cairo_image_surface_get_stride(mFallbackSurface),
-            format);
-    *aBufferMode = BufferMode::BUFFER_NONE;
   }
-
   return dt.forget();
 }
 
-already_AddRefed<DrawTarget>
-nsWindow::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, BufferMode* aBufferMode)
-{
-  return GetDrawTarget(aInvalidRegion, aBufferMode);
-}
-
 void
 nsWindow::EndRemoteDrawingInRegion(DrawTarget* aDrawTarget,
                                    LayoutDeviceIntRegion& aInvalidRegion)
 {
-#ifdef MOZ_X11
-#  ifdef MOZ_HAVE_SHMIMAGE
-  if (!mGdkWindow) {
-    return;
-  }
-
-  if (mBackShmImage) {
-    mBackShmImage->Put(aInvalidRegion);
-  }
-#  endif // MOZ_HAVE_SHMIMAGE
-#endif // MOZ_X11
-
-  if (mFallbackSurface) {
-    cairo_t* cr = gdk_cairo_create(mGdkWindow);
-    cairo_surface_mark_dirty(mFallbackSurface);
-    cairo_set_source_surface(cr, mFallbackSurface, 0, 0);
-
-    for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
-      // Transform our path into the window's coordinate system.
-      const IntRectTyped<LayoutDevicePixel> & r = iter.Get();
-      cairo_rectangle(cr, DevicePixelsToGdkCoordRoundDown(r.x),
-                          DevicePixelsToGdkCoordRoundDown(r.y),
-                          DevicePixelsToGdkCoordRoundUp(r.width),
-                          DevicePixelsToGdkCoordRoundUp(r.height));
-    }
-    cairo_fill(cr);
-    cairo_destroy(cr);
-  }
+  if (mWindowSurface)
+    mWindowSurface->Commit(aInvalidRegion);
 }
 
 // Code shared begin BeginMoveDrag and BeginResizeDrag
 bool
 nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent,
                       GdkWindow** aWindow, gint* aButton,
                       gint* aRootX, gint* aRootY)
 {
@@ -7114,8 +7013,44 @@ nsWindow::SynthesizeNativeTouchPoint(uin
 }
 #endif
 
 int32_t
 nsWindow::RoundsWidgetCoordinatesTo()
 {
     return GdkScaleFactor();
 }
+
+UniquePtr<WindowSurface>
+nsWindow::CreateWindowSurface()
+{
+  if (!mGdkWindow)
+    return nullptr;
+
+  // TODO: Add path for Wayland. We can't use gdk_cairo_create as it's not
+  //       threadsafe.
+  if (!mIsX11Display)
+    return nullptr;
+
+#ifdef MOZ_X11
+  // Blit to the window with the following priority:
+  // 1. XRender (iff XRender is enabled)
+  // 2. MIT-SHM
+  // 3. XPutImage
+
+#ifdef MOZ_WIDGET_GTK
+  if (gfxPlatformGtk::GetPlatform()->UseXRender()) {
+    LOGDRAW(("Drawing to nsWindow %p using XRender\n", (void*)this));
+    return MakeUnique<WindowSurfaceXRender>(mXDisplay, mXWindow, mXVisual, mXDepth);
+  }
+#endif // MOZ_WIDGET_GTK
+
+#ifdef MOZ_HAVE_SHMIMAGE
+  if (nsShmImage::UseShm()) {
+    LOGDRAW(("Drawing to nsWindow %p using MIT-SHM\n", (void*)this));
+    return MakeUnique<WindowSurfaceX11SHM>(mXDisplay, mXWindow, mXVisual, mXDepth);
+  }
+#endif // MOZ_HAVE_SHMIMAGE
+
+  LOGDRAW(("Drawing to nsWindow %p using XPutImage\n", (void*)this));
+  return MakeUnique<WindowSurfaceX11Image>(mXDisplay, mXWindow, mXVisual, mXDepth);
+#endif // MOZ_X11
+}
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -19,17 +19,17 @@
 #include "nsBaseWidget.h"
 #include <gdk/gdk.h>
 #include <gtk/gtk.h>
 
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 #endif /* MOZ_X11 */
 
-#include "nsShmImage.h"
+#include "mozilla/widget/WindowSurface.h"
 
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/Accessible.h"
 #endif
 #include "mozilla/EventForwards.h"
 #include "mozilla/TouchEvents.h"
 
 #include "IMContextWrapper.h"
@@ -312,19 +312,16 @@ public:
     void               ClearTransparencyBitmap();
 
    virtual void        SetTransparencyMode(nsTransparencyMode aMode) override;
    virtual nsTransparencyMode GetTransparencyMode() override;
    virtual nsresult    ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override;
    nsresult            UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
                                                             uint8_t* aAlphas, int32_t aStride);
 
-    already_AddRefed<mozilla::gfx::DrawTarget> GetDrawTarget(const LayoutDeviceIntRegion& aRegion,
-                                                             mozilla::layers::BufferMode* aBufferMode);
-
 #if (MOZ_WIDGET_GTK == 2)
     static already_AddRefed<DrawTarget> GetDrawTargetForGdkDrawable(GdkDrawable* aDrawable,
                                                                     const mozilla::gfx::IntSize& aSize);
 #endif
     NS_IMETHOD         ReparentNativeWidget(nsIWidget* aNewParent) override;
 
     virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
                                                 uint32_t aNativeMessage,
@@ -453,32 +450,25 @@ private:
 #if GTK_CHECK_VERSION(3,4,0)
     // This field omits duplicate scroll events caused by GNOME bug 726878.
     guint32             mLastScrollEventTime;
 
     // for touch event handling
     nsRefPtrHashtable<nsPtrHashKey<GdkEventSequence>, mozilla::dom::Touch> mTouches;
 #endif
 
+    mozilla::UniquePtr<mozilla::widget::WindowSurface> mWindowSurface;
+
 #ifdef MOZ_X11
     Display*            mXDisplay;
     Window              mXWindow;
     Visual*             mXVisual;
     int                 mXDepth;
 #endif
 
-#ifdef MOZ_HAVE_SHMIMAGE
-    // If we're using xshm rendering
-    RefPtr<nsShmImage>  mFrontShmImage;
-    RefPtr<nsShmImage>  mBackShmImage;
-#endif
-
-    // A fallback image surface when a SHM surface is unavailable.
-    cairo_surface_t* mFallbackSurface;
-
     // Upper bound on pending ConfigureNotify events to be dispatched to the
     // window. See bug 1225044.
     int mPendingConfigures;
 
 #ifdef ACCESSIBILITY
     RefPtr<mozilla::a11y::Accessible> mRootAccessible;
 
     /**
@@ -557,16 +547,18 @@ private:
     virtual LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                                           LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                                           LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT) override;
 
     void CleanLayerManagerRecursive();
 
     virtual int32_t RoundsWidgetCoordinatesTo() override;
 
+    mozilla::UniquePtr<mozilla::widget::WindowSurface> CreateWindowSurface();
+
     /**
      * |mIMContext| takes all IME related stuff.
      *
      * This is owned by the top-level nsWindow or the topmost child
      * nsWindow embedded in a non-Gecko widget.
      *
      * The instance is created when the top level widget is created.  And when
      * the widget is destroyed, it's released.  All child windows refer its
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -128,16 +128,17 @@ EXPORTS.mozilla += [
 ]
 
 EXPORTS.mozilla.widget += [
     'CompositorWidget.h',
     'IMEData.h',
     'InProcessCompositorWidget.h',
     'PuppetBidiKeyboard.h',
     'WidgetMessageUtils.h',
+    'WindowSurface.h'
 ]
 
 UNIFIED_SOURCES += [
     'CompositorWidget.cpp',
     'ContentCache.cpp',
     'GfxDriverInfo.cpp',
     'GfxInfoBase.cpp',
     'GfxInfoCollector.cpp',
@@ -198,17 +199,18 @@ if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
 EXPORTS.ipc = ['nsGUIEventIPC.h']
 
 if CONFIG['MOZ_X11']:
     DIRS += ['x11']
     UNIFIED_SOURCES += [
         'GfxInfoX11.cpp'
     ]
     SOURCES += [
-        'nsShmImage.cpp'
+        'nsShmImage.cpp',
+        'WindowSurfaceX11SHM.cpp',
     ]
 
 if toolkit in ('cocoa', 'windows'):
     UNIFIED_SOURCES += [
         'nsBaseClipboard.cpp',
     ]
 
 if toolkit in {'gtk2', 'gtk3', 'cocoa', 'windows',