diff --git a/widget/gtk/WindowSurfaceWayland.h b/widget/gtk/WindowSurfaceWayland.h --- a/widget/gtk/WindowSurfaceWayland.h +++ b/widget/gtk/WindowSurfaceWayland.h @@ -3,16 +3,17 @@ * This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ #ifndef _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H #define _MOZILLA_WIDGET_GTK_WINDOW_SURFACE_WAYLAND_H #include +#include "mozilla/gfx/Types.h" namespace mozilla { namespace widget { // Our general connection to Wayland display server, // holds our display connection and runs event loop. class nsWaylandDisplay : public nsISupports { NS_DECL_THREADSAFE_ISUPPORTS @@ -61,17 +62,17 @@ private: }; // Holds actual graphics data for wl_surface class WindowBackBuffer { public: WindowBackBuffer(nsWaylandDisplay* aDisplay, int aWidth, int aHeight); ~WindowBackBuffer(); - already_AddRefed Lock(const LayoutDeviceIntRegion& aRegion); + already_AddRefed Lock(); void Attach(wl_surface* aSurface); void Detach(); bool IsAttached() { return mAttached; } bool Resize(int aWidth, int aHeight); bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer); @@ -107,27 +108,33 @@ public: WindowSurfaceWayland(nsWindow *aWindow); ~WindowSurfaceWayland(); already_AddRefed Lock(const LayoutDeviceIntRegion& aRegion) override; void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final; void FrameCallbackHandler(); private: - WindowBackBuffer* GetBufferToDraw(int aWidth, int aHeight); + WindowBackBuffer* GetFrontBufferToDraw(int aWidth, int aHeight); void UpdateScaleFactor(); + already_AddRefed LockFrontBuffer(int aWidth, int aHeight); + already_AddRefed LockImageSurface(const gfx::IntSize& aLockSize); + bool CommitImageSurface(const LayoutDeviceIntRegion& aRegion); + // TODO: Do we need to hold a reference to nsWindow object? nsWindow* mWindow; nsWaylandDisplay* mWaylandDisplay; WindowBackBuffer* mFrontBuffer; WindowBackBuffer* mBackBuffer; + RefPtr mImageSurface; wl_callback* mFrameCallback; wl_surface* mFrameCallbackSurface; MessageLoop* mDisplayThreadMessageLoop; + bool mDirectWlBufferDraw; bool mDelayedCommit; bool mFullScreenDamage; bool mIsMainThread; }; } // namespace widget } // namespace mozilla diff --git a/widget/gtk/WindowSurfaceWayland.cpp b/widget/gtk/WindowSurfaceWayland.cpp --- a/widget/gtk/WindowSurfaceWayland.cpp +++ b/widget/gtk/WindowSurfaceWayland.cpp @@ -299,16 +299,17 @@ nsWaylandDisplay::Matches(wl_display *aD } NS_IMPL_ISUPPORTS(nsWaylandDisplay, nsISupports); nsWaylandDisplay::nsWaylandDisplay(wl_display *aDisplay) : mThreadId(PR_GetCurrentThread()) // gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format // and is always present. + // TODO: Provide also format without alpha (Bug 1470126). , mFormat(gfx::SurfaceFormat::B8G8R8A8) , mShm(nullptr) , mDisplay(aDisplay) { if (NS_IsMainThread()) { // Use default event queue in main thread operated by Gtk+. mEventQueue = nullptr; } else { @@ -530,21 +531,19 @@ WindowBackBuffer::SetImageDataFromBackBu } mShmPool.SetImageDataFromPool(&aSourceBuffer->mShmPool, aSourceBuffer->mWidth * aSourceBuffer->mHeight * BUFFER_BPP); return true; } already_AddRefed -WindowBackBuffer::Lock(const LayoutDeviceIntRegion& aRegion) +WindowBackBuffer::Lock() { - gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); - gfx::IntSize lockSize(bounds.XMost(), bounds.YMost()); - + gfx::IntSize lockSize(mWidth, mHeight); return gfxPlatform::CreateDrawTargetForData(static_cast(mShmPool.GetImageData()), lockSize, BUFFER_BPP * mWidth, mWaylandDisplay->GetSurfaceFormat()); } static void frame_callback_handler(void *data, struct wl_callback *callback, uint32_t time) @@ -560,16 +559,17 @@ static const struct wl_callback_listener WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow) : mWindow(aWindow) , mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay())) , mFrontBuffer(nullptr) , mBackBuffer(nullptr) , mFrameCallback(nullptr) , mFrameCallbackSurface(nullptr) , mDisplayThreadMessageLoop(MessageLoop::current()) + , mDirectWlBufferDraw(true) , mDelayedCommit(false) , mFullScreenDamage(false) , mIsMainThread(NS_IsMainThread()) { } WindowSurfaceWayland::~WindowSurfaceWayland() { @@ -598,17 +598,17 @@ WindowSurfaceWayland::UpdateScaleFactor( { wl_surface* waylandSurface = mWindow->GetWaylandSurface(); if (waylandSurface) { wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor()); } } WindowBackBuffer* -WindowSurfaceWayland::GetBufferToDraw(int aWidth, int aHeight) +WindowSurfaceWayland::GetFrontBufferToDraw(int aWidth, int aHeight) { if (!mFrontBuffer) { mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); return mFrontBuffer; } if (!mFrontBuffer->IsAttached()) { @@ -647,46 +647,149 @@ WindowSurfaceWayland::GetBufferToDraw(in // the new buffer and leave gecko to render new whole content. mFrontBuffer->Resize(aWidth, aHeight); } return mFrontBuffer; } already_AddRefed +WindowSurfaceWayland::LockFrontBuffer(int aWidth, int aHeight) +{ + WindowBackBuffer* buffer = GetFrontBufferToDraw(aWidth, aHeight); + if (buffer) { + return buffer->Lock(); + } + + NS_WARNING("WindowSurfaceWayland::LockFrontBuffer(): No buffer available"); + return nullptr; +} + +already_AddRefed +WindowSurfaceWayland::LockImageSurface(const gfx::IntSize& aLockSize) +{ + if (!mImageSurface || mImageSurface->CairoStatus() || + !(aLockSize <= mImageSurface->GetSize())) { + mImageSurface = new gfxImageSurface(aLockSize, + SurfaceFormatToImageFormat(mWaylandDisplay->GetSurfaceFormat())); + if (mImageSurface->CairoStatus()) { + return nullptr; + } + } + + return gfxPlatform::CreateDrawTargetForData(mImageSurface->Data(), + mImageSurface->GetSize(), + mImageSurface->Stride(), + mWaylandDisplay->GetSurfaceFormat()); +} + +/* + There are some situations which can happen here: + + A) Lock() is called to whole surface. In that case we don't need + to clip/buffer the drawing and we can return wl_buffer directly + for drawing. + - mFrontBuffer is available - that's an ideal situation. + - mFrontBuffer is locked by compositor - flip buffers and draw. + - if we can't flip buffers - go B) + + B) Lock() is requested for part(s) of screen. We need to provide temporary + surface to draw into and copy result (clipped) to target wl_surface. + */ +already_AddRefed WindowSurfaceWayland::Lock(const LayoutDeviceIntRegion& aRegion) { MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); - // We allocate back buffer to widget size but return only - // portion requested by aRegion. - LayoutDeviceIntRect rect = mWindow->GetBounds(); - WindowBackBuffer* buffer = GetBufferToDraw(rect.width, - rect.height); - if (!buffer) { - NS_WARNING("No drawing buffer available"); - return nullptr; + LayoutDeviceIntRect screenRect = mWindow->GetBounds(); + gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); + gfx::IntSize lockSize(bounds.XMost(), bounds.YMost()); + + // Are we asked for entire nsWindow to draw? + mDirectWlBufferDraw = (aRegion.GetNumRects() == 1 && + bounds.x == 0 && bounds.y == 0 && + lockSize.width == screenRect.width && + lockSize.height == screenRect.height); + + if (mDirectWlBufferDraw) { + RefPtr dt = LockFrontBuffer(screenRect.width, + screenRect.height); + if (dt) { + return dt.forget(); + } + + // We don't have any front buffer available. Try indirect drawing + // to mImageSurface which is mirrored to front buffer at commit. + mDirectWlBufferDraw = false; } - return buffer->Lock(aRegion); + return LockImageSurface(lockSize); +} + +bool +WindowSurfaceWayland::CommitImageSurface(const LayoutDeviceIntRegion& aRegion) +{ + MOZ_ASSERT(!mDirectWlBufferDraw); + + LayoutDeviceIntRect screenRect = mWindow->GetBounds(); + gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); + + gfx::Rect rect(bounds); + if (rect.IsEmpty()) { + return false; + } + + RefPtr dt = LockFrontBuffer(screenRect.width, + screenRect.height); + RefPtr surf = + gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(), + mImageSurface->GetSize(), + mImageSurface->Format()); + if (!dt || !surf) { + return false; + } + + uint32_t numRects = aRegion.GetNumRects(); + if (numRects != 1) { + AutoTArray rects; + rects.SetCapacity(numRects); + for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { + rects.AppendElement(iter.Get().ToUnknownRect()); + } + dt->PushDeviceSpaceClipRects(rects.Elements(), rects.Length()); + } + + dt->DrawSurface(surf, rect, rect); + + if (numRects != 1) { + dt->PopClip(); + } + + return true; } void WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) { MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); wl_surface* waylandSurface = mWindow->GetWaylandSurface(); if (!waylandSurface) { // Target window is already destroyed - don't bother to render there. + NS_WARNING("WindowSurfaceWayland::Commit(): parent wl_surface is already hidden/deleted."); return; } wl_proxy_set_queue((struct wl_proxy *)waylandSurface, mWaylandDisplay->GetEventQueue()); + if (!mDirectWlBufferDraw) { + // We have new content at mImageSurface - copy data to mFrontBuffer first. + CommitImageSurface(aInvalidRegion); + } + if (mFullScreenDamage) { LayoutDeviceIntRect rect = mWindow->GetBounds(); wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height); mFullScreenDamage = false; } else { for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) { const mozilla::LayoutDeviceIntRect &r = iter.Get(); wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height); @@ -730,17 +833,17 @@ WindowSurfaceWayland::FrameCallbackHandl mFrameCallback = nullptr; mFrameCallbackSurface = nullptr; } if (mDelayedCommit) { wl_surface* waylandSurface = mWindow->GetWaylandSurface(); if (!waylandSurface) { // Target window is already destroyed - don't bother to render there. - NS_WARNING("No drawing buffer available"); + NS_WARNING("WindowSurfaceWayland::FrameCallbackHandler(): parent wl_surface is already hidden/deleted."); return; } wl_proxy_set_queue((struct wl_proxy *)waylandSurface, mWaylandDisplay->GetEventQueue()); // Send pending surface to compositor and register frame callback // for possible subsequent drawing. mFrameCallback = wl_surface_frame(waylandSurface);