Bug 1319554 - allow WindowSurfaceX11Image to render to any visual format. r=rhunt
authorLee Salzman <lsalzman@mozilla.com>
Thu, 01 Dec 2016 12:15:58 -0500
changeset 324961 47e2650093d575c5042b43feb0575ef9678352b1
parent 324960 87e3edcefc5bc35b707dc9df00190fbf203b5c8d
child 324962 083ffc01652a13e80d12288074fd5dbc39621e93
push id84561
push userlsalzman@mozilla.com
push dateThu, 01 Dec 2016 17:16:16 +0000
treeherdermozilla-inbound@47e2650093d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhunt
bugs1319554
milestone53.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 1319554 - allow WindowSurfaceX11Image to render to any visual format. r=rhunt MozReview-Commit-ID: JW4REAg1AKn
widget/WindowSurface.h
widget/gtk/WindowSurfaceProvider.cpp
widget/gtk/WindowSurfaceX11.cpp
widget/gtk/WindowSurfaceX11.h
widget/gtk/WindowSurfaceX11Image.cpp
widget/gtk/WindowSurfaceX11Image.h
widget/gtk/WindowSurfaceXRender.cpp
widget/gtk/WindowSurfaceXRender.h
--- a/widget/WindowSurface.h
+++ b/widget/WindowSurface.h
@@ -21,14 +21,17 @@ public:
   // 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;
+
+  // Whether the window surface represents a fallback method.
+  virtual bool IsFallback() const { return false; }
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // _MOZILLA_WIDGET_WINDOW_SURFACE_H
--- a/widget/gtk/WindowSurfaceProvider.cpp
+++ b/widget/gtk/WindowSurfaceProvider.cpp
@@ -88,19 +88,21 @@ WindowSurfaceProvider::StartRemoteDrawin
   if (!mWindowSurface) {
     mWindowSurface = CreateWindowSurface();
     if (!mWindowSurface)
       return nullptr;
   }
 
   *aBufferMode = BufferMode::BUFFER_NONE;
   RefPtr<DrawTarget> dt = nullptr;
-  if (!(dt = mWindowSurface->Lock(aInvalidRegion))) {
+  if (!(dt = mWindowSurface->Lock(aInvalidRegion)) &&
+      !mWindowSurface->IsFallback()) {
     gfxWarningOnce() << "Failed to lock WindowSurface, falling back to XPutImage backend.";
     mWindowSurface = MakeUnique<WindowSurfaceX11Image>(mXDisplay, mXWindow, mXVisual, mXDepth);
+    dt = mWindowSurface->Lock(aInvalidRegion);
   }
   return dt.forget();
 }
 
 void
 WindowSurfaceProvider::EndRemoteDrawingInRegion(gfx::DrawTarget* aDrawTarget,
                                               LayoutDeviceIntRegion& aInvalidRegion)
 {
--- a/widget/gtk/WindowSurfaceX11.cpp
+++ b/widget/gtk/WindowSurfaceX11.cpp
@@ -15,50 +15,17 @@ WindowSurfaceX11::WindowSurfaceX11(Displ
                                    Window aWindow,
                                    Visual* aVisual,
                                    unsigned int aDepth)
   : mDisplay(aDisplay)
   , mWindow(aWindow)
   , mVisual(aVisual)
   , mDepth(aDepth)
   , mFormat(GetVisualFormat(aVisual, aDepth))
-  , mGC(X11None)
 {
-  MOZ_ASSERT(mFormat != gfx::SurfaceFormat::UNKNOWN,
-             "Could not find SurfaceFormat for visual!");
-}
-
-WindowSurfaceX11::~WindowSurfaceX11()
-{
-  if (mGC != X11None)
-    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:
--- a/widget/gtk/WindowSurfaceX11.h
+++ b/widget/gtk/WindowSurfaceX11.h
@@ -16,36 +16,24 @@
 
 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) override = 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
--- a/widget/gtk/WindowSurfaceX11Image.cpp
+++ b/widget/gtk/WindowSurfaceX11Image.cpp
@@ -3,75 +3,117 @@
  * 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 "mozilla/gfx/gfxVars.h"
 #include "gfxPlatform.h"
+#include "gfx2DGlue.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>(size.width, gfx::BytesPerPixel(mFormat));
-    if (stride == 0) {
-        return nullptr;
-    }
-    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 (!mWindowSurface || mWindowSurface->CairoStatus() ||
+      !(size <= mWindowSurface->GetSize())) {
+    mWindowSurface = new gfxXlibSurface(mDisplay, mWindow, mVisual, size);
+  }
+  if (mWindowSurface->CairoStatus()) {
+    return nullptr;
   }
 
-  if (!mImage)
-    return nullptr;
+  if (!mImageSurface || mImageSurface->CairoStatus() ||
+      !(size <= mImageSurface->GetSize())) {
+    gfxImageFormat format = SurfaceFormatToImageFormat(mFormat);
+    if (format == gfx::SurfaceFormat::UNKNOWN) {
+      format = mDepth == 32 ?
+                 gfx::SurfaceFormat::A8R8G8B8_UINT32 :
+                 gfx::SurfaceFormat::X8R8G8B8_UINT32;
+    }
+
+    mImageSurface = new gfxImageSurface(size, format);
+    if (mImageSurface->CairoStatus()) {
+      return nullptr;
+    }
+  }
 
-  unsigned char* data = (unsigned char*) mImage->data;
-  return gfxPlatform::CreateDrawTargetForData(data,
-                                              size,
-                                              mImage->bytes_per_line,
-                                              mFormat);
+  gfxImageFormat format = mImageSurface->Format();
+  // Cairo prefers compositing to BGRX instead of BGRA where possible.
+  if (format == gfx::SurfaceFormat::X8R8G8B8_UINT32) {
+    gfx::BackendType backend = gfxVars::ContentBackend();
+    if (!gfx::Factory::DoesBackendSupportDataDrawtarget(backend)) {
+#ifdef USE_SKIA
+      backend = gfx::BackendType::SKIA;
+#else
+      backend = gfx::BackendType::CAIRO;
+#endif
+    }
+    if (backend != gfx::BackendType::CAIRO) {
+      format = gfx::SurfaceFormat::A8R8G8B8_UINT32;
+    }
+  }
+
+  return gfxPlatform::CreateDrawTargetForData(mImageSurface->Data(),
+                                              mImageSurface->GetSize(),
+                                              mImageSurface->Stride(),
+                                              ImageFormatToSurfaceFormat(format));
 }
 
 void
-WindowSurfaceX11Image::CommitToDrawable(Drawable aDest, GC aGC,
-                                        const LayoutDeviceIntRegion& aInvalidRegion)
+WindowSurfaceX11Image::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
 {
-  MOZ_ASSERT(mImage, "Attempted to commit invalid surface!");
+  RefPtr<gfx::DrawTarget> dt =
+    gfx::Factory::CreateDrawTargetForCairoSurface(mWindowSurface->CairoSurface(),
+                                                  mWindowSurface->GetSize());
+  RefPtr<gfx::SourceSurface> surf =
+    gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(),
+                                                     mImageSurface->GetSize(),
+                                                     mImageSurface->Format());
+  if (!dt || !surf) {
+    return;
+  }
 
   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);
+  gfx::Rect rect(0, 0, bounds.XMost(), bounds.YMost());
+  if (rect.IsEmpty()) {
+    return;
+  }
+
+  uint32_t numRects = aInvalidRegion.GetNumRects();
+  if (numRects != 1) {
+    AutoTArray<IntRect, 32> rects;
+    rects.SetCapacity(numRects);
+    for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+      rects.AppendElement(iter.Get().ToUnknownRect());
+    }
+    dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
+  }
+
+  dt->DrawSurface(surf, rect, rect);
+
+  if (numRects != 1) {
+    dt->PopClip();
+  }
 }
 
 }  // namespace widget
 }  // namespace mozilla
--- a/widget/gtk/WindowSurfaceX11Image.h
+++ b/widget/gtk/WindowSurfaceX11Image.h
@@ -5,31 +5,34 @@
  * 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"
+#include "gfxXlibSurface.h"
+#include "gfxImageSurface.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;
+  void Commit(const LayoutDeviceIntRegion& aInvalidRegion) override;
+  bool IsFallback() const override { return true; }
 
 private:
-  XImage* mImage;
+  RefPtr<gfxXlibSurface> mWindowSurface;
+  RefPtr<gfxImageSurface> mImageSurface;
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // MOZ_X11
 #endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_X11_IMAGE_H
--- a/widget/gtk/WindowSurfaceXRender.cpp
+++ b/widget/gtk/WindowSurfaceXRender.cpp
@@ -14,46 +14,70 @@ namespace mozilla {
 namespace widget {
 
 WindowSurfaceXRender::WindowSurfaceXRender(Display* aDisplay,
                                            Window aWindow,
                                            Visual* aVisual,
                                            unsigned int aDepth)
   : WindowSurfaceX11(aDisplay, aWindow, aVisual, aDepth)
   , mXlibSurface(nullptr)
+  , mGC(X11None)
 {
 }
 
+WindowSurfaceXRender::~WindowSurfaceXRender()
+{
+  if (mGC != X11None) {
+    XFreeGC(mDisplay, mGC);
+  }
+}
+
 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)
-  {
+  if (!mXlibSurface || mXlibSurface->CairoStatus() ||
+      !(size <= mXlibSurface->GetSize())) {
     mXlibSurface = gfxXlibSurface::Create(DefaultScreenOfDisplay(mDisplay),
                                           mVisual,
                                           size,
                                           mWindow);
   }
+  if (!mXlibSurface || mXlibSurface->CairoStatus()) {
+    return nullptr;
+  }
 
-  if (mXlibSurface && mXlibSurface->CairoStatus() == 0) {
-    return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mXlibSurface.get(), size);
-  }
-  return nullptr;
+  return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mXlibSurface, size);
 }
 
 void
-WindowSurfaceXRender::CommitToDrawable(Drawable aDest, GC aGC,
-                                       const LayoutDeviceIntRegion& aInvalidRegion)
+WindowSurfaceXRender::Commit(const LayoutDeviceIntRegion& aInvalidRegion)
 {
+  AutoTArray<XRectangle, 32> xrects;
+  xrects.SetCapacity(aInvalidRegion.GetNumRects());
+
+  for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+    const 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);
+
   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,
+  XCopyArea(mDisplay, mXlibSurface->XDrawable(), mWindow, mGC, bounds.x, bounds.y,
             size.width, size.height, bounds.x, bounds.y);
 }
 
 }  // namespace widget
 }  // namespace mozilla
--- a/widget/gtk/WindowSurfaceXRender.h
+++ b/widget/gtk/WindowSurfaceXRender.h
@@ -14,22 +14,23 @@
 
 namespace mozilla {
 namespace widget {
 
 class WindowSurfaceXRender : public WindowSurfaceX11 {
 public:
   WindowSurfaceXRender(Display* aDisplay, Window aWindow, Visual* aVisual,
                        unsigned int aDepth);
+  ~WindowSurfaceXRender();
 
   already_AddRefed<gfx::DrawTarget> Lock(const LayoutDeviceIntRegion& aRegion) override;
-  void CommitToDrawable(Drawable aDest, GC aGC,
-                        const LayoutDeviceIntRegion& aInvalidRegion) override;
+  void Commit(const LayoutDeviceIntRegion& aInvalidRegion) override;
 
 private:
   RefPtr<gfxXlibSurface> mXlibSurface;
+  GC mGC;
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif // MOZ_X11
 #endif // _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_XRENDER_H