commit 65ab92784908b38307a512257d480dcfde9dcbb2
parent a0d567ff3daa66e23a72d032b9b7e43dadf3a2dc
Author: stransky <stransky@redhat.com>
Date: Thu, 20 Nov 2025 13:18:13 +0000
Bug 1998657 [Wayland] Create EGLWindow over offscreen wl_surface and make it always available r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D271773
Diffstat:
4 files changed, 45 insertions(+), 148 deletions(-)
diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp
@@ -262,25 +262,6 @@ static bool moz_container_wayland_ensure_surface(MozContainer* container,
return true;
}
-struct wl_egl_window* moz_container_wayland_get_egl_window(
- MozContainer* container) {
- LOGCONTAINER("%s [%p] mapped %d eglwindow %d", __FUNCTION__,
- (void*)moz_container_get_nsWindow(container),
- MOZ_WL_SURFACE(container)->IsMapped(),
- MOZ_WL_SURFACE(container)->HasEGLWindow());
-
- if (!MOZ_WL_SURFACE(container)->IsMapped()) {
- return nullptr;
- }
-
- // TODO: Get size from bounds instead of GdkWindow?
- // We may be in rendering/compositor thread here.
- GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
- DesktopIntSize size(gdk_window_get_width(window),
- gdk_window_get_height(window));
- return MOZ_WL_SURFACE(container)->GetEGLWindow(size);
-}
-
double moz_container_wayland_get_scale(MozContainer* container) {
nsWindow* window = moz_container_get_nsWindow(container);
return window ? window->FractionalScaleFactor() : 1.0;
diff --git a/widget/gtk/MozContainerWayland.h b/widget/gtk/MozContainerWayland.h
@@ -49,10 +49,6 @@ void moz_container_wayland_map(GtkWidget*);
gboolean moz_container_wayland_map_event(GtkWidget*, GdkEventAny*);
void moz_container_wayland_size_allocate(GtkWidget*, GtkAllocation*);
void moz_container_wayland_unmap(GtkWidget*);
-
-struct wl_egl_window* moz_container_wayland_get_egl_window(
- MozContainer* container);
-
wl_surface* moz_gtk_widget_get_wl_surface(GtkWidget* aWidget);
double moz_container_wayland_get_scale(MozContainer* container);
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
@@ -411,8 +411,7 @@ static void GtkWindowSetTransientFor(GtkWindow* aWindow, GtkWindow* aParent) {
}
nsWindow::nsWindow()
- : mWindowVisibilityMutex("nsWindow::mWindowVisibilityMutex"),
- mIsMapped(false),
+ : mIsMapped(false),
mIsDestroyed(false),
mIsShown(false),
mNeedsShow(false),
@@ -719,6 +718,14 @@ void nsWindow::Destroy() {
gtk_accessible_set_widget(GTK_ACCESSIBLE(ac), nullptr);
}
+ // Owned by WaylandSurface or it's X11 ID,
+ // just drop the reference here.
+ mEGLWindow = nullptr;
+
+ // mGdkWindow is owned by mContainer, will be deleted with mShell/mContainer.
+ g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
+ mGdkWindow = nullptr;
+
gtk_widget_destroy(mShell);
mShell = nullptr;
mContainer = nullptr;
@@ -726,9 +733,6 @@ void nsWindow::Destroy() {
mSurface = nullptr;
#endif
- MOZ_ASSERT(!mGdkWindow,
- "mGdkWindow should be NULL when mContainer is destroyed");
-
#ifdef ACCESSIBILITY
if (mRootAccessible) {
mRootAccessible = nullptr;
@@ -3740,41 +3744,8 @@ void* nsWindow::GetNativeData(uint32_t aDataType) {
}
case NS_NATIVE_OPENGL_CONTEXT:
return nullptr;
- case NS_NATIVE_EGL_WINDOW: {
- // On X11 we call it:
- // 1) If window is mapped on OnMap() by nsWindow::ResumeCompositorImpl(),
- // new EGLSurface/XWindow is created.
- // 2) If window is hidden on OnUnmap(), we replace EGLSurface/XWindow
- // by offline surface and release XWindow.
-
- // On Wayland it:
- // 1) If window is mapped on OnMap(), we request frame callback
- // at MozContainer. If we get frame callback at MozContainer,
- // nsWindow::ResumeCompositorImpl() is called from it
- // and EGLSurface/wl_surface is created.
- // 2) If window is hidden on OnUnmap(), we replace EGLSurface/wl_surface
- // by offline surface and release XWindow.
-
- // If nsWindow is already destroyed, don't try to get EGL window at all,
- // we're going to be deleted anyway.
- MutexAutoLock lock(mWindowVisibilityMutex);
- void* eglWindow = nullptr;
- if (mIsMapped && !mIsDestroyed) {
-#ifdef MOZ_X11
- if (GdkIsX11Display()) {
- eglWindow = (void*)GDK_WINDOW_XID(mGdkWindow);
- }
-#endif
-#ifdef MOZ_WAYLAND
- if (GdkIsWaylandDisplay()) {
- eglWindow = moz_container_wayland_get_egl_window(mContainer);
- }
-#endif
- }
- LOG("Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned eglWindow %p",
- mGdkWindow, eglWindow);
- return eglWindow;
- }
+ case NS_NATIVE_EGL_WINDOW:
+ return mIsDestroyed ? nullptr : mEGLWindow;
default:
NS_WARNING("nsWindow::GetNativeData called with bad value");
return nullptr;
@@ -4103,8 +4074,8 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) {
}
// Windows that are not visible will be painted after they become visible.
- if (!mGdkWindow || !mHasMappedToplevel) {
- LOG("quit, !mGdkWindow || !mHasMappedToplevel");
+ if (!mHasMappedToplevel) {
+ LOG("quit, !mHasMappedToplevel");
return FALSE;
}
@@ -4138,8 +4109,8 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) {
// If the window has been destroyed during the will paint notification,
// there is nothing left to do.
- if (!mGdkWindow || mIsDestroyed) {
- LOG("quit, !mGdkWindow || mIsDestroyed");
+ if (mIsDestroyed) {
+ LOG("quit, mIsDestroyed");
return TRUE;
}
@@ -4292,7 +4263,7 @@ gboolean nsWindow::OnShellConfigureEvent(GdkEventConfigure* aEvent) {
// Don't fire configure event for scale changes, we handle that
// OnScaleEvent event. Skip that for toplevel windows only.
- if (mGdkWindow && IsTopLevelWidget() &&
+ if (IsTopLevelWidget() &&
mCeiledScaleFactor != gdk_window_get_scale_factor(mGdkWindow)) {
LOG(" scale factor changed to %d, return early",
gdk_window_get_scale_factor(mGdkWindow));
@@ -4318,10 +4289,6 @@ void nsWindow::OnContainerSizeAllocate(GtkAllocation* aAllocation) {
mReceivedClientArea = DesktopIntRect(aAllocation->x, aAllocation->y,
aAllocation->width, aAllocation->height);
- if (!mGdkWindow) {
- return;
- }
-
// Bounds will get updated on the main configure.
// Gecko permits running nested event loops during processing of events,
// GtkWindow callers of gtk_widget_size_allocate expect the signal handlers
@@ -4592,10 +4559,6 @@ void nsWindow::EmulateResizeDrag(GdkEventMotion* aEvent) {
void nsWindow::OnMotionNotifyEvent(GdkEventMotion* aEvent) {
mLastMouseCoordinates.Set(aEvent);
- if (!mGdkWindow) {
- return;
- }
-
// Emulate gdk_window_begin_resize_drag() for windows
// with fixed aspect ratio on Wayland.
if (mAspectResizer && mAspectRatio != 0.0f) {
@@ -4612,10 +4575,8 @@ void nsWindow::OnMotionNotifyEvent(GdkEventMotion* aEvent) {
GdkWindow* dragWindow = nullptr;
// find the top-level window
- if (mGdkWindow) {
- dragWindow = gdk_window_get_toplevel(mGdkWindow);
- MOZ_ASSERT(dragWindow, "gdk_window_get_toplevel should not return null");
- }
+ dragWindow = gdk_window_get_toplevel(mGdkWindow);
+ MOZ_ASSERT(dragWindow, "gdk_window_get_toplevel should not return null");
#ifdef MOZ_X11
if (dragWindow && GdkIsX11Display()) {
@@ -5050,10 +5011,6 @@ void nsWindow::OnButtonReleaseEvent(GdkEventButton* aEvent) {
SetLastPointerDownEvent(nullptr);
mLastMouseCoordinates.Set(aEvent);
- if (!mGdkWindow) {
- return;
- }
-
if (mAspectResizer) {
mAspectResizer = Nothing();
return;
@@ -5231,7 +5188,7 @@ WidgetEventTime nsWindow::GetWidgetEventTime(guint32 aEventTime) {
}
TimeStamp nsWindow::GetEventTimeStamp(guint32 aEventTime) {
- if (MOZ_UNLIKELY(!mGdkWindow)) {
+ if (MOZ_UNLIKELY(mIsDestroyed)) {
// nsWindow has been Destroy()ed.
return TimeStamp::Now();
}
@@ -5267,7 +5224,6 @@ TimeStamp nsWindow::GetEventTimeStamp(guint32 aEventTime) {
#ifdef MOZ_X11
mozilla::CurrentX11TimeGetter* nsWindow::GetCurrentTimeGetter() {
- MOZ_ASSERT(mGdkWindow, "Expected mGdkWindow to be set");
if (MOZ_UNLIKELY(!mCurrentTimeGetter)) {
mCurrentTimeGetter = MakeUnique<CurrentX11TimeGetter>(mGdkWindow);
}
@@ -5721,7 +5677,7 @@ void nsWindow::OnCompositedChanged() {
// Let's follow the working scenario for now to avoid complexity
// and maybe fix that later.
void nsWindow::OnScaleEvent() {
- if (!mGdkWindow || !IsTopLevelWidget()) {
+ if (!IsTopLevelWidget()) {
return;
}
@@ -5739,7 +5695,6 @@ void nsWindow::RefreshScale(bool aRefreshScreen, bool aForceRefresh) {
LOG("nsWindow::RefreshScale() GdkWindow scale %d refresh %d",
gdk_window_get_scale_factor(mGdkWindow), aRefreshScreen);
- MOZ_DIAGNOSTIC_ASSERT(mIsMapped && mGdkWindow);
int ceiledScale = gdk_window_get_scale_factor(mGdkWindow);
const bool scaleChanged =
aForceRefresh || GdkCeiledScaleFactor() != ceiledScale;
@@ -5784,7 +5739,7 @@ void nsWindow::SetDragPopupSurface(
mDragPopupSurface = aDragPopupSurface;
mDragPopupSurfaceRegion = aInvalidRegion;
- if (mGdkWindow) {
+ if (!mIsDestroyed) {
gdk_window_invalidate_rect(mGdkWindow, nullptr, false);
}
}
@@ -6195,28 +6150,17 @@ nsCString nsWindow::GetPopupTypeName() {
Window nsWindow::GetX11Window() {
#ifdef MOZ_X11
if (GdkIsX11Display()) {
- return mGdkWindow ? gdk_x11_window_get_xid(mGdkWindow) : X11None;
+ return gdk_x11_window_get_xid(mGdkWindow);
}
#endif
return (Window) nullptr;
}
-void nsWindow::EnsureGdkWindow() {
- MOZ_DIAGNOSTIC_ASSERT(mIsMapped);
- if (!mGdkWindow) {
- mGdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer));
- g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
- }
-}
-
void nsWindow::ConfigureCompositor() {
- MOZ_DIAGNOSTIC_ASSERT(mIsMapped);
-
LOG("nsWindow::ConfigureCompositor()");
- if (mIsDestroyed || !mIsMapped) {
- LOG(" quit, mIsDestroyed = %d mIsMapped = %d", !!mIsDestroyed,
- !!mIsMapped);
+ if (mIsDestroyed) {
+ LOG(" quit, mIsDestroyed = %d", !!mIsDestroyed);
return;
}
// Compositor will be resumed at nsWindow::SetCompositorWidgetDelegate().
@@ -6479,6 +6423,24 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
gtk_widget_realize(container);
+ mGdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer));
+ g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
+
+#ifdef MOZ_X11
+ if (GdkIsX11Display()) {
+ mEGLWindow = (void*)GDK_WINDOW_XID(mGdkWindow);
+ }
+#endif
+#ifdef MOZ_WAYLAND
+ if (GdkIsWaylandDisplay() && mIsAccelerated) {
+ mEGLWindow = MOZ_WL_SURFACE(container)->GetEGLWindow(mClientArea.Size());
+ }
+#endif
+ if (mEGLWindow) {
+ LOG("Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned mEGLWindow %p",
+ mGdkWindow, mEGLWindow);
+ }
+
// make sure this is the focus widget in the container
gtk_widget_show(container);
@@ -9921,7 +9883,7 @@ bool nsWindow::SetEGLNativeWindowSize(
// SetEGLNativeWindowSize() is Wayland only call.
MOZ_ASSERT(GdkIsWaylandDisplay());
- if (!mIsMapped) {
+ if (mIsDestroyed) {
return true;
}
@@ -9958,10 +9920,8 @@ void nsWindow::OnMap() {
MaybeCreatePipResources();
{
- MutexAutoLock lock(mWindowVisibilityMutex);
mIsMapped = true;
- EnsureGdkWindow();
RefreshScale(/* aRefreshScreen */ false);
if (mIsAlert) {
@@ -10013,7 +9973,6 @@ void nsWindow::OnUnmap() {
ClearPipResources();
{
- MutexAutoLock lock(mWindowVisibilityMutex);
mIsMapped = false;
mHasReceivedSizeAllocate = false;
@@ -10027,23 +9986,8 @@ void nsWindow::OnUnmap() {
}
}
- if (mGdkWindow) {
- g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
- mGdkWindow = nullptr;
- }
-
// Reset scale for hidden windows
mCeiledScaleFactor = sNoScale;
-
- // Clear resources (mainly XWindow) stored at GtkCompositorWidget.
- // It makes sure we don't paint to it when nsWindow becomes hiden/deleted
- // and XWindow is released.
- if (mCompositorWidgetDelegate) {
- mCompositorWidgetDelegate->CleanupResources();
- }
-
- // Clear nsWindow resources used for old (in-thread) rendering.
- mSurfaceProvider.CleanupResources();
}
// Until bug 1654938 is fixed we delete layer manager for hidden popups,
@@ -10054,28 +9998,6 @@ void nsWindow::OnUnmap() {
// see bug 1958695.
if (mWindowType == WindowType::Popup && !mPopupTemporaryHidden) {
DestroyLayerManager();
- } else {
- // Widget is backed by OpenGL EGLSurface created over wl_surface/XWindow.
- //
- // RenderCompositorEGL::Resume() deletes recent EGLSurface,
- // calls nsWindow::GetNativeData(NS_NATIVE_EGL_WINDOW) from compositor
- // thread to get new native rendering surface.
- //
- // For hidden/unmapped windows we return nullptr NS_NATIVE_EGL_WINDOW at
- // nsWindow::GetNativeData() so RenderCompositorEGL::Resume() creates
- // offscreen fallback EGLSurface to avoid compositor pause.
- //
- // We don't want to pause compositor as it may lead to whole
- // browser freeze (Bug 1777664).
- //
- // If RenderCompositorSWGL compositor is used (SW fallback)
- // RenderCompositorSWGL::Resume() only requests full render for next paint
- // as wl_surface/XWindow is managed by WindowSurfaceProvider owned
- // directly by GtkCompositorWidget and that's covered by
- // mCompositorWidgetDelegate->CleanupResources() call above.
- if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) {
- remoteRenderer->SendResume();
- }
}
}
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
@@ -548,7 +548,6 @@ class nsWindow final : public nsIWidget {
GtkWidget* GetToplevelWidget() const;
nsWindow* GetContainerWindow() const;
Window GetX11Window();
- void EnsureGdkWindow();
void SetUrgencyHint(GtkWidget* top_window, bool state);
void SetDefaultIcon(void);
void SetWindowDecoration(BorderStyle aStyle);
@@ -592,6 +591,8 @@ class nsWindow final : public nsIWidget {
GtkWidget* mShell = nullptr;
MozContainer* mContainer = nullptr;
GdkWindow* mGdkWindow = nullptr;
+ // mEGLWindow is owned by WaylandSurface or it's X11 ID.
+ void* mEGLWindow = nullptr;
#ifdef MOZ_WAYLAND
RefPtr<mozilla::widget::WaylandSurface> mSurface;
#endif
@@ -700,9 +701,6 @@ class nsWindow final : public nsIWidget {
// If true, draw our own window titlebar.
bool mDrawInTitlebar = false;
- // This mutex protect window visibility changes.
- mozilla::Mutex mWindowVisibilityMutex;
-
// This track real window visibility from OS perspective.
// It's set by OnMap/OnUnmap which is based on Gtk events.
mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped;