Wayland patches from Firefox 63. diff -up firefox-62.0.3/widget/gtk/mozcontainer.cpp.old firefox-62.0.3/widget/gtk/mozcontainer.cpp --- firefox-62.0.3/widget/gtk/mozcontainer.cpp.old 2018-10-08 11:52:33.442449788 +0200 +++ firefox-62.0.3/widget/gtk/mozcontainer.cpp 2018-08-21 14:22:12.668885853 +0200 @@ -605,6 +605,15 @@ moz_container_get_wl_surface(MozContaine return nullptr; moz_container_map_surface(container); + // Set the scale factor for the buffer right after we create it. + if (container->surface) { + static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*)) + dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor"); + if (sGdkWindowGetScaleFactorPtr && window) { + gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window); + wl_surface_set_buffer_scale(container->surface, scaleFactor); + } + } } return container->surface; diff -up firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp.old firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp --- firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp.old 2018-10-08 11:52:47.907392869 +0200 +++ firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp 2018-09-06 11:01:18.801964790 +0200 @@ -151,8 +151,9 @@ static nsWaylandDisplay* WaylandDisplayG static void WaylandDisplayRelease(wl_display *aDisplay); static void WaylandDisplayLoop(wl_display *aDisplay); -// TODO: is the 60pfs loop correct? -#define EVENT_LOOP_DELAY (1000/60) +// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with +// compositor event loop. +#define EVENT_LOOP_DELAY (1000/240) // Get WaylandDisplay for given wl_display and actual calling thread. static nsWaylandDisplay* @@ -523,7 +524,7 @@ WindowBackBuffer::Detach() } bool -WindowBackBuffer::SetImageDataFromBackBuffer( +WindowBackBuffer::SetImageDataFromBuffer( class WindowBackBuffer* aSourceBuffer) { if (!IsMatchingSize(aSourceBuffer)) { @@ -550,6 +551,8 @@ frame_callback_handler(void *data, struc { auto surface = reinterpret_cast(data); surface->FrameCallbackHandler(); + + gfxPlatformGtk::GetPlatform()->SetWaylandLastVsync(time); } static const struct wl_callback_listener frame_listener = { @@ -559,27 +562,40 @@ static const struct wl_callback_listener WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow) : mWindow(aWindow) , mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay())) - , mFrontBuffer(nullptr) - , mBackBuffer(nullptr) + , mWaylandBuffer(nullptr) + , mBackupBuffer(nullptr) , mFrameCallback(nullptr) - , mFrameCallbackSurface(nullptr) + , mLastCommittedSurface(nullptr) , mDisplayThreadMessageLoop(MessageLoop::current()) - , mDirectWlBufferDraw(true) - , mDelayedCommit(false) - , mFullScreenDamage(false) + , mDelayedCommitHandle(nullptr) + , mDrawToWaylandBufferDirectly(true) + , mPendingCommit(false) + , mWaylandBufferFullScreenDamage(false) , mIsMainThread(NS_IsMainThread()) + , mNeedScaleFactorUpdate(true) { } WindowSurfaceWayland::~WindowSurfaceWayland() { - delete mFrontBuffer; - delete mBackBuffer; + if (mPendingCommit) { + NS_WARNING("Deleted WindowSurfaceWayland with a pending commit!"); + } + + if (mDelayedCommitHandle) { + // Delete reference to this to prevent WaylandBufferDelayCommitHandler() + // operate on released this. mDelayedCommitHandle itself will + // be released at WaylandBufferDelayCommitHandler(). + *mDelayedCommitHandle = nullptr; + } if (mFrameCallback) { wl_callback_destroy(mFrameCallback); } + delete mWaylandBuffer; + delete mBackupBuffer; + if (!mIsMainThread) { // We can be destroyed from main thread even though we was created/used // in compositor thread. We have to unref/delete WaylandDisplay in compositor @@ -593,73 +609,64 @@ WindowSurfaceWayland::~WindowSurfaceWayl } } -void -WindowSurfaceWayland::UpdateScaleFactor() -{ - wl_surface* waylandSurface = mWindow->GetWaylandSurface(); - if (waylandSurface) { - wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor()); - } -} - WindowBackBuffer* -WindowSurfaceWayland::GetFrontBufferToDraw(int aWidth, int aHeight) +WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth, int aHeight) { - if (!mFrontBuffer) { - mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); - mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); - return mFrontBuffer; + if (!mWaylandBuffer) { + mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); + mBackupBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); + return mWaylandBuffer; } - if (!mFrontBuffer->IsAttached()) { - if (!mFrontBuffer->IsMatchingSize(aWidth, aHeight)) { - mFrontBuffer->Resize(aWidth, aHeight); + if (!mWaylandBuffer->IsAttached()) { + if (!mWaylandBuffer->IsMatchingSize(aWidth, aHeight)) { + mWaylandBuffer->Resize(aWidth, aHeight); // There's a chance that scale factor has been changed // when buffer size changed - UpdateScaleFactor(); + mNeedScaleFactorUpdate = true; } - return mFrontBuffer; + return mWaylandBuffer; } // Front buffer is used by compositor, draw to back buffer - if (mBackBuffer->IsAttached()) { + if (mBackupBuffer->IsAttached()) { NS_WARNING("No drawing buffer available"); return nullptr; } - MOZ_ASSERT(!mDelayedCommit, + MOZ_ASSERT(!mPendingCommit, "Uncommitted buffer switch, screen artifacts ahead."); - WindowBackBuffer *tmp = mFrontBuffer; - mFrontBuffer = mBackBuffer; - mBackBuffer = tmp; + WindowBackBuffer *tmp = mWaylandBuffer; + mWaylandBuffer = mBackupBuffer; + mBackupBuffer = tmp; - if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) { + if (mBackupBuffer->IsMatchingSize(aWidth, aHeight)) { // Former front buffer has the same size as a requested one. // Gecko may expect a content already drawn on screen so copy // existing data to the new buffer. - mFrontBuffer->SetImageDataFromBackBuffer(mBackBuffer); + mWaylandBuffer->SetImageDataFromBuffer(mBackupBuffer); // When buffer switches we need to damage whole screen // (https://bugzilla.redhat.com/show_bug.cgi?id=1418260) - mFullScreenDamage = true; + mWaylandBufferFullScreenDamage = true; } else { // Former buffer has different size from the new request. Only resize // the new buffer and leave gecko to render new whole content. - mFrontBuffer->Resize(aWidth, aHeight); + mWaylandBuffer->Resize(aWidth, aHeight); } - return mFrontBuffer; + return mWaylandBuffer; } already_AddRefed -WindowSurfaceWayland::LockFrontBuffer(int aWidth, int aHeight) +WindowSurfaceWayland::LockWaylandBuffer(int aWidth, int aHeight) { - WindowBackBuffer* buffer = GetFrontBufferToDraw(aWidth, aHeight); + WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight); if (buffer) { return buffer->Lock(); } - NS_WARNING("WindowSurfaceWayland::LockFrontBuffer(): No buffer available"); + NS_WARNING("WindowSurfaceWayland::LockWaylandBuffer(): No buffer available"); return nullptr; } @@ -687,8 +694,8 @@ WindowSurfaceWayland::LockImageSurface(c 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. + - mWaylandBuffer is available - that's an ideal situation. + - mWaylandBuffer 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 @@ -704,30 +711,30 @@ WindowSurfaceWayland::Lock(const LayoutD 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); + mDrawToWaylandBufferDirectly = (aRegion.GetNumRects() == 1 && + bounds.x == 0 && bounds.y == 0 && + lockSize.width == screenRect.width && + lockSize.height == screenRect.height); + + if (mDrawToWaylandBufferDirectly) { + RefPtr dt = LockWaylandBuffer(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; + mDrawToWaylandBufferDirectly = false; } return LockImageSurface(lockSize); } bool -WindowSurfaceWayland::CommitImageSurface(const LayoutDeviceIntRegion& aRegion) +WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion) { - MOZ_ASSERT(!mDirectWlBufferDraw); + MOZ_ASSERT(!mDrawToWaylandBufferDirectly); LayoutDeviceIntRect screenRect = mWindow->GetBounds(); gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); @@ -737,8 +744,8 @@ WindowSurfaceWayland::CommitImageSurface return false; } - RefPtr dt = LockFrontBuffer(screenRect.width, - screenRect.height); + RefPtr dt = LockWaylandBuffer(screenRect.width, + screenRect.height); RefPtr surf = gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(), mImageSurface->GetSize(), @@ -766,92 +773,145 @@ WindowSurfaceWayland::CommitImageSurface return true; } +static void +WaylandBufferDelayCommitHandler(WindowSurfaceWayland **aSurface) +{ + if (*aSurface) { + (*aSurface)->DelayedCommitHandler(); + } else { + // Referenced WindowSurfaceWayland is already deleted. + // Do nothing but just release the mDelayedCommitHandle allocated at + // WindowSurfaceWayland::CommitWaylandBuffer(). + free(aSurface); + } +} + void -WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) +WindowSurfaceWayland::CommitWaylandBuffer() { - MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); + MOZ_ASSERT(mPendingCommit, "Committing empty surface!"); 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."); + // Target window is not created yet - delay the commit. This can happen only + // when the window is newly created and there's no active + // frame callback pending. + MOZ_ASSERT(!mFrameCallback || waylandSurface != mLastCommittedSurface, + "Missing wayland surface at frame callback!"); + + // Do nothing if there's already mDelayedCommitHandle pending. + if (!mDelayedCommitHandle) { + mDelayedCommitHandle = static_cast( + moz_xmalloc(sizeof(*mDelayedCommitHandle))); + *mDelayedCommitHandle = this; + + MessageLoop::current()->PostDelayedTask( + NewRunnableFunction("WaylandBackBufferCommit", + &WaylandBufferDelayCommitHandler, + mDelayedCommitHandle), + EVENT_LOOP_DELAY); + } 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); + // We have an active frame callback request so handle it. + if (mFrameCallback) { + if (waylandSurface == mLastCommittedSurface) { + // We have an active frame callback pending from our recent surface. + // It means we should defer the commit to FrameCallbackHandler(). + return; + } + // If our stored wl_surface does not match the actual one it means the frame + // callback is no longer active and we should release it. + wl_callback_destroy(mFrameCallback); + mFrameCallback = nullptr; + mLastCommittedSurface = nullptr; } - if (mFullScreenDamage) { + if (mWaylandBufferFullScreenDamage) { LayoutDeviceIntRect rect = mWindow->GetBounds(); wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height); - mFullScreenDamage = false; + mWaylandBufferFullScreenDamage = false; } else { - for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) { + gint scaleFactor = mWindow->GdkScaleFactor(); + for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done(); iter.Next()) { const mozilla::LayoutDeviceIntRect &r = iter.Get(); - wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height); + // We need to remove the scale factor because the wl_surface_damage + // also multiplies by current scale factor. + wl_surface_damage(waylandSurface, r.x/scaleFactor, r.y/scaleFactor, + r.width/scaleFactor, r.height/scaleFactor); } } - // Frame callback is always connected to actual wl_surface. When the surface - // is unmapped/deleted the frame callback is never called. Unfortunatelly - // we don't know if the frame callback is not going to be called. - // But our mozcontainer code deletes wl_surface when the GdkWindow is hidden - // creates a new one when is visible. - if (mFrameCallback && mFrameCallbackSurface == waylandSurface) { - // Do nothing here - we have a valid wl_surface and the buffer will be - // commited to compositor in next frame callback event. - mDelayedCommit = true; - return; - } else { - if (mFrameCallback) { - // Delete frame callback connected to obsoleted wl_surface. - wl_callback_destroy(mFrameCallback); - } + // Clear all back buffer damage as we're committing + // all requested regions. + mWaylandBufferDamage.SetEmpty(); + + mFrameCallback = wl_surface_frame(waylandSurface); + wl_callback_add_listener(mFrameCallback, &frame_listener, this); - mFrameCallback = wl_surface_frame(waylandSurface); - wl_callback_add_listener(mFrameCallback, &frame_listener, this); - mFrameCallbackSurface = waylandSurface; - - // There's no pending frame callback so we can draw immediately - // and create frame callback for possible subsequent drawing. - mFrontBuffer->Attach(waylandSurface); - mDelayedCommit = false; + if (mNeedScaleFactorUpdate || mLastCommittedSurface != waylandSurface) { + wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor()); + mNeedScaleFactorUpdate = false; } + + mWaylandBuffer->Attach(waylandSurface); + mLastCommittedSurface = waylandSurface; + + // There's no pending commit, all changes are sent to compositor. + mPendingCommit = false; +} + +void +WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) +{ + MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); + + // We have new content at mImageSurface - copy data to mWaylandBuffer first. + if (!mDrawToWaylandBufferDirectly) { + CommitImageSurfaceToWaylandBuffer(aInvalidRegion); + } + + // If we're not at fullscreen damage add drawing area from aInvalidRegion + if (!mWaylandBufferFullScreenDamage) { + mWaylandBufferDamage.OrWith(aInvalidRegion); + } + + // We're ready to commit. + mPendingCommit = true; + CommitWaylandBuffer(); } void WindowSurfaceWayland::FrameCallbackHandler() { MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); + MOZ_ASSERT(mFrameCallback != nullptr, + "FrameCallbackHandler() called without valid frame callback!"); + MOZ_ASSERT(mLastCommittedSurface != nullptr, + "FrameCallbackHandler() called without valid wl_surface!"); - if (mFrameCallback) { - wl_callback_destroy(mFrameCallback); - mFrameCallback = nullptr; - mFrameCallbackSurface = nullptr; + wl_callback_destroy(mFrameCallback); + mFrameCallback = nullptr; + + if (mPendingCommit) { + CommitWaylandBuffer(); } +} - if (mDelayedCommit) { - wl_surface* waylandSurface = mWindow->GetWaylandSurface(); - if (!waylandSurface) { - // Target window is already destroyed - don't bother to render there. - NS_WARNING("WindowSurfaceWayland::FrameCallbackHandler(): parent wl_surface is already hidden/deleted."); - return; - } - wl_proxy_set_queue((struct wl_proxy *)waylandSurface, - mWaylandDisplay->GetEventQueue()); +void +WindowSurfaceWayland::DelayedCommitHandler() +{ + MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!"); - // Send pending surface to compositor and register frame callback - // for possible subsequent drawing. - mFrameCallback = wl_surface_frame(waylandSurface); - wl_callback_add_listener(mFrameCallback, &frame_listener, this); - mFrameCallbackSurface = waylandSurface; + *mDelayedCommitHandle = nullptr; + free(mDelayedCommitHandle); + mDelayedCommitHandle = nullptr; - mFrontBuffer->Attach(waylandSurface); - mDelayedCommit = false; + if (mPendingCommit) { + CommitWaylandBuffer(); } } diff -up firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h.old firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h --- firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h.old 2018-10-08 11:52:52.154376159 +0200 +++ firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h 2018-09-06 11:01:18.802964787 +0200 @@ -74,7 +74,7 @@ public: bool IsAttached() { return mAttached; } bool Resize(int aWidth, int aHeight); - bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer); + bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer); bool IsMatchingSize(int aWidth, int aHeight) { @@ -111,28 +111,32 @@ public: already_AddRefed Lock(const LayoutDeviceIntRegion& aRegion) override; void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final; void FrameCallbackHandler(); + void DelayedCommitHandler(); private: - WindowBackBuffer* GetFrontBufferToDraw(int aWidth, int aHeight); - void UpdateScaleFactor(); + WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight); - already_AddRefed LockFrontBuffer(int aWidth, int aHeight); + already_AddRefed LockWaylandBuffer(int aWidth, int aHeight); already_AddRefed LockImageSurface(const gfx::IntSize& aLockSize); - bool CommitImageSurface(const LayoutDeviceIntRegion& aRegion); + bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion); + void CommitWaylandBuffer(); // TODO: Do we need to hold a reference to nsWindow object? nsWindow* mWindow; nsWaylandDisplay* mWaylandDisplay; - WindowBackBuffer* mFrontBuffer; - WindowBackBuffer* mBackBuffer; + WindowBackBuffer* mWaylandBuffer; + LayoutDeviceIntRegion mWaylandBufferDamage; + WindowBackBuffer* mBackupBuffer; RefPtr mImageSurface; wl_callback* mFrameCallback; - wl_surface* mFrameCallbackSurface; + wl_surface* mLastCommittedSurface; MessageLoop* mDisplayThreadMessageLoop; - bool mDirectWlBufferDraw; - bool mDelayedCommit; - bool mFullScreenDamage; + WindowSurfaceWayland** mDelayedCommitHandle; + bool mDrawToWaylandBufferDirectly; + bool mPendingCommit; + bool mWaylandBufferFullScreenDamage; bool mIsMainThread; + bool mNeedScaleFactorUpdate; }; } // namespace widget