From 2d3967c30f8574ad857737bd183da916b3482487 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Thu, 18 Feb 2016 20:57:29 -0500 Subject: [PATCH] Bug 1019856 - avoid double-buffering in BasicCompositor when window allows it. r=mattwoodrow --- gfx/layers/basic/BasicCompositor.cpp | 63 ++++++++++++++++++++-------- gfx/layers/basic/BasicCompositor.h | 5 +++ widget/cocoa/nsChildView.h | 4 +- widget/cocoa/nsChildView.mm | 3 +- widget/gtk/nsWindow.cpp | 22 ++++------ widget/gtk/nsWindow.h | 7 +++- widget/nsIWidget.h | 6 ++- 7 files changed, 73 insertions(+), 37 deletions(-) diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index 3259fda31018..241dfb4e143a 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -153,6 +153,32 @@ BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect, return nullptr; } +already_AddRefed +BasicCompositor::CreateRenderTargetForWindow(const IntRect& aRect, SurfaceInitMode aInit, BufferMode aBufferMode) +{ + if (aBufferMode != BufferMode::BUFFER_NONE) { + return CreateRenderTarget(aRect, aInit); + } + + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + + 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); + + if (aInit == INIT_MODE_CLEAR) { + mDrawTarget->ClearRect(gfx::Rect(aRect)); + } + + return rt.forget(); +} + already_AddRefed BasicCompositor::CreateDataTextureSource(TextureFlags aFlags) { @@ -557,13 +583,14 @@ BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion, *aRenderBoundsOut = Rect(); } + BufferMode bufferMode = BufferMode::BUFFERED; if (mTarget) { // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Use a dummy // placeholder so that CreateRenderTarget() works. mDrawTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); } else { // StartRemoteDrawingInRegion can mutate mInvalidRegion. - mDrawTarget = mWidget->StartRemoteDrawingInRegion(mInvalidRegion); + mDrawTarget = mWidget->StartRemoteDrawingInRegion(mInvalidRegion, &bufferMode); if (!mDrawTarget) { return; } @@ -581,7 +608,7 @@ BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion, // Setup an intermediate render target to buffer all compositing. We will // copy this into mDrawTarget (the widget), and/or mTarget in EndFrame() RefPtr target = - CreateRenderTarget(mInvalidRect.ToUnknownRect(), INIT_MODE_CLEAR); + CreateRenderTargetForWindow(mInvalidRect.ToUnknownRect(), INIT_MODE_CLEAR, bufferMode); if (!target) { if (!mTarget) { mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion); @@ -592,8 +619,7 @@ BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion, // We only allocate a surface sized to the invalidated region, so we need to // translate future coordinates. - mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mInvalidRect.x, - -mInvalidRect.y)); + mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mRenderTarget->GetOrigin())); gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, mInvalidRegion.ToUnknownRegion()); @@ -631,22 +657,25 @@ BasicCompositor::EndFrame() // Pop aInvalidregion mRenderTarget->mDrawTarget->PopClip(); - // Note: Most platforms require us to buffer drawing to the widget surface. - // That's why we don't draw to mDrawTarget directly. - RefPtr source = mRenderTarget->mDrawTarget->Snapshot(); - RefPtr dest(mTarget ? mTarget : mDrawTarget); + if (mTarget || mRenderTarget->mDrawTarget != mDrawTarget) { + // Note: Most platforms require us to buffer drawing to the widget surface. + // That's why we don't draw to mDrawTarget directly. + RefPtr source = mRenderTarget->mDrawTarget->Snapshot(); + RefPtr dest(mTarget ? mTarget : mDrawTarget); - nsIntPoint offset = mTarget ? mTargetBounds.TopLeft() : nsIntPoint(); + nsIntPoint offset = mTarget ? mTargetBounds.TopLeft() : nsIntPoint(); - // The source DrawTarget is clipped to the invalidation region, so we have - // to copy the individual rectangles in the region or else we'll draw blank - // pixels. - for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) { - const LayoutDeviceIntRect& r = iter.Get(); - dest->CopySurface(source, - IntRect(r.x - mInvalidRect.x, r.y - mInvalidRect.y, r.width, r.height), - IntPoint(r.x - offset.x, r.y - offset.y)); + // The source DrawTarget is clipped to the invalidation region, so we have + // to copy the individual rectangles in the region or else we'll draw blank + // pixels. + for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) { + const LayoutDeviceIntRect& r = iter.Get(); + dest->CopySurface(source, + IntRect(r.x, r.y, r.width, r.height) - mRenderTarget->GetOrigin(), + IntPoint(r.x, r.y) - offset); + } } + if (!mTarget) { mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion); } diff --git a/gfx/layers/basic/BasicCompositor.h b/gfx/layers/basic/BasicCompositor.h index dfc7d8491fca..fcb7e680403b 100644 --- a/gfx/layers/basic/BasicCompositor.h +++ b/gfx/layers/basic/BasicCompositor.h @@ -62,6 +62,11 @@ public: const CompositingRenderTarget *aSource, const gfx::IntPoint &aSourcePoint) override; + virtual already_AddRefed + CreateRenderTargetForWindow(const gfx::IntRect& aRect, + SurfaceInitMode aInit, + BufferMode aBufferMode); + virtual already_AddRefed CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override; diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index ffe3aef394db..e41f2ef5c7bd 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -539,7 +539,9 @@ public: return nsCocoaUtils::DevPixelsToCocoaPoints(aRect, BackingScaleFactor()); } - already_AddRefed StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion) override; + already_AddRefed + StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, + mozilla::layers::BufferMode* aBufferMode) override; void EndRemoteDrawing() override; void CleanupRemoteDrawing() override; bool InitCompositor(mozilla::layers::Compositor* aCompositor) override; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 56b45cbd27d1..76af2e1ae4c9 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -2635,7 +2635,8 @@ nsChildView::SwipeFinished() } already_AddRefed -nsChildView::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion) +nsChildView::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, + BufferMode* aBufferMode) { // should have created the GLPresenter in InitCompositor. MOZ_ASSERT(mGLPresenter); diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 56d963df3aa8..4869b1710088 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -2225,7 +2225,8 @@ nsWindow::OnExposeEvent(cairo_t *cr) return TRUE; } - RefPtr dt = GetDrawTarget(region); + BufferMode layerBuffering = BufferMode::BUFFERED; + RefPtr dt = GetDrawTarget(region, &layerBuffering); if (!dt) { return FALSE; } @@ -2245,7 +2246,6 @@ nsWindow::OnExposeEvent(cairo_t *cr) gfxUtils::ClipToRegion(dt, region.ToUnknownRegion()); } - BufferMode layerBuffering; 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 @@ -2254,16 +2254,6 @@ nsWindow::OnExposeEvent(cairo_t *cr) RefPtr destDT = dt->CreateSimilarDrawTarget(boundsRect.Size(), SurfaceFormat::B8G8R8A8); ctx = new gfxContext(destDT, boundsRect.TopLeft()); } else { -#ifdef MOZ_HAVE_SHMIMAGE - if (nsShmImage::UseShm()) { - // We're using an xshm mapping as a back buffer. - layerBuffering = BufferMode::BUFFER_NONE; - } else -#endif // MOZ_HAVE_SHMIMAGE - { - // Get the layer manager to do double buffering (if necessary). - layerBuffering = BufferMode::BUFFERED; - } ctx = new gfxContext(dt); } @@ -6455,7 +6445,7 @@ nsWindow::GetSurfaceForGdkDrawable(GdkDrawable* aDrawable, #endif already_AddRefed -nsWindow::GetDrawTarget(const LayoutDeviceIntRegion& aRegion) +nsWindow::GetDrawTarget(const LayoutDeviceIntRegion& aRegion, BufferMode* aBufferMode) { if (!mGdkWindow) { return nullptr; @@ -6474,12 +6464,14 @@ nsWindow::GetDrawTarget(const LayoutDeviceIntRegion& aRegion) if (nsShmImage::UseShm()) { dt = nsShmImage::EnsureShmImage(size, mXDisplay, mXVisual, mXDepth, mShmImage); + *aBufferMode = BufferMode::BUFFER_NONE; } # endif // MOZ_HAVE_SHMIMAGE if (!dt) { RefPtr surf = new gfxXlibSurface(mXDisplay, mXWindow, mXVisual, size.ToUnknownSize()); if (!surf->CairoStatus()) { dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf.get(), surf->GetSize()); + *aBufferMode = BufferMode::BUFFERED; } } #endif // MOZ_X11 @@ -6488,9 +6480,9 @@ nsWindow::GetDrawTarget(const LayoutDeviceIntRegion& aRegion) } already_AddRefed -nsWindow::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion) +nsWindow::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, BufferMode* aBufferMode) { - return GetDrawTarget(aInvalidRegion); + return GetDrawTarget(aInvalidRegion, aBufferMode); } void diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index f42438217e8c..aa3b7b3d9c3c 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -216,7 +216,8 @@ public: #endif virtual already_AddRefed - StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion) override; + StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, + mozilla::layers::BufferMode* aBufferMode) override; virtual void EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget, LayoutDeviceIntRegion& aInvalidRegion) override; @@ -309,7 +310,9 @@ public: virtual nsresult ConfigureChildren(const nsTArray& aConfigurations) override; nsresult UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect, uint8_t* aAlphas, int32_t aStride); - virtual already_AddRefed GetDrawTarget(const LayoutDeviceIntRegion& aRegion); + + already_AddRefed GetDrawTarget(const LayoutDeviceIntRegion& aRegion, + mozilla::layers::BufferMode* aBufferMode); #if (MOZ_WIDGET_GTK == 2) static already_AddRefed GetSurfaceForGdkDrawable(GdkDrawable* aDrawable, diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 7c99fc42bc2a..0d8caff3f646 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -1287,9 +1287,13 @@ class nsIWidget : public nsISupports { * * Called by BasicCompositor on the compositor thread for OMTC drawing * before each composition. + * + * The window may specify its buffer mode. If unspecified, it is assumed + * to require double-buffering. */ virtual already_AddRefed StartRemoteDrawing() = 0; - virtual already_AddRefed StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion) { + virtual already_AddRefed StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, + mozilla::layers::BufferMode* aBufferMode) { return StartRemoteDrawing(); }