From bf234f25b7ace9c6c6b8a936bd3dd168fd2a5f01 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Thu, 25 Feb 2016 14:38:05 -0500 Subject: [PATCH] Bug 1249813 - part 1 - revise nsShmImage to allow draw targets anywhere inside its bounds. r=jrmuizel --- gfx/layers/basic/BasicCompositor.cpp | 9 +- widget/gtk/nsWindow.cpp | 40 ++--- widget/nsShmImage.cpp | 222 ++++++++++++--------------- widget/nsShmImage.h | 72 ++++----- 4 files changed, 161 insertions(+), 182 deletions(-) diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index be1ba70dcb50..96685861fe58 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -186,11 +186,14 @@ BasicCompositor::CreateRenderTargetForWindow(const IntRect& aRect, SurfaceInitMo MOZ_ASSERT(mDrawTarget); // Adjust bounds rect to account for new origin at (0, 0). - IntRect rect(0, 0, aRect.XMost(), aRect.YMost()); - RefPtr rt = new BasicCompositingRenderTarget(mDrawTarget, rect); + IntRect windowRect = aRect; + if (aRect.Size() != mDrawTarget->GetSize()) { + windowRect.ExpandToEnclose(IntPoint(0, 0)); + } + RefPtr rt = new BasicCompositingRenderTarget(mDrawTarget, windowRect); if (aInit == INIT_MODE_CLEAR) { - mDrawTarget->ClearRect(gfx::Rect(aRect)); + mDrawTarget->ClearRect(Rect(aRect - rt->GetOrigin())); } return rt.forget(); diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 2d0f8a32c957..f3af4e08a4cf 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -2236,22 +2236,21 @@ nsWindow::OnExposeEvent(cairo_t *cr) return FALSE; } RefPtr ctx; + IntRect boundsRect = region.GetBounds().ToUnknownRect(); + IntPoint offset(0, 0); + if (dt->GetSize() == boundsRect.Size()) { + offset = boundsRect.TopLeft(); + dt->SetTransform(Matrix::Translation(-offset)); + } #ifdef MOZ_X11 - nsIntRect boundsRect; // for shaped only - if (shaped) { // Collapse update area to the bounding box. This is so we only have to // call UpdateTranslucentWindowAlpha once. After we have dropped // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be // our private interface so we can rework things to avoid this. - boundsRect = region.GetBounds().ToUnknownRect(); dt->PushClipRect(Rect(boundsRect)); - } else { - gfxUtils::ClipToRegion(dt, region.ToUnknownRegion()); - } - if (shaped) { // The double buffering is done here to extract the shape mask. // (The shape mask won't be necessary when a visual with an alpha // channel is used on compositing window managers.) @@ -2259,7 +2258,9 @@ nsWindow::OnExposeEvent(cairo_t *cr) RefPtr destDT = dt->CreateSimilarDrawTarget(boundsRect.Size(), SurfaceFormat::B8G8R8A8); ctx = new gfxContext(destDT, boundsRect.TopLeft()); } else { - ctx = new gfxContext(dt); + gfxUtils::ClipToRegion(dt, region.ToUnknownRegion()); + + ctx = new gfxContext(dt, offset); } #if 0 @@ -2304,7 +2305,7 @@ nsWindow::OnExposeEvent(cairo_t *cr) # ifdef MOZ_HAVE_SHMIMAGE if (mShmImage && MOZ_LIKELY(!mIsDestroyed)) { - mShmImage->Put(mXDisplay, mXWindow, region); + mShmImage->Put(region); } # endif // MOZ_HAVE_SHMIMAGE #endif // MOZ_X11 @@ -6487,13 +6488,7 @@ nsWindow::GetSurfaceForGdkDrawable(GdkDrawable* aDrawable, already_AddRefed nsWindow::GetDrawTarget(const LayoutDeviceIntRegion& aRegion, BufferMode* aBufferMode) { - if (!mGdkWindow) { - return nullptr; - } - - LayoutDeviceIntRect bounds = aRegion.GetBounds(); - LayoutDeviceIntSize size(bounds.XMost(), bounds.YMost()); - if (size.width <= 0 || size.height <= 0) { + if (!mGdkWindow || aRegion.IsEmpty()) { return nullptr; } @@ -6502,12 +6497,19 @@ nsWindow::GetDrawTarget(const LayoutDeviceIntRegion& aRegion, BufferMode* aBuffe #ifdef MOZ_X11 # ifdef MOZ_HAVE_SHMIMAGE if (nsShmImage::UseShm()) { - dt = nsShmImage::EnsureShmImage(size, - mXDisplay, mXVisual, mXDepth, mShmImage); + if (!mShmImage) { + mShmImage = new nsShmImage(mXDisplay, mXWindow, mXVisual, mXDepth); + } + dt = mShmImage->CreateDrawTarget(aRegion); *aBufferMode = BufferMode::BUFFER_NONE; + if (!dt) { + mShmImage = nullptr; + } } # endif // MOZ_HAVE_SHMIMAGE if (!dt) { + LayoutDeviceIntRect bounds = aRegion.GetBounds(); + LayoutDeviceIntSize size(bounds.XMost(), bounds.YMost()); RefPtr surf = new gfxXlibSurface(mXDisplay, mXWindow, mXVisual, size.ToUnknownSize()); if (!surf->CairoStatus()) { dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf.get(), surf->GetSize()); @@ -6535,7 +6537,7 @@ nsWindow::EndRemoteDrawingInRegion(DrawTarget* aDrawTarget, return; } - mShmImage->Put(mXDisplay, mXWindow, aInvalidRegion); + mShmImage->Put(aInvalidRegion); # endif // MOZ_HAVE_SHMIMAGE #endif // MOZ_X11 } diff --git a/widget/nsShmImage.cpp b/widget/nsShmImage.cpp index 47e91cf27a89..a2a131276611 100644 --- a/widget/nsShmImage.cpp +++ b/widget/nsShmImage.cpp @@ -4,13 +4,6 @@ * 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/. */ -#if defined(MOZ_WIDGET_GTK) -#include -#include -#elif defined(MOZ_WIDGET_QT) -#include -#endif - #include "nsShmImage.h" #ifdef MOZ_WIDGET_GTK #include "gfxPlatformGtk.h" @@ -35,9 +28,9 @@ static bool gShmAvailable = true; bool nsShmImage::UseShm() { #ifdef MOZ_WIDGET_GTK - return (gShmAvailable && !gfxPlatformGtk::GetPlatform()->UseXRender()); + return (gShmAvailable && !gfxPlatformGtk::GetPlatform()->UseXRender()); #else - return gShmAvailable; + return gShmAvailable; #endif } @@ -47,9 +40,9 @@ 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; + // store the error code and ignore the error + gShmError = aEvent->error_code; + return 0; } #endif @@ -68,16 +61,19 @@ nsShmImage::CreateShmSegment() } mInfo.shmaddr = (char *)shmat(mInfo.shmid, nullptr, 0); + + // Mark the handle removed so that it will destroy the segment when unmapped. + shmctl(mInfo.shmid, IPC_RMID, nullptr); + if (mInfo.shmaddr == (void *)-1) { + // Since mapping failed, the segment is already destroyed. + mInfo.shmid = -1; + nsPrintfCString warning("shmat(): %s (%d)\n", strerror(errno), errno); NS_WARNING(warning.get()); return false; } - // Mark the handle as deleted so that, should this process go away, the - // segment is cleaned up. - shmctl(mInfo.shmid, IPC_RMID, 0); - #ifdef DEBUG struct shmid_ds info; if (shmctl(mInfo.shmid, IPC_STAT, &info) < 0) { @@ -105,55 +101,25 @@ nsShmImage::DestroyShmSegment() } bool -nsShmImage::CreateImage(const LayoutDeviceIntSize& aSize, - Display* aDisplay, Visual* aVisual, unsigned int aDepth) +nsShmImage::CreateImage(const IntSize& aSize) { - mDisplay = aDisplay; - mImage = XShmCreateImage(aDisplay, aVisual, aDepth, - ZPixmap, nullptr, - &mInfo, - aSize.width, aSize.height); - if (!mImage || !CreateShmSegment()) { - return false; - } + MOZ_ASSERT(mDisplay && mVisual); -#if defined(MOZ_WIDGET_GTK) - gShmError = 0; - XErrorHandler previousHandler = XSetErrorHandler(TrapShmError); - Status attachOk = XShmAttach(aDisplay, &mInfo); - XSync(aDisplay, False); - XSetErrorHandler(previousHandler); - if (gShmError) { - attachOk = 0; - } -#elif defined(MOZ_WIDGET_QT) - Status attachOk = XShmAttach(aDisplay, &mInfo); -#endif - - if (!attachOk) { - // Assume XShm isn't available, and don't attempt to use it - // again. - gShmAvailable = false; - return false; - } - - mXAttached = true; - mSize = aSize; mFormat = SurfaceFormat::UNKNOWN; - switch (mImage->depth) { + switch (mDepth) { case 32: - if ((mImage->red_mask == 0xff0000) && - (mImage->green_mask == 0xff00) && - (mImage->blue_mask == 0xff)) { + if (mVisual->red_mask == 0xff0000 && + mVisual->green_mask == 0xff00 && + mVisual->blue_mask == 0xff) { mFormat = SurfaceFormat::B8G8R8A8; } break; case 24: // Only support the BGRX layout, and report it as BGRA to the compositor. // The alpha channel will be discarded when we put the image. - if ((mImage->red_mask == 0xff0000) && - (mImage->green_mask == 0xff00) && - (mImage->blue_mask == 0xff)) { + if (mVisual->red_mask == 0xff0000 && + mVisual->green_mask == 0xff00 && + mVisual->blue_mask == 0xff) { mFormat = SurfaceFormat::B8G8R8A8; } break; @@ -168,96 +134,108 @@ nsShmImage::CreateImage(const LayoutDeviceIntSize& aSize, return false; } + mImage = XShmCreateImage(mDisplay, mVisual, mDepth, + 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; + return false; + } + return true; } -nsShmImage::~nsShmImage() +void +nsShmImage::DestroyImage() { if (mImage) { mozilla::FinishX(mDisplay); - if (mXAttached) { + if (mInfo.shmid != -1) { XShmDetach(mDisplay, &mInfo); } XDestroyImage(mImage); + mImage = nullptr; } DestroyShmSegment(); } already_AddRefed -nsShmImage::CreateDrawTarget() +nsShmImage::CreateDrawTarget(const LayoutDeviceIntRegion& aRegion) { + // 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(); + if (!CreateImage(size)) { + return nullptr; + } + } + return gfxPlatform::GetPlatform()->CreateDrawTargetForData( - reinterpret_cast(mImage->data), - mSize.ToUnknownSize(), + reinterpret_cast(mImage->data) + + bounds.y * mImage->bytes_per_line + bounds.x * BytesPerPixel(mFormat), + bounds.Size(), mImage->bytes_per_line, mFormat); } -#ifdef MOZ_WIDGET_GTK void -nsShmImage::Put(Display* aDisplay, Drawable aWindow, - const LayoutDeviceIntRegion& aRegion) +nsShmImage::Put(const LayoutDeviceIntRegion& aRegion) { - GC gc = XCreateGC(aDisplay, aWindow, 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(aDisplay, aWindow, gc, mImage, - r.x, r.y, - r.x, r.y, - r.width, r.height, - False); - } - - XFreeGC(aDisplay, 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(aDisplay, False); -} - -#elif defined(MOZ_WIDGET_QT) -void -nsShmImage::Put(QWindow* aWindow, QRect& aRect) -{ - Display* dpy = gfxQtPlatform::GetXDisplay(aWindow); - Drawable d = aWindow->winId(); - - GC gc = XCreateGC(dpy, d, 0, nullptr); - // Avoid out of bounds painting - QRect inter = aRect.intersected(aWindow->geometry()); - XShmPutImage(dpy, d, gc, mImage, - inter.x(), inter.y(), - inter.x(), inter.y(), - inter.width(), inter.height(), - False); - XFreeGC(dpy, gc); -} -#endif - -already_AddRefed -nsShmImage::EnsureShmImage(const LayoutDeviceIntSize& aSize, - Display* aDisplay, Visual* aVisual, unsigned int aDepth, - RefPtr& aImage) -{ - if (!aImage || aImage->Size() != aSize) { - // Because we XSync() after XShmAttach() to trap errors, we - // know that the X server has the old image's memory mapped - // into its address space, so it's OK to destroy the old image - // here even if there are outstanding Puts. The Detach is - // ordered after the Puts. - aImage = new nsShmImage; - if (!aImage->CreateImage(aSize, aDisplay, aVisual, aDepth)) { - aImage = nullptr; - } + if (!mImage) { + return; } - return !aImage ? nullptr : aImage->CreateDrawTarget(); + + 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); + } + + 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); } #endif // MOZ_HAVE_SHMIMAGE diff --git a/widget/nsShmImage.h b/widget/nsShmImage.h index 27c9d23f5554..c9e3e02834bc 100644 --- a/widget/nsShmImage.h +++ b/widget/nsShmImage.h @@ -20,55 +20,51 @@ #include #include -#ifdef MOZ_WIDGET_QT -class QRect; -class QWindow; -#endif - 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) + // 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(); - static already_AddRefed - EnsureShmImage(const mozilla::LayoutDeviceIntSize& aSize, - Display* aDisplay, Visual* aVisual, unsigned int aDepth, - RefPtr& aImage); + static bool UseShm(); - already_AddRefed CreateDrawTarget(); + already_AddRefed + CreateDrawTarget(const mozilla::LayoutDeviceIntRegion& aRegion); -#ifdef MOZ_WIDGET_GTK - void Put(Display* aDisplay, Drawable aWindow, - const mozilla::LayoutDeviceIntRegion& aRegion); -#elif defined(MOZ_WIDGET_QT) - void Put(QWindow* aWindow, QRect& aRect); -#endif + void Put(const mozilla::LayoutDeviceIntRegion& aRegion); - mozilla::LayoutDeviceIntSize Size() const { return mSize; } + 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) + { + mInfo.shmid = -1; + } private: - nsShmImage() - : mImage(nullptr) - , mDisplay(nullptr) - , mFormat(mozilla::gfx::SurfaceFormat::UNKNOWN) - , mXAttached(false) - { mInfo.shmid = -1; } + ~nsShmImage() + { + DestroyImage(); + } - ~nsShmImage(); + bool CreateShmSegment(); + void DestroyShmSegment(); - bool CreateShmSegment(); - void DestroyShmSegment(); + bool CreateImage(const mozilla::gfx::IntSize& aSize); + void DestroyImage(); - bool CreateImage(const mozilla::LayoutDeviceIntSize& aSize, - Display* aDisplay, Visual* aVisual, unsigned int aDepth); - - XImage* mImage; - Display* mDisplay; - XShmSegmentInfo mInfo; - mozilla::LayoutDeviceIntSize mSize; - mozilla::gfx::SurfaceFormat mFormat; - bool mXAttached; + XImage* mImage; + Display* mDisplay; + Drawable mWindow; + Visual* mVisual; + unsigned int mDepth; + XShmSegmentInfo mInfo; + mozilla::gfx::SurfaceFormat mFormat; }; #endif // MOZ_HAVE_SHMIMAGE