Bug 994081 - [3/3] Optimize surfaces on Cairo X11, r=Bas f=karlt
authorMichael Wu <mwu@mozilla.com>
Thu, 22 May 2014 20:06:16 -0400
changeset 207548 79624417d24728cd9b64a4196712530825ff2390
parent 207547 013456bbf74d5517f53ca1f04206d758ddeae0be
child 207549 303d1ac47d5c2b153f27318ed2d3713b34db6d3c
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBas
bugs994081
milestone32.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 994081 - [3/3] Optimize surfaces on Cairo X11, r=Bas f=karlt
gfx/2d/DrawTargetCairo.cpp
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -6,41 +6,46 @@
 #include "DrawTargetCairo.h"
 
 #include "SourceSurfaceCairo.h"
 #include "PathCairo.h"
 #include "HelpersCairo.h"
 #include "ScaledFontBase.h"
 #include "BorrowedContext.h"
 #include "FilterNodeSoftware.h"
+#include "mozilla/Scoped.h"
 
 #include "cairo.h"
 #include "cairo-tee.h"
 #include <string.h>
 
 #include "Blur.h"
 #include "Logging.h"
 #include "Tools.h"
 
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
 #include "cairo-quartz.h"
 #include <ApplicationServices/ApplicationServices.h>
 #endif
 
 #ifdef CAIRO_HAS_XLIB_SURFACE
 #include "cairo-xlib.h"
+#include "cairo-xlib-xrender.h"
 #endif
 
 #ifdef CAIRO_HAS_WIN32_SURFACE
 #include "cairo-win32.h"
 #endif
 
 #include <algorithm>
 
 namespace mozilla {
+
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCairoSurface, cairo_surface_t, cairo_surface_destroy);
+
 namespace gfx {
 
 cairo_surface_t *DrawTargetCairo::mDummySurface;
 
 namespace {
 
 // An RAII class to prepare to draw a context and optional path. Saves and
 // restores the context on construction/destruction.
@@ -1052,19 +1057,116 @@ DrawTargetCairo::CreateSourceSurfaceFrom
   CopyDataToCairoSurface(surf, aData, aSize, aStride, BytesPerPixel(aFormat));
 
   RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
   cairo_surface_destroy(surf);
 
   return source_surf;
 }
 
+#ifdef CAIRO_HAS_XLIB_SURFACE
+static cairo_user_data_key_t gDestroyPixmapKey;
+
+struct DestroyPixmapClosure {
+  DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
+  ~DestroyPixmapClosure() {
+    XFreePixmap(DisplayOfScreen(mScreen), mPixmap);
+  }
+  Drawable mPixmap;
+  Screen *mScreen;
+};
+
+static void
+DestroyPixmap(void *data)
+{
+  delete static_cast<DestroyPixmapClosure*>(data);
+}
+#endif
+
 TemporaryRef<SourceSurface>
 DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
 {
+#ifdef CAIRO_HAS_XLIB_SURFACE
+  if (cairo_surface_get_type(mSurface) != CAIRO_SURFACE_TYPE_XLIB) {
+    return aSurface;
+  }
+
+  IntSize size = aSurface->GetSize();
+  if (!size.width || !size.height) {
+    return aSurface;
+  }
+
+  // Although the dimension parameters in the xCreatePixmapReq wire protocol are
+  // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
+  // either dimension cannot be represented by a 16-bit *signed* integer.
+  #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
+
+  if (size.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+      size.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+    return aSurface;
+  }
+
+  SurfaceFormat format = aSurface->GetFormat();
+  Screen *screen = cairo_xlib_surface_get_screen(mSurface);
+  Display *dpy = DisplayOfScreen(screen);
+  XRenderPictFormat* xrenderFormat = nullptr;
+  switch (format) {
+  case SurfaceFormat::B8G8R8A8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
+    break;
+  case SurfaceFormat::B8G8R8X8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardRGB24);
+    break;
+  case SurfaceFormat::A8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardA8);
+    break;
+  default:
+    return aSurface;
+  }
+  if (!xrenderFormat) {
+    return aSurface;
+  }
+
+  Drawable pixmap = XCreatePixmap(dpy, RootWindowOfScreen(screen),
+                                  size.width, size.height,
+                                  xrenderFormat->depth);
+  if (!pixmap) {
+    return aSurface;
+  }
+
+  ScopedDeletePtr<DestroyPixmapClosure> closure(
+    new DestroyPixmapClosure(pixmap, screen));
+
+  ScopedCairoSurface csurf(
+    cairo_xlib_surface_create_with_xrender_format(dpy, pixmap,
+                                                  screen, xrenderFormat,
+                                                  size.width, size.height));
+  if (!csurf || cairo_surface_status(csurf)) {
+    return aSurface;
+  }
+
+  cairo_surface_set_user_data(csurf, &gDestroyPixmapKey,
+                              closure.forget(), DestroyPixmap);
+
+  RefPtr<DrawTargetCairo> dt = new DrawTargetCairo();
+  if (!dt->Init(csurf, size, &format)) {
+    return aSurface;
+  }
+
+  dt->CopySurface(aSurface,
+                  IntRect(0, 0, size.width, size.height),
+                  IntPoint(0, 0));
+  dt->Flush();
+
+  RefPtr<SourceSurface> surf =
+    new SourceSurfaceCairo(csurf, size, format);
+
+  return surf;
+#endif
+
   return aSurface;
 }
 
 TemporaryRef<SourceSurface>
 DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
 {
   if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) {
     if (aSurface.mSize.width <= 0 ||