Bug 1286649 - Port Xlib code in nsShmImage to use XCB. r=lsalzman
authorAndrew Comminos <andrew@comminos.com>
Wed, 13 Jul 2016 16:28:28 -0400
changeset 305067 803d0028289a74df1be4220a61dd88802a3563a9
parent 305066 328df07cbfc206d3be093e422f06a6b21c1f1c53
child 305068 20a6fd076505809220e031dd594e03029459a664
push id30453
push usercbook@mozilla.com
push dateSun, 17 Jul 2016 07:16:01 +0000
treeherdermozilla-central@9ee862a030f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1286649
milestone50.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 1286649 - Port Xlib code in nsShmImage to use XCB. r=lsalzman MozReview-Commit-ID: DUIv1RQBWW4
widget/nsShmImage.cpp
widget/nsShmImage.h
--- a/widget/nsShmImage.cpp
+++ b/widget/nsShmImage.cpp
@@ -20,154 +20,139 @@
 
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 
 nsShmImage::nsShmImage(Display* aDisplay,
                        Drawable aWindow,
                        Visual* aVisual,
                        unsigned int aDepth)
-  : mImage(nullptr)
-  , mDisplay(aDisplay)
-  , mWindow(aWindow)
+  : mWindow(aWindow)
   , mVisual(aVisual)
   , mDepth(aDepth)
   , mFormat(mozilla::gfx::SurfaceFormat::UNKNOWN)
-  , mPixmap(None)
-  , mGC(nullptr)
-  , mRequest(0)
-  , mPreviousRequestProcessed(0)
+  , mSize(0, 0)
+  , mPixmap(XCB_NONE)
+  , mGC(XCB_NONE)
+  , mShmSeg(XCB_NONE)
+  , mShmId(-1)
+  , mShmAddr(nullptr)
 {
-  memset(&mInfo, -1, sizeof(mInfo));
+  mConnection = XGetXCBConnection(aDisplay);
+  mozilla::PodZero(&mLastRequest);
 }
 
 nsShmImage::~nsShmImage()
 {
   DestroyImage();
 }
 
 // If XShm isn't available to our client, we'll try XShm once, fail,
 // set this to false and then never try again.
 static bool gShmAvailable = true;
 bool nsShmImage::UseShm()
 {
   return gShmAvailable;
 }
 
-static int gShmError = 0;
-
-static int
-TrapShmError(Display* aDisplay, XErrorEvent* aEvent)
-{
-  // store the error code and ignore the error
-  gShmError = aEvent->error_code;
-  return 0;
-}
-
 bool
 nsShmImage::CreateShmSegment()
 {
-  if (!mImage) {
-    return false;
-  }
+  size_t size = SharedMemory::PageAlignedSize(BytesPerPixel(mFormat) *
+                                              mSize.width * mSize.height);
 
-  size_t size = SharedMemory::PageAlignedSize(mImage->bytes_per_line * mImage->height);
-
-  mInfo.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600);
-  if (mInfo.shmid == -1) {
+  mShmId = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600);
+  if (mShmId == -1) {
     return false;
   }
-
-  mInfo.shmaddr = (char *)shmat(mInfo.shmid, nullptr, 0);
+  mShmAddr = (uint8_t*) shmat(mShmId, nullptr, 0);
+  mShmSeg = xcb_generate_id(mConnection);
 
   // Mark the handle removed so that it will destroy the segment when unmapped.
-  shmctl(mInfo.shmid, IPC_RMID, nullptr);
+  shmctl(mShmId, IPC_RMID, nullptr);
 
-  if (mInfo.shmaddr == (void *)-1) {
+  if (mShmAddr == (void *)-1) {
     // Since mapping failed, the segment is already destroyed.
-    mInfo.shmid = -1;
+    mShmId = -1;
 
     nsPrintfCString warning("shmat(): %s (%d)\n", strerror(errno), errno);
     NS_WARNING(warning.get());
     return false;
   }
 
 #ifdef DEBUG
   struct shmid_ds info;
-  if (shmctl(mInfo.shmid, IPC_STAT, &info) < 0) {
+  if (shmctl(mShmId, IPC_STAT, &info) < 0) {
     return false;
   }
 
   MOZ_ASSERT(size <= info.shm_segsz,
              "Segment doesn't have enough space!");
 #endif
 
-  mInfo.readOnly = False;
-
-  mImage->data = mInfo.shmaddr;
-
   return true;
 }
 
 void
 nsShmImage::DestroyShmSegment()
 {
-  if (mInfo.shmid != -1) {
-    shmdt(mInfo.shmaddr);
-    mInfo.shmid = -1;
+  if (mShmId != -1) {
+    shmdt(mShmAddr);
+    mShmId = -1;
   }
 }
 
 static bool gShmInitialized = false;
-static int gShmEvent = -1;
-static Atom gShmPixmapAtom = None;
+static bool gUseShmPixmaps = false;
 
 bool
 nsShmImage::InitExtension()
 {
   if (gShmInitialized) {
     return gShmAvailable;
   }
 
   gShmInitialized = true;
 
-  if (!XShmQueryExtension(mDisplay)) {
+  const xcb_query_extension_reply_t* extReply;
+  extReply = xcb_get_extension_data(mConnection, &xcb_shm_id);
+  if (!extReply || !extReply->present) {
     gShmAvailable = false;
     return false;
   }
 
-  int major, minor;
-  Bool pixmaps;
-  if (!XShmQueryVersion(mDisplay, &major, &minor, &pixmaps)) {
+  xcb_shm_query_version_reply_t* shmReply = xcb_shm_query_version_reply(
+      mConnection,
+      xcb_shm_query_version(mConnection),
+      nullptr);
+
+  if (!shmReply) {
     gShmAvailable = false;
     return false;
   }
 
-  gShmEvent = XShmGetEventBase(mDisplay);
-  if (gShmEvent < 0) {
-    gShmAvailable = false;
-    return false;
-  }
+  gUseShmPixmaps = shmReply->shared_pixmaps &&
+                   shmReply->pixmap_format == XCB_IMAGE_FORMAT_Z_PIXMAP;
 
-  if (pixmaps && XShmPixmapFormat(mDisplay) == ZPixmap) {
-    gShmPixmapAtom = XInternAtom(mDisplay, "_MOZ_SHM_PIXMAP", False);
-  }
+  free(shmReply);
 
   return true;
 }
 
 bool
 nsShmImage::CreateImage(const IntSize& aSize)
 {
-  MOZ_ASSERT(mDisplay && mVisual);
+  MOZ_ASSERT(mConnection && mVisual);
 
   if (!InitExtension()) {
     return false;
   }
 
+  mSize = aSize;
+
   BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
 
   mFormat = SurfaceFormat::UNKNOWN;
   switch (mDepth) {
   case 32:
     if (mVisual->red_mask == 0xff0000 &&
         mVisual->green_mask == 0xff00 &&
         mVisual->blue_mask == 0xff) {
@@ -195,183 +180,133 @@ nsShmImage::CreateImage(const IntSize& a
   }
 
   if (mFormat == SurfaceFormat::UNKNOWN) {
     NS_WARNING("Unsupported XShm Image format!");
     gShmAvailable = false;
     return false;
   }
 
-  mImage = XShmCreateImage(mDisplay, mVisual, mDepth,
-                           ZPixmap, nullptr,
-                           &mInfo,
-                           aSize.width, aSize.height);
-  if (!mImage || !CreateShmSegment()) {
+  if (!CreateShmSegment()) {
     DestroyImage();
     return false;
   }
 
-  gShmError = 0;
-  XErrorHandler previousHandler = XSetErrorHandler(TrapShmError);
-  Status attachOk = XShmAttach(mDisplay, &mInfo);
-  XSync(mDisplay, False);
-  XSetErrorHandler(previousHandler);
-  if (gShmError) {
-    attachOk = 0;
-  }
+  xcb_generic_error_t* error;
+  xcb_void_cookie_t cookie;
+
+  cookie = xcb_shm_attach_checked(mConnection, mShmSeg, mShmId, 0);
 
-  if (!attachOk) {
-    DestroyShmSegment();
+  if ((error = xcb_request_check(mConnection, cookie))) {
+    NS_WARNING("Failed to attach MIT-SHM segment.");
     DestroyImage();
-
-    // Assume XShm isn't available, and don't attempt to use it
-    // again.
     gShmAvailable = false;
+    free(error);
     return false;
   }
 
-  if (gShmPixmapAtom != None) {
-    mPixmap = XShmCreatePixmap(mDisplay, mWindow,
-                               mImage->data, &mInfo,
-                               mImage->width, mImage->height, mImage->depth);
+  if (gUseShmPixmaps) {
+    mPixmap = xcb_generate_id(mConnection);
+    cookie = xcb_shm_create_pixmap_checked(mConnection, mPixmap, mWindow,
+                                           aSize.width, aSize.height, mDepth,
+                                           mShmSeg, 0);
+
+    if ((error = xcb_request_check(mConnection, cookie))) {
+      // Disable shared pixmaps permanently if creation failed.
+      mPixmap = XCB_NONE;
+      gUseShmPixmaps = false;
+      free(error);
+    }
   }
 
   return true;
 }
 
 void
 nsShmImage::DestroyImage()
 {
-  if (mImage) {
-    mozilla::FinishX(mDisplay);
-  }
   if (mGC) {
-    XFreeGC(mDisplay, mGC);
-    mGC = nullptr;
+    xcb_free_gc(mConnection, mGC);
+    mGC = XCB_NONE;
   }
-  if (mPixmap != None) {
-    XFreePixmap(mDisplay, mPixmap);
-    mPixmap = None;
+  if (mPixmap != XCB_NONE) {
+    xcb_free_pixmap(mConnection, mPixmap);
+    mPixmap = XCB_NONE;
   }
-  if (mImage) {
-    if (mInfo.shmid != -1) {
-      XShmDetach(mDisplay, &mInfo);
-    }
-    XDestroyImage(mImage);
-    mImage = nullptr;
+  if (mShmSeg != XCB_NONE) {
+    xcb_shm_detach_checked(mConnection, mShmSeg);
+    mShmSeg = XCB_NONE;
   }
   DestroyShmSegment();
 }
 
 already_AddRefed<DrawTarget>
 nsShmImage::CreateDrawTarget(const mozilla::LayoutDeviceIntRegion& aRegion)
 {
-  // Wait for any in-flight XShmPutImage requests to complete.
-  WaitForRequest();
+  // Wait for any in-flight requests to complete.
+  // Typically X clients would wait for a XShmCompletionEvent to be received,
+  // but this works as it's sent immediately after the request is processed.
+  xcb_generic_error_t* error;
+  if (mLastRequest.sequence != XCB_NONE &&
+      (error = xcb_request_check(mConnection, mLastRequest)))
+  {
+    gShmAvailable = false;
+    free(error);
+    return nullptr;
+  }
 
   // Due to bug 1205045, we must avoid making GTK calls off the main thread to query window size.
   // Instead we just track the largest offset within the image we are drawing to and grow the image
   // to accomodate it. Since usually the entire window is invalidated on the first paint to it,
   // this should grow the image to the necessary size quickly without many intermediate reallocations.
   IntRect bounds = aRegion.GetBounds().ToUnknownRect();
   IntSize size(bounds.XMost(), bounds.YMost());
-  if (!mImage || size.width > mImage->width || size.height > mImage->height) {
+  if (size.width > mSize.width || size.height > mSize.height) {
     DestroyImage();
     if (!CreateImage(size)) {
       return nullptr;
     }
   }
 
   return gfxPlatform::GetPlatform()->CreateDrawTargetForData(
-    reinterpret_cast<unsigned char*>(mImage->data)
-      + bounds.y * mImage->bytes_per_line + bounds.x * BytesPerPixel(mFormat),
+    reinterpret_cast<unsigned char*>(mShmAddr)
+      + BytesPerPixel(mFormat) * (bounds.y * mSize.width + bounds.x),
     bounds.Size(),
-    mImage->bytes_per_line,
+    BytesPerPixel(mFormat) * mSize.width,
     mFormat);
 }
 
-bool
-nsShmImage::RequestWasProcessed()
-{
-  // Check for either that the sequence number has advanced to the request,
-  // or that it has advanced so far around that it appears to be before the
-  // last request processed at the time the request was initially sent.
-  unsigned long processed = LastKnownRequestProcessed(mDisplay);
-  return long(processed - mRequest) >= 0 ||
-         long(processed - mPreviousRequestProcessed) < 0;
-}
-
-Bool
-nsShmImage::FindEvent(Display* aDisplay, XEvent* aEvent, XPointer aArg)
-{
-  nsShmImage* image = (nsShmImage*)aArg;
-  return image->RequestWasProcessed();
-}
-
-void
-nsShmImage::WaitForRequest()
-{
-  if (!mRequest) {
-    return;
-  }
-
-  if (!RequestWasProcessed()) {
-    XEvent event;
-    XPeekIfEvent(mDisplay, &event, FindEvent, (XPointer)this);
-  }
-
-  mRequest = 0;
-}
-
-void
-nsShmImage::SendEvent()
-{
-  XClientMessageEvent event;
-  memset(&event, 0, sizeof(event));
-
-  event.type = ClientMessage;
-  event.window = mWindow;
-  event.message_type = gShmPixmapAtom;
-  event.format = 32;
-  event.data.l[0] = (long)mInfo.shmseg;
-
-  XSendEvent(mDisplay, mWindow, False, 0, (XEvent*)&event);
-}
-
 void
 nsShmImage::Put(const mozilla::LayoutDeviceIntRegion& aRegion)
 {
-  if (!mImage) {
-    return;
-  }
-
-  AutoTArray<XRectangle, 32> xrects;
+  AutoTArray<xcb_rectangle_t, 32> xrects;
   xrects.SetCapacity(aRegion.GetNumRects());
 
   for (auto iter = aRegion.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 };
+    xcb_rectangle_t 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) {
-      return;
-    }
-  }
-  XSetClipRectangles(mDisplay, mGC, 0, 0, xrects.Elements(), xrects.Length(), YXBanded);
-
-  mRequest = XNextRequest(mDisplay);
-  if (mPixmap != None) {
-    XCopyArea(mDisplay, mPixmap, mWindow, mGC, 0, 0, mImage->width, mImage->height, 0, 0);
-    // Send a synthetic event to ensure WaitForRequest can safely poll it.
-    SendEvent();
-  } else {
-    // The send_event parameter is True here for WaitForRequest polling.
-    XShmPutImage(mDisplay, mWindow, mGC, mImage, 0, 0, 0, 0, mImage->width, mImage->height, True);
+    mGC = xcb_generate_id(mConnection);
+    xcb_create_gc(mConnection, mGC, mWindow, 0, nullptr);
   }
 
-  mPreviousRequestProcessed = LastKnownRequestProcessed(mDisplay);
-  XFlush(mDisplay);
+  xcb_set_clip_rectangles(mConnection, XCB_CLIP_ORDERING_YX_BANDED, mGC, 0, 0,
+                          xrects.Length(), xrects.Elements());
+
+  if (mPixmap != XCB_NONE) {
+    mLastRequest = xcb_copy_area_checked(mConnection, mPixmap, mWindow, mGC,
+                                         0, 0, 0, 0, mSize.width, mSize.height);
+  } else {
+    mLastRequest = xcb_shm_put_image_checked(mConnection, mWindow, mGC,
+                                             mSize.width, mSize.height,
+                                             0, 0, mSize.width, mSize.height,
+                                             0, 0, mDepth,
+                                             XCB_IMAGE_FORMAT_Z_PIXMAP, 0,
+                                             mShmSeg, 0);
+  }
+
+  xcb_flush(mConnection);
 }
 
 #endif  // MOZ_HAVE_SHMIMAGE
--- a/widget/nsShmImage.h
+++ b/widget/nsShmImage.h
@@ -12,18 +12,18 @@
 #endif
 
 #ifdef MOZ_HAVE_SHMIMAGE
 
 #include "mozilla/gfx/2D.h"
 #include "nsIWidget.h"
 #include "Units.h"
 
-#include <X11/Xlib.h>
-#include <X11/extensions/XShm.h>
+#include <X11/Xlib-xcb.h>
+#include <xcb/shm.h>
 
 class nsShmImage {
   // bug 1168843, compositor thread may create shared memory instances that are destroyed by main thread on shutdown, so this must use thread-safe RC to avoid hitting assertion
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsShmImage)
 
 public:
   static bool UseShm();
 
@@ -43,29 +43,28 @@ private:
   bool InitExtension();
 
   bool CreateShmSegment();
   void DestroyShmSegment();
 
   bool CreateImage(const mozilla::gfx::IntSize& aSize);
   void DestroyImage();
 
-  static Bool FindEvent(Display* aDisplay, XEvent* aEvent, XPointer aArg);
-  bool RequestWasProcessed();
-  void WaitForRequest();
-  void SendEvent();
-
-  XImage*                      mImage;
-  Display*                     mDisplay;
+  xcb_connection_t*            mConnection;
   Window                       mWindow;
   Visual*                      mVisual;
   unsigned int                 mDepth;
-  XShmSegmentInfo              mInfo;
+
   mozilla::gfx::SurfaceFormat  mFormat;
-  Pixmap                       mPixmap;
-  GC                           mGC;
-  unsigned long                mRequest;
-  unsigned long                mPreviousRequestProcessed;
+  mozilla::gfx::IntSize        mSize;
+
+  xcb_pixmap_t                 mPixmap;
+  xcb_gcontext_t               mGC;
+  xcb_void_cookie_t            mLastRequest;
+
+  xcb_shm_seg_t                mShmSeg;
+  int                          mShmId;
+  uint8_t*                     mShmAddr;
 };
 
 #endif // MOZ_HAVE_SHMIMAGE
 
 #endif