From dc3ae625447b873a55242c9a29b0fb288e2bebb8 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Tue, 6 Jul 2021 07:42:42 +0000 Subject: [PATCH] Bug 1635451 - Allow GLX to work in headless content processes. r=jgilbert This follows what we're already doing for EGL: a refcounted object which can own the X connection, where we hold a weak reference from the library object so that multiple contexts opportunistically share the display but we close the connection when the last context is freed/GCed. In a process where GTK is initialized, we borrow its display instead of opening a new one, which preserves the existing behavior. Differential Revision: https://phabricator.services.mozilla.com/D112195 --- gfx/gl/GLContextGLX.h | 13 +-- gfx/gl/GLContextProviderGLX.cpp | 151 +++++++++++++++++++++----------- gfx/gl/GLXLibrary.h | 72 ++++++++------- gfx/thebes/XlibDisplay.cpp | 40 +++++++++ gfx/thebes/XlibDisplay.h | 41 +++++++++ gfx/thebes/gfxPlatformGtk.cpp | 10 ++- gfx/thebes/gfxXlibSurface.cpp | 39 ++++++--- gfx/thebes/gfxXlibSurface.h | 16 +++- gfx/thebes/moz.build | 4 + 9 files changed, 276 insertions(+), 110 deletions(-) create mode 100644 gfx/thebes/XlibDisplay.cpp create mode 100644 gfx/thebes/XlibDisplay.h diff --git a/gfx/gl/GLContextGLX.h b/gfx/gl/GLContextGLX.h index 6cd338226ed5..d79c939321f9 100644 --- a/gfx/gl/GLContextGLX.h +++ b/gfx/gl/GLContextGLX.h @@ -20,8 +20,9 @@ class GLContextGLX : public GLContext { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextGLX, override) static already_AddRefed CreateGLContext( - const GLContextDesc&, Display* display, GLXDrawable drawable, - GLXFBConfig cfg, bool deleteDrawable, gfxXlibSurface* pixmap); + const GLContextDesc&, std::shared_ptr display, + GLXDrawable drawable, GLXFBConfig cfg, bool deleteDrawable, + gfxXlibSurface* pixmap); static bool FindVisual(Display* display, int screen, bool useWebRender, bool useAlpha, int* const out_visualId); @@ -67,12 +68,12 @@ class GLContextGLX : public GLContext { private: friend class GLContextProviderGLX; - GLContextGLX(const GLContextDesc&, Display* aDisplay, GLXDrawable aDrawable, - GLXContext aContext, bool aDeleteDrawable, bool aDoubleBuffered, - gfxXlibSurface* aPixmap); + GLContextGLX(const GLContextDesc&, std::shared_ptr aDisplay, + GLXDrawable aDrawable, GLXContext aContext, bool aDeleteDrawable, + bool aDoubleBuffered, gfxXlibSurface* aPixmap); const GLXContext mContext; - Display* const mDisplay; + const std::shared_ptr mDisplay; const GLXDrawable mDrawable; const bool mDeleteDrawable; const bool mDoubleBuffered; diff --git a/gfx/gl/GLContextProviderGLX.cpp b/gfx/gl/GLContextProviderGLX.cpp index 514719ecb42a..bda814611b7c 100644 --- a/gfx/gl/GLContextProviderGLX.cpp +++ b/gfx/gl/GLContextProviderGLX.cpp @@ -19,6 +19,7 @@ #include "mozilla/layers/CompositorOptions.h" #include "mozilla/Range.h" #include "mozilla/ScopeExit.h" +#include "mozilla/Sprintf.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/widget/CompositorWidget.h" @@ -59,7 +60,7 @@ static inline bool HasExtension(const char* aExtensions, reinterpret_cast(aExtensions), aRequiredExtension); } -bool GLXLibrary::EnsureInitialized() { +bool GLXLibrary::EnsureInitialized(Display* aDisplay) { if (mInitialized) { return true; } @@ -70,6 +71,11 @@ bool GLXLibrary::EnsureInitialized() { } mTriedInitializing = true; + MOZ_ASSERT(aDisplay); + if (!aDisplay) { + return false; + } + // Force enabling s3 texture compression. (Bug 774134) PR_SetEnv("force_s3tc_enable=true"); @@ -148,12 +154,11 @@ bool GLXLibrary::EnsureInitialized() { } const SymbolLoader pfnLoader(mSymbols.fGetProcAddress); - Display* display = DefaultXDisplay(); - int screen = DefaultScreen(display); + int screen = DefaultScreen(aDisplay); { int major, minor; - if (!fQueryVersion(display, &major, &minor) || major != 1 || minor < 3) { + if (!fQueryVersion(aDisplay, &major, &minor) || major != 1 || minor < 3) { NS_ERROR("GLX version older than 1.3. (released in 1998)"); return false; } @@ -181,10 +186,10 @@ bool GLXLibrary::EnsureInitialized() { return false; }; - const char* clientVendor = fGetClientString(display, LOCAL_GLX_VENDOR); + const char* clientVendor = fGetClientString(aDisplay, LOCAL_GLX_VENDOR); const char* serverVendor = - fQueryServerString(display, screen, LOCAL_GLX_VENDOR); - const char* extensionsStr = fQueryExtensionsString(display, screen); + fQueryServerString(aDisplay, screen, LOCAL_GLX_VENDOR); + const char* extensionsStr = fQueryExtensionsString(aDisplay, screen); if (HasExtension(extensionsStr, "GLX_EXT_texture_from_pixmap") && fnLoadSymbols(symbols_texturefrompixmap)) { @@ -240,19 +245,20 @@ bool GLXLibrary::EnsureInitialized() { } bool GLXLibrary::SupportsTextureFromPixmap(gfxASurface* aSurface) { - if (!EnsureInitialized()) { + if (aSurface->GetType() != gfxSurfaceType::Xlib) { return false; } - if (aSurface->GetType() != gfxSurfaceType::Xlib || !mUseTextureFromPixmap) { + gfxXlibSurface* xs = static_cast(aSurface); + if (!EnsureInitialized(xs->XDisplay())) { return false; } - return true; + return mUseTextureFromPixmap; } -bool GLXLibrary::SupportsVideoSync() { - if (!EnsureInitialized()) { +bool GLXLibrary::SupportsVideoSync(Display* aDisplay) { + if (!EnsureInitialized(aDisplay)) { return false; } @@ -450,8 +456,9 @@ static int GLXErrorHandler(Display* display, XErrorEvent* ev) { } GLXLibrary::WrapperScope::WrapperScope(const GLXLibrary& glx, - const char* const funcName) - : mGlx(glx), mFuncName(funcName) { + const char* const funcName, + Display* aDisplay) + : mGlx(glx), mFuncName(funcName), mDisplay(aDisplay) { if (mGlx.mDebug) { sOldErrorHandler = XSetErrorHandler(GLXErrorHandler); } @@ -459,11 +466,17 @@ GLXLibrary::WrapperScope::WrapperScope(const GLXLibrary& glx, GLXLibrary::WrapperScope::~WrapperScope() { if (mGlx.mDebug) { - FinishX(DefaultXDisplay()); + if (mDisplay) { + FinishX(mDisplay); + } if (sErrorEvent.mError.error_code) { char buffer[100] = {}; - XGetErrorText(DefaultXDisplay(), sErrorEvent.mError.error_code, buffer, - sizeof(buffer)); + if (mDisplay) { + XGetErrorText(mDisplay, sErrorEvent.mError.error_code, buffer, + sizeof(buffer)); + } else { + SprintfLiteral(buffer, "%d", sErrorEvent.mError.error_code); + } printf_stderr("X ERROR after %s: %s (%i) - Request: %i.%i, Serial: %lu", mFuncName, buffer, sErrorEvent.mError.error_code, sErrorEvent.mError.request_code, @@ -474,13 +487,45 @@ GLXLibrary::WrapperScope::~WrapperScope() { } } +// Returns the GTK display if available; otherwise, if a display was +// previously opened by this method and is still open, returns a +// reference to it; otherwise, opens a new connection. (The non-GTK +// cases are similar to what we do for EGL.) +std::shared_ptr GLXLibrary::GetDisplay() { + std::shared_ptr display; + +#ifdef MOZ_WIDGET_GTK + static const bool kHaveGtk = !!gdk_display_get_default(); + if (kHaveGtk) { + display = XlibDisplay::Borrow(DefaultXDisplay()); + } +#endif + if (display) { + return display; + } + + auto ownDisplay = mOwnDisplay.Lock(); + display = ownDisplay->lock(); + if (display) { + return display; + } + + display = XlibDisplay::Open(nullptr); + if (NS_WARN_IF(!display)) { + return nullptr; + } + *ownDisplay = display; + return display; +} + already_AddRefed GLContextGLX::CreateGLContext( - const GLContextDesc& desc, Display* display, GLXDrawable drawable, - GLXFBConfig cfg, bool deleteDrawable, gfxXlibSurface* pixmap) { + const GLContextDesc& desc, std::shared_ptr display, + GLXDrawable drawable, GLXFBConfig cfg, bool deleteDrawable, + gfxXlibSurface* pixmap) { GLXLibrary& glx = sGLXLibrary; int isDoubleBuffered = 0; - int err = glx.fGetFBConfigAttrib(display, cfg, LOCAL_GLX_DOUBLEBUFFER, + int err = glx.fGetFBConfigAttrib(*display, cfg, LOCAL_GLX_DOUBLEBUFFER, &isDoubleBuffered); if (LOCAL_GLX_BAD_ATTRIBUTE != err) { if (ShouldSpew()) { @@ -507,15 +552,15 @@ already_AddRefed GLContextGLX::CreateGLContext( // we should not try to use such contexts. (Errors may come from the // distant server, or something) const auto glxContext = glx.fCreateContextAttribs( - display, cfg, nullptr, X11True, terminated.data()); + *display, cfg, nullptr, X11True, terminated.data()); if (!glxContext) return nullptr; const RefPtr ret = new GLContextGLX(desc, display, drawable, glxContext, deleteDrawable, isDoubleBuffered, pixmap); - if (handler.SyncAndGetError(display)) return nullptr; + if (handler.SyncAndGetError(*display)) return nullptr; if (!ret->Init()) return nullptr; - if (handler.SyncAndGetError(display)) return nullptr; + if (handler.SyncAndGetError(*display)) return nullptr; return ret; }; @@ -602,15 +647,15 @@ GLContextGLX::~GLContextGLX() { #ifdef DEBUG bool success = #endif - mGLX->fMakeCurrent(mDisplay, X11None, nullptr); + mGLX->fMakeCurrent(*mDisplay, X11None, nullptr); MOZ_ASSERT(success, "glXMakeCurrent failed to release GL context before we call " "glXDestroyContext!"); - mGLX->fDestroyContext(mDisplay, mContext); + mGLX->fDestroyContext(*mDisplay, mContext); if (mDeleteDrawable) { - mGLX->fDestroyPixmap(mDisplay, mDrawable); + mGLX->fDestroyPixmap(*mDisplay, mDrawable); } } @@ -632,10 +677,10 @@ bool GLContextGLX::MakeCurrentImpl() const { if (mGLX->IsMesa()) { // Read into the event queue to ensure that Mesa receives a // DRI2InvalidateBuffers event before drawing. See bug 1280653. - Unused << XPending(mDisplay); + Unused << XPending(*mDisplay); } - const bool succeeded = mGLX->fMakeCurrent(mDisplay, mDrawable, mContext); + const bool succeeded = mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext); NS_ASSERTION(succeeded, "Failed to make GL context current!"); if (!IsOffscreen() && mGLX->SupportsSwapControl()) { @@ -643,7 +688,7 @@ bool GLContextGLX::MakeCurrentImpl() const { // VBlank when calling glXSwapBuffers. We want to run unthrottled // in ASAP mode. See bug 1280744. const bool isASAP = (StaticPrefs::layout_frame_rate() == 0); - mGLX->fSwapInterval(mDisplay, mDrawable, isASAP ? 0 : 1); + mGLX->fSwapInterval(*mDisplay, mDrawable, isASAP ? 0 : 1); } return succeeded; } @@ -661,7 +706,7 @@ bool GLContextGLX::IsDoubleBuffered() const { return mDoubleBuffered; } bool GLContextGLX::SwapBuffers() { if (!mDoubleBuffered) return false; - mGLX->fSwapBuffers(mDisplay, mDrawable); + mGLX->fSwapBuffers(*mDisplay, mDrawable); return true; } @@ -671,7 +716,7 @@ GLint GLContextGLX::GetBufferAge() const { } GLuint result = 0; - mGLX->fQueryDrawable(mDisplay, mDrawable, LOCAL_GLX_BACK_BUFFER_AGE_EXT, + mGLX->fQueryDrawable(*mDisplay, mDrawable, LOCAL_GLX_BACK_BUFFER_AGE_EXT, &result); if (result > INT32_MAX) { // If the result can't fit, just assume the buffer cannot be reused. @@ -681,34 +726,34 @@ GLint GLContextGLX::GetBufferAge() const { } void GLContextGLX::GetWSIInfo(nsCString* const out) const { - Display* display = DefaultXDisplay(); - int screen = DefaultScreen(display); + int screen = DefaultScreen(mDisplay->get()); int majorVersion, minorVersion; - sGLXLibrary.fQueryVersion(display, &majorVersion, &minorVersion); + sGLXLibrary.fQueryVersion(*mDisplay, &majorVersion, &minorVersion); out->Append(nsPrintfCString("GLX %u.%u", majorVersion, minorVersion)); out->AppendLiteral("\nGLX_VENDOR(client): "); - out->Append(sGLXLibrary.fGetClientString(display, LOCAL_GLX_VENDOR)); + out->Append(sGLXLibrary.fGetClientString(*mDisplay, LOCAL_GLX_VENDOR)); out->AppendLiteral("\nGLX_VENDOR(server): "); out->Append( - sGLXLibrary.fQueryServerString(display, screen, LOCAL_GLX_VENDOR)); + sGLXLibrary.fQueryServerString(*mDisplay, screen, LOCAL_GLX_VENDOR)); out->AppendLiteral("\nExtensions: "); - out->Append(sGLXLibrary.fQueryExtensionsString(display, screen)); + out->Append(sGLXLibrary.fQueryExtensionsString(*mDisplay, screen)); } bool GLContextGLX::OverrideDrawable(GLXDrawable drawable) { - return mGLX->fMakeCurrent(mDisplay, drawable, mContext); + return mGLX->fMakeCurrent(*mDisplay, drawable, mContext); } bool GLContextGLX::RestoreDrawable() { - return mGLX->fMakeCurrent(mDisplay, mDrawable, mContext); + return mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext); } -GLContextGLX::GLContextGLX(const GLContextDesc& desc, Display* aDisplay, +GLContextGLX::GLContextGLX(const GLContextDesc& desc, + std::shared_ptr aDisplay, GLXDrawable aDrawable, GLXContext aContext, bool aDeleteDrawable, bool aDoubleBuffered, gfxXlibSurface* aPixmap) @@ -741,7 +786,7 @@ static bool AreCompatibleVisuals(Visual* one, Visual* two) { already_AddRefed CreateForWidget(Display* aXDisplay, Window aXWindow, bool aHardwareWebRender, bool aForceAccelerated) { - if (!sGLXLibrary.EnsureInitialized()) { + if (!sGLXLibrary.EnsureInitialized(aXDisplay)) { return nullptr; } @@ -774,7 +819,8 @@ already_AddRefed CreateForWidget(Display* aXDisplay, Window aXWindow, } else { flags = CreateContextFlags::REQUIRE_COMPAT_PROFILE; } - return GLContextGLX::CreateGLContext({{flags}, false}, aXDisplay, aXWindow, + return GLContextGLX::CreateGLContext({{flags}, false}, + XlibDisplay::Borrow(aXDisplay), aXWindow, config, false, nullptr); } @@ -856,7 +902,7 @@ static bool ChooseConfig(GLXLibrary* glx, Display* display, int screen, bool GLContextGLX::FindVisual(Display* display, int screen, bool useWebRender, bool useAlpha, int* const out_visualId) { - if (!sGLXLibrary.EnsureInitialized()) { + if (!sGLXLibrary.EnsureInitialized(display)) { return false; } @@ -1005,31 +1051,32 @@ static already_AddRefed CreateOffscreenPixmapContext( const GLContextCreateDesc& desc, const IntSize& size, nsACString* const out_failureId) { GLXLibrary* glx = &sGLXLibrary; - if (!glx->EnsureInitialized()) return nullptr; + auto display = glx->GetDisplay(); - Display* display = DefaultXDisplay(); - int screen = DefaultScreen(display); + if (!display || !glx->EnsureInitialized(*display)) return nullptr; + + int screen = DefaultScreen(display->get()); ScopedXFree scopedConfigArr; GLXFBConfig config; int visid; - if (!ChooseConfig(glx, display, screen, &scopedConfigArr, &config, &visid)) { + if (!ChooseConfig(glx, *display, screen, &scopedConfigArr, &config, &visid)) { NS_WARNING("Failed to find a compatible config."); return nullptr; } Visual* visual; int depth; - FindVisualAndDepth(display, visid, &visual, &depth); + FindVisualAndDepth(*display, visid, &visual, &depth); OffMainThreadScopedXErrorHandler xErrorHandler; bool error = false; gfx::IntSize dummySize(16, 16); RefPtr surface = gfxXlibSurface::Create( - DefaultScreenOfDisplay(display), visual, dummySize); + display, DefaultScreenOfDisplay(display->get()), visual, dummySize); if (surface->CairoStatus() != 0) { - mozilla::Unused << xErrorHandler.SyncAndGetError(display); + mozilla::Unused << xErrorHandler.SyncAndGetError(*display); return nullptr; } @@ -1037,12 +1084,12 @@ static already_AddRefed CreateOffscreenPixmapContext( // its pre-GLX-1.3 extension equivalent (though given the ABI, we // might not need to). const auto drawable = surface->XDrawable(); - const auto pixmap = glx->fCreatePixmap(display, config, drawable, nullptr); + const auto pixmap = glx->fCreatePixmap(*display, config, drawable, nullptr); if (pixmap == 0) { error = true; } - bool serverError = xErrorHandler.SyncAndGetError(display); + bool serverError = xErrorHandler.SyncAndGetError(*display); if (error || serverError) return nullptr; auto fullDesc = GLContextDesc{desc}; diff --git a/gfx/gl/GLXLibrary.h b/gfx/gl/GLXLibrary.h index 550ffb9e2b6c..365ed070cf0b 100644 --- a/gfx/gl/GLXLibrary.h +++ b/gfx/gl/GLXLibrary.h @@ -8,6 +8,8 @@ #include "GLContextTypes.h" #include "mozilla/Assertions.h" +#include "mozilla/DataMutex.h" +#include "mozilla/gfx/XlibDisplay.h" #include "prlink.h" typedef realGLboolean GLboolean; @@ -33,172 +35,174 @@ class GLContextGLX; class GLXLibrary final { public: - bool EnsureInitialized(); + bool EnsureInitialized(Display* aDisplay); private: class WrapperScope final { const GLXLibrary& mGlx; const char* const mFuncName; + Display* const mDisplay; public: - WrapperScope(const GLXLibrary& glx, const char* const funcName); + WrapperScope(const GLXLibrary& glx, const char* const funcName, + Display* aDisplay); ~WrapperScope(); }; public: #ifdef DEBUG -# define DECL_WRAPPER_SCOPE const WrapperScope wrapperScope(*this, __func__); +# define DECL_WRAPPER_SCOPE(display) \ + const WrapperScope wrapperScope(*this, __func__, display); #else -# define DECL_WRAPPER_SCOPE +# define DECL_WRAPPER_SCOPE(display) #endif void fDestroyContext(Display* display, GLXContext context) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fDestroyContext(display, context); } Bool fMakeCurrent(Display* display, GLXDrawable drawable, GLXContext context) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fMakeCurrent(display, drawable, context); } XVisualInfo* fGetConfig(Display* display, XVisualInfo* info, int attrib, int* value) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fGetConfig(display, info, attrib, value); } GLXContext fGetCurrentContext() const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(nullptr) return mSymbols.fGetCurrentContext(); } GLXFBConfig* fChooseFBConfig(Display* display, int screen, const int* attrib_list, int* nelements) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fChooseFBConfig(display, screen, attrib_list, nelements); } XVisualInfo* fChooseVisual(Display* display, int screen, int* attrib_list) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fChooseVisual(display, screen, attrib_list); } GLXFBConfig* fGetFBConfigs(Display* display, int screen, int* nelements) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fGetFBConfigs(display, screen, nelements); } GLXContext fCreateNewContext(Display* display, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fCreateNewContext(display, config, render_type, share_list, direct); } int fGetFBConfigAttrib(Display* display, GLXFBConfig config, int attribute, int* value) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fGetFBConfigAttrib(display, config, attribute, value); } void fSwapBuffers(Display* display, GLXDrawable drawable) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fSwapBuffers(display, drawable); } const char* fQueryExtensionsString(Display* display, int screen) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fQueryExtensionsString(display, screen); } const char* fGetClientString(Display* display, int screen) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fGetClientString(display, screen); } const char* fQueryServerString(Display* display, int screen, int name) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fQueryServerString(display, screen, name); } GLXPixmap fCreatePixmap(Display* display, GLXFBConfig config, Pixmap pixmap, const int* attrib_list) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fCreatePixmap(display, config, pixmap, attrib_list); } GLXPixmap fCreateGLXPixmapWithConfig(Display* display, GLXFBConfig config, Pixmap pixmap) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fCreateGLXPixmapWithConfig(display, config, pixmap); } void fDestroyPixmap(Display* display, GLXPixmap pixmap) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fDestroyPixmap(display, pixmap); } Bool fQueryVersion(Display* display, int* major, int* minor) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fQueryVersion(display, major, minor); } void fBindTexImage(Display* display, GLXDrawable drawable, int buffer, const int* attrib_list) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fBindTexImageEXT(display, drawable, buffer, attrib_list); } void fReleaseTexImage(Display* display, GLXDrawable drawable, int buffer) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fReleaseTexImageEXT(display, drawable, buffer); } void fWaitGL() const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(nullptr) return mSymbols.fWaitGL(); } void fWaitX() const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(nullptr) return mSymbols.fWaitX(); } GLXContext fCreateContextAttribs(Display* display, GLXFBConfig config, GLXContext share_list, Bool direct, const int* attrib_list) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(display) return mSymbols.fCreateContextAttribsARB(display, config, share_list, direct, attrib_list); } int fGetVideoSync(unsigned int* count) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(nullptr) return mSymbols.fGetVideoSyncSGI(count); } int fWaitVideoSync(int divisor, int remainder, unsigned int* count) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(nullptr) return mSymbols.fWaitVideoSyncSGI(divisor, remainder, count); } void fSwapInterval(Display* dpy, GLXDrawable drawable, int interval) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(dpy) return mSymbols.fSwapIntervalEXT(dpy, drawable, interval); } int fQueryDrawable(Display* dpy, GLXDrawable drawable, int attribute, unsigned int* value) const { - DECL_WRAPPER_SCOPE + DECL_WRAPPER_SCOPE(dpy) return mSymbols.fQueryDrawable(dpy, drawable, attribute, value); } - #undef DECL_WRAPPER_SCOPE //// @@ -216,7 +220,7 @@ class GLXLibrary final { bool HasVideoMemoryPurge() { return mHasVideoMemoryPurge; } bool HasCreateContextAttribs() { return mHasCreateContextAttribs; } bool SupportsTextureFromPixmap(gfxASurface* aSurface); - bool SupportsVideoSync(); + bool SupportsVideoSync(Display* aDisplay); bool SupportsSwapControl() const { return bool(mSymbols.fSwapIntervalEXT); } bool SupportsBufferAge() const { MOZ_ASSERT(mInitialized); @@ -227,6 +231,8 @@ class GLXLibrary final { auto GetGetProcAddress() const { return mSymbols.fGetProcAddress; } + std::shared_ptr GetDisplay(); + private: struct { void(GLAPIENTRY* fDestroyContext)(Display*, GLXContext); @@ -276,6 +282,8 @@ class GLXLibrary final { bool mIsNVIDIA = false; bool mClientIsMesa = false; PRLibrary* mOGLLibrary = nullptr; + StaticDataMutex> mOwnDisplay{ + "GLXLibrary::mOwnDisplay"}; }; // a global GLXLibrary instance diff --git a/gfx/thebes/XlibDisplay.cpp b/gfx/thebes/XlibDisplay.cpp new file mode 100644 index 000000000000..3bae50194ede --- /dev/null +++ b/gfx/thebes/XlibDisplay.cpp @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "XlibDisplay.h" + +#include "mozilla/Assertions.h" + +namespace mozilla::gfx { + +XlibDisplay::XlibDisplay(Display* aDisplay, bool aOwned) + : mDisplay(aDisplay), mOwned(aOwned) { + MOZ_ASSERT(mDisplay); +} + +XlibDisplay::~XlibDisplay() { + if (mOwned) { + XCloseDisplay(mDisplay); + } +} + +/* static */ +std::shared_ptr XlibDisplay::Borrow(Display* aDisplay) { + if (!aDisplay) { + return nullptr; + } + return std::shared_ptr(new XlibDisplay(aDisplay, false)); +} + +/* static */ +std::shared_ptr XlibDisplay::Open(const char* aDisplayName) { + Display* disp = XOpenDisplay(aDisplayName); + if (!disp) { + return nullptr; + } + return std::shared_ptr(new XlibDisplay(disp, true)); +} + +} // namespace mozilla::gfx diff --git a/gfx/thebes/XlibDisplay.h b/gfx/thebes/XlibDisplay.h new file mode 100644 index 000000000000..0c0f8413a43c --- /dev/null +++ b/gfx/thebes/XlibDisplay.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_XLIBDISPLAY_H +#define GFX_XLIBDISPLAY_H + +#include +#include "X11UndefineNone.h" + +#include + +namespace mozilla::gfx { + +// Represents an X11 display connection which may be either borrowed +// (e.g., from GTK) or owned; in the latter case it will be closed +// with this object becomes unreferenced. See also the `EglDisplay` +// class. +class XlibDisplay final { + public: + ~XlibDisplay(); + + // Explicit `->get()` may be needed with some `Xlib.h` macros that + // expand to C-style pointer casts. + Display* get() const { return mDisplay; } + operator Display*() const { return mDisplay; } + + static std::shared_ptr Borrow(Display* aDisplay); + static std::shared_ptr Open(const char* aDisplayName); + + private: + Display* const mDisplay; + bool const mOwned; + + XlibDisplay(Display*, bool); +}; + +} // namespace mozilla::gfx + +#endif // GFX_XLIBDISPLAY_H diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp index 6a703edcf39a..5fc10e4416ae 100644 --- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -30,6 +30,7 @@ #include "mozilla/FontPropertyTypes.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/XlibDisplay.h" #include "mozilla/Monitor.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_gfx.h" @@ -686,8 +687,9 @@ class GtkVsyncSource final : public VsyncSource { return; } - mGLContext = gl::GLContextGLX::CreateGLContext({}, mXDisplay, root, - config, false, nullptr); + mGLContext = gl::GLContextGLX::CreateGLContext( + {}, gfx::XlibDisplay::Borrow(mXDisplay), root, config, false, + nullptr); if (!mGLContext) { lock.NotifyAll(); @@ -831,7 +833,7 @@ already_AddRefed gfxPlatformGtk::CreateHardwareVsyncSource() { } # endif - // Only use GLX vsync when the OpenGL compositor / WebRedner is being used. + // Only use GLX vsync when the OpenGL compositor / WebRender is being used. // The extra cost of initializing a GLX context while blocking the main // thread is not worth it when using basic composition. // @@ -847,7 +849,7 @@ already_AddRefed gfxPlatformGtk::CreateHardwareVsyncSource() { // Nvidia doesn't support GLX at the same time as EGL but Mesa does. if (!gfxVars::UseEGL() || (adapterDriverVendor.Find("mesa") != -1)) { - useGlxVsync = gl::sGLXLibrary.SupportsVideoSync(); + useGlxVsync = gl::sGLXLibrary.SupportsVideoSync(DefaultXDisplay()); } if (useGlxVsync) { RefPtr vsyncSource = new GtkVsyncSource(); diff --git a/gfx/thebes/gfxXlibSurface.cpp b/gfx/thebes/gfxXlibSurface.cpp index de6eb0779e9c..f9173b0c6a96 100644 --- a/gfx/thebes/gfxXlibSurface.cpp +++ b/gfx/thebes/gfxXlibSurface.cpp @@ -25,7 +25,7 @@ using namespace mozilla::gfx; gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual) : mPixmapTaken(false), - mDisplay(dpy), + mDisplay(XlibDisplay::Borrow(dpy)), mDrawable(drawable), mGLXPixmap(X11None) { const gfx::IntSize size = DoSizeQuery(); @@ -36,6 +36,11 @@ gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual) gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual, const gfx::IntSize& size) + : gfxXlibSurface(XlibDisplay::Borrow(dpy), drawable, visual, size) {} + +gfxXlibSurface::gfxXlibSurface(const std::shared_ptr& dpy, + Drawable drawable, Visual* visual, + const gfx::IntSize& size) : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable), @@ -43,8 +48,8 @@ gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual, NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT), "Bad size"); - cairo_surface_t* surf = - cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height); + cairo_surface_t* surf = cairo_xlib_surface_create(*dpy, drawable, visual, + size.width, size.height); Init(surf); } @@ -52,14 +57,14 @@ gfxXlibSurface::gfxXlibSurface(Screen* screen, Drawable drawable, XRenderPictFormat* format, const gfx::IntSize& size) : mPixmapTaken(false), - mDisplay(DisplayOfScreen(screen)), + mDisplay(XlibDisplay::Borrow(DisplayOfScreen(screen))), mDrawable(drawable), mGLXPixmap(X11None) { NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT), "Bad Size"); cairo_surface_t* surf = cairo_xlib_surface_create_with_xrender_format( - mDisplay, drawable, screen, format, size.width, size.height); + *mDisplay, drawable, screen, format, size.width, size.height); Init(surf); } @@ -69,7 +74,7 @@ gfxXlibSurface::gfxXlibSurface(cairo_surface_t* csurf) "Not expecting an error surface"); mDrawable = cairo_xlib_surface_get_drawable(csurf); - mDisplay = cairo_xlib_surface_get_display(csurf); + mDisplay = XlibDisplay::Borrow(cairo_xlib_surface_get_display(csurf)); Init(csurf, true); } @@ -78,9 +83,9 @@ gfxXlibSurface::~gfxXlibSurface() { // gfxASurface's destructor calls RecordMemoryFreed(). if (mPixmapTaken) { if (mGLXPixmap) { - gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap); + gl::sGLXLibrary.DestroyPixmap(*mDisplay, mGLXPixmap); } - XFreePixmap(mDisplay, mDrawable); + XFreePixmap(*mDisplay, mDrawable); } } @@ -171,12 +176,22 @@ cairo_surface_t* gfxXlibSurface::CreateCairoSurface(Screen* screen, already_AddRefed gfxXlibSurface::Create( Screen* screen, Visual* visual, const gfx::IntSize& size, Drawable relatedDrawable) { + return Create(XlibDisplay::Borrow(DisplayOfScreen(screen)), screen, visual, + size, relatedDrawable); +}; + +/* static */ +already_AddRefed gfxXlibSurface::Create( + const std::shared_ptr& display, Screen* screen, Visual* visual, + const gfx::IntSize& size, Drawable relatedDrawable) { + MOZ_ASSERT(*display == DisplayOfScreen(screen)); + Drawable drawable = CreatePixmap(screen, size, DepthOfVisual(screen, visual), relatedDrawable); if (!drawable) return nullptr; RefPtr result = - new gfxXlibSurface(DisplayOfScreen(screen), drawable, visual, size); + new gfxXlibSurface(display, drawable, visual, size); result->TakePixmap(); if (result->CairoStatus() != 0) return nullptr; @@ -218,7 +233,7 @@ already_AddRefed gfxXlibSurface::CreateSimilarSurface( static bool force24bpp = GetForce24bppPref(); if (force24bpp && cairo_xlib_surface_get_depth(CairoSurface()) != 24) { XRenderPictFormat* format = - XRenderFindStandardFormat(mDisplay, PictStandardRGB24); + XRenderFindStandardFormat(*mDisplay, PictStandardRGB24); if (format) { // Cairo only performs simple self-copies as desired if it // knows that this is a Pixmap surface. It only knows that @@ -240,7 +255,7 @@ already_AddRefed gfxXlibSurface::CreateSimilarSurface( void gfxXlibSurface::Finish() { if (mPixmapTaken && mGLXPixmap) { - gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap); + gl::sGLXLibrary.DestroyPixmap(*mDisplay, mGLXPixmap); mGLXPixmap = X11None; } gfxASurface::Finish(); @@ -259,7 +274,7 @@ const gfx::IntSize gfxXlibSurface::DoSizeQuery() { int x_ignore, y_ignore; unsigned int bwidth_ignore, width, height, depth; - XGetGeometry(mDisplay, mDrawable, &root_ignore, &x_ignore, &y_ignore, &width, + XGetGeometry(*mDisplay, mDrawable, &root_ignore, &x_ignore, &y_ignore, &width, &height, &bwidth_ignore, &depth); return gfx::IntSize(width, height); diff --git a/gfx/thebes/gfxXlibSurface.h b/gfx/thebes/gfxXlibSurface.h index 4b0ed9261551..902e9d4f9f01 100644 --- a/gfx/thebes/gfxXlibSurface.h +++ b/gfx/thebes/gfxXlibSurface.h @@ -13,6 +13,7 @@ #include "X11UndefineNone.h" #include "GLXLibrary.h" +#include "mozilla/gfx/XlibDisplay.h" #include "nsSize.h" @@ -31,6 +32,9 @@ class gfxXlibSurface final : public gfxASurface { // and known width/height. gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual, const mozilla::gfx::IntSize& size); + gfxXlibSurface(const std::shared_ptr& dpy, + Drawable drawable, Visual* visual, + const mozilla::gfx::IntSize& size); // construct a wrapper around the specified drawable with dpy/format, // and known width/height. @@ -46,6 +50,10 @@ class gfxXlibSurface final : public gfxASurface { static already_AddRefed Create( ::Screen* screen, Visual* visual, const mozilla::gfx::IntSize& size, Drawable relatedDrawable = X11None); + static already_AddRefed Create( + const std::shared_ptr& display, + ::Screen* screen, Visual* visual, const mozilla::gfx::IntSize& size, + Drawable relatedDrawable = X11None); static cairo_surface_t* CreateCairoSurface( ::Screen* screen, Visual* visual, const mozilla::gfx::IntSize& size, Drawable relatedDrawable = X11None); @@ -61,7 +69,7 @@ class gfxXlibSurface final : public gfxASurface { const mozilla::gfx::IntSize GetSize() const override; - Display* XDisplay() { return mDisplay; } + Display* XDisplay() { return *mDisplay; } ::Screen* XScreen(); Drawable XDrawable() { return mDrawable; } XRenderPictFormat* XRenderFormat(); @@ -98,15 +106,15 @@ class gfxXlibSurface final : public gfxASurface { bool IsPadSlow() { // The test here matches that for buggy_pad_reflect in // _cairo_xlib_device_create. - return VendorRelease(mDisplay) >= 60700000 || - VendorRelease(mDisplay) < 10699000; + return VendorRelease(mDisplay->get()) >= 60700000 || + VendorRelease(mDisplay->get()) < 10699000; } protected: // if TakePixmap() has been called on this bool mPixmapTaken; - Display* mDisplay; + std::shared_ptr mDisplay; Drawable mDrawable; const mozilla::gfx::IntSize DoSizeQuery(); diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index 49391c359e78..baa7005a8f82 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -135,9 +135,13 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": "gfxXlibNativeRenderer.h", "gfxXlibSurface.h", ] + EXPORTS.mozilla.gfx += [ + "XlibDisplay.h", + ] SOURCES += [ "gfxXlibNativeRenderer.cpp", "gfxXlibSurface.cpp", + "XlibDisplay.cpp", ] elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":