tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GLContextProviderGLX.cpp (27226B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #ifdef MOZ_WIDGET_GTK
      7 #  include <gdk/gdk.h>
      8 #  include <gdk/gdkx.h>
      9 #  define GET_NATIVE_WINDOW(aWidget) \
     10    GDK_WINDOW_XID((GdkWindow*)aWidget->GetNativeData(NS_NATIVE_WINDOW))
     11 #endif
     12 
     13 #include <X11/Xlib.h>
     14 #include <X11/Xutil.h>
     15 #include "X11UndefineNone.h"
     16 
     17 #include "mozilla/StaticPtr.h"
     18 #include "mozilla/layers/CompositorOptions.h"
     19 #include "mozilla/Range.h"
     20 #include "mozilla/ScopeExit.h"
     21 #include "mozilla/Sprintf.h"
     22 #include "mozilla/StaticPrefs_gfx.h"
     23 #include "mozilla/StaticPrefs_layout.h"
     24 #include "mozilla/widget/CompositorWidget.h"
     25 #include "mozilla/widget/GtkCompositorWidget.h"
     26 
     27 #include "prenv.h"
     28 #include "GLContextProvider.h"
     29 #include "GLLibraryLoader.h"
     30 #include "nsDebug.h"
     31 #include "nsIWidget.h"
     32 #include "GLXLibrary.h"
     33 #include "gfxContext.h"
     34 #include "gfxEnv.h"
     35 #include "gfxPlatform.h"
     36 #include "GLContextGLX.h"
     37 #include "gfxUtils.h"
     38 #include "gfx2DGlue.h"
     39 #include "GLScreenBuffer.h"
     40 
     41 #include "gfxCrashReporterUtils.h"
     42 
     43 #ifdef MOZ_WIDGET_GTK
     44 #  include "gfxPlatformGtk.h"
     45 #endif
     46 
     47 namespace mozilla::gl {
     48 
     49 using namespace mozilla::gfx;
     50 using namespace mozilla::widget;
     51 
     52 MOZ_RUNINIT GLXLibrary sGLXLibrary;
     53 
     54 static inline bool HasExtension(const char* aExtensions,
     55                                const char* aRequiredExtension) {
     56  return GLContext::ListHasExtension(
     57      reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension);
     58 }
     59 
     60 bool GLXLibrary::EnsureInitialized(Display* aDisplay) {
     61  if (mInitialized) {
     62    return true;
     63  }
     64 
     65  // Don't repeatedly try to initialize.
     66  if (mTriedInitializing) {
     67    return false;
     68  }
     69  mTriedInitializing = true;
     70 
     71  MOZ_ASSERT(aDisplay);
     72  if (!aDisplay) {
     73    return false;
     74  }
     75 
     76  // Force enabling s3 texture compression. (Bug 774134)
     77  PR_SetEnv("force_s3tc_enable=true");
     78 
     79  if (!mOGLLibrary) {
     80    // see e.g. bug 608526: it is intrinsically interesting to know whether we
     81    // have dynamically linked to libGL.so.1 because at least the NVIDIA
     82    // implementation requires an executable stack, which causes mprotect calls,
     83    // which trigger glibc bug
     84    // http://sourceware.org/bugzilla/show_bug.cgi?id=12225
     85    const char* libGLfilename = "libGL.so.1";
     86 #if defined(__OpenBSD__) || defined(__NetBSD__)
     87    libGLfilename = "libGL.so";
     88 #endif
     89 
     90    const bool forceFeatureReport = false;
     91    ScopedGfxFeatureReporter reporter(libGLfilename, forceFeatureReport);
     92    mOGLLibrary = PR_LoadLibrary(libGLfilename);
     93    if (!mOGLLibrary) {
     94      NS_WARNING("Couldn't load OpenGL shared library.");
     95      return false;
     96    }
     97    reporter.SetSuccessful();
     98  }
     99 
    100  if (gfxEnv::MOZ_GLX_DEBUG()) {
    101    mDebug = true;
    102  }
    103 
    104 #define SYMBOL(X)                 \
    105  {                               \
    106    (PRFuncPtr*)&mSymbols.f##X, { \
    107      {                           \
    108        "glX" #X                  \
    109      }                           \
    110    }                             \
    111  }
    112 #define END_OF_SYMBOLS \
    113  {                    \
    114    nullptr, {}        \
    115  }
    116 
    117  const SymLoadStruct symbols[] = {
    118      /* functions that were in GLX 1.0 */
    119      SYMBOL(DestroyContext),
    120      SYMBOL(MakeCurrent),
    121      SYMBOL(SwapBuffers),
    122      SYMBOL(QueryVersion),
    123      SYMBOL(GetConfig),
    124      SYMBOL(GetCurrentContext),
    125      SYMBOL(WaitGL),
    126      SYMBOL(WaitX),
    127 
    128      /* functions introduced in GLX 1.1 */
    129      SYMBOL(QueryExtensionsString),
    130      SYMBOL(GetClientString),
    131      SYMBOL(QueryServerString),
    132 
    133      /* functions introduced in GLX 1.3 */
    134      SYMBOL(ChooseFBConfig),
    135      SYMBOL(ChooseVisual),
    136      SYMBOL(GetFBConfigAttrib),
    137      SYMBOL(GetFBConfigs),
    138      SYMBOL(CreatePixmap),
    139      SYMBOL(DestroyPixmap),
    140      SYMBOL(CreateNewContext),
    141 
    142      // Core in GLX 1.4, ARB extension before.
    143      {(PRFuncPtr*)&mSymbols.fGetProcAddress,
    144       {{"glXGetProcAddress", "glXGetProcAddressARB"}}},
    145      END_OF_SYMBOLS};
    146 
    147  {
    148    const SymbolLoader libLoader(*mOGLLibrary);
    149    if (!libLoader.LoadSymbols(symbols)) {
    150      NS_WARNING("Couldn't load required GLX symbols.");
    151      return false;
    152    }
    153  }
    154  const SymbolLoader pfnLoader(mSymbols.fGetProcAddress);
    155 
    156  int screen = DefaultScreen(aDisplay);
    157 
    158  {
    159    int major, minor;
    160    if (!fQueryVersion(aDisplay, &major, &minor) || major != 1 || minor < 3) {
    161      NS_ERROR("GLX version older than 1.3. (released in 1998)");
    162      return false;
    163    }
    164  }
    165 
    166  const SymLoadStruct symbols_createcontext[] = {
    167      SYMBOL(CreateContextAttribsARB), END_OF_SYMBOLS};
    168 
    169  const SymLoadStruct symbols_videosync[] = {
    170      SYMBOL(GetVideoSyncSGI), SYMBOL(WaitVideoSyncSGI), END_OF_SYMBOLS};
    171 
    172  const SymLoadStruct symbols_swapcontrol[] = {SYMBOL(SwapIntervalEXT),
    173                                               END_OF_SYMBOLS};
    174 
    175  const SymLoadStruct symbols_querydrawable[] = {SYMBOL(QueryDrawable),
    176                                                 END_OF_SYMBOLS};
    177 
    178  const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) {
    179    if (pfnLoader.LoadSymbols(symbols)) return true;
    180 
    181    ClearSymbols(symbols);
    182    return false;
    183  };
    184 
    185  const char* clientVendor = fGetClientString(aDisplay, LOCAL_GLX_VENDOR);
    186  const char* serverVendor =
    187      fQueryServerString(aDisplay, screen, LOCAL_GLX_VENDOR);
    188  const char* extensionsStr = fQueryExtensionsString(aDisplay, screen);
    189 
    190  if (HasExtension(extensionsStr, "GLX_ARB_create_context") &&
    191      HasExtension(extensionsStr, "GLX_ARB_create_context_profile") &&
    192      fnLoadSymbols(symbols_createcontext)) {
    193    mHasCreateContextAttribs = true;
    194  }
    195 
    196  if (HasExtension(extensionsStr, "GLX_ARB_create_context_robustness")) {
    197    mHasRobustness = true;
    198  }
    199 
    200  if (HasExtension(extensionsStr, "GLX_NV_robustness_video_memory_purge")) {
    201    mHasVideoMemoryPurge = true;
    202  }
    203 
    204  if (HasExtension(extensionsStr, "GLX_SGI_video_sync") &&
    205      fnLoadSymbols(symbols_videosync)) {
    206    mHasVideoSync = true;
    207  }
    208 
    209  if (!HasExtension(extensionsStr, "GLX_EXT_swap_control") ||
    210      !fnLoadSymbols(symbols_swapcontrol)) {
    211    NS_WARNING(
    212        "GLX_swap_control unsupported, ASAP mode may still block on buffer "
    213        "swaps.");
    214  }
    215 
    216  if (HasExtension(extensionsStr, "GLX_EXT_buffer_age") &&
    217      fnLoadSymbols(symbols_querydrawable)) {
    218    mHasBufferAge = true;
    219  }
    220 
    221  mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI");
    222  mIsNVIDIA =
    223      serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation");
    224  mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa");
    225 
    226  mInitialized = true;
    227 
    228  // This needs to be after `fQueryServerString` is called so that the
    229  // driver is loaded.
    230  MesaMemoryLeakWorkaround();
    231 
    232  return true;
    233 }
    234 
    235 bool GLXLibrary::SupportsVideoSync(Display* aDisplay) {
    236  if (!EnsureInitialized(aDisplay)) {
    237    return false;
    238  }
    239 
    240  return mHasVideoSync;
    241 }
    242 
    243 static int (*sOldErrorHandler)(Display*, XErrorEvent*);
    244 static XErrorEvent sErrorEvent = {};
    245 
    246 static int GLXErrorHandler(Display* display, XErrorEvent* ev) {
    247  if (!sErrorEvent.error_code) {
    248    sErrorEvent = *ev;
    249  }
    250  return 0;
    251 }
    252 
    253 GLXLibrary::WrapperScope::WrapperScope(const GLXLibrary& glx,
    254                                       const char* const funcName,
    255                                       Display* aDisplay)
    256    : mGlx(glx), mFuncName(funcName), mDisplay(aDisplay) {
    257  if (mGlx.mDebug) {
    258    sOldErrorHandler = XSetErrorHandler(GLXErrorHandler);
    259  }
    260 }
    261 
    262 GLXLibrary::WrapperScope::~WrapperScope() {
    263  if (mGlx.mDebug) {
    264    if (mDisplay) {
    265      FinishX(mDisplay);
    266    }
    267    if (sErrorEvent.error_code) {
    268      char buffer[100] = {};
    269      if (mDisplay) {
    270        XGetErrorText(mDisplay, sErrorEvent.error_code, buffer, sizeof(buffer));
    271      } else {
    272        SprintfLiteral(buffer, "%d", sErrorEvent.error_code);
    273      }
    274      printf_stderr("X ERROR after %s: %s (%i) - Request: %i.%i, Serial: %lu",
    275                    mFuncName, buffer, sErrorEvent.error_code,
    276                    sErrorEvent.request_code, sErrorEvent.minor_code,
    277                    sErrorEvent.serial);
    278      MOZ_ASSERT_UNREACHABLE("AfterGLXCall sErrorEvent");
    279    }
    280    const auto was = XSetErrorHandler(sOldErrorHandler);
    281    if (was != GLXErrorHandler) {
    282      NS_WARNING("Concurrent XSetErrorHandlers");
    283    }
    284  }
    285 }
    286 
    287 // Returns the GTK display if available; otherwise, if a display was
    288 // previously opened by this method and is still open, returns a
    289 // reference to it; otherwise, opens a new connection.  (The non-GTK
    290 // cases are similar to what we do for EGL.)
    291 std::shared_ptr<XlibDisplay> GLXLibrary::GetDisplay() {
    292  std::shared_ptr<XlibDisplay> display;
    293 
    294 #ifdef MOZ_WIDGET_GTK
    295  static const bool kHaveGtk = !!gdk_display_get_default();
    296  if (kHaveGtk) {
    297    display = XlibDisplay::Borrow(DefaultXDisplay());
    298  }
    299 #endif
    300  if (display) {
    301    return display;
    302  }
    303 
    304  auto ownDisplay = mOwnDisplay.Lock();
    305  display = ownDisplay->lock();
    306  if (display) {
    307    return display;
    308  }
    309 
    310  display = XlibDisplay::Open(nullptr);
    311  if (NS_WARN_IF(!display)) {
    312    return nullptr;
    313  }
    314  *ownDisplay = display;
    315  return display;
    316 }
    317 
    318 already_AddRefed<GLContextGLX> GLContextGLX::CreateGLContext(
    319    const GLContextDesc& desc, std::shared_ptr<XlibDisplay> display,
    320    GLXDrawable drawable, GLXFBConfig cfg, Drawable ownedPixmap) {
    321  GLXLibrary& glx = sGLXLibrary;
    322 
    323  int isDoubleBuffered = 0;
    324  int err = glx.fGetFBConfigAttrib(*display, cfg, LOCAL_GLX_DOUBLEBUFFER,
    325                                   &isDoubleBuffered);
    326  if (LOCAL_GLX_BAD_ATTRIBUTE != err) {
    327    if (ShouldSpew()) {
    328      printf("[GLX] FBConfig is %sdouble-buffered\n",
    329             isDoubleBuffered ? "" : "not ");
    330    }
    331  }
    332 
    333  if (!glx.HasCreateContextAttribs()) {
    334    NS_WARNING("Cannot create GLContextGLX without glxCreateContextAttribs");
    335    return nullptr;
    336  }
    337 
    338  // -
    339 
    340  const auto CreateWithAttribs =
    341      [&](const std::vector<int>& attribs) -> RefPtr<GLContextGLX> {
    342    auto terminated = attribs;
    343    terminated.push_back(0);
    344 
    345    const auto glxContext = glx.fCreateContextAttribs(
    346        *display, cfg, nullptr, X11True, terminated.data());
    347    if (!glxContext) return nullptr;
    348    const RefPtr<GLContextGLX> ret = new GLContextGLX(
    349        desc, display, drawable, glxContext, isDoubleBuffered, ownedPixmap);
    350 
    351    if (!ret->Init()) return nullptr;
    352 
    353    return ret;
    354  };
    355 
    356  // -
    357 
    358  RefPtr<GLContextGLX> glContext;
    359 
    360  std::vector<int> attribs;
    361  attribs.insert(attribs.end(), {
    362                                    LOCAL_GLX_RENDER_TYPE,
    363                                    LOCAL_GLX_RGBA_TYPE,
    364                                });
    365  if (glx.HasVideoMemoryPurge()) {
    366    attribs.insert(attribs.end(),
    367                   {
    368                       LOCAL_GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV,
    369                       LOCAL_GL_TRUE,
    370                   });
    371  }
    372  const bool useCore =
    373      !(desc.flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE);
    374  if (useCore) {
    375    attribs.insert(attribs.end(), {
    376                                      LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB,
    377                                      3,
    378                                      LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB,
    379                                      2,
    380                                      LOCAL_GLX_CONTEXT_PROFILE_MASK_ARB,
    381                                      LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
    382                                  });
    383  }
    384 
    385  if (glx.HasRobustness()) {
    386    auto withRobustness = attribs;
    387    withRobustness.insert(withRobustness.end(),
    388                          {
    389                              LOCAL_GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
    390                              LOCAL_GLX_LOSE_CONTEXT_ON_RESET_ARB,
    391                          });
    392 
    393    {
    394      auto withRBAB = withRobustness;
    395      withRBAB.insert(withRBAB.end(),
    396                      {
    397                          LOCAL_GLX_CONTEXT_FLAGS_ARB,
    398                          LOCAL_GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
    399                      });
    400      if (!glContext) {
    401        glContext = CreateWithAttribs(withRBAB);
    402        if (!glContext) {
    403          NS_WARNING("Failed to create+init GLContextGLX with RBAB");
    404        }
    405      }
    406    }
    407 
    408    if (!glContext) {
    409      glContext = CreateWithAttribs(withRobustness);
    410      if (!glContext) {
    411        NS_WARNING("Failed to create+init GLContextGLX with Robustness");
    412      }
    413    }
    414  }
    415 
    416  if (!glContext) {
    417    glContext = CreateWithAttribs(attribs);
    418    if (!glContext) {
    419      NS_WARNING("Failed to create+init GLContextGLX with required attribs");
    420    }
    421  }
    422 
    423  return glContext.forget();
    424 }
    425 
    426 GLContextGLX::~GLContextGLX() {
    427  MarkDestroyed();
    428 
    429  // Wrapped context should not destroy glxContext/Surface
    430  if (!mOwnsContext) {
    431    return;
    432  }
    433 
    434  // see bug 659842 comment 76
    435  bool success = mGLX->fMakeCurrent(*mDisplay, X11None, nullptr);
    436  if (!success) {
    437    NS_WARNING(
    438        "glXMakeCurrent failed to release GL context before we call "
    439        "glXDestroyContext!");
    440  }
    441 
    442  mGLX->fDestroyContext(*mDisplay, mContext);
    443 
    444  // If we own the enclosed X pixmap, then free it after we free the enclosing
    445  // GLX pixmap.
    446  if (mOwnedPixmap) {
    447    mGLX->fDestroyPixmap(*mDisplay, mDrawable);
    448    XFreePixmap(*mDisplay, mOwnedPixmap);
    449  }
    450 }
    451 
    452 bool GLContextGLX::Init() {
    453  if (!GLContext::Init()) {
    454    return false;
    455  }
    456 
    457  // EXT_framebuffer_object is not supported on Core contexts
    458  // so we'll also check for ARB_framebuffer_object
    459  if (!IsExtensionSupported(EXT_framebuffer_object) &&
    460      !IsSupported(GLFeature::framebuffer_object))
    461    return false;
    462 
    463  return true;
    464 }
    465 
    466 bool GLContextGLX::MakeCurrentImpl() const {
    467  if (mGLX->IsMesa()) {
    468    // Read into the event queue to ensure that Mesa receives a
    469    // DRI2InvalidateBuffers event before drawing. See bug 1280653.
    470    (void)XPending(*mDisplay);
    471  }
    472 
    473  const bool succeeded = mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext);
    474  if (!succeeded) {
    475    NS_WARNING("Failed to make GL context current!");
    476  }
    477 
    478  if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
    479    // Many GLX implementations default to blocking until the next
    480    // VBlank when calling glXSwapBuffers. We want to run unthrottled
    481    // in ASAP mode. See bug 1280744.
    482    const bool swapInterval = gfxVars::SwapIntervalGLX();
    483    const bool isASAP = (StaticPrefs::layout_frame_rate() == 0);
    484    const int interval = (swapInterval && !isASAP) ? 1 : 0;
    485    mGLX->fSwapInterval(*mDisplay, mDrawable, interval);
    486  }
    487  return succeeded;
    488 }
    489 
    490 bool GLContextGLX::IsCurrentImpl() const {
    491  return mGLX->fGetCurrentContext() == mContext;
    492 }
    493 
    494 Maybe<SymbolLoader> GLContextGLX::GetSymbolLoader() const {
    495  const auto pfn = sGLXLibrary.GetGetProcAddress();
    496  return Some(SymbolLoader(pfn));
    497 }
    498 
    499 bool GLContextGLX::IsDoubleBuffered() const { return mDoubleBuffered; }
    500 
    501 bool GLContextGLX::SwapBuffers() {
    502  if (!mDoubleBuffered) return false;
    503  mGLX->fSwapBuffers(*mDisplay, mDrawable);
    504  return true;
    505 }
    506 
    507 GLint GLContextGLX::GetBufferAge() const {
    508  if (!sGLXLibrary.SupportsBufferAge()) {
    509    return 0;
    510  }
    511 
    512  GLuint result = 0;
    513  mGLX->fQueryDrawable(*mDisplay, mDrawable, LOCAL_GLX_BACK_BUFFER_AGE_EXT,
    514                       &result);
    515  if (result > INT32_MAX) {
    516    // If the result can't fit, just assume the buffer cannot be reused.
    517    return 0;
    518  }
    519  return result;
    520 }
    521 
    522 void GLContextGLX::GetWSIInfo(nsCString* const out) const {
    523  int screen = DefaultScreen(mDisplay->get());
    524 
    525  int majorVersion, minorVersion;
    526  sGLXLibrary.fQueryVersion(*mDisplay, &majorVersion, &minorVersion);
    527 
    528  out->Append(nsPrintfCString("GLX %u.%u", majorVersion, minorVersion));
    529 
    530  out->AppendLiteral("\nGLX_VENDOR(client): ");
    531  out->Append(sGLXLibrary.fGetClientString(*mDisplay, LOCAL_GLX_VENDOR));
    532 
    533  out->AppendLiteral("\nGLX_VENDOR(server): ");
    534  out->Append(
    535      sGLXLibrary.fQueryServerString(*mDisplay, screen, LOCAL_GLX_VENDOR));
    536 
    537  out->AppendLiteral("\nExtensions: ");
    538  out->Append(sGLXLibrary.fQueryExtensionsString(*mDisplay, screen));
    539 }
    540 
    541 bool GLContextGLX::OverrideDrawable(GLXDrawable drawable) {
    542  return mGLX->fMakeCurrent(*mDisplay, drawable, mContext);
    543 }
    544 
    545 bool GLContextGLX::RestoreDrawable() {
    546  return mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext);
    547 }
    548 
    549 GLContextGLX::GLContextGLX(const GLContextDesc& desc,
    550                           std::shared_ptr<XlibDisplay> aDisplay,
    551                           GLXDrawable aDrawable, GLXContext aContext,
    552                           bool aDoubleBuffered, Drawable aOwnedPixmap)
    553    : GLContext(desc, nullptr),
    554      mContext(aContext),
    555      mDisplay(aDisplay),
    556      mDrawable(aDrawable),
    557      mOwnedPixmap(aOwnedPixmap),
    558      mDoubleBuffered(aDoubleBuffered),
    559      mGLX(&sGLXLibrary) {}
    560 
    561 static bool AreCompatibleVisuals(Visual* one, Visual* two) {
    562  if (one->c_class != two->c_class) {
    563    return false;
    564  }
    565 
    566  if (one->red_mask != two->red_mask || one->green_mask != two->green_mask ||
    567      one->blue_mask != two->blue_mask) {
    568    return false;
    569  }
    570 
    571  if (one->bits_per_rgb != two->bits_per_rgb) {
    572    return false;
    573  }
    574 
    575  return true;
    576 }
    577 
    578 already_AddRefed<GLContext> CreateForWidget(Display* aXDisplay, Window aXWindow,
    579                                            bool aHardwareWebRender,
    580                                            bool aForceAccelerated) {
    581  if (!sGLXLibrary.EnsureInitialized(aXDisplay)) {
    582    return nullptr;
    583  }
    584 
    585  // Currently, we take whatever Visual the window already has, and
    586  // try to create an fbconfig for that visual.  This isn't
    587  // necessarily what we want in the long run; an fbconfig may not
    588  // be available for the existing visual, or if it is, the GL
    589  // performance might be suboptimal.  But using the existing visual
    590  // is a relatively safe intermediate step.
    591 
    592  if (!aXDisplay) {
    593    NS_ERROR("X Display required for GLX Context provider");
    594    return nullptr;
    595  }
    596 
    597  if (!aXWindow) {
    598    NS_ERROR("X window required for GLX Context provider");
    599    return nullptr;
    600  }
    601 
    602  int xscreen = DefaultScreen(aXDisplay);
    603 
    604  GLXFBConfig config;
    605  int visid;
    606  if (!GLContextGLX::FindFBConfigForWindow(
    607          aXDisplay, xscreen, aXWindow, &config, &visid, aHardwareWebRender)) {
    608    return nullptr;
    609  }
    610 
    611  CreateContextFlags flags;
    612  if (aHardwareWebRender) {
    613    flags = CreateContextFlags::NONE;  // WR needs GL3.2+
    614  } else {
    615    flags = CreateContextFlags::REQUIRE_COMPAT_PROFILE;
    616  }
    617  return GLContextGLX::CreateGLContext(
    618      {{flags}, false}, XlibDisplay::Borrow(aXDisplay), aXWindow, config);
    619 }
    620 
    621 already_AddRefed<GLContext> GLContextProviderGLX::CreateForCompositorWidget(
    622    CompositorWidget* aCompositorWidget, bool aHardwareWebRender,
    623    bool aForceAccelerated) {
    624  if (!aCompositorWidget) {
    625    MOZ_ASSERT(false);
    626    return nullptr;
    627  }
    628  GtkCompositorWidget* compWidget = aCompositorWidget->AsGTK();
    629  MOZ_ASSERT(compWidget);
    630 
    631  return CreateForWidget(DefaultXDisplay(), compWidget->XWindow(),
    632                         aHardwareWebRender, aForceAccelerated);
    633 }
    634 
    635 static bool ChooseConfig(GLXLibrary* glx, Display* display, int screen,
    636                         GLXFBConfig* const out_config, int* const out_visid) {
    637  const int attribs[] = {
    638      LOCAL_GLX_RENDER_TYPE,
    639      LOCAL_GLX_RGBA_BIT,
    640      LOCAL_GLX_DRAWABLE_TYPE,
    641      LOCAL_GLX_PIXMAP_BIT,
    642      LOCAL_GLX_X_RENDERABLE,
    643      X11True,
    644      LOCAL_GLX_RED_SIZE,
    645      8,
    646      LOCAL_GLX_GREEN_SIZE,
    647      8,
    648      LOCAL_GLX_BLUE_SIZE,
    649      8,
    650      LOCAL_GLX_ALPHA_SIZE,
    651      8,
    652      LOCAL_GLX_DEPTH_SIZE,
    653      0,
    654      LOCAL_GLX_STENCIL_SIZE,
    655      0,
    656      0,
    657  };
    658 
    659  int numConfigs = 0;
    660  const auto scopedConfigArr =
    661      glx->fChooseFBConfig(display, screen, attribs, &numConfigs);
    662  const auto freeConfigList = MakeScopeExit([&]() {
    663    if (scopedConfigArr) {
    664      XFree(scopedConfigArr);
    665    }
    666  });
    667  if (!scopedConfigArr || !numConfigs) return false;
    668 
    669  // Issues with glxChooseFBConfig selection and sorting:
    670  // * ALPHA_SIZE is sorted as 'largest total RGBA bits first'. If we don't
    671  // request
    672  //   alpha bits, we'll probably get RGBA anyways, since 32 is more than 24.
    673  // * DEPTH_SIZE is sorted largest first, including for `0` inputs.
    674  // * STENCIL_SIZE is smallest first, but it might return `8` even though we
    675  // ask for
    676  //   `0`.
    677 
    678  // For now, we don't care about these. We *will* care when we do XPixmap
    679  // sharing.
    680 
    681  for (int i = 0; i < numConfigs; ++i) {
    682    GLXFBConfig curConfig = scopedConfigArr[i];
    683 
    684    int visid;
    685    if (glx->fGetFBConfigAttrib(display, curConfig, LOCAL_GLX_VISUAL_ID,
    686                                &visid) != Success) {
    687      continue;
    688    }
    689 
    690    if (!visid) continue;
    691 
    692    *out_config = curConfig;
    693    *out_visid = visid;
    694    return true;
    695  }
    696 
    697  return false;
    698 }
    699 
    700 bool GLContextGLX::FindVisual(Display* display, int screen,
    701                              int* const out_visualId) {
    702  if (!sGLXLibrary.EnsureInitialized(display)) {
    703    return false;
    704  }
    705 
    706  XVisualInfo visualTemplate;
    707  visualTemplate.screen = screen;
    708 
    709  // Get all visuals of screen
    710 
    711  int visualsLen = 0;
    712  XVisualInfo* xVisuals =
    713      XGetVisualInfo(display, VisualScreenMask, &visualTemplate, &visualsLen);
    714  if (!xVisuals) {
    715    return false;
    716  }
    717  const Range<XVisualInfo> visualInfos(xVisuals, visualsLen);
    718  auto cleanupVisuals = MakeScopeExit([&] { XFree(xVisuals); });
    719 
    720  // Get default visual info
    721 
    722  Visual* defaultVisual = DefaultVisual(display, screen);
    723  const auto defaultVisualInfo = [&]() -> const XVisualInfo* {
    724    for (const auto& cur : visualInfos) {
    725      if (cur.visual == defaultVisual) {
    726        return &cur;
    727      }
    728    }
    729    return nullptr;
    730  }();
    731  if (!defaultVisualInfo) {
    732    MOZ_ASSERT(false);
    733    return false;
    734  }
    735 
    736  const int bpp = 32;
    737 
    738  for (auto& cur : visualInfos) {
    739    const auto fnConfigMatches = [&](const int pname, const int expected) {
    740      int actual;
    741      if (sGLXLibrary.fGetConfig(display, &cur, pname, &actual)) {
    742        return false;
    743      }
    744      return actual == expected;
    745    };
    746 
    747    // Check if visual is compatible.
    748    if (cur.depth != bpp || cur.c_class != defaultVisualInfo->c_class) {
    749      continue;
    750    }
    751 
    752    // Check if visual is compatible to GL requests.
    753    if (fnConfigMatches(LOCAL_GLX_USE_GL, 1) &&
    754        fnConfigMatches(LOCAL_GLX_DOUBLEBUFFER, 1) &&
    755        fnConfigMatches(LOCAL_GLX_RED_SIZE, 8) &&
    756        fnConfigMatches(LOCAL_GLX_GREEN_SIZE, 8) &&
    757        fnConfigMatches(LOCAL_GLX_BLUE_SIZE, 8) &&
    758        fnConfigMatches(LOCAL_GLX_ALPHA_SIZE, 8)) {
    759      *out_visualId = cur.visualid;
    760      return true;
    761    }
    762  }
    763 
    764  return false;
    765 }
    766 
    767 bool GLContextGLX::FindFBConfigForWindow(Display* display, int screen,
    768                                         Window window,
    769                                         GLXFBConfig* const out_config,
    770                                         int* const out_visid,
    771                                         bool aWebRender) {
    772  // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
    773  // we could probably do this first and replace the glXGetFBConfigs
    774  // with glXChooseConfigs.  Docs are sparklingly clear as always.
    775  XWindowAttributes windowAttrs;
    776  if (!XGetWindowAttributes(display, window, &windowAttrs)) {
    777    NS_WARNING("[GLX] XGetWindowAttributes() failed");
    778    return false;
    779  }
    780 
    781  GLXFBConfig* cfgs = nullptr;
    782  const auto freeConfigList = MakeScopeExit([&]() {
    783    if (cfgs) {
    784      XFree(cfgs);
    785    }
    786  });
    787  int numConfigs;
    788  const int webrenderAttribs[] = {LOCAL_GLX_ALPHA_SIZE,
    789                                  windowAttrs.depth == 32 ? 8 : 0,
    790                                  LOCAL_GLX_DOUBLEBUFFER, X11True, 0};
    791 
    792  if (aWebRender) {
    793    cfgs = sGLXLibrary.fChooseFBConfig(display, screen, webrenderAttribs,
    794                                       &numConfigs);
    795  } else {
    796    cfgs = sGLXLibrary.fGetFBConfigs(display, screen, &numConfigs);
    797  }
    798 
    799  if (!cfgs) {
    800    NS_WARNING("[GLX] glXGetFBConfigs() failed");
    801    return false;
    802  }
    803  NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
    804 
    805  const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
    806 #ifdef DEBUG
    807  printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
    808 #endif
    809 
    810  for (int i = 0; i < numConfigs; i++) {
    811    int visid = X11None;
    812    sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID,
    813                                   &visid);
    814    if (visid) {
    815      // WebRender compatible GLX visual is configured
    816      // at nsWindow::Create() by GLContextGLX::FindVisual(),
    817      // just reuse it here.
    818      if (windowVisualID == static_cast<VisualID>(visid)) {
    819        *out_config = cfgs[i];
    820        *out_visid = visid;
    821        return true;
    822      }
    823    }
    824  }
    825 
    826  // We don't have a frame buffer visual which matches the GLX visual
    827  // from GLContextGLX::FindVisual(). Let's try to find a near one and hope
    828  // we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there.
    829  for (int i = 0; i < numConfigs; i++) {
    830    int visid = X11None;
    831    sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID,
    832                                   &visid);
    833    if (visid) {
    834      int depth;
    835      Visual* visual;
    836      FindVisualAndDepth(display, visid, &visual, &depth);
    837      if (depth == windowAttrs.depth &&
    838          AreCompatibleVisuals(windowAttrs.visual, visual)) {
    839        *out_config = cfgs[i];
    840        *out_visid = visid;
    841        return true;
    842      }
    843    }
    844  }
    845 
    846  NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
    847  return false;
    848 }
    849 
    850 static already_AddRefed<GLContextGLX> CreateOffscreenPixmapContext(
    851    const GLContextCreateDesc& desc, const IntSize& size,
    852    nsACString* const out_failureId) {
    853  GLXLibrary* glx = &sGLXLibrary;
    854  auto display = glx->GetDisplay();
    855 
    856  if (!display || !glx->EnsureInitialized(*display)) return nullptr;
    857 
    858  int screen = DefaultScreen(display->get());
    859 
    860  GLXFBConfig config;
    861  int visid;
    862  if (!ChooseConfig(glx, *display, screen, &config, &visid)) {
    863    NS_WARNING("Failed to find a compatible config.");
    864    return nullptr;
    865  }
    866 
    867  Visual* visual;
    868  int depth;
    869  FindVisualAndDepth(*display, visid, &visual, &depth);
    870 
    871  gfx::IntSize dummySize(16, 16);
    872  const auto drawable =
    873      XCreatePixmap(*display, DefaultRootWindow(display->get()),
    874                    dummySize.width, dummySize.height, depth);
    875  if (!drawable) {
    876    return nullptr;
    877  }
    878 
    879  // Handle slightly different signature between glXCreatePixmap and
    880  // its pre-GLX-1.3 extension equivalent (though given the ABI, we
    881  // might not need to).
    882  const auto pixmap = glx->fCreatePixmap(*display, config, drawable, nullptr);
    883  if (pixmap == 0) {
    884    XFreePixmap(*display, drawable);
    885    return nullptr;
    886  }
    887 
    888  auto fullDesc = GLContextDesc{desc};
    889  fullDesc.isOffscreen = true;
    890 
    891  return GLContextGLX::CreateGLContext(fullDesc, display, pixmap, config,
    892                                       drawable);
    893 }
    894 
    895 /*static*/
    896 already_AddRefed<GLContext> GLContextProviderGLX::CreateHeadless(
    897    const GLContextCreateDesc& desc, nsACString* const out_failureId) {
    898  IntSize dummySize = IntSize(16, 16);
    899  return CreateOffscreenPixmapContext(desc, dummySize, out_failureId);
    900 }
    901 
    902 /*static*/
    903 GLContext* GLContextProviderGLX::GetGlobalContext() {
    904  // Context sharing not supported.
    905  return nullptr;
    906 }
    907 
    908 /*static*/
    909 void GLContextProviderGLX::Shutdown() {}
    910 
    911 }  // namespace mozilla::gl