commit 6e1be6241390b4b4e3524f607afd300fd8309705
parent ba045db40f9da6b6e095466ee06c4cad8d458537
Author: Alexandru Marc <amarc@mozilla.com>
Date: Tue, 6 Jan 2026 23:03:59 +0200
Revert "Bug 2001628 [Linux] Make mWindowSurface persistent r=emilio" for causing bc failures @ browser_bookmark_context_menu_contents.js
This reverts commit ac30d4d7974faeacc0816c65d88d75e016979d91.
Diffstat:
17 files changed, 284 insertions(+), 68 deletions(-)
diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp
@@ -339,8 +339,26 @@ EGLSurface GLContextEGL::CreateEGLSurfaceForCompositorWidget(
EGLNativeWindowType window =
GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget);
if (!window) {
+#ifdef MOZ_WIDGET_GTK
+ // RenderCompositorEGL does not like EGL_NO_SURFACE as it fallbacks
+ // to SW rendering or claims itself as paused.
+ // In case we're missing valid native window because aCompositorWidget
+ // hidden, just create a fallback EGLSurface. Actual EGLSurface will be
+ // created by widget code later when aCompositorWidget becomes visible.
+ mozilla::gfx::IntSize pbSize(16, 16);
+# ifdef MOZ_WAYLAND
+ if (GdkIsWaylandDisplay()) {
+ return CreateWaylandOffscreenSurface(*egl, aConfig, pbSize);
+ } else
+# endif
+ {
+ return CreatePBufferSurfaceTryingPowerOfTwo(*egl, aConfig, LOCAL_EGL_NONE,
+ pbSize);
+ }
+#else
gfxCriticalNote << "window is null";
return EGL_NO_SURFACE;
+#endif
}
return mozilla::gl::CreateSurfaceFromNativeWindow(*egl, window, aConfig);
diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -268,8 +268,9 @@ bool WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) {
mDisplayItemCache.SkipWaitingForPartialDisplayList();
- mLatestTransactionId =
- mTransactionIdAllocator->GetTransactionId(/*aThrottle*/ true);
+ // Don't block on hidden windows on Linux as it may block all rendering.
+ const bool throttle = mWidget->IsMapped();
+ mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(throttle);
if (aFlags & EndTransactionFlags::END_NO_COMPOSITE &&
!mWebRenderCommandBuilder.NeedsEmptyTransaction()) {
@@ -420,8 +421,9 @@ void WebRenderLayerManager::EndTransactionWithoutLayer(
// an empty transaction.
ClearAndNotifyOfFullTransactionPendingScrollInfoUpdate();
- mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(
- /*aThrottle*/ !aRenderOffscreen);
+ // Don't block on hidden windows on Linux as it may block all rendering.
+ const bool throttle = mWidget->IsMapped() && !aRenderOffscreen;
+ mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(throttle);
// Get the time of when the refresh driver start its tick (if available),
// otherwise use the time of when LayerManager::BeginTransaction was called.
diff --git a/gfx/webrender_bindings/RenderCompositorEGL.cpp b/gfx/webrender_bindings/RenderCompositorEGL.cpp
@@ -72,24 +72,6 @@ RenderCompositorEGL::RenderCompositorEGL(
: RenderCompositor(aWidget), mGL(aGL), mEGLSurface(EGL_NO_SURFACE) {
MOZ_ASSERT(mGL);
LOG("RenderCompositorEGL::RenderCompositorEGL()");
-
-#ifdef MOZ_WIDGET_GTK
- mEGLSurface = CreateEGLSurface();
- if (!mEGLSurface) {
- RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
- return;
- }
- // We have a new EGL surface, which on wayland needs to be configured for
- // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL
- // context before we call eglSwapInterval, which is why we do it here
- // rather than where the surface was created.
- const auto& gle = gl::GLContextEGL::Cast(gl());
- const auto& egl = gle->mEgl;
- MakeCurrent();
-
- const int interval = gfx::gfxVars::SwapIntervalEGL() ? 1 : 0;
- egl->fSwapInterval(interval);
-#endif
}
RenderCompositorEGL::~RenderCompositorEGL() {
@@ -169,7 +151,7 @@ RenderedFrameId RenderCompositorEGL::EndFrame(
gl()->SetDamage(bufferInvalid);
}
-#ifdef MOZ_WAYLAND
+#ifdef MOZ_WIDGET_GTK
// Rendering on Wayland has to be atomic (buffer attach + commit) and
// wayland surface is also used by main thread so lock it before
// we paint at SwapBuffers().
@@ -182,11 +164,7 @@ RenderedFrameId RenderCompositorEGL::EndFrame(
return frameId;
}
-void RenderCompositorEGL::Pause() {
- if (kIsAndroid) {
- DestroyEGLSurface();
- }
-}
+void RenderCompositorEGL::Pause() { DestroyEGLSurface(); }
bool RenderCompositorEGL::Resume() {
if (kIsAndroid) {
@@ -224,17 +202,31 @@ bool RenderCompositorEGL::Resume() {
mHandlingNewSurfaceError = false;
gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
+ } else if (kIsLinux) {
+ // Destroy EGLSurface if it exists and create a new one. We will set the
+ // swap interval after MakeCurrent() has been called.
+ DestroyEGLSurface();
+ mEGLSurface = CreateEGLSurface();
+ if (mEGLSurface != EGL_NO_SURFACE) {
+ // We have a new EGL surface, which on wayland needs to be configured for
+ // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL
+ // context before we call eglSwapInterval, which is why we do it here
+ // rather than where the surface was created.
+ const auto& gle = gl::GLContextEGL::Cast(gl());
+ const auto& egl = gle->mEgl;
+ MakeCurrent();
+
+ const int interval = gfx::gfxVars::SwapIntervalEGL() ? 1 : 0;
+ egl->fSwapInterval(interval);
+ } else {
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ return false;
+ }
}
return true;
}
-bool RenderCompositorEGL::IsPaused() {
-#ifdef MOZ_WIDGET_ANDROID
- return mEGLSurface == EGL_NO_SURFACE;
-#else
- return false;
-#endif
-}
+bool RenderCompositorEGL::IsPaused() { return mEGLSurface == EGL_NO_SURFACE; }
bool RenderCompositorEGL::MakeCurrent() {
const auto& gle = gl::GLContextEGL::Cast(gl());
diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp
@@ -218,6 +218,8 @@ bool RenderCompositorOGLSWGL::RequestFullRender() { return mFullRender; }
void RenderCompositorOGLSWGL::Pause() {
#ifdef MOZ_WIDGET_ANDROID
DestroyEGLSurface();
+#elif defined(MOZ_WIDGET_GTK)
+ mCompositor->Pause();
#endif
}
@@ -259,6 +261,12 @@ bool RenderCompositorOGLSWGL::Resume() {
gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface);
mCompositor->SetDestinationSurfaceSize(size.ToUnknownSize());
+#elif defined(MOZ_WIDGET_GTK)
+ bool resumed = mCompositor->Resume();
+ if (!resumed) {
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ return false;
+ }
#endif
return true;
}
diff --git a/gfx/webrender_bindings/RenderCompositorSWGL.cpp b/gfx/webrender_bindings/RenderCompositorSWGL.cpp
@@ -78,8 +78,10 @@ bool RenderCompositorSWGL::AllocateMappedBuffer(
MOZ_ASSERT(!mDT);
mDT = mWidget->StartRemoteDrawingInRegion(mDirtyRegion);
if (!mDT) {
+#if !defined(MOZ_WAYLAND)
gfxCriticalNoteOnce
<< "RenderCompositorSWGL failed mapping default framebuffer, no dt";
+#endif
return false;
}
// Attempt to lock the underlying buffer directly from the draw target.
diff --git a/widget/gtk/CompositorWidgetChild.cpp b/widget/gtk/CompositorWidgetChild.cpp
@@ -43,5 +43,9 @@ void CompositorWidgetChild::NotifyClientSizeChanged(
void CompositorWidgetChild::CleanupResources() { (void)SendCleanupResources(); }
+void CompositorWidgetChild::SetRenderingSurface(const uintptr_t aXWindow) {
+ (void)SendSetRenderingSurface(aXWindow);
+}
+
} // namespace widget
} // namespace mozilla
diff --git a/widget/gtk/CompositorWidgetChild.h b/widget/gtk/CompositorWidgetChild.h
@@ -30,6 +30,7 @@ class CompositorWidgetChild final : public PCompositorWidgetChild,
void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize) override;
void NotifyFullscreenChanged(bool aIsFullscreen) override {};
void CleanupResources() override;
+ void SetRenderingSurface(const uintptr_t aXWindow) override;
private:
~CompositorWidgetChild() override;
diff --git a/widget/gtk/CompositorWidgetParent.cpp b/widget/gtk/CompositorWidgetParent.cpp
@@ -44,4 +44,10 @@ mozilla::ipc::IPCResult CompositorWidgetParent::RecvCleanupResources() {
return IPC_OK();
}
+mozilla::ipc::IPCResult CompositorWidgetParent::RecvSetRenderingSurface(
+ const uintptr_t& aXWindow) {
+ SetRenderingSurface(aXWindow);
+ return IPC_OK();
+}
+
} // namespace mozilla::widget
diff --git a/widget/gtk/CompositorWidgetParent.h b/widget/gtk/CompositorWidgetParent.h
@@ -30,6 +30,8 @@ class CompositorWidgetParent final : public PCompositorWidgetParent,
const LayoutDeviceIntSize& aClientSize) override;
mozilla::ipc::IPCResult RecvCleanupResources() override;
+ mozilla::ipc::IPCResult RecvSetRenderingSurface(
+ const uintptr_t& aXWindow) override;
private:
~CompositorWidgetParent() override;
diff --git a/widget/gtk/GtkCompositorWidget.cpp b/widget/gtk/GtkCompositorWidget.cpp
@@ -181,6 +181,23 @@ void GtkCompositorWidget::ConfigureX11Backend(Window aXWindow) {
}
#endif
+void GtkCompositorWidget::SetRenderingSurface(const uintptr_t aXWindow) {
+ LOG("GtkCompositorWidget::SetRenderingSurface() [%p]\n", mWidget.get());
+
+#if defined(MOZ_WAYLAND)
+ if (GdkIsWaylandDisplay()) {
+ LOG(" configure widget %p\n", mWidget.get());
+ ConfigureWaylandBackend();
+ }
+#endif
+#if defined(MOZ_X11)
+ if (GdkIsX11Display()) {
+ LOG(" configure XWindow %p\n", (void*)aXWindow);
+ ConfigureX11Backend((Window)aXWindow);
+ }
+#endif
+}
+
#ifdef MOZ_LOGGING
bool GtkCompositorWidget::IsPopup() {
return mWidget ? mWidget->IsPopup() : false;
diff --git a/widget/gtk/GtkCompositorWidget.h b/widget/gtk/GtkCompositorWidget.h
@@ -32,6 +32,7 @@ class PlatformCompositorWidgetDelegate : public CompositorWidgetDelegate {
virtual GtkCompositorWidget* AsGtkCompositorWidget() { return nullptr; };
virtual void CleanupResources() = 0;
+ virtual void SetRenderingSurface(const uintptr_t aXWindow) = 0;
// CompositorWidgetDelegate Overrides
@@ -75,6 +76,9 @@ class GtkCompositorWidget : public CompositorWidget,
// Can be used when underlying window is hidden/unmapped.
void CleanupResources() override;
+ // Resume rendering with to given aXWindow (X11) or nsWindow (Wayland).
+ void SetRenderingSurface(const uintptr_t aXWindow) override;
+
// Set EGLWindow size to avoid rendering artifacts
void SetEGLNativeWindowSize(const LayoutDeviceIntSize& aEGLWindowSize);
diff --git a/widget/gtk/PCompositorWidget.ipdl b/widget/gtk/PCompositorWidget.ipdl
@@ -23,6 +23,7 @@ parent:
async NotifyClientSizeChanged(LayoutDeviceIntSize aClientSize);
async CleanupResources();
+ async SetRenderingSurface(uintptr_t aXWindow);
child:
diff --git a/widget/gtk/WindowSurfaceProvider.cpp b/widget/gtk/WindowSurfaceProvider.cpp
@@ -11,6 +11,7 @@
#include "mozilla/gfx/Logging.h"
#include "mozilla/layers/LayersTypes.h"
#include "nsWindow.h"
+#include "mozilla/ScopeExit.h"
#include "WidgetUtilsGtk.h"
#ifdef MOZ_WAYLAND
@@ -41,7 +42,9 @@ namespace widget {
using namespace mozilla::layers;
WindowSurfaceProvider::WindowSurfaceProvider()
- : mWindowSurface(nullptr)
+ : mWindowSurface(nullptr),
+ mMutex("WindowSurfaceProvider"),
+ mWindowSurfaceValid(false)
#ifdef MOZ_X11
,
mXDepth(0),
@@ -63,10 +66,12 @@ WindowSurfaceProvider::~WindowSurfaceProvider() {
#ifdef MOZ_WAYLAND
bool WindowSurfaceProvider::Initialize(RefPtr<nsWindow> aWidget) {
+ mWindowSurfaceValid = false;
mWidget = std::move(aWidget);
return true;
}
bool WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) {
+ mWindowSurfaceValid = false;
mCompositorWidget = aCompositorWidget;
mWidget = static_cast<nsWindow*>(aCompositorWidget->RealWidget());
return true;
@@ -74,6 +79,8 @@ bool WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) {
#endif
#ifdef MOZ_X11
bool WindowSurfaceProvider::Initialize(Window aWindow) {
+ mWindowSurfaceValid = false;
+
// Grab the window's visual and depth
XWindowAttributes windowAttrs;
if (!XGetWindowAttributes(DefaultXDisplay(), aWindow, &windowAttrs)) {
@@ -89,6 +96,8 @@ bool WindowSurfaceProvider::Initialize(Window aWindow) {
#endif
void WindowSurfaceProvider::CleanupResources() {
+ MutexAutoLock lock(mMutex);
+ mWindowSurfaceValid = false;
#ifdef MOZ_WAYLAND
mWidget = nullptr;
#endif
@@ -137,6 +146,11 @@ RefPtr<WindowSurface> WindowSurfaceProvider::CreateWindowSurface() {
MOZ_RELEASE_ASSERT(false);
}
+// We need to ignore thread safety checks here. We need to hold mMutex
+// between StartRemoteDrawingInRegion()/EndRemoteDrawingInRegion() calls
+// which confuses it.
+MOZ_PUSH_IGNORE_THREAD_SAFETY
+
already_AddRefed<gfx::DrawTarget>
WindowSurfaceProvider::StartRemoteDrawingInRegion(
const LayoutDeviceIntRegion& aInvalidRegion) {
@@ -144,6 +158,19 @@ WindowSurfaceProvider::StartRemoteDrawingInRegion(
return nullptr;
}
+ // We return a reference to mWindowSurface inside draw target so we need to
+ // hold the mutex untill EndRemoteDrawingInRegion() call where draw target
+ // is returned.
+ // If we return null dt, EndRemoteDrawingInRegion() won't be called to
+ // release mutex.
+ mMutex.Lock();
+ auto unlockMutex = MakeScopeExit([&] { mMutex.Unlock(); });
+
+ if (!mWindowSurfaceValid) {
+ mWindowSurface = nullptr;
+ mWindowSurfaceValid = true;
+ }
+
if (!mWindowSurface) {
mWindowSurface = CreateWindowSurface();
if (!mWindowSurface) {
@@ -163,13 +190,37 @@ WindowSurfaceProvider::StartRemoteDrawingInRegion(
dt = mWindowSurface->Lock(aInvalidRegion);
}
#endif
+ if (dt) {
+ // We have valid dt, mutex will be released in EndRemoteDrawingInRegion().
+ unlockMutex.release();
+ }
+
return dt.forget();
}
void WindowSurfaceProvider::EndRemoteDrawingInRegion(
gfx::DrawTarget* aDrawTarget, const LayoutDeviceIntRegion& aInvalidRegion) {
+ // Unlock mutex from StartRemoteDrawingInRegion().
+ mMutex.AssertCurrentThreadOwns();
+ auto unlockMutex = MakeScopeExit([&] { mMutex.Unlock(); });
+
+ // Commit to mWindowSurface only if we have a valid one.
+ if (!mWindowSurface || !mWindowSurfaceValid) {
+ return;
+ }
+#if defined(MOZ_WAYLAND)
+ if (GdkIsWaylandDisplay()) {
+ // We're called too early or we're unmapped.
+ // Don't draw anything.
+ if (!mWidget || !mWidget->IsMapped()) {
+ return;
+ }
+ }
+#endif
mWindowSurface->Commit(aInvalidRegion);
}
+MOZ_POP_THREAD_SAFETY
+
} // namespace widget
} // namespace mozilla
diff --git a/widget/gtk/WindowSurfaceProvider.h b/widget/gtk/WindowSurfaceProvider.h
@@ -70,6 +70,16 @@ class WindowSurfaceProvider final {
RefPtr<WindowSurface> mWindowSurface;
+ /* While CleanupResources() can be called from Main thread when nsWindow is
+ * destroyed/hidden, StartRemoteDrawingInRegion()/EndRemoteDrawingInRegion()
+ * is called from Compositor thread during rendering.
+ *
+ * As nsWindow CleanupResources() call comes from Gtk/X11 we can't synchronize
+ * that with WebRender so we use lock to synchronize the access.
+ */
+ mozilla::Mutex mMutex MOZ_UNANNOTATED;
+ // WindowSurface needs to be re-created as underlying window was changed.
+ bool mWindowSurfaceValid;
#ifdef MOZ_WAYLAND
RefPtr<nsWindow> mWidget;
// WindowSurfaceProvider is owned by GtkCompositorWidget so we don't need
@@ -78,7 +88,12 @@ class WindowSurfaceProvider final {
#endif
#ifdef MOZ_X11
int mXDepth;
- Window mXWindow;
+ // Make mXWindow atomic to allow it read from different threads
+ // and make tsan happy.
+ // We don't care much about actual mXWindow value (it may be valid XWindow or
+ // nullptr) because we invalidate mXWindow at compositor/renderer thread
+ // before it's release in unmap handler.
+ Atomic<Window, Relaxed> mXWindow;
Visual* mXVisual;
#endif
};
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
@@ -859,6 +859,8 @@ void nsWindow::SetModal(bool aModal) {
// nsIWidget method, which means IsShown.
bool nsWindow::IsVisible() const { return mIsShown; }
+bool nsWindow::IsMapped() const { return mIsMapped; }
+
void nsWindow::RegisterTouchWindow() {
mHandleTouchEvent = true;
mTouches.Clear();
@@ -1396,6 +1398,7 @@ void nsWindow::HideWaylandToplevelWindow() {
popup = prev;
}
}
+ WaylandStopVsync();
gtk_widget_hide(mShell);
}
@@ -6079,6 +6082,22 @@ Window nsWindow::GetX11Window() {
return (Window) nullptr;
}
+void nsWindow::ConfigureCompositor() {
+ LOG("nsWindow::ConfigureCompositor()");
+
+ if (mIsDestroyed) {
+ LOG(" quit, mIsDestroyed = %d", !!mIsDestroyed);
+ return;
+ }
+ // Compositor will be resumed at nsWindow::SetCompositorWidgetDelegate().
+ if (!mCompositorWidgetDelegate) {
+ LOG(" quit, missing mCompositorWidgetDelegate");
+ return;
+ }
+
+ ResumeCompositorImpl();
+}
+
void nsWindow::SetGdkWindow(GdkWindow* aGdkWindow) {
LOG("nsWindow::SetGdkWindow() %p", aGdkWindow);
if (!aGdkWindow) {
@@ -6342,8 +6361,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
}
gtk_widget_realize(container);
- // mGdkWindow is set by moz_container_realize() / SetGdkWindow().
- MOZ_DIAGNOSTIC_ASSERT(mGdkWindow, "MozContainer realize failed?");
#ifdef MOZ_X11
if (GdkIsX11Display()) {
@@ -6352,7 +6369,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
#endif
#ifdef MOZ_WAYLAND
if (GdkIsWaylandDisplay() && mIsAccelerated) {
- mEGLWindow = mSurface->GetEGLWindow(mClientArea.Size());
+ mEGLWindow = MOZ_WL_SURFACE(container)->GetEGLWindow(mClientArea.Size());
}
#endif
if (mEGLWindow) {
@@ -6360,24 +6377,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
mGdkWindow, mEGLWindow);
}
-#ifdef MOZ_X11
- if (GdkIsX11Display()) {
- mSurfaceProvider.Initialize(GetX11Window());
-
- // Set window manager hint to keep fullscreen windows composited.
- //
- // If the window were to get unredirected, there could be visible
- // tearing because Gecko does not align its framebuffer updates with
- // vblank.
- SetCompositorHint(GTK_WIDGET_COMPOSITED_ENABLED);
- }
-#endif
-#ifdef MOZ_WAYLAND
- if (GdkIsWaylandDisplay()) {
- mSurfaceProvider.Initialize(this);
- }
-#endif
-
// make sure this is the focus widget in the container
gtk_widget_show(container);
@@ -6508,7 +6507,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
mWaylandVsyncSource = new WaylandVsyncSource(this);
mWaylandVsyncSource->Init();
mWaylandVsyncDispatcher = new VsyncDispatcher(mWaylandVsyncSource);
- mWaylandVsyncSource->EnableVSyncSource();
}
#endif
@@ -6793,6 +6791,44 @@ void nsWindow::NativeMoveResize(bool aMoved, bool aResized) {
}
}
+void nsWindow::ResumeCompositorImpl() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ LOG("nsWindow::ResumeCompositorImpl()\n");
+
+ MOZ_DIAGNOSTIC_ASSERT(mCompositorWidgetDelegate);
+ mCompositorWidgetDelegate->SetRenderingSurface(GetX11Window());
+
+ // As WaylandStartVsync needs mCompositorWidgetDelegate this is the right
+ // time to start it.
+ WaylandStartVsync();
+
+ CompositorBridgeChild* remoteRenderer = GetRemoteRenderer();
+ MOZ_RELEASE_ASSERT(remoteRenderer);
+ remoteRenderer->SendResume();
+ remoteRenderer->SendForcePresent(wr::RenderReasons::WIDGET);
+}
+
+void nsWindow::WaylandStartVsync() {
+#ifdef MOZ_WAYLAND
+ if (!mWaylandVsyncSource) {
+ return;
+ }
+ LOG_VSYNC("nsWindow::WaylandStartVsync");
+ mWaylandVsyncSource->EnableVSyncSource();
+#endif
+}
+
+void nsWindow::WaylandStopVsync() {
+#ifdef MOZ_WAYLAND
+ if (!mWaylandVsyncSource) {
+ return;
+ }
+ LOG_VSYNC("nsWindow::WaylandStopVsync");
+ mWaylandVsyncSource->DisableVSyncSource();
+#endif
+}
+
void nsWindow::NativeShow(bool aAction) {
if (aAction) {
// unset our flag now that our window has been shown
@@ -8897,8 +8933,17 @@ void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) {
delegate, !!mIsMapped, mCompositorWidgetDelegate);
MOZ_RELEASE_ASSERT(NS_IsMainThread());
- mCompositorWidgetDelegate =
- delegate ? delegate->AsPlatformSpecificDelegate() : nullptr;
+ if (delegate) {
+ mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
+ MOZ_ASSERT(mCompositorWidgetDelegate,
+ "nsWindow::SetCompositorWidgetDelegate called with a "
+ "non-PlatformCompositorWidgetDelegate");
+ if (mIsMapped) {
+ ConfigureCompositor();
+ }
+ } else {
+ mCompositorWidgetDelegate = nullptr;
+ }
}
bool nsWindow::IsAlwaysUndecoratedWindow() const {
@@ -9472,11 +9517,24 @@ nsWindow::GtkWindowDecoration nsWindow::GetSystemGtkWindowDecoration() {
void nsWindow::GetCompositorWidgetInitData(
mozilla::widget::CompositorWidgetInitData* aInitData) {
- MOZ_DIAGNOSTIC_ASSERT(!mIsDestroyed);
+ nsCString displayName;
LOG("nsWindow::GetCompositorWidgetInitData");
- nsCString displayName;
+ Window window = GetX11Window();
+#ifdef MOZ_X11
+ // We're bit hackish here. Old GLX backend needs XWindow when GLContext
+ // is created so get XWindow now before map signal.
+ // We may see crashes/errors when nsWindow is unmapped (XWindow is
+ // invalidated) but we can't do anything about it.
+ if (!window && !gfxVars::UseEGL()) {
+ window =
+ gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(mContainer)));
+ }
+#endif
+ *aInitData = mozilla::widget::GtkCompositorWidgetInitData(
+ window, displayName, GdkIsX11Display(), GetClientSize());
+
#ifdef MOZ_X11
if (GdkIsX11Display()) {
// Make sure the window XID is propagated to X server, we can fail otherwise
@@ -9486,9 +9544,6 @@ void nsWindow::GetCompositorWidgetInitData(
displayName = nsCString(XDisplayString(display));
}
#endif
-
- *aInitData = mozilla::widget::GtkCompositorWidgetInitData(
- GetX11Window(), displayName, GdkIsX11Display(), GetClientSize());
}
#ifdef MOZ_X11
@@ -9776,6 +9831,24 @@ void nsWindow::OnMap() {
if (mIsAlert) {
gdk_window_set_override_redirect(GetToplevelGdkWindow(), TRUE);
}
+
+#ifdef MOZ_X11
+ if (GdkIsX11Display()) {
+ mSurfaceProvider.Initialize(GetX11Window());
+
+ // Set window manager hint to keep fullscreen windows composited.
+ //
+ // If the window were to get unredirected, there could be visible
+ // tearing because Gecko does not align its framebuffer updates with
+ // vblank.
+ SetCompositorHint(GTK_WIDGET_COMPOSITED_ENABLED);
+ }
+#endif
+#ifdef MOZ_WAYLAND
+ if (GdkIsWaylandDisplay()) {
+ mSurfaceProvider.Initialize(this);
+ }
+#endif
}
if (mIsDragPopup && GdkIsX11Display()) {
@@ -9791,7 +9864,12 @@ void nsWindow::OnMap() {
RefreshWindowClass();
- LOG(" finished, GdkWindow %p XID 0x%lx\n", mGdkWindow, GetX11Window());
+ // We're not mapped yet but we have already created compositor.
+ if (mCompositorWidgetDelegate) {
+ ConfigureCompositor();
+ }
+
+ LOG(" finished, new GdkWindow %p XID 0x%lx\n", mGdkWindow, GetX11Window());
}
void nsWindow::OnUnmap() {
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
@@ -194,6 +194,7 @@ class nsWindow final : public nsIWidget {
mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override;
void SetModal(bool aModal) override;
bool IsVisible() const override;
+ bool IsMapped() const override;
void ConstrainPosition(DesktopIntPoint&) override;
void SetSizeConstraints(const SizeConstraints&) override;
void LockAspectRatio(bool aShouldLock) override;
@@ -530,6 +531,8 @@ class nsWindow final : public nsIWidget {
LayoutDeviceIntSize GetMoveToRectPopupSize() override;
#endif
+ void ResumeCompositorImpl();
+
bool ApplyEnterLeaveMutterWorkaround();
void NotifyOcclusionState(mozilla::widget::OcclusionState aState) override;
@@ -574,6 +577,8 @@ class nsWindow final : public nsIWidget {
bool DoTitlebarAction(mozilla::LookAndFeel::TitlebarEvent aEvent,
GdkEventButton* aButtonEvent);
+ void WaylandStartVsync();
+ void WaylandStopVsync();
void DestroyChildWindows();
GtkWidget* GetToplevelWidget() const;
nsWindow* GetContainerWindow() const;
@@ -711,7 +716,7 @@ class nsWindow final : public nsIWidget {
// This track real window visibility from OS perspective.
// It's set by OnMap/OnUnmap which is based on Gtk events.
- bool mIsMapped;
+ mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped;
// Has this widget been destroyed yet?
mozilla::Atomic<bool, mozilla::Relaxed> mIsDestroyed;
// mIsShown tracks requested visible status from browser perspective, i.e.
@@ -866,6 +871,8 @@ class nsWindow final : public nsIWidget {
void DispatchMissedButtonReleases(GdkEventCrossing* aGdkEvent);
+ void ConfigureCompositor();
+
bool IsAlwaysUndecoratedWindow() const;
// nsIWidget
diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h
@@ -727,6 +727,14 @@ class nsIWidget : public nsSupportsWeakReference {
virtual bool IsVisible() const = 0;
/**
+ * Returns whether the window has allocated resources so
+ * we can paint into it.
+ * Recently it's used on Linux/Gtk where we should not paint
+ * to invisible window.
+ */
+ virtual bool IsMapped() const { return true; }
+
+ /**
* Perform platform-dependent sanity check on a potential window position.
* This is guaranteed to work only for top-level windows.
*/