changeset: 553676:ca48ea0dfb91 tag: tip parent: 553673:a42aa9514794 user: stransky date: Mon Oct 05 15:06:55 2020 +0200 files: widget/gtk/WindowSurfaceWayland.cpp widget/gtk/WindowSurfaceWayland.h description: Bug 1656727 [Wayland] Track delayed commits globally, r?jhorak Track delayed commits in a global list and don't store them in actual wayland surfaces. When a delayed commit is called, check that the associated wayland surface is still valid. Differential Revision: https://phabricator.services.mozilla.com/D92432 diff --git a/widget/gtk/WindowSurfaceWayland.cpp b/widget/gtk/WindowSurfaceWayland.cpp --- a/widget/gtk/WindowSurfaceWayland.cpp +++ b/widget/gtk/WindowSurfaceWayland.cpp @@ -159,7 +159,6 @@ We allocate shared memory (shm) by mmap( between us and wayland compositor. We draw our graphics data to the shm and handle to wayland compositor by WindowBackBuffer/WindowSurfaceWayland (wl_buffer/wl_surface). - */ #define EVENT_LOOP_DELAY (1000 / 240) @@ -167,6 +166,44 @@ handle to wayland compositor by WindowBa #define BUFFER_BPP 4 gfx::SurfaceFormat WindowBackBuffer::mFormat = gfx::SurfaceFormat::B8G8R8A8; +static mozilla::Mutex* gDelayedCommitLock = nullptr; +static GList* gDelayedCommits = nullptr; + +static void DelayedCommitsEnsureMutext() { + if (!gDelayedCommitLock) { + gDelayedCommitLock = new mozilla::Mutex("DelayedCommit lock"); + } +} + +static bool DelayedCommitsCheckAndRemoveSurface( + WindowSurfaceWayland* aSurface) { + MutexAutoLock lock(*gDelayedCommitLock); + GList* foundCommit = g_list_find(gDelayedCommits, aSurface); + if (foundCommit) { + gDelayedCommits = g_list_delete_link(gDelayedCommits, foundCommit); + } + return foundCommit != nullptr; +} + +static bool DelayedCommitsCheckAndAddSurface(WindowSurfaceWayland* aSurface) { + MutexAutoLock lock(*gDelayedCommitLock); + GList* foundCommit = g_list_find(gDelayedCommits, aSurface); + if (!foundCommit) { + gDelayedCommits = g_list_prepend(gDelayedCommits, aSurface); + } + return foundCommit == nullptr; +} + +// When a new window is created we may not have a valid wl_surface +// for drawing (Gtk haven't created it yet). All commits are queued +// and CommitWaylandBuffer() is called by timer when wl_surface is ready +// for drawing. +static void WaylandBufferDelayCommitHandler(WindowSurfaceWayland* aSurface) { + if (DelayedCommitsCheckAndRemoveSurface(aSurface)) { + aSurface->CommitWaylandBuffer(); + } +} + RefPtr WindowBackBuffer::GetWaylandDisplay() { return mWindowSurfaceWayland->GetWaylandDisplay(); } @@ -400,7 +437,6 @@ WindowSurfaceWayland::WindowSurfaceWayla mWaylandFullscreenDamage(false), mFrameCallback(nullptr), mLastCommittedSurface(nullptr), - mDelayedCommitHandle(nullptr), mLastCommitTime(0), mDrawToWaylandBufferDirectly(true), mCanSwitchWaylandBuffer(true), @@ -412,6 +448,7 @@ WindowSurfaceWayland::WindowSurfaceWayla for (int i = 0; i < BACK_BUFFER_NUM; i++) { mShmBackupBuffer[i] = nullptr; } + DelayedCommitsEnsureMutext(); } WindowSurfaceWayland::~WindowSurfaceWayland() { @@ -419,12 +456,9 @@ WindowSurfaceWayland::~WindowSurfaceWayl 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; - } + // Delete reference to this to prevent WaylandBufferDelayCommitHandler() + // operate on released this. + DelayedCommitsCheckAndRemoveSurface(this); if (mFrameCallback) { wl_callback_destroy(mFrameCallback); @@ -865,23 +899,11 @@ bool WindowSurfaceWayland::CommitImageCa 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::CommitWaylandBuffer() { LOGWAYLAND(("WindowSurfaceWayland::CommitWaylandBuffer [%p]\n", (void*)this)); LOGWAYLAND( (" mDrawToWaylandBufferDirectly = %d\n", mDrawToWaylandBufferDirectly)); LOGWAYLAND((" mCanSwitchWaylandBuffer = %d\n", mCanSwitchWaylandBuffer)); - LOGWAYLAND((" mDelayedCommitHandle = %p\n", mDelayedCommitHandle)); LOGWAYLAND((" mFrameCallback = %p\n", mFrameCallback)); LOGWAYLAND((" mLastCommittedSurface = %p\n", mLastCommittedSurface)); LOGWAYLAND((" mBufferPendingCommit = %d\n", mBufferPendingCommit)); @@ -917,16 +939,10 @@ void WindowSurfaceWayland::CommitWayland 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; - + if (DelayedCommitsCheckAndAddSurface(this)) { MessageLoop::current()->PostDelayedTask( NewRunnableFunction("WaylandBackBufferCommit", - &WaylandBufferDelayCommitHandler, - mDelayedCommitHandle), + &WaylandBufferDelayCommitHandler, this), EVENT_LOOP_DELAY); } return; @@ -1039,24 +1055,5 @@ void WindowSurfaceWayland::FrameCallback CommitWaylandBuffer(); } -void WindowSurfaceWayland::DelayedCommitHandler() { - MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); - MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!"); - - LOGWAYLAND( - ("WindowSurfaceWayland::DelayedCommitHandler [%p]\n", (void*)this)); - - if (!mDelayedCommitHandle) { - LOGWAYLAND((" We're missing mDelayedCommitHandle!\n")); - return; - } - - *mDelayedCommitHandle = nullptr; - free(mDelayedCommitHandle); - mDelayedCommitHandle = nullptr; - - CommitWaylandBuffer(); -} - } // namespace widget } // namespace mozilla diff --git a/widget/gtk/WindowSurfaceWayland.h b/widget/gtk/WindowSurfaceWayland.h --- a/widget/gtk/WindowSurfaceWayland.h +++ b/widget/gtk/WindowSurfaceWayland.h @@ -161,7 +161,7 @@ class WindowSurfaceWayland : public Wind // If we fail (wayland compositor is busy, // wl_surface is not created yet) we queue the painting // and we send it to wayland compositor in FrameCallbackHandler()/ - // DelayedCommitHandler/CommitWaylandBuffer(). + // CommitWaylandBuffer(). already_AddRefed Lock( const LayoutDeviceIntRegion& aRegion) override; void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final; @@ -171,12 +171,6 @@ class WindowSurfaceWayland : public Wind // queued commits. void FrameCallbackHandler(); - // When a new window is created we may not have a valid wl_surface - // for drawing (Gtk haven't created it yet). All commits are queued - // and DelayedCommitHandler() is called by timer when wl_surface is ready - // for drawing. - void DelayedCommitHandler(); - // Try to commit all queued drawings to Wayland compositor. This is usually // called from other routines but can be used to explicitly flush // all drawings as we do when wl_buffer is released @@ -249,17 +243,14 @@ class WindowSurfaceWayland : public Wind wl_callback* mFrameCallback; wl_surface* mLastCommittedSurface; - // Registered reference to pending DelayedCommitHandler() call. - WindowSurfaceWayland** mDelayedCommitHandle; - // Cached drawings. If we can't get WaylandBuffer (wl_buffer) at // WindowSurfaceWayland::Lock() we direct gecko rendering to // mImageSurface. // If we can't get WaylandBuffer at WindowSurfaceWayland::Commit() // time, mImageSurface is moved to mDelayedImageCommits which // holds all cached drawings. - // mDelayedImageCommits can be drawn by FrameCallbackHandler(), - // DelayedCommitHandler() or when WaylandBuffer is detached. + // mDelayedImageCommits can be drawn by FrameCallbackHandler() + // or when WaylandBuffer is detached. RefPtr mImageSurface; AutoTArray mDelayedImageCommits; @@ -282,8 +273,8 @@ class WindowSurfaceWayland : public Wind // We can't send WaylandBuffer (wl_buffer) to compositor when gecko // is rendering into it (i.e. between WindowSurfaceWayland::Lock() / // WindowSurfaceWayland::Commit()). - // Thus we use mBufferCommitAllowed to disable commit by callbacks - // (FrameCallbackHandler(), DelayedCommitHandler()) + // Thus we use mBufferCommitAllowed to disable commit by + // CommitWaylandBuffer(). bool mBufferCommitAllowed; // We need to clear WaylandBuffer when entire transparent window is repainted.