Bug 1247935 - Part 2: use shared pixmaps with XShm for nsShmImage. r=jrmuizel
authorLee Salzman <lsalzman@mozilla.com>
Sat, 27 Feb 2016 23:16:57 -0500
changeset 285962 f37626eba5c683d42aaa510b55ad7e24f490063d
parent 285961 f1ada5d7a0922b62fecfe4b45290f2cddc49504d
child 285963 7d1baa75f591451a5df54c7f9ea1bc0fece43669
push id30036
push usercbook@mozilla.com
push dateMon, 29 Feb 2016 10:35:59 +0000
treeherdermozilla-central@9da51cb4974e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1247935
milestone47.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 1247935 - Part 2: use shared pixmaps with XShm for nsShmImage. r=jrmuizel
widget/gtk/nsWindow.h
widget/nsShmImage.cpp
widget/nsShmImage.h
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -445,17 +445,17 @@ private:
     guint32             mLastScrollEventTime;
 
     // for touch event handling
     nsRefPtrHashtable<nsPtrHashKey<GdkEventSequence>, mozilla::dom::Touch> mTouches;
 #endif
 
 #ifdef MOZ_X11
     Display*            mXDisplay;
-    Drawable            mXWindow;
+    Window              mXWindow;
     Visual*             mXVisual;
     int                 mXDepth;
 #endif
 
 #ifdef MOZ_HAVE_SHMIMAGE
     // If we're using xshm rendering
     RefPtr<nsShmImage>  mFrontShmImage;
     RefPtr<nsShmImage>  mBackShmImage;
--- a/widget/nsShmImage.cpp
+++ b/widget/nsShmImage.cpp
@@ -14,21 +14,43 @@
 #include "mozilla/ipc/SharedMemory.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 
 #include <errno.h>
 #include <string.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
-#include <X11/Xlibint.h>
 
 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)
+  , mVisual(aVisual)
+  , mDepth(aDepth)
+  , mFormat(mozilla::gfx::SurfaceFormat::UNKNOWN)
+  , mPixmap(None)
+  , mGC(nullptr)
+  , mRequest(0)
+  , mPreviousRequestProcessed(0)
+{
+  memset(&mInfo, -1, sizeof(mInfo));
+}
+
+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()
 {
 #ifdef MOZ_WIDGET_GTK
   return (gShmAvailable && !gfxPlatformGtk::GetPlatform()->UseXRender());
 #else
@@ -95,21 +117,63 @@ void
 nsShmImage::DestroyShmSegment()
 {
   if (mInfo.shmid != -1) {
     shmdt(mInfo.shmaddr);
     mInfo.shmid = -1;
   }
 }
 
+static bool gShmInitialized = false;
+static int gShmEvent = -1;
+static Atom gShmPixmapAtom = None;
+
+bool
+nsShmImage::InitExtension()
+{
+  if (gShmInitialized) {
+    return gShmAvailable;
+  }
+
+  gShmInitialized = true;
+
+  if (!XShmQueryExtension(mDisplay)) {
+    gShmAvailable = false;
+    return false;
+  }
+
+  int major, minor;
+  Bool pixmaps;
+  if (!XShmQueryVersion(mDisplay, &major, &minor, &pixmaps)) {
+    gShmAvailable = false;
+    return false;
+  }
+
+  gShmEvent = XShmGetEventBase(mDisplay);
+  if (gShmEvent < 0) {
+    gShmAvailable = false;
+    return false;
+  }
+
+  if (pixmaps && XShmPixmapFormat(mDisplay) == ZPixmap) {
+    gShmPixmapAtom = XInternAtom(mDisplay, "_MOZ_SHM_PIXMAP", 0);
+  }
+
+  return true;
+}
+
 bool
 nsShmImage::CreateImage(const IntSize& aSize)
 {
   MOZ_ASSERT(mDisplay && mVisual);
 
+  if (!InitExtension()) {
+    return false;
+  }
+
   mFormat = SurfaceFormat::UNKNOWN;
   switch (mDepth) {
   case 32:
     if (mVisual->red_mask == 0xff0000 &&
         mVisual->green_mask == 0xff00 &&
         mVisual->blue_mask == 0xff) {
       mFormat = SurfaceFormat::B8G8R8A8;
     }
@@ -119,17 +183,21 @@ nsShmImage::CreateImage(const IntSize& a
     // The alpha channel will be discarded when we put the image.
     if (mVisual->red_mask == 0xff0000 &&
         mVisual->green_mask == 0xff00 &&
         mVisual->blue_mask == 0xff) {
       mFormat = SurfaceFormat::B8G8R8A8;
     }
     break;
   case 16:
-    mFormat = SurfaceFormat::R5G6B5_UINT16;
+    if (mVisual->red_mask == 0xf800 &&
+        mVisual->green_mask == 0x07e0 &&
+        mVisual->blue_mask == 0x1f) {
+      mFormat = SurfaceFormat::R5G6B5_UINT16;
+    }
     break;
   }
 
   if (mFormat == SurfaceFormat::UNKNOWN) {
     NS_WARNING("Unsupported XShm Image format!");
     gShmAvailable = false;
     return false;
   }
@@ -157,24 +225,40 @@ nsShmImage::CreateImage(const IntSize& a
     DestroyImage();
 
     // Assume XShm isn't available, and don't attempt to use it
     // again.
     gShmAvailable = false;
     return false;
   }
 
+  if (gShmPixmapAtom != None) {
+    mPixmap = XShmCreatePixmap(mDisplay, mWindow,
+                               mImage->data, &mInfo,
+                               mImage->width, mImage->height, mImage->depth);
+  }
+
   return true;
 }
 
 void
 nsShmImage::DestroyImage()
 {
   if (mImage) {
     mozilla::FinishX(mDisplay);
+  }
+  if (mGC) {
+    XFreeGC(mDisplay, mGC);
+    mGC = nullptr;
+  }
+  if (mPixmap != None) {
+    XFreePixmap(mDisplay, mPixmap);
+    mPixmap = None;
+  }
+  if (mImage) {
     if (mInfo.shmid != -1) {
       XShmDetach(mDisplay, &mInfo);
     }
     XDestroyImage(mImage);
     mImage = nullptr;
   }
   DestroyShmSegment();
 }
@@ -201,56 +285,95 @@ nsShmImage::CreateDrawTarget(const mozil
   return gfxPlatform::GetPlatform()->CreateDrawTargetForData(
     reinterpret_cast<unsigned char*>(mImage->data)
       + bounds.y * mImage->bytes_per_line + bounds.x * BytesPerPixel(mFormat),
     bounds.Size(),
     mImage->bytes_per_line,
     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;
   }
 
-  while (long(LastKnownRequestProcessed(mDisplay) - mRequest) < 0) {
-    LockDisplay(mDisplay);
-    _XReadEvents(mDisplay);
-    UnlockDisplay(mDisplay);
+  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;
   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 };
     xrects.AppendElement(xrect);
   }
 
-  GC gc = XCreateGC(mDisplay, mWindow, 0, nullptr);
-  XSetClipRectangles(mDisplay, gc, 0, 0, xrects.Elements(), xrects.Length(), YXBanded);
+  if (!mGC) {
+    mGC = XCreateGC(mDisplay, mWindow, 0, nullptr);
+    if (!mGC) {
+      return;
+    }
+  }
+  XSetClipRectangles(mDisplay, mGC, 0, 0, xrects.Elements(), xrects.Length(), YXBanded);
 
   mRequest = XNextRequest(mDisplay);
-  XShmPutImage(mDisplay, mWindow, gc, mImage,
-               0, 0,
-               0, 0,
-               mImage->width, mImage->height,
-               True);
+  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);
+  }
 
-  XFreeGC(mDisplay, gc);
-
+  mPreviousRequestProcessed = LastKnownRequestProcessed(mDisplay);
   XFlush(mDisplay);
 }
 
 #endif  // MOZ_HAVE_SHMIMAGE
--- a/widget/nsShmImage.h
+++ b/widget/nsShmImage.h
@@ -31,47 +31,42 @@ public:
   already_AddRefed<mozilla::gfx::DrawTarget>
     CreateDrawTarget(const mozilla::LayoutDeviceIntRegion& aRegion);
 
   void Put(const mozilla::LayoutDeviceIntRegion& aRegion);
 
   nsShmImage(Display* aDisplay,
              Drawable aWindow,
              Visual* aVisual,
-             unsigned int aDepth)
-    : mImage(nullptr)
-    , mDisplay(aDisplay)
-    , mWindow(aWindow)
-    , mVisual(aVisual)
-    , mDepth(aDepth)
-    , mFormat(mozilla::gfx::SurfaceFormat::UNKNOWN)
-    , mRequest(0)
-  {
-    mInfo.shmid = -1;
-  }
+             unsigned int aDepth);
 
 private:
-  ~nsShmImage()
-  {
-    DestroyImage();
-  }
+  ~nsShmImage();
+
+  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;
-  Drawable                     mWindow;
+  Window                       mWindow;
   Visual*                      mVisual;
   unsigned int                 mDepth;
   XShmSegmentInfo              mInfo;
   mozilla::gfx::SurfaceFormat  mFormat;
+  Pixmap                       mPixmap;
+  GC                           mGC;
   unsigned long                mRequest;
+  unsigned long                mPreviousRequestProcessed;
 };
 
 #endif // MOZ_HAVE_SHMIMAGE
 
 #endif