commit a0d567ff3daa66e23a72d032b9b7e43dadf3a2dc
parent c3999a869557cb3a4c99357a3833ad39156a3bc4
Author: stransky <stransky@redhat.com>
Date: Thu, 20 Nov 2025 13:18:13 +0000
Bug 1998657 [Wayland] Make WaylandSurface ready to draw right after its created r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D271772
Diffstat:
8 files changed, 28 insertions(+), 265 deletions(-)
diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp
@@ -99,11 +99,6 @@ static void moz_container_wayland_invalidate(MozContainer* container) {
gdk_window_invalidate_rect(window, nullptr, true);
}
-void moz_container_wayland_add_or_fire_initial_draw_callback(
- MozContainer* container, const std::function<void(void)>& initial_draw_cb) {
- MOZ_WL_SURFACE(container)->AddOrFireReadyToDrawCallback(initial_draw_cb);
-}
-
void moz_container_wayland_unmap(GtkWidget* widget) {
g_return_if_fail(IS_MOZ_CONTAINER(widget));
@@ -143,23 +138,9 @@ gboolean moz_container_wayland_map_event(GtkWidget* widget,
// below.
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
- // Set waiting_to_show flag. It means the mozcontainer is cofigured/mapped
- // and it's supposed to be visible. *But* it's really visible when we get
- // moz_container_wayland_add_or_fire_initial_draw_callback() which means
- // wayland compositor makes it live.
- MOZ_WL_CONTAINER(widget)->waiting_to_show = true;
- MozContainer* container = MOZ_CONTAINER(widget);
- MOZ_WL_SURFACE(container)->AddOrFireReadyToDrawCallback(
- [container]() -> void {
- LOGCONTAINER(
- "[%p] moz_container_wayland_add_or_fire_initial_draw_callback set "
- "visible",
- moz_container_get_nsWindow(container));
- moz_container_wayland_clear_waiting_to_show_flag(container);
- });
-
// Don't create wl_subsurface in map_event when it's already created or
// if we create it for the first time.
+ MozContainer* container = MOZ_CONTAINER(widget);
if (MOZ_WL_SURFACE(container)->IsMapped() ||
MOZ_WL_CONTAINER(container)->before_first_size_alloc) {
return false;
@@ -300,21 +281,7 @@ struct wl_egl_window* moz_container_wayland_get_egl_window(
return MOZ_WL_SURFACE(container)->GetEGLWindow(size);
}
-gboolean moz_container_wayland_can_draw(MozContainer* container) {
- return MOZ_WL_SURFACE(container)->IsReadyToDraw();
-}
-
double moz_container_wayland_get_scale(MozContainer* container) {
nsWindow* window = moz_container_get_nsWindow(container);
return window ? window->FractionalScaleFactor() : 1.0;
}
-
-bool moz_container_wayland_is_waiting_to_show(MozContainer* container) {
- MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
- return MOZ_WL_CONTAINER(container)->waiting_to_show;
-}
-
-void moz_container_wayland_clear_waiting_to_show_flag(MozContainer* container) {
- MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
- MOZ_WL_CONTAINER(container)->waiting_to_show = false;
-}
diff --git a/widget/gtk/MozContainerWayland.h b/widget/gtk/MozContainerWayland.h
@@ -43,7 +43,6 @@ struct MozContainerWayland {
RefPtr<mozilla::widget::WaylandSurface> mSurface;
gboolean before_first_size_alloc = false;
- gboolean waiting_to_show = false;
};
void moz_container_wayland_map(GtkWidget*);
@@ -54,13 +53,7 @@ void moz_container_wayland_unmap(GtkWidget*);
struct wl_egl_window* moz_container_wayland_get_egl_window(
MozContainer* container);
-void moz_container_wayland_add_or_fire_initial_draw_callback(
- MozContainer* container, const std::function<void(void)>& initial_draw_cb);
-
wl_surface* moz_gtk_widget_get_wl_surface(GtkWidget* aWidget);
-gboolean moz_container_wayland_can_draw(MozContainer* container);
double moz_container_wayland_get_scale(MozContainer* container);
-bool moz_container_wayland_is_waiting_to_show(MozContainer* container);
-void moz_container_wayland_clear_waiting_to_show_flag(MozContainer* container);
#endif /* __MOZ_CONTAINER_WAYLAND_H__ */
diff --git a/widget/gtk/WaylandSurface.cpp b/widget/gtk/WaylandSurface.cpp
@@ -117,104 +117,6 @@ WaylandSurface::~WaylandSurface() {
"We can't release WaylandSurface with numap callback!");
}
-void WaylandSurface::ReadyToDrawFrameCallbackHandler(
- struct wl_callback* callback) {
- LOGWAYLAND(
- "WaylandSurface::ReadyToDrawFrameCallbackHandler() "
- "mReadyToDrawFrameCallback %p mIsReadyToDraw %d initial_draw callback "
- "%zd\n",
- (void*)mReadyToDrawFrameCallback, (bool)mIsReadyToDraw,
- mReadyToDrawCallbacks.size());
-
- // We're supposed to run on main thread only.
- AssertIsOnMainThread();
-
- // mReadyToDrawFrameCallback/callback can be nullptr when redering directly
- // to GtkWidget and ReadyToDrawFrameCallbackHandler is called by us from main
- // thread by WaylandSurface::Map().
- MOZ_RELEASE_ASSERT(mReadyToDrawFrameCallback == callback);
-
- std::vector<std::function<void(void)>> cbs;
- {
- WaylandSurfaceLock lock(this);
- MozClearPointer(mReadyToDrawFrameCallback, wl_callback_destroy);
- // It's possible that we're already unmapped so quit in such case.
- if (!mIsMapped) {
- LOGWAYLAND(" WaylandSurface is unmapped, quit.");
- if (!mReadyToDrawCallbacks.empty()) {
- NS_WARNING("Unmapping WaylandSurface with active draw callback!");
- mReadyToDrawCallbacks.clear();
- }
- return;
- }
- if (mIsReadyToDraw) {
- return;
- }
- mIsReadyToDraw = true;
- cbs = std::move(mReadyToDrawCallbacks);
-
- RequestFrameCallbackLocked(lock);
- }
-
- // We can't call the callbacks under lock
-#ifdef MOZ_LOGGING
- int callbackNum = 0;
-#endif
- for (auto const& cb : cbs) {
- LOGWAYLAND(" initial callback fire [%d]", callbackNum++);
- cb();
- }
-}
-
-static void ReadyToDrawFrameCallbackHandler(void* aWaylandSurface,
- struct wl_callback* callback,
- uint32_t time) {
- auto* waylandSurface = static_cast<WaylandSurface*>(aWaylandSurface);
- waylandSurface->ReadyToDrawFrameCallbackHandler(callback);
-}
-
-static const struct wl_callback_listener
- sWaylandSurfaceReadyToDrawFrameListener = {
- ::ReadyToDrawFrameCallbackHandler};
-
-void WaylandSurface::AddReadyToDrawCallbackLocked(
- const WaylandSurfaceLock& aProofOfLock,
- const std::function<void(void)>& aDrawCB) {
- LOGVERBOSE("WaylandSurface::AddReadyToDrawCallbackLocked()");
- MOZ_DIAGNOSTIC_ASSERT(&aProofOfLock == mSurfaceLock);
- mReadyToDrawCallbacks.push_back(aDrawCB);
-}
-
-void WaylandSurface::AddOrFireReadyToDrawCallback(
- const std::function<void(void)>& aDrawCB) {
- {
- WaylandSurfaceLock lock(this);
- if (!mIsReadyToDraw) {
- LOGVERBOSE(
- "WaylandSurface::AddOrFireReadyToDrawCallback() callback stored");
- mReadyToDrawCallbacks.push_back(aDrawCB);
- return;
- }
- }
-
- LOGWAYLAND("WaylandSurface::AddOrFireReadyToDrawCallback() callback fire");
-
- // We're ready to draw and we have a surface to draw into.
- aDrawCB();
-}
-
-void WaylandSurface::ClearReadyToDrawCallbacksLocked(
- const WaylandSurfaceLock& aProofOfLock) {
- MOZ_DIAGNOSTIC_ASSERT(&aProofOfLock == mSurfaceLock);
- MozClearPointer(mReadyToDrawFrameCallback, wl_callback_destroy);
- mReadyToDrawCallbacks.clear();
-}
-
-void WaylandSurface::ClearReadyToDrawCallbacks() {
- WaylandSurfaceLock lock(this);
- ClearReadyToDrawCallbacksLocked(lock);
-}
-
bool WaylandSurface::HasEmulatedFrameCallbackLocked(
const WaylandSurfaceLock& aProofOfLock) const {
return mFrameCallbackHandler.IsSet() && mFrameCallbackHandler.mEmulated;
@@ -233,7 +135,7 @@ void WaylandSurface::FrameCallbackHandler(struct wl_callback* aCallback,
WaylandSurfaceLock lock(this);
// Don't run emulated callbacks on hidden surfaces
- if ((emulatedCallback || aRoutedFromChildSurface) && !mIsReadyToDraw) {
+ if ((emulatedCallback || aRoutedFromChildSurface) && !mIsVisible) {
return;
}
@@ -259,6 +161,7 @@ void WaylandSurface::FrameCallbackHandler(struct wl_callback* aCallback,
// We're getting regular frame callback from this surface so we must
// have buffer attached.
if (!emulatedCallback && !aRoutedFromChildSurface) {
+ mIsVisible = true;
mBufferAttached = true;
}
@@ -478,8 +381,7 @@ bool WaylandSurface::MapLocked(const WaylandSurfaceLock& aProofOfLock,
wl_surface* aParentWLSurface,
WaylandSurfaceLock* aParentWaylandSurfaceLock,
gfx::IntPoint aSubsurfacePosition,
- bool aSubsurfaceDesync,
- bool aUseReadyToDrawCallback) {
+ bool aSubsurfaceDesync) {
LOGWAYLAND("WaylandSurface::MapLocked()");
MOZ_DIAGNOSTIC_ASSERT(&aProofOfLock == mSurfaceLock);
MOZ_DIAGNOSTIC_ASSERT(!mIsMapped, "Already mapped?");
@@ -519,14 +421,6 @@ bool WaylandSurface::MapLocked(const WaylandSurfaceLock& aProofOfLock,
LOGWAYLAND(" subsurface position [%d,%d]", (int)mSubsurfacePosition.x,
(int)mSubsurfacePosition.y);
- if (aUseReadyToDrawCallback) {
- mReadyToDrawFrameCallback = wl_surface_frame(mParentSurface);
- wl_callback_add_listener(mReadyToDrawFrameCallback,
- &sWaylandSurfaceReadyToDrawFrameListener, this);
- LOGWAYLAND(" created ready to draw frame callback ID %d\n",
- wl_proxy_get_id((struct wl_proxy*)mReadyToDrawFrameCallback));
- }
-
LOGWAYLAND(" register frame callback");
RequestFrameCallbackLocked(aProofOfLock);
@@ -556,8 +450,7 @@ bool WaylandSurface::MapLocked(const WaylandSurfaceLock& aProofOfLock,
gfx::IntPoint aSubsurfacePosition) {
return MapLocked(aProofOfLock, nullptr, aParentWaylandSurfaceLock,
aSubsurfacePosition,
- /* aSubsurfaceDesync */ false,
- /* aUseReadyToDrawCallback */ false);
+ /* aSubsurfaceDesync */ false);
}
void WaylandSurface::SetUnmapCallbackLocked(
@@ -609,11 +502,11 @@ void WaylandSurface::UnmapLocked(WaylandSurfaceLock& aSurfaceLock) {
return;
}
mIsMapped = false;
+ mIsVisible = false;
LOGWAYLAND("WaylandSurface::UnmapLocked()");
RemoveAttachedBufferLocked(aSurfaceLock);
- ClearReadyToDrawCallbacksLocked(aSurfaceLock);
ClearFrameCallbackLocked(aSurfaceLock);
ClearScaleLocked(aSurfaceLock);
@@ -633,9 +526,6 @@ void WaylandSurface::UnmapLocked(WaylandSurfaceLock& aSurfaceLock) {
MozClearPointer(mPendingOpaqueRegion, wl_region_destroy);
MozClearPointer(mOpaqueRegionFrameCallback, wl_callback_destroy);
- mIsReadyToDraw = false;
- mBufferAttached = false;
-
// Remove references to WaylandBuffers attached to mSurface,
// we don't want to get any buffer release callback when we're unmapped.
ReleaseAllWaylandTransactionsLocked(aSurfaceLock);
diff --git a/widget/gtk/WaylandSurface.h b/widget/gtk/WaylandSurface.h
@@ -43,10 +43,6 @@ class WaylandSurface final {
void SetLoggingWidget(void* aWidget) { mLoggingWidget = aWidget; }
#endif
- void ReadyToDrawFrameCallbackHandler(struct wl_callback* aCallback);
- void AddOrFireReadyToDrawCallback(const std::function<void(void)>& aDrawCB);
- void ClearReadyToDrawCallbacks();
-
void FrameCallbackHandler(struct wl_callback* aCallback, uint32_t aTime,
bool aRoutedFromChildSurface);
@@ -78,11 +74,12 @@ class WaylandSurface final {
bool SetEGLWindowSize(LayoutDeviceIntSize aSize);
bool HasEGLWindow() const { return !!mEGLWindow; }
- // Read to draw means we got frame callback from parent surface
- // where we attached to.
- bool IsReadyToDraw() const { return mIsReadyToDraw; }
// Mapped means we have all internals created.
bool IsMapped() const { return mIsMapped; }
+
+ // We've got first frame callback so we're really visible now.
+ bool IsVisible() const { return mIsVisible; }
+
// Indicate that Wayland surface uses Gdk resources which
// need to be released on main thread by GdkCleanUpLocked().
// It may be called after Unmap() to make sure
@@ -122,10 +119,6 @@ class WaylandSurface final {
bool CreateViewportLocked(const WaylandSurfaceLock& aProofOfLock,
bool aFollowsSizeChanges);
- void AddReadyToDrawCallbackLocked(
- const WaylandSurfaceLock& aProofOfLock,
- const std::function<void(void)>& aInitialDrawCB);
-
// Attach WaylandBuffer which shows WaylandBuffer content
// on screen.
bool AttachLocked(const WaylandSurfaceLock& aSurfaceLock,
@@ -281,8 +274,7 @@ class WaylandSurface final {
bool MapLocked(const WaylandSurfaceLock& aProofOfLock,
wl_surface* aParentWLSurface,
WaylandSurfaceLock* aParentWaylandSurfaceLock,
- gfx::IntPoint aSubsurfacePosition, bool aSubsurfaceDesync,
- bool aUseReadyToDrawCallback = true);
+ gfx::IntPoint aSubsurfacePosition, bool aSubsurfaceDesync);
void SetSizeLocked(const WaylandSurfaceLock& aProofOfLock,
gfx::IntSize aSizeScaled, gfx::IntSize aUnscaledSize);
@@ -304,21 +296,18 @@ class WaylandSurface final {
bool HasEmulatedFrameCallbackLocked(
const WaylandSurfaceLock& aProofOfLock) const;
- void ClearReadyToDrawCallbacksLocked(const WaylandSurfaceLock& aProofOfLock);
-
void ClearScaleLocked(const WaylandSurfaceLock& aProofOfLock);
// Weak ref to owning widget (nsWindow or NativeLayerWayland),
// used for diagnostics/logging only.
void* mLoggingWidget = nullptr;
- // WaylandSurface mapped - we have valid wl_surface where we can paint to.
+ // mIsMapped means we're supposed to be visible
+ // (or not if Wayland compositor decides so).
mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped{false};
- // Wayland shows only subsurfaces of visible parent surfaces.
- // mIsReadyToDraw means our parent wl_surface has content so
- // this WaylandSurface can be visible on screen and get get frame callback.
- mozilla::Atomic<bool, mozilla::Relaxed> mIsReadyToDraw{false};
+ // mIsVisible means we're really visible as we've got frame callback.
+ mozilla::Atomic<bool, mozilla::Relaxed> mIsVisible{false};
// We used Gdk functions which needs clean up in main thread.
mozilla::Atomic<bool, mozilla::Relaxed> mIsPendingGdkCleanup{false};
@@ -385,11 +374,6 @@ class WaylandSurface final {
bool mBufferTransformFlippedX = false;
bool mBufferTransformFlippedY = false;
- // Frame callback registered to parent surface. When we get it we know
- // parent surface is ready and we can paint.
- wl_callback* mReadyToDrawFrameCallback = nullptr;
- std::vector<std::function<void(void)>> mReadyToDrawCallbacks;
-
// Frame callbacks of this surface
wl_callback* mFrameCallback = nullptr;
diff --git a/widget/gtk/WindowSurfaceWaylandMultiBuffer.cpp b/widget/gtk/WindowSurfaceWaylandMultiBuffer.cpp
@@ -156,9 +156,7 @@ WindowSurfaceWaylandMB::WindowSurfaceWaylandMB(
RefPtr<nsWindow> aWindow, GtkCompositorWidget* aCompositorWidget)
: mSurfaceLock("WindowSurfaceWayland lock"),
mWindow(std::move(aWindow)),
- mCompositorWidget(aCompositorWidget),
- mFrameInProcess(false),
- mCallbackRequested(false) {}
+ mCompositorWidget(aCompositorWidget) {}
bool WindowSurfaceWaylandMB::MaybeUpdateWindowSize() {
// We want to get window size from compositor widget as it matches window
@@ -189,7 +187,6 @@ already_AddRefed<DrawTarget> WindowSurfaceWaylandMB::Lock(
if (mWindow->GetWindowType() == WindowType::Invisible) {
return nullptr;
}
- mFrameInProcess = true;
CollectPendingSurfaces(lock);
@@ -281,30 +278,10 @@ void WindowSurfaceWaylandMB::Commit(
// invisible window
return;
}
- mFrameInProcess = false;
MozContainer* container = mWindow->GetMozContainer();
WaylandSurface* waylandSurface = MOZ_WL_SURFACE(container);
WaylandSurfaceLock lock(waylandSurface);
- if (!waylandSurface->IsMapped()) {
- LOGWAYLAND(
- "WindowSurfaceWaylandMB::Commit [%p] frame queued: can't lock "
- "wl_surface\n",
- (void*)mWindow.get());
- if (!mCallbackRequested) {
- RefPtr<WindowSurfaceWaylandMB> self(this);
- waylandSurface->AddReadyToDrawCallbackLocked(
- lock, [self, aInvalidRegion]() -> void {
- MutexAutoLock lock(self->mSurfaceLock);
- if (!self->mFrameInProcess) {
- self->Commit(lock, aInvalidRegion);
- }
- self->mCallbackRequested = false;
- });
- mCallbackRequested = true;
- }
- return;
- }
waylandSurface->InvalidateRegionLocked(lock,
aInvalidRegion.ToUnknownRegion());
diff --git a/widget/gtk/WindowSurfaceWaylandMultiBuffer.h b/widget/gtk/WindowSurfaceWaylandMultiBuffer.h
@@ -73,10 +73,6 @@ class WindowSurfaceWaylandMB : public WindowSurface {
nsTArray<RefPtr<WaylandBufferSHM>> mInUseBuffers;
nsTArray<RefPtr<WaylandBufferSHM>> mPendingBuffers;
nsTArray<RefPtr<WaylandBufferSHM>> mAvailableBuffers;
-
- // delayed commits
- bool mFrameInProcess;
- bool mCallbackRequested;
};
} // namespace mozilla::widget
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
@@ -1371,15 +1371,6 @@ void nsWindow::HideWaylandPopupWindow(bool aTemporaryHide,
if (mPopupClosed) {
LOG(" Clearing mMoveToRectPopupSize\n");
mMoveToRectPopupSize = {};
-#ifdef MOZ_WAYLAND
- if (moz_container_wayland_is_waiting_to_show(mContainer)) {
- // We need to clear rendering queue, see Bug 1782948.
- LOG(" popup failed to show by Wayland compositor, clear rendering "
- "queue.");
- moz_container_wayland_clear_waiting_to_show_flag(mContainer);
- ClearRenderingQueue();
- }
-#endif
}
}
@@ -1440,8 +1431,7 @@ void nsWindow::WaylandPopupCloseOrphanedPopups() {
nsWindow* popup = mWaylandPopupNext;
bool dangling = false;
while (popup) {
- if (!dangling &&
- moz_container_wayland_is_waiting_to_show(popup->GetMozContainer())) {
+ if (!dangling && !MOZ_WL_SURFACE(popup->GetMozContainer())->IsVisible()) {
LOG(" popup [%p] is waiting to show, close all child popups", popup);
dangling = true;
} else if (dangling) {
@@ -4117,13 +4107,6 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) {
LOG("quit, !mGdkWindow || !mHasMappedToplevel");
return FALSE;
}
-#ifdef MOZ_WAYLAND
- if (!mIsDragPopup && GdkIsWaylandDisplay() &&
- !moz_container_wayland_can_draw(mContainer)) {
- LOG("quit, !moz_container_wayland_can_draw()");
- return FALSE;
- }
-#endif
if (!GetPaintListener()) {
LOG("quit, !GetPaintListener()");
@@ -6230,33 +6213,19 @@ void nsWindow::ConfigureCompositor() {
MOZ_DIAGNOSTIC_ASSERT(mIsMapped);
LOG("nsWindow::ConfigureCompositor()");
- auto startCompositing = [self = RefPtr{this}, this]() -> void {
- LOG(" moz_container_wayland_add_or_fire_initial_draw_callback "
- "ConfigureCompositor");
-
- // too late
- if (mIsDestroyed || !mIsMapped) {
- LOG(" quit, mIsDestroyed = %d mIsMapped = %d", !!mIsDestroyed,
- !!mIsMapped);
- return;
- }
- // Compositor will be resumed at nsWindow::SetCompositorWidgetDelegate().
- if (!mCompositorWidgetDelegate) {
- LOG(" quit, missing mCompositorWidgetDelegate");
- return;
- }
- ResumeCompositorImpl();
- };
-
- if (GdkIsWaylandDisplay()) {
-#ifdef MOZ_WAYLAND
- moz_container_wayland_add_or_fire_initial_draw_callback(mContainer,
- startCompositing);
-#endif
- } else {
- startCompositing();
+ if (mIsDestroyed || !mIsMapped) {
+ LOG(" quit, mIsDestroyed = %d mIsMapped = %d", !!mIsDestroyed,
+ !!mIsMapped);
+ return;
}
+ // Compositor will be resumed at nsWindow::SetCompositorWidgetDelegate().
+ if (!mCompositorWidgetDelegate) {
+ LOG(" quit, missing mCompositorWidgetDelegate");
+ return;
+ }
+
+ ResumeCompositorImpl();
}
nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
@@ -9981,15 +9950,6 @@ nsWindow* nsWindow::GetWindow(GdkWindow* window) {
return get_window_for_gdk_window(window);
}
-void nsWindow::ClearRenderingQueue() {
- LOG("nsWindow::ClearRenderingQueue()");
-
- if (mWidgetListener) {
- mWidgetListener->RequestWindowClose(this);
- }
- DestroyLayerManager();
-}
-
// nsWindow::OnMap() / nsWindow::OnUnmap() is called from map/unmap mContainer
// handlers directly as we paint to mContainer.
void nsWindow::OnMap() {
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
@@ -498,10 +498,6 @@ class nsWindow final : public nsIWidget {
void ResumeCompositorImpl();
- // Force hide this window, remove compositor etc. to avoid
- // rendering queue blocking (see Bug 1782948).
- void ClearRenderingQueue();
-
bool ApplyEnterLeaveMutterWorkaround();
void NotifyOcclusionState(mozilla::widget::OcclusionState aState) override;