Bug 1247935 - Part 1: double-buffer nsShmImage. r=jrmuizel
authorLee Salzman <lsalzman@mozilla.com>
Sat, 27 Feb 2016 23:16:57 -0500
changeset 285961 f1ada5d7a0922b62fecfe4b45290f2cddc49504d
parent 285960 db9d813769f633b433a36734f5fc7369dced81d5
child 285962 f37626eba5c683d42aaa510b55ad7e24f490063d
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 1: double-buffer nsShmImage. r=jrmuizel
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/moz.build
widget/nsShmImage.cpp
widget/nsShmImage.h
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -2299,18 +2299,18 @@ nsWindow::OnExposeEvent(cairo_t *cr)
             }
         }
     }
 
     ctx = nullptr;
     dt->PopClip();
 
 #  ifdef MOZ_HAVE_SHMIMAGE
-    if (mShmImage && MOZ_LIKELY(!mIsDestroyed)) {
-      mShmImage->Put(region);
+    if (mBackShmImage && MOZ_LIKELY(!mIsDestroyed)) {
+      mBackShmImage->Put(region);
     }
 #  endif  // MOZ_HAVE_SHMIMAGE
 #endif // MOZ_X11
 
     listener->DidPaintWindow();
 
     // Synchronously flush any new dirty areas
 #if (MOZ_WIDGET_GTK == 2)
@@ -6492,23 +6492,24 @@ nsWindow::GetDrawTarget(const LayoutDevi
     return nullptr;
   }
 
   RefPtr<DrawTarget> dt;
 
 #ifdef MOZ_X11
 #  ifdef MOZ_HAVE_SHMIMAGE
   if (nsShmImage::UseShm()) {
-    if (!mShmImage) {
-      mShmImage = new nsShmImage(mXDisplay, mXWindow, mXVisual, mXDepth);
-    }
-    dt = mShmImage->CreateDrawTarget(aRegion);
+    mBackShmImage.swap(mFrontShmImage);
+    if (!mBackShmImage) {
+      mBackShmImage = new nsShmImage(mXDisplay, mXWindow, mXVisual, mXDepth);
+    }
+    dt = mBackShmImage->CreateDrawTarget(aRegion);
     *aBufferMode = BufferMode::BUFFER_NONE;
     if (!dt) {
-      mShmImage = nullptr;
+      mBackShmImage = nullptr;
     }
   }
 #  endif  // MOZ_HAVE_SHMIMAGE
   if (!dt) {
     LayoutDeviceIntRect bounds = aRegion.GetBounds();
     LayoutDeviceIntSize size(bounds.XMost(), bounds.YMost());
     RefPtr<gfxXlibSurface> surf = new gfxXlibSurface(mXDisplay, mXWindow, mXVisual, size.ToUnknownSize());
     if (!surf->CairoStatus()) {
@@ -6528,21 +6529,21 @@ nsWindow::StartRemoteDrawingInRegion(Lay
 }
 
 void
 nsWindow::EndRemoteDrawingInRegion(DrawTarget* aDrawTarget,
                                    LayoutDeviceIntRegion& aInvalidRegion)
 {
 #ifdef MOZ_X11
 #  ifdef MOZ_HAVE_SHMIMAGE
-  if (!mGdkWindow || !mShmImage) {
+  if (!mGdkWindow || !mBackShmImage) {
     return;
   }
 
-  mShmImage->Put(aInvalidRegion);
+  mBackShmImage->Put(aInvalidRegion);
 #  endif // MOZ_HAVE_SHMIMAGE
 #endif // MOZ_X11
 }
 
 // Code shared begin BeginMoveDrag and BeginResizeDrag
 bool
 nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent,
                       GdkWindow** aWindow, gint* aButton,
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -452,17 +452,18 @@ private:
     Display*            mXDisplay;
     Drawable            mXWindow;
     Visual*             mXVisual;
     int                 mXDepth;
 #endif
 
 #ifdef MOZ_HAVE_SHMIMAGE
     // If we're using xshm rendering
-    RefPtr<nsShmImage>  mShmImage;
+    RefPtr<nsShmImage>  mFrontShmImage;
+    RefPtr<nsShmImage>  mBackShmImage;
 #endif
 
 #ifdef ACCESSIBILITY
     RefPtr<mozilla::a11y::Accessible> mRootAccessible;
 
     /**
      * Request to create the accessible for this window if it is top level.
      */
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -154,17 +154,16 @@ UNIFIED_SOURCES += [
     'nsFilePickerProxy.cpp',
     'nsHTMLFormatConverter.cpp',
     'nsIdleService.cpp',
     'nsIWidgetListener.cpp',
     'nsPrimitiveHelpers.cpp',
     'nsPrintSession.cpp',
     'nsPrintSettingsImpl.cpp',
     'nsScreenManagerProxy.cpp',
-    'nsShmImage.cpp',
     'nsTransferable.cpp',
     'nsXPLookAndFeel.cpp',
     'PluginWidgetProxy.cpp',
     'PuppetBidiKeyboard.cpp',
     'PuppetWidget.cpp',
     'ScreenProxy.cpp',
     'SharedWidgetUtils.cpp',
     'TextEventDispatcher.cpp',
@@ -193,16 +192,19 @@ if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
 
 EXPORTS.ipc = ['nsGUIEventIPC.h']
 
 if CONFIG['MOZ_X11']:
     DIRS += ['x11']
     UNIFIED_SOURCES += [
         'GfxInfoX11.cpp'
     ]
+    SOURCES += [
+        'nsShmImage.cpp'
+    ]
 
 if toolkit in ('cocoa', 'windows'):
     UNIFIED_SOURCES += [
         'nsBaseClipboard.cpp',
     ]
 
 if toolkit in {'qt', 'gtk2', 'gtk3', 'cocoa', 'windows',
                'android', 'gonk', 'uikit'}:
--- a/widget/nsShmImage.cpp
+++ b/widget/nsShmImage.cpp
@@ -6,50 +6,50 @@
 
 #include "nsShmImage.h"
 #ifdef MOZ_WIDGET_GTK
 #include "gfxPlatformGtk.h"
 #endif
 
 #ifdef MOZ_HAVE_SHMIMAGE
 #include "mozilla/X11Util.h"
-
 #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;
 
 // 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
   return gShmAvailable;
 #endif
 }
 
-#ifdef MOZ_WIDGET_GTK
 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;
 }
-#endif
 
 bool
 nsShmImage::CreateShmSegment()
 {
   if (!mImage) {
     return false;
   }
 
@@ -138,28 +138,24 @@ nsShmImage::CreateImage(const IntSize& a
                            ZPixmap, nullptr,
                            &mInfo,
                            aSize.width, aSize.height);
   if (!mImage || !CreateShmSegment()) {
     DestroyImage();
     return false;
   }
 
-#ifdef MOZ_WIDGET_GTK
   gShmError = 0;
   XErrorHandler previousHandler = XSetErrorHandler(TrapShmError);
   Status attachOk = XShmAttach(mDisplay, &mInfo);
   XSync(mDisplay, False);
   XSetErrorHandler(previousHandler);
   if (gShmError) {
     attachOk = 0;
   }
-#else
-  Status attachOk = XShmAttach(mDisplay, &mInfo);
-#endif
 
   if (!attachOk) {
     DestroyShmSegment();
     DestroyImage();
 
     // Assume XShm isn't available, and don't attempt to use it
     // again.
     gShmAvailable = false;
@@ -179,18 +175,21 @@ nsShmImage::DestroyImage()
     }
     XDestroyImage(mImage);
     mImage = nullptr;
   }
   DestroyShmSegment();
 }
 
 already_AddRefed<DrawTarget>
-nsShmImage::CreateDrawTarget(const LayoutDeviceIntRegion& aRegion)
+nsShmImage::CreateDrawTarget(const mozilla::LayoutDeviceIntRegion& aRegion)
 {
+  // Wait for any in-flight XShmPutImage requests to complete.
+  WaitForRequest();
+
   // 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) {
     DestroyImage();
@@ -203,39 +202,55 @@ nsShmImage::CreateDrawTarget(const Layou
     reinterpret_cast<unsigned char*>(mImage->data)
       + bounds.y * mImage->bytes_per_line + bounds.x * BytesPerPixel(mFormat),
     bounds.Size(),
     mImage->bytes_per_line,
     mFormat);
 }
 
 void
-nsShmImage::Put(const LayoutDeviceIntRegion& aRegion)
+nsShmImage::WaitForRequest()
+{
+  if (!mRequest) {
+    return;
+  }
+
+  while (long(LastKnownRequestProcessed(mDisplay) - mRequest) < 0) {
+    LockDisplay(mDisplay);
+    _XReadEvents(mDisplay);
+    UnlockDisplay(mDisplay);
+  }
+
+  mRequest = 0;
+}
+
+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);
-  LayoutDeviceIntRegion bounded;
-  bounded.And(aRegion,
-              LayoutDeviceIntRect(0, 0, mImage->width, mImage->height));
-  for (auto iter = bounded.RectIter(); !iter.Done(); iter.Next()) {
-    const LayoutDeviceIntRect& r = iter.Get();
-    XShmPutImage(mDisplay, mWindow, gc, mImage,
-                 r.x, r.y,
-                 r.x, r.y,
-                 r.width, r.height,
-                 False);
-  }
+  XSetClipRectangles(mDisplay, gc, 0, 0, xrects.Elements(), xrects.Length(), YXBanded);
+
+  mRequest = XNextRequest(mDisplay);
+  XShmPutImage(mDisplay, mWindow, gc, mImage,
+               0, 0,
+               0, 0,
+               mImage->width, mImage->height,
+               True);
 
   XFreeGC(mDisplay, gc);
 
-  // FIXME/bug 597336: we need to ensure that the shm image isn't
-  // scribbled over before all its pending XShmPutImage()s complete.
-  // However, XSync() is an unnecessarily heavyweight
-  // synchronization mechanism; other options are possible.  If this
-  // XSync is shown to hurt responsiveness, we need to explore the
-  // other options.
-  XSync(mDisplay, False);
+  XFlush(mDisplay);
 }
 
 #endif  // MOZ_HAVE_SHMIMAGE
--- a/widget/nsShmImage.h
+++ b/widget/nsShmImage.h
@@ -11,16 +11,17 @@
 #  define MOZ_HAVE_SHMIMAGE
 #endif
 
 #ifdef MOZ_HAVE_SHMIMAGE
 
 #include "mozilla/gfx/2D.h"
 #include "nsIWidget.h"
 #include "nsAutoPtr.h"
+#include "Units.h"
 
 #include <X11/Xlib.h>
 #include <X11/extensions/XShm.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)
 
@@ -37,36 +38,40 @@ public:
              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;
   }
 
 private:
   ~nsShmImage()
   {
     DestroyImage();
   }
 
   bool CreateShmSegment();
   void DestroyShmSegment();
 
   bool CreateImage(const mozilla::gfx::IntSize& aSize);
   void DestroyImage();
 
+  void WaitForRequest();
+
   XImage*                      mImage;
   Display*                     mDisplay;
   Drawable                     mWindow;
   Visual*                      mVisual;
   unsigned int                 mDepth;
   XShmSegmentInfo              mInfo;
   mozilla::gfx::SurfaceFormat  mFormat;
+  unsigned long                mRequest;
 };
 
 #endif // MOZ_HAVE_SHMIMAGE
 
 #endif