From 91e7aee4b82f8a757863205d86b8f48c271a3924 Mon Sep 17 00:00:00 2001 From: Cristina Horotan Date: Mon, 24 Apr 2023 12:50:04 +0300 Subject: [PATCH] Backed out 6 changesets (bug 1829303, bug 1828071) for causing causing Linux crashes (bug 1829462) a=backout Backed out changeset 90256c29ed1f (bug 1829303) Backed out changeset 9c6ea77a90eb (bug 1828071) Backed out changeset b71c46d9b101 (bug 1828071) Backed out changeset c333d6127dfe (bug 1828071) Backed out changeset c2c5841b88e0 (bug 1828071) Backed out changeset 0b2180c4f9a5 (bug 1828071) --- gfx/gl/SharedSurface.cpp | 2 +- gfx/gl/SharedSurfaceDMABUF.cpp | 4 +- gfx/layers/CanvasRenderer.cpp | 2 +- gfx/layers/NativeLayerWayland.cpp | 2 +- gfx/layers/client/TextureClient.cpp | 2 +- gfx/thebes/gfxPlatformGtk.cpp | 2 +- toolkit/xre/nsAppRunner.cpp | 9 - widget/gtk/DMABufLibWrapper.cpp | 189 +++++++++++++++----- widget/gtk/DMABufLibWrapper.h | 39 ++-- widget/gtk/DMABufSurface.cpp | 10 +- widget/gtk/WakeLockListener.cpp | 4 +- widget/gtk/WaylandBuffer.cpp | 17 +- widget/gtk/WaylandBuffer.h | 4 +- widget/gtk/nsAppShell.cpp | 3 + widget/gtk/nsWaylandDisplay.cpp | 267 +++++++++++++++++++--------- widget/gtk/nsWaylandDisplay.h | 43 +++-- widget/gtk/nsWindow.h | 1 + 17 files changed, 411 insertions(+), 189 deletions(-) diff --git a/gfx/gl/SharedSurface.cpp b/gfx/gl/SharedSurface.cpp index 6b7ba5c3b0658..3a49d86f84ca5 100644 --- a/gfx/gl/SharedSurface.cpp +++ b/gfx/gl/SharedSurface.cpp @@ -100,7 +100,7 @@ UniquePtr SurfaceFactory::Create( case layers::TextureType::DMABUF: #ifdef MOZ_WAYLAND if (gl.GetContextType() == GLContextType::EGL && - widget::nsDMABufDevice::IsDMABufWebGLEnabled()) { + widget::GetDMABufDevice()->IsDMABufWebGLEnabled()) { return SurfaceFactory_DMABUF::Create(gl); } #endif diff --git a/gfx/gl/SharedSurfaceDMABUF.cpp b/gfx/gl/SharedSurfaceDMABUF.cpp index a549768825c62..302c3e5a3c1f5 100644 --- a/gfx/gl/SharedSurfaceDMABUF.cpp +++ b/gfx/gl/SharedSurfaceDMABUF.cpp @@ -89,7 +89,7 @@ Maybe SharedSurface_DMABUF::ToSurfaceDescriptor() { /*static*/ UniquePtr SurfaceFactory_DMABUF::Create(GLContext& gl) { - if (!widget::nsDMABufDevice::IsDMABufWebGLEnabled()) { + if (!widget::GetDMABufDevice()->IsDMABufWebGLEnabled()) { return nullptr; } @@ -100,7 +100,7 @@ UniquePtr SurfaceFactory_DMABUF::Create(GLContext& gl) { LOGDMABUF( ("SurfaceFactory_DMABUF::Create() failed, fallback to SW buffers.\n")); - widget::nsDMABufDevice::DisableDMABufWebGL(); + widget::GetDMABufDevice()->DisableDMABufWebGL(); return nullptr; } diff --git a/gfx/layers/CanvasRenderer.cpp b/gfx/layers/CanvasRenderer.cpp index 3c4154d969de2..2db28480307e6 100644 --- a/gfx/layers/CanvasRenderer.cpp +++ b/gfx/layers/CanvasRenderer.cpp @@ -113,7 +113,7 @@ TextureType TexTypeForWebgl(KnowsCompositor* const knowsCompositor) { #ifdef MOZ_WAYLAND if (kIsWayland) { if (!knowsCompositor->UsingSoftwareWebRender() && - widget::nsDMABufDevice::IsDMABufWebGLEnabled()) { + widget::GetDMABufDevice()->IsDMABufWebGLEnabled()) { return TextureType::DMABUF; } } diff --git a/gfx/layers/NativeLayerWayland.cpp b/gfx/layers/NativeLayerWayland.cpp index 55250b3de0cbd..5b9eecba36e41 100644 --- a/gfx/layers/NativeLayerWayland.cpp +++ b/gfx/layers/NativeLayerWayland.cpp @@ -359,7 +359,7 @@ NativeLayerWayland::NativeLayerWayland( MOZ_RELEASE_ASSERT(mSurfacePoolHandle, "Need a non-null surface pool handle."); - widget::nsWaylandDisplay* waylandDisplay = widget::WaylandDisplayGet(); + RefPtr waylandDisplay = widget::WaylandDisplayGet(); wl_compositor* compositor = waylandDisplay->GetCompositor(); mWlSurface = wl_compositor_create_surface(compositor); diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index 9b988a1e6f7eb..0ca2320c4f0bc 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -275,7 +275,7 @@ static TextureType GetTextureType(gfx::SurfaceFormat aFormat, #ifdef MOZ_WAYLAND if ((layersBackend == LayersBackend::LAYERS_WR && !aKnowsCompositor->UsingSoftwareWebRender()) && - widget::nsDMABufDevice::IsDMABufTexturesEnabled() && + widget::GetDMABufDevice()->IsDMABufTexturesEnabled() && aFormat != SurfaceFormat::A8) { return TextureType::DMABUF; } diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp index 8453dd8cc4ae7..234871666486d 100644 --- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -212,7 +212,7 @@ void gfxPlatformGtk::InitDmabufConfig() { gfxInfo->GetDrmRenderDevice(drmRenderDevice); gfxVars::SetDrmRenderDevice(drmRenderDevice); - if (GetDMABufDevice()->IsEnabled(failureId)) { + if (!GetDMABufDevice()->Configure(failureId)) { feature.ForceDisable(FeatureStatus::Failed, "Failed to configure", failureId); } diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 4e3b73c76fce6..093ba5e03b3a9 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -5112,15 +5112,6 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) { GfxInfo::FireGLXTestProcess(); #endif -#ifdef MOZ_WAYLAND - // Make sure we have wayland connection for main thread. - // It's used as template to create display connections - // for different threads. - if (IsWaylandEnabled()) { - MOZ_UNUSED(WaylandDisplayGet()); - } -#endif - return 0; } diff --git a/widget/gtk/DMABufLibWrapper.cpp b/widget/gtk/DMABufLibWrapper.cpp index e48c1e3db12dd..ebf8e76c3e712 100644 --- a/widget/gtk/DMABufLibWrapper.cpp +++ b/widget/gtk/DMABufLibWrapper.cpp @@ -26,8 +26,6 @@ using namespace mozilla::gfx; namespace mozilla { namespace widget { -bool nsDMABufDevice::sUseWebGLDmabufBackend = true; - #define GBMLIB_NAME "libgbm.so.1" #define DRMLIB_NAME "libdrm.so.2" @@ -130,40 +128,74 @@ bool nsGbmLib::Load() { } gbm_device* nsDMABufDevice::GetGbmDevice() { return mGbmDevice; } -int nsDMABufDevice::GetDRMFd() { return mDRMFd; } -bool nsDMABufDevice::IsEnabled(nsACString& aFailureId) { - if (mDRMFd == -1) { - aFailureId = mFailureId; +static void dmabuf_modifiers(void* data, + struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf, + uint32_t format, uint32_t modifier_hi, + uint32_t modifier_lo) { + // skip modifiers marked as invalid + if (modifier_hi == (DRM_FORMAT_MOD_INVALID >> 32) && + modifier_lo == (DRM_FORMAT_MOD_INVALID & 0xffffffff)) { + return; } - return mDRMFd != -1; -} -nsDMABufDevice::nsDMABufDevice() { Configure(); } - -nsDMABufDevice::~nsDMABufDevice() { - if (mGbmDevice) { - nsGbmLib::DestroyDevice(mGbmDevice); - mGbmDevice = nullptr; - } - if (mDRMFd != -1) { - close(mDRMFd); - mDRMFd = -1; + auto* device = static_cast(data); + switch (format) { + case GBM_FORMAT_ARGB8888: + device->AddFormatModifier(true, format, modifier_hi, modifier_lo); + break; + case GBM_FORMAT_XRGB8888: + device->AddFormatModifier(false, format, modifier_hi, modifier_lo); + break; + default: + break; } } -void nsDMABufDevice::Configure() { - if (mInitialized) { - return; - } - mInitialized = true; +static void dmabuf_format(void* data, + struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf, + uint32_t format) { + // XXX: deprecated +} - LOGDMABUF(("nsDMABufDevice::Configure()")); +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + dmabuf_format, dmabuf_modifiers}; + +static void global_registry_handler(void* data, wl_registry* registry, + uint32_t id, const char* interface, + uint32_t version) { + auto* device = static_cast(data); + if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0 && version > 2) { + auto* dmabuf = WaylandRegistryBind( + registry, id, &zwp_linux_dmabuf_v1_interface, 3); + LOGDMABUF(("zwp_linux_dmabuf_v1 is available.")); + device->ResetFormatsModifiers(); + zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, data); + } else if (strcmp(interface, "wl_drm") == 0) { + LOGDMABUF(("wl_drm is available.")); + } +} - if (!nsGbmLib::IsAvailable()) { - LOGDMABUF(("nsGbmLib is not available!")); - mFailureId = "FEATURE_FAILURE_NO_LIBGBM"; - return; +static void global_registry_remover(void* data, wl_registry* registry, + uint32_t id) {} + +static const struct wl_registry_listener registry_listener = { + global_registry_handler, global_registry_remover}; + +nsDMABufDevice::nsDMABufDevice() + : mUseWebGLDmabufBackend(true), + mXRGBFormat({true, false, GBM_FORMAT_XRGB8888, nullptr, 0}), + mARGBFormat({true, true, GBM_FORMAT_ARGB8888, nullptr, 0}), + mDRMFd(-1), + mGbmDevice(nullptr), + mInitialized(false) { + if (GdkIsWaylandDisplay()) { + wl_display* display = WaylandDisplayGetWLDisplay(); + wl_registry* registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, this); + wl_display_roundtrip(display); + wl_display_roundtrip(display); + wl_registry_destroy(registry); } nsAutoCString drm_render_node(getenv("MOZ_DRM_DEVICE")); @@ -177,30 +209,50 @@ void nsDMABufDevice::Configure() { if (mDRMFd < 0) { LOGDMABUF(("Failed to open drm render node %s error %s\n", drm_render_node.get(), strerror(errno))); - mFailureId = "FEATURE_FAILURE_NO_DRM_DEVICE"; - return; } } else { LOGDMABUF(("We're missing DRM render device!\n")); - mFailureId = "FEATURE_FAILURE_NO_DRM_DEVICE"; - return; } +} + +nsDMABufDevice::~nsDMABufDevice() { + if (mGbmDevice) { + nsGbmLib::DestroyDevice(mGbmDevice); + mGbmDevice = nullptr; + } + if (mDRMFd != -1) { + close(mDRMFd); + mDRMFd = -1; + } +} + +int nsDMABufDevice::GetDRMFd() { return mDRMFd; } + +bool nsDMABufDevice::Configure(nsACString& aFailureId) { + if (mInitialized) { + return true; + } + + LOGDMABUF(("nsDMABufDevice::Configure()")); + mInitialized = true; - // mGbmDevice is optional and it's used to create dmabuf surfaces - // directly on GPU. Some drivers (NVIDIA) doesn't support that - // but we still can use mDRMFd to operate with dmabuf surfaces - // created by GFX drivers and exported by OpenGL. + if (!nsGbmLib::IsAvailable()) { + LOGDMABUF(("nsGbmLib is not available!")); + aFailureId = "FEATURE_FAILURE_NO_LIBGBM"; + return false; + } // fd passed to gbm_create_device() should be kept open until // gbm_device_destroy() is called. - mGbmDevice = nsGbmLib::CreateDevice(mDRMFd); + mGbmDevice = nsGbmLib::CreateDevice(GetDRMFd()); if (!mGbmDevice) { - LOGDMABUF( - ("Failed to create drm render device. Direct dmabuf surface creation " - "will be disabled.")); + LOGDMABUF(("Failed to create drm render device")); + aFailureId = "FEATURE_FAILURE_BAD_DRM_RENDER_NODE"; + return false; } LOGDMABUF(("DMABuf is enabled")); + return true; } #ifdef NIGHTLY_BUILD @@ -214,21 +266,68 @@ bool nsDMABufDevice::IsDMABufTexturesEnabled() { return false; } bool nsDMABufDevice::IsDMABufWebGLEnabled() { LOGDMABUF( ("nsDMABufDevice::IsDMABufWebGLEnabled: UseDMABuf %d " - "sUseWebGLDmabufBackend %d " + "mUseWebGLDmabufBackend %d " "widget_dmabuf_webgl_enabled %d\n", - gfx::gfxVars::UseDMABuf(), sUseWebGLDmabufBackend, + gfx::gfxVars::UseDMABuf(), mUseWebGLDmabufBackend, StaticPrefs::widget_dmabuf_webgl_enabled())); - return gfx::gfxVars::UseDMABuf() && sUseWebGLDmabufBackend && + return gfx::gfxVars::UseDMABuf() && mUseWebGLDmabufBackend && StaticPrefs::widget_dmabuf_webgl_enabled(); } -void nsDMABufDevice::DisableDMABufWebGL() { sUseWebGLDmabufBackend = false; } +void nsDMABufDevice::DisableDMABufWebGL() { mUseWebGLDmabufBackend = false; } + +GbmFormat* nsDMABufDevice::GetGbmFormat(bool aHasAlpha) { + GbmFormat* format = aHasAlpha ? &mARGBFormat : &mXRGBFormat; + return format->mIsSupported ? format : nullptr; +} + +GbmFormat* nsDMABufDevice::GetExactGbmFormat(int aFormat) { + if (aFormat == mARGBFormat.mFormat) { + return &mARGBFormat; + } else if (aFormat == mXRGBFormat.mFormat) { + return &mXRGBFormat; + } + + return nullptr; +} + +void nsDMABufDevice::AddFormatModifier(bool aHasAlpha, int aFormat, + uint32_t mModifierHi, + uint32_t mModifierLo) { + GbmFormat* format = aHasAlpha ? &mARGBFormat : &mXRGBFormat; + format->mIsSupported = true; + format->mHasAlpha = aHasAlpha; + format->mFormat = aFormat; + format->mModifiersCount++; + format->mModifiers = + (uint64_t*)realloc(format->mModifiers, + format->mModifiersCount * sizeof(*format->mModifiers)); + format->mModifiers[format->mModifiersCount - 1] = + ((uint64_t)mModifierHi << 32) | mModifierLo; +} + +void nsDMABufDevice::ResetFormatsModifiers() { + mARGBFormat.mModifiersCount = 0; + free(mARGBFormat.mModifiers); + mARGBFormat.mModifiers = nullptr; + + mXRGBFormat.mModifiersCount = 0; + free(mXRGBFormat.mModifiers); + mXRGBFormat.mModifiers = nullptr; +} -// TODO: Make private or make sure it's configured nsDMABufDevice* GetDMABufDevice() { static nsDMABufDevice dmaBufDevice; return &dmaBufDevice; } +nsDMABufDevice* GetAndConfigureDMABufDevice() { + nsCString failureId; + if (GetDMABufDevice()->Configure(failureId)) { + return GetDMABufDevice(); + } + return nullptr; +} + } // namespace widget } // namespace mozilla diff --git a/widget/gtk/DMABufLibWrapper.h b/widget/gtk/DMABufLibWrapper.h index de5e083041279..aa0a396aac753 100644 --- a/widget/gtk/DMABufLibWrapper.h +++ b/widget/gtk/DMABufLibWrapper.h @@ -173,34 +173,49 @@ class nsGbmLib { static mozilla::StaticMutex sDRILock MOZ_UNANNOTATED; }; +struct GbmFormat { + bool mIsSupported; + bool mHasAlpha; + int mFormat; + uint64_t* mModifiers; + int mModifiersCount; +}; + class nsDMABufDevice { public: nsDMABufDevice(); ~nsDMABufDevice(); - int GetDRMFd(); gbm_device* GetGbmDevice(); - bool IsEnabled(nsACString& aFailureId); - // Use dmabuf for WebRender general web content - static bool IsDMABufTexturesEnabled(); + bool IsDMABufTexturesEnabled(); // Use dmabuf for WebGL content - static bool IsDMABufWebGLEnabled(); - static void DisableDMABufWebGL(); + bool IsDMABufWebGLEnabled(); + void DisableDMABufWebGL(); + + int GetDRMFd(); + GbmFormat* GetGbmFormat(bool aHasAlpha); + GbmFormat* GetExactGbmFormat(int aFormat); + void ResetFormatsModifiers(); + void AddFormatModifier(bool aHasAlpha, int aFormat, uint32_t mModifierHi, + uint32_t mModifierLo); + bool Configure(nsACString& aFailureId); private: - void Configure(); + bool mUseWebGLDmabufBackend; - int mDRMFd = -1; - gbm_device* mGbmDevice = nullptr; - bool mInitialized = false; - const char* mFailureId = nullptr; + private: + GbmFormat mXRGBFormat; + GbmFormat mARGBFormat; - static bool sUseWebGLDmabufBackend; + int mDRMFd; + gbm_device* mGbmDevice; + bool mInitialized; }; nsDMABufDevice* GetDMABufDevice(); +nsDMABufDevice* GetAndConfigureDMABufDevice(); } // namespace widget } // namespace mozilla diff --git a/widget/gtk/DMABufSurface.cpp b/widget/gtk/DMABufSurface.cpp index 13e5b7e819357..f436b98cbd3c0 100644 --- a/widget/gtk/DMABufSurface.cpp +++ b/widget/gtk/DMABufSurface.cpp @@ -398,7 +398,11 @@ bool DMABufSurfaceRGBA::Create(int aWidth, int aHeight, return false; } - mGmbFormat = nsWaylandDisplay::GetGbmFormat(mSurfaceFlags & DMABUF_ALPHA); + mGmbFormat = GetDMABufDevice()->GetGbmFormat(mSurfaceFlags & DMABUF_ALPHA); + if (!mGmbFormat) { + // Requested DRM format is not supported. + return false; + } mDrmFormats[0] = mGmbFormat->mFormat; bool useModifiers = (aDMABufSurfaceFlags & DMABUF_USE_MODIFIERS) && @@ -713,7 +717,7 @@ bool DMABufSurfaceRGBA::CreateWlBuffer() { return false; } - nsWaylandDisplay* waylandDisplay = widget::WaylandDisplayGet(); + RefPtr waylandDisplay = widget::WaylandDisplayGet(); if (!waylandDisplay->GetDmabuf()) { CloseFileDescriptors(lockFD); return false; @@ -1074,7 +1078,7 @@ bool DMABufSurfaceYUV::CreateYUVPlane(int aPlane) { LOGDMABUF(("DMABufSurfaceYUV::CreateYUVPlane() UID %d size %d x %d", mUID, mWidth[aPlane], mHeight[aPlane])); - if (!GetDMABufDevice()->GetGbmDevice()) { + if (!GetAndConfigureDMABufDevice()->GetGbmDevice()) { LOGDMABUF((" Missing GbmDevice!")); return false; } diff --git a/widget/gtk/WakeLockListener.cpp b/widget/gtk/WakeLockListener.cpp index e3517c5437d69..2b71b97ea40dd 100644 --- a/widget/gtk/WakeLockListener.cpp +++ b/widget/gtk/WakeLockListener.cpp @@ -262,12 +262,12 @@ bool WakeLockTopic::InhibitXScreenSaver(bool inhibit) { /* static */ bool WakeLockTopic::CheckWaylandIdleInhibitSupport() { - nsWaylandDisplay* waylandDisplay = WaylandDisplayGet(); + RefPtr waylandDisplay = WaylandDisplayGet(); return waylandDisplay && waylandDisplay->GetIdleInhibitManager() != nullptr; } bool WakeLockTopic::InhibitWaylandIdle() { - nsWaylandDisplay* waylandDisplay = WaylandDisplayGet(); + RefPtr waylandDisplay = WaylandDisplayGet(); if (!waylandDisplay) { return false; } diff --git a/widget/gtk/WaylandBuffer.cpp b/widget/gtk/WaylandBuffer.cpp index f9e4419d8fc8e..d92d0666db3cd 100644 --- a/widget/gtk/WaylandBuffer.cpp +++ b/widget/gtk/WaylandBuffer.cpp @@ -101,8 +101,8 @@ static int WaylandAllocateShmMemory(int aSize) { } /* static */ -RefPtr WaylandShmPool::Create(nsWaylandDisplay* aWaylandDisplay, - int aSize) { +RefPtr WaylandShmPool::Create( + const RefPtr& aWaylandDisplay, int aSize) { if (!aWaylandDisplay->GetShm()) { NS_WARNING("Missing Wayland shm interface!"); return nullptr; @@ -128,6 +128,10 @@ RefPtr WaylandShmPool::Create(nsWaylandDisplay* aWaylandDisplay, return nullptr; } + // We set our queue to get mShmPool events at compositor thread. + wl_proxy_set_queue((struct wl_proxy*)shmPool->mShmPool, + aWaylandDisplay->GetEventQueue()); + return shmPool; } @@ -189,7 +193,7 @@ void WaylandBuffer::BufferReleaseCallbackHandler(void* aData, RefPtr WaylandBufferSHM::Create( const LayoutDeviceIntSize& aSize) { RefPtr buffer = new WaylandBufferSHM(aSize); - nsWaylandDisplay* waylandDisplay = WaylandDisplayGet(); + RefPtr waylandDisplay = WaylandDisplayGet(); int size = aSize.width * aSize.height * BUFFER_BPP; buffer->mShmPool = WaylandShmPool::Create(waylandDisplay, size); @@ -204,11 +208,13 @@ RefPtr WaylandBufferSHM::Create( return nullptr; } + wl_proxy_set_queue((struct wl_proxy*)buffer->GetWlBuffer(), + waylandDisplay->GetEventQueue()); wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer, buffer.get()); LOGWAYLAND("WaylandBufferSHM Created [%p] WaylandDisplay [%p]\n", - buffer.get(), waylandDisplay); + buffer.get(), waylandDisplay.get()); return buffer; } @@ -276,6 +282,9 @@ RefPtr WaylandBufferDMABUF::Create( return nullptr; } + RefPtr waylandDisplay = WaylandDisplayGet(); + wl_proxy_set_queue((struct wl_proxy*)buffer->GetWlBuffer(), + waylandDisplay->GetEventQueue()); wl_buffer_add_listener(buffer->GetWlBuffer(), &sBufferListenerWaylandBuffer, buffer.get()); diff --git a/widget/gtk/WaylandBuffer.h b/widget/gtk/WaylandBuffer.h index fb3fc685ac5a9..7101c53771cb8 100644 --- a/widget/gtk/WaylandBuffer.h +++ b/widget/gtk/WaylandBuffer.h @@ -23,8 +23,8 @@ class WaylandShmPool { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandShmPool); - static RefPtr Create(nsWaylandDisplay* aWaylandDisplay, - int aSize); + static RefPtr Create( + const RefPtr& aWaylandDisplay, int aSize); wl_shm_pool* GetShmPool() { return mShmPool; }; void* GetImageData() { return mImageData; }; diff --git a/widget/gtk/nsAppShell.cpp b/widget/gtk/nsAppShell.cpp index c232ec22196ab..97a6fb58f3d29 100644 --- a/widget/gtk/nsAppShell.cpp +++ b/widget/gtk/nsAppShell.cpp @@ -424,5 +424,8 @@ bool nsAppShell::ProcessNextNativeEvent(bool mayWait) { return false; } bool didProcessEvent = g_main_context_iteration(nullptr, mayWait); +#ifdef MOZ_WAYLAND + mozilla::widget::WaylandDispatchDisplays(); +#endif return didProcessEvent; } diff --git a/widget/gtk/nsWaylandDisplay.cpp b/widget/gtk/nsWaylandDisplay.cpp index 8be8f02f4a3e6..065f95245b32b 100644 --- a/widget/gtk/nsWaylandDisplay.cpp +++ b/widget/gtk/nsWaylandDisplay.cpp @@ -22,22 +22,76 @@ namespace mozilla::widget { -GbmFormat nsWaylandDisplay::sXRGBFormat = {true, false, GBM_FORMAT_XRGB8888, - nullptr, 0}; -GbmFormat nsWaylandDisplay::sARGBFormat = {true, true, GBM_FORMAT_ARGB8888, - nullptr, 0}; - -static nsWaylandDisplay* gWaylandDisplay; +// nsWaylandDisplay needs to be created for each calling thread(main thread, +// compositor thread and render thread) +#define MAX_DISPLAY_CONNECTIONS 10 + +// An array of active Wayland displays. We need a display for every thread +// where Wayland interface used as we need to dispatch Wayland's events there. +static StaticDataMutex< + Array, MAX_DISPLAY_CONNECTIONS>> + gWaylandDisplays{"gWaylandDisplays"}; + +MOZ_THREAD_LOCAL(nsWaylandDisplay*) sTLSDisplay; + +// Dispatch events to Compositor/Render queues +void WaylandDispatchDisplays() { + auto lock = gWaylandDisplays.Lock(); + for (auto& display : *lock) { + if (display) { + display->DispatchEventQueue(); + } + } +} void WaylandDisplayRelease() { - MOZ_RELEASE_ASSERT(NS_IsMainThread(), - "WaylandDisplay can be released in main thread only!"); - if (!gWaylandDisplay) { - NS_WARNING("WaylandDisplayRelease(): Wayland display is missing!"); - return; + auto lock = gWaylandDisplays.Lock(); + for (auto& display : *lock) { + if (display) { + display->ShutdownEventQueue(); + // NOTE: Intentionally leaking the object as otherwise the TLS cache is + // stale. + } } - delete gWaylandDisplay; - gWaylandDisplay = nullptr; +} + +// Get WaylandDisplay for given wl_display and actual calling thread. +RefPtr WaylandDisplayGet() { + bool hasTLS = false; + if (MOZ_LIKELY(sTLSDisplay.init())) { + if (auto* disp = sTLSDisplay.get()) { + return disp; + } + hasTLS = true; + } + + wl_display* waylandDisplay = WaylandDisplayGetWLDisplay(); + if (!waylandDisplay) { + return nullptr; + } + + RefPtr ret; + if (MOZ_LIKELY(hasTLS)) { + ret = new nsWaylandDisplay(waylandDisplay); + sTLSDisplay.set(ret.get()); + } + + auto lock = gWaylandDisplays.Lock(); + + // Search existing display connections for wl_display:thread combination. + for (auto& display : *lock) { + if (display) { + if (display->Matches(waylandDisplay)) { + MOZ_ASSERT(!hasTLS, "We shouldn't have got here"); + return display; + } + } else { + display = ret ? ret.get() : new nsWaylandDisplay(waylandDisplay); + return display; + } + } + MOZ_CRASH("There's too many wayland display conections!"); + return nullptr; } wl_display* WaylandDisplayGetWLDisplay() { @@ -48,19 +102,6 @@ wl_display* WaylandDisplayGetWLDisplay() { return gdk_wayland_display_get_wl_display(disp); } -nsWaylandDisplay* WaylandDisplayGet() { - if (!gWaylandDisplay) { - MOZ_RELEASE_ASSERT(NS_IsMainThread(), - "WaylandDisplay can be created in main thread only!"); - wl_display* waylandDisplay = WaylandDisplayGetWLDisplay(); - if (!waylandDisplay) { - return nullptr; - } - gWaylandDisplay = new nsWaylandDisplay(waylandDisplay); - } - return gWaylandDisplay; -} - void nsWaylandDisplay::SetShm(wl_shm* aShm) { mShm = aShm; } void nsWaylandDisplay::SetCompositor(wl_compositor* aCompositor) { @@ -98,58 +139,6 @@ void nsWaylandDisplay::SetXdgActivation(xdg_activation_v1* aXdgActivation) { mXdgActivation = aXdgActivation; } -GbmFormat* nsWaylandDisplay::GetGbmFormat(bool aHasAlpha) { - return aHasAlpha ? &sARGBFormat : &sXRGBFormat; -} - -void nsWaylandDisplay::AddFormatModifier(bool aHasAlpha, int aFormat, - uint32_t aModifierHi, - uint32_t aModifierLo) { - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - GbmFormat* format = aHasAlpha ? &sARGBFormat : &sXRGBFormat; - format->mIsSupported = true; - format->mHasAlpha = aHasAlpha; - format->mFormat = aFormat; - format->mModifiersCount++; - format->mModifiers = - (uint64_t*)realloc(format->mModifiers, - format->mModifiersCount * sizeof(*format->mModifiers)); - format->mModifiers[format->mModifiersCount - 1] = - ((uint64_t)aModifierHi << 32) | aModifierLo; -} - -static void dmabuf_modifiers(void* data, - struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf, - uint32_t format, uint32_t modifier_hi, - uint32_t modifier_lo) { - // skip modifiers marked as invalid - if (modifier_hi == (DRM_FORMAT_MOD_INVALID >> 32) && - modifier_lo == (DRM_FORMAT_MOD_INVALID & 0xffffffff)) { - return; - } - switch (format) { - case GBM_FORMAT_ARGB8888: - nsWaylandDisplay::AddFormatModifier(true, format, modifier_hi, - modifier_lo); - break; - case GBM_FORMAT_XRGB8888: - nsWaylandDisplay::AddFormatModifier(false, format, modifier_hi, - modifier_lo); - break; - default: - break; - } -} - -static void dmabuf_format(void* data, - struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf, - uint32_t format) { - // XXX: deprecated -} - -static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { - dmabuf_format, dmabuf_modifiers}; - static void global_registry_handler(void* data, wl_registry* registry, uint32_t id, const char* interface, uint32_t version) { @@ -160,41 +149,50 @@ static void global_registry_handler(void* data, wl_registry* registry, if (strcmp(interface, "wl_shm") == 0) { auto* shm = WaylandRegistryBind(registry, id, &wl_shm_interface, 1); + wl_proxy_set_queue((struct wl_proxy*)shm, display->GetEventQueue()); display->SetShm(shm); } else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) { auto* idle_inhibit_manager = WaylandRegistryBind( registry, id, &zwp_idle_inhibit_manager_v1_interface, 1); + wl_proxy_set_queue((struct wl_proxy*)idle_inhibit_manager, + display->GetEventQueue()); display->SetIdleInhibitManager(idle_inhibit_manager); } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { auto* relative_pointer_manager = WaylandRegistryBind( registry, id, &zwp_relative_pointer_manager_v1_interface, 1); + wl_proxy_set_queue((struct wl_proxy*)relative_pointer_manager, + display->GetEventQueue()); display->SetRelativePointerManager(relative_pointer_manager); } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { auto* pointer_constraints = WaylandRegistryBind( registry, id, &zwp_pointer_constraints_v1_interface, 1); + wl_proxy_set_queue((struct wl_proxy*)pointer_constraints, + display->GetEventQueue()); display->SetPointerConstraints(pointer_constraints); } else if (strcmp(interface, "wl_compositor") == 0) { // Requested wl_compositor version 4 as we need wl_surface_damage_buffer(). auto* compositor = WaylandRegistryBind( registry, id, &wl_compositor_interface, 4); + wl_proxy_set_queue((struct wl_proxy*)compositor, display->GetEventQueue()); display->SetCompositor(compositor); } else if (strcmp(interface, "wl_subcompositor") == 0) { auto* subcompositor = WaylandRegistryBind( registry, id, &wl_subcompositor_interface, 1); + wl_proxy_set_queue((struct wl_proxy*)subcompositor, + display->GetEventQueue()); display->SetSubcompositor(subcompositor); } else if (strcmp(interface, "wp_viewporter") == 0) { auto* viewporter = WaylandRegistryBind( registry, id, &wp_viewporter_interface, 1); + wl_proxy_set_queue((struct wl_proxy*)viewporter, display->GetEventQueue()); display->SetViewporter(viewporter); } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0 && version > 2) { auto* dmabuf = WaylandRegistryBind( registry, id, &zwp_linux_dmabuf_v1_interface, 3); + wl_proxy_set_queue((struct wl_proxy*)dmabuf, display->GetEventQueue()); display->SetDmabuf(dmabuf); - if (NS_IsMainThread()) { - zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, nullptr); - } } else if (strcmp(interface, "xdg_activation_v1") == 0) { auto* activation = WaylandRegistryBind( registry, id, &xdg_activation_v1_interface, 1); @@ -215,7 +213,92 @@ static void global_registry_remover(void* data, wl_registry* registry, static const struct wl_registry_listener registry_listener = { global_registry_handler, global_registry_remover}; -nsWaylandDisplay::~nsWaylandDisplay() {} +void nsWaylandDisplay::DispatchEventQueue() { + MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); + if (mEventQueue) { + wl_display_dispatch_queue_pending(mDisplay, mEventQueue); + } +} + +void nsWaylandDisplay::ShutdownEventQueue() { + MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); + if (mEventQueue) { + wl_event_queue_destroy(mEventQueue); + mEventQueue = nullptr; + } +} + +void nsWaylandDisplay::SyncEnd() { + wl_callback_destroy(mSyncCallback); + mSyncCallback = nullptr; +} + +static void wayland_sync_callback(void* data, struct wl_callback* callback, + uint32_t time) { + auto display = static_cast(data); + display->SyncEnd(); +} + +static const struct wl_callback_listener sync_callback_listener = { + .done = wayland_sync_callback}; + +void nsWaylandDisplay::SyncBegin() { + WaitForSyncEnd(); + + // Use wl_display_sync() to synchronize wayland events. + // See dri2_wl_swap_buffers_with_damage() from MESA + // or wl_display_roundtrip_queue() from wayland-client. + struct wl_display* displayWrapper = + static_cast(wl_proxy_create_wrapper((void*)mDisplay)); + if (!displayWrapper) { + NS_WARNING("Failed to create wl_proxy wrapper!"); + return; + } + + wl_proxy_set_queue((struct wl_proxy*)displayWrapper, mEventQueue); + mSyncCallback = wl_display_sync(displayWrapper); + wl_proxy_wrapper_destroy((void*)displayWrapper); + + if (!mSyncCallback) { + NS_WARNING("Failed to create wl_display_sync callback!"); + return; + } + + wl_callback_add_listener(mSyncCallback, &sync_callback_listener, this); + wl_display_flush(mDisplay); +} + +void nsWaylandDisplay::QueueSyncBegin() { + RefPtr self(this); + NS_DispatchToMainThread( + NS_NewRunnableFunction("nsWaylandDisplay::QueueSyncBegin", + [self]() -> void { self->SyncBegin(); })); +} + +void nsWaylandDisplay::WaitForSyncEnd() { + MOZ_RELEASE_ASSERT( + NS_IsMainThread(), + "nsWaylandDisplay::WaitForSyncEnd() can be called in main thread only!"); + + // We're done here + if (!mSyncCallback) { + return; + } + + while (mSyncCallback != nullptr) { + // TODO: wl_display_dispatch_queue() should not be called while + // glib main loop is iterated at nsAppShell::ProcessNextNativeEvent(). + if (wl_display_dispatch_queue(mDisplay, mEventQueue) == -1) { + NS_WARNING("wl_display_dispatch_queue failed!"); + SyncEnd(); + return; + } + } +} + +bool nsWaylandDisplay::Matches(wl_display* aDisplay) { + return mThreadId == PR_GetCurrentThread() && aDisplay == mDisplay; +} static void WlLogHandler(const char* format, va_list args) { char error[1000]; @@ -229,10 +312,20 @@ nsWaylandDisplay::nsWaylandDisplay(wl_display* aDisplay) // in a similar fashion wl_log_set_handler_client(WlLogHandler); - mRegistry = wl_display_get_registry(mDisplay); - wl_registry_add_listener(mRegistry, ®istry_listener, this); - wl_display_roundtrip(mDisplay); - wl_display_roundtrip(mDisplay); + wl_registry* registry = wl_display_get_registry(mDisplay); + wl_registry_add_listener(registry, ®istry_listener, this); + if (!NS_IsMainThread()) { + mEventQueue = wl_display_create_queue(mDisplay); + wl_proxy_set_queue((struct wl_proxy*)registry, mEventQueue); + } + if (mEventQueue) { + wl_display_roundtrip_queue(mDisplay, mEventQueue); + wl_display_roundtrip_queue(mDisplay, mEventQueue); + } else { + wl_display_roundtrip(mDisplay); + wl_display_roundtrip(mDisplay); + } + wl_registry_destroy(registry); // Check we have critical Wayland interfaces. // Missing ones indicates a compositor bug and we can't continue. @@ -242,4 +335,6 @@ nsWaylandDisplay::nsWaylandDisplay(wl_display* aDisplay) "We're missing subcompositor interface!"); } +nsWaylandDisplay::~nsWaylandDisplay() { ShutdownEventQueue(); } + } // namespace mozilla::widget diff --git a/widget/gtk/nsWaylandDisplay.h b/widget/gtk/nsWaylandDisplay.h index 479e4fec03361..e2317780d9880 100644 --- a/widget/gtk/nsWaylandDisplay.h +++ b/widget/gtk/nsWaylandDisplay.h @@ -23,24 +23,29 @@ namespace mozilla { namespace widget { -struct GbmFormat { - bool mIsSupported; - bool mHasAlpha; - int mFormat; - uint64_t* mModifiers; - int mModifiersCount; -}; - // Our general connection to Wayland display server, // holds our display connection and runs event loop. // We have a global nsWaylandDisplay object for each thread. class nsWaylandDisplay { public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsWaylandDisplay) + // Create nsWaylandDisplay object on top of native Wayland wl_display // connection. explicit nsWaylandDisplay(wl_display* aDisplay); + void DispatchEventQueue(); + void ShutdownEventQueue(); + + void SyncBegin(); + void QueueSyncBegin(); + void SyncEnd(); + void WaitForSyncEnd(); + + bool Matches(wl_display* aDisplay); + wl_display* GetDisplay() { return mDisplay; }; + wl_event_queue* GetEventQueue() { return mEventQueue; }; wl_compositor* GetCompositor() { return mCompositor; }; wl_subcompositor* GetSubcompositor() { return mSubcompositor; }; wl_shm* GetShm() { return mShm; }; @@ -57,6 +62,8 @@ class nsWaylandDisplay { zwp_linux_dmabuf_v1* GetDmabuf() { return mDmabuf; }; xdg_activation_v1* GetXdgActivation() { return mXdgActivation; }; + bool IsMainThreadDisplay() { return mEventQueue == nullptr; } + void SetShm(wl_shm* aShm); void SetCompositor(wl_compositor* aCompositor); void SetSubcompositor(wl_subcompositor* aSubcompositor); @@ -69,19 +76,18 @@ class nsWaylandDisplay { void SetDmabuf(zwp_linux_dmabuf_v1* aDmabuf); void SetXdgActivation(xdg_activation_v1* aXdgActivation); - ~nsWaylandDisplay(); - - static GbmFormat* GetGbmFormat(bool aHasAlpha); - static void AddFormatModifier(bool aHasAlpha, int aFormat, - uint32_t aModifierHi, uint32_t aModifierLo); + bool IsExplicitSyncEnabled() { return mExplicitSync; } private: + ~nsWaylandDisplay(); + PRThread* mThreadId = nullptr; - wl_registry* mRegistry = nullptr; wl_display* mDisplay = nullptr; + wl_event_queue* mEventQueue = nullptr; wl_compositor* mCompositor = nullptr; wl_subcompositor* mSubcompositor = nullptr; wl_shm* mShm = nullptr; + wl_callback* mSyncCallback = nullptr; zwp_idle_inhibit_manager_v1* mIdleInhibitManager = nullptr; zwp_relative_pointer_manager_v1* mRelativePointerManager = nullptr; zwp_pointer_constraints_v1* mPointerConstraints = nullptr; @@ -89,15 +95,14 @@ class nsWaylandDisplay { zwp_linux_dmabuf_v1* mDmabuf = nullptr; xdg_activation_v1* mXdgActivation = nullptr; bool mExplicitSync = false; - - static GbmFormat sXRGBFormat; - static GbmFormat sARGBFormat; }; -wl_display* WaylandDisplayGetWLDisplay(); -nsWaylandDisplay* WaylandDisplayGet(); +void WaylandDispatchDisplays(); void WaylandDisplayRelease(); +RefPtr WaylandDisplayGet(); +wl_display* WaylandDisplayGetWLDisplay(); + } // namespace widget } // namespace mozilla diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index 39c558a817572..ea9675066f40a 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -423,6 +423,7 @@ class nsWindow final : public nsBaseWidget { void SetEGLNativeWindowSize(const LayoutDeviceIntSize& aEGLWindowSize); void WaylandDragWorkaround(GdkEventButton* aEvent); + wl_display* GetWaylandDisplay(); void CreateCompositorVsyncDispatcher() override; LayoutDeviceIntPoint GetNativePointerLockCenter() { return mNativePointerLockCenter;