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/MathAlgorithms.h"
18 #include "mozilla/StaticPtr.h"
19 #include "mozilla/layers/CompositorOptions.h"
20 #include "mozilla/Range.h"
21 #include "mozilla/ScopeExit.h"
22 #include "mozilla/Sprintf.h"
23 #include "mozilla/StaticPrefs_gfx.h"
24 #include "mozilla/StaticPrefs_layout.h"
25 #include "mozilla/widget/CompositorWidget.h"
26 #include "mozilla/widget/GtkCompositorWidget.h"
27 #include "mozilla/Unused.h"
28 
29 #include "prenv.h"
30 #include "GLContextProvider.h"
31 #include "GLLibraryLoader.h"
32 #include "nsDebug.h"
33 #include "nsIWidget.h"
34 #include "GLXLibrary.h"
35 #include "gfxXlibSurface.h"
36 #include "gfxContext.h"
37 #include "gfxEnv.h"
38 #include "gfxPlatform.h"
39 #include "GLContextGLX.h"
40 #include "gfxUtils.h"
41 #include "gfx2DGlue.h"
42 #include "GLScreenBuffer.h"
43 
44 #include "gfxCrashReporterUtils.h"
45 
46 #ifdef MOZ_WIDGET_GTK
47 #  include "gfxPlatformGtk.h"
48 #endif
49 
50 namespace mozilla::gl {
51 
52 using namespace mozilla::gfx;
53 using namespace mozilla::widget;
54 
55 GLXLibrary sGLXLibrary;
56 
HasExtension(const char * aExtensions,const char * aRequiredExtension)57 static inline bool HasExtension(const char* aExtensions,
58                                 const char* aRequiredExtension) {
59   return GLContext::ListHasExtension(
60       reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension);
61 }
62 
EnsureInitialized(Display * aDisplay)63 bool GLXLibrary::EnsureInitialized(Display* aDisplay) {
64   if (mInitialized) {
65     return true;
66   }
67 
68   // Don't repeatedly try to initialize.
69   if (mTriedInitializing) {
70     return false;
71   }
72   mTriedInitializing = true;
73 
74   MOZ_ASSERT(aDisplay);
75   if (!aDisplay) {
76     return false;
77   }
78 
79   // Force enabling s3 texture compression. (Bug 774134)
80   PR_SetEnv("force_s3tc_enable=true");
81 
82   if (!mOGLLibrary) {
83     // see e.g. bug 608526: it is intrinsically interesting to know whether we
84     // have dynamically linked to libGL.so.1 because at least the NVIDIA
85     // implementation requires an executable stack, which causes mprotect calls,
86     // which trigger glibc bug
87     // http://sourceware.org/bugzilla/show_bug.cgi?id=12225
88     const char* libGLfilename = "libGL.so.1";
89 #if defined(__OpenBSD__) || defined(__NetBSD__)
90     libGLfilename = "libGL.so";
91 #endif
92 
93     const bool forceFeatureReport = false;
94     ScopedGfxFeatureReporter reporter(libGLfilename, forceFeatureReport);
95     mOGLLibrary = PR_LoadLibrary(libGLfilename);
96     if (!mOGLLibrary) {
97       NS_WARNING("Couldn't load OpenGL shared library.");
98       return false;
99     }
100     reporter.SetSuccessful();
101   }
102 
103   if (gfxEnv::GlxDebug()) {
104     mDebug = true;
105   }
106 
107 #define SYMBOL(X)                 \
108   {                               \
109     (PRFuncPtr*)&mSymbols.f##X, { \
110       { "glX" #X }                \
111     }                             \
112   }
113 #define END_OF_SYMBOLS \
114   {                    \
115     nullptr, {}        \
116   }
117 
118   const SymLoadStruct symbols[] = {
119       /* functions that were in GLX 1.0 */
120       SYMBOL(DestroyContext),
121       SYMBOL(MakeCurrent),
122       SYMBOL(SwapBuffers),
123       SYMBOL(QueryVersion),
124       SYMBOL(GetConfig),
125       SYMBOL(GetCurrentContext),
126       SYMBOL(WaitGL),
127       SYMBOL(WaitX),
128 
129       /* functions introduced in GLX 1.1 */
130       SYMBOL(QueryExtensionsString),
131       SYMBOL(GetClientString),
132       SYMBOL(QueryServerString),
133 
134       /* functions introduced in GLX 1.3 */
135       SYMBOL(ChooseFBConfig),
136       SYMBOL(ChooseVisual),
137       SYMBOL(GetFBConfigAttrib),
138       SYMBOL(GetFBConfigs),
139       SYMBOL(CreatePixmap),
140       SYMBOL(DestroyPixmap),
141       SYMBOL(CreateNewContext),
142 
143       // Core in GLX 1.4, ARB extension before.
144       {(PRFuncPtr*)&mSymbols.fGetProcAddress,
145        {{"glXGetProcAddress", "glXGetProcAddressARB"}}},
146       END_OF_SYMBOLS};
147 
148   {
149     const SymbolLoader libLoader(*mOGLLibrary);
150     if (!libLoader.LoadSymbols(symbols)) {
151       NS_WARNING("Couldn't load required GLX symbols.");
152       return false;
153     }
154   }
155   const SymbolLoader pfnLoader(mSymbols.fGetProcAddress);
156 
157   int screen = DefaultScreen(aDisplay);
158 
159   {
160     int major, minor;
161     if (!fQueryVersion(aDisplay, &major, &minor) || major != 1 || minor < 3) {
162       NS_ERROR("GLX version older than 1.3. (released in 1998)");
163       return false;
164     }
165   }
166 
167   const SymLoadStruct symbols_texturefrompixmap[] = {
168       SYMBOL(BindTexImageEXT), SYMBOL(ReleaseTexImageEXT), END_OF_SYMBOLS};
169 
170   const SymLoadStruct symbols_createcontext[] = {
171       SYMBOL(CreateContextAttribsARB), END_OF_SYMBOLS};
172 
173   const SymLoadStruct symbols_videosync[] = {
174       SYMBOL(GetVideoSyncSGI), SYMBOL(WaitVideoSyncSGI), END_OF_SYMBOLS};
175 
176   const SymLoadStruct symbols_swapcontrol[] = {SYMBOL(SwapIntervalEXT),
177                                                END_OF_SYMBOLS};
178 
179   const SymLoadStruct symbols_querydrawable[] = {SYMBOL(QueryDrawable),
180                                                  END_OF_SYMBOLS};
181 
182   const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) {
183     if (pfnLoader.LoadSymbols(symbols)) return true;
184 
185     ClearSymbols(symbols);
186     return false;
187   };
188 
189   const char* clientVendor = fGetClientString(aDisplay, LOCAL_GLX_VENDOR);
190   const char* serverVendor =
191       fQueryServerString(aDisplay, screen, LOCAL_GLX_VENDOR);
192   const char* extensionsStr = fQueryExtensionsString(aDisplay, screen);
193 
194   if (HasExtension(extensionsStr, "GLX_EXT_texture_from_pixmap") &&
195       fnLoadSymbols(symbols_texturefrompixmap)) {
196     mUseTextureFromPixmap = StaticPrefs::gfx_use_glx_texture_from_pixmap();
197   } else {
198     mUseTextureFromPixmap = false;
199     NS_WARNING("Texture from pixmap disabled");
200   }
201 
202   if (HasExtension(extensionsStr, "GLX_ARB_create_context") &&
203       HasExtension(extensionsStr, "GLX_ARB_create_context_profile") &&
204       fnLoadSymbols(symbols_createcontext)) {
205     mHasCreateContextAttribs = true;
206   }
207 
208   if (HasExtension(extensionsStr, "GLX_ARB_create_context_robustness")) {
209     mHasRobustness = true;
210   }
211 
212   if (HasExtension(extensionsStr, "GLX_NV_robustness_video_memory_purge")) {
213     mHasVideoMemoryPurge = true;
214   }
215 
216   if (HasExtension(extensionsStr, "GLX_SGI_video_sync") &&
217       fnLoadSymbols(symbols_videosync)) {
218     mHasVideoSync = true;
219   }
220 
221   if (!HasExtension(extensionsStr, "GLX_EXT_swap_control") ||
222       !fnLoadSymbols(symbols_swapcontrol)) {
223     NS_WARNING(
224         "GLX_swap_control unsupported, ASAP mode may still block on buffer "
225         "swaps.");
226   }
227 
228   if (HasExtension(extensionsStr, "GLX_EXT_buffer_age") &&
229       fnLoadSymbols(symbols_querydrawable)) {
230     mHasBufferAge = true;
231   }
232 
233   mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI");
234   mIsNVIDIA =
235       serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation");
236   mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa");
237 
238   mInitialized = true;
239 
240   // This needs to be after `fQueryServerString` is called so that the
241   // driver is loaded.
242   MesaMemoryLeakWorkaround();
243 
244   return true;
245 }
246 
SupportsTextureFromPixmap(gfxASurface * aSurface)247 bool GLXLibrary::SupportsTextureFromPixmap(gfxASurface* aSurface) {
248   if (aSurface->GetType() != gfxSurfaceType::Xlib) {
249     return false;
250   }
251 
252   gfxXlibSurface* xs = static_cast<gfxXlibSurface*>(aSurface);
253   if (!EnsureInitialized(xs->XDisplay())) {
254     return false;
255   }
256 
257   return mUseTextureFromPixmap;
258 }
259 
SupportsVideoSync(Display * aDisplay)260 bool GLXLibrary::SupportsVideoSync(Display* aDisplay) {
261   if (!EnsureInitialized(aDisplay)) {
262     return false;
263   }
264 
265   return mHasVideoSync;
266 }
267 
CreatePixmap(gfxASurface * aSurface)268 GLXPixmap GLXLibrary::CreatePixmap(gfxASurface* aSurface) {
269   if (!SupportsTextureFromPixmap(aSurface)) {
270     return X11None;
271   }
272 
273   gfxXlibSurface* xs = static_cast<gfxXlibSurface*>(aSurface);
274   const XRenderPictFormat* format = xs->XRenderFormat();
275   if (!format || format->type != PictTypeDirect) {
276     return X11None;
277   }
278   const XRenderDirectFormat& direct = format->direct;
279   int alphaSize = FloorLog2(direct.alphaMask + 1);
280   NS_ASSERTION((1 << alphaSize) - 1 == direct.alphaMask,
281                "Unexpected render format with non-adjacent alpha bits");
282 
283   int attribs[] = {LOCAL_GLX_DOUBLEBUFFER,
284                    X11False,
285                    LOCAL_GLX_DRAWABLE_TYPE,
286                    LOCAL_GLX_PIXMAP_BIT,
287                    LOCAL_GLX_ALPHA_SIZE,
288                    alphaSize,
289                    (alphaSize ? LOCAL_GLX_BIND_TO_TEXTURE_RGBA_EXT
290                               : LOCAL_GLX_BIND_TO_TEXTURE_RGB_EXT),
291                    X11True,
292                    LOCAL_GLX_RENDER_TYPE,
293                    LOCAL_GLX_RGBA_BIT,
294                    X11None};
295 
296   int numConfigs = 0;
297   Display* display = xs->XDisplay();
298   int xscreen = DefaultScreen(display);
299 
300   ScopedXFree<GLXFBConfig> cfgs(
301       fChooseFBConfig(display, xscreen, attribs, &numConfigs));
302 
303   // Find an fbconfig that matches the pixel format used on the Pixmap.
304   int matchIndex = -1;
305   unsigned long redMask = static_cast<unsigned long>(direct.redMask)
306                           << direct.red;
307   unsigned long greenMask = static_cast<unsigned long>(direct.greenMask)
308                             << direct.green;
309   unsigned long blueMask = static_cast<unsigned long>(direct.blueMask)
310                            << direct.blue;
311   // This is true if the Pixmap has bits for alpha or unused bits.
312   bool haveNonColorBits =
313       ~(redMask | greenMask | blueMask) != -1UL << format->depth;
314 
315   for (int i = 0; i < numConfigs; i++) {
316     int id = X11None;
317     sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &id);
318     Visual* visual;
319     int depth;
320     FindVisualAndDepth(display, id, &visual, &depth);
321     if (!visual || visual->c_class != TrueColor ||
322         visual->red_mask != redMask || visual->green_mask != greenMask ||
323         visual->blue_mask != blueMask) {
324       continue;
325     }
326 
327     // Historically Xlib Visuals did not try to represent an alpha channel
328     // and there was no means to use an alpha channel on a Pixmap.  The
329     // Xlib Visual from the fbconfig was not intended to have any
330     // information about alpha bits.
331     //
332     // Since then, RENDER has added formats for 32 bit depth Pixmaps.
333     // Some of these formats have bits for alpha and some have unused
334     // bits.
335     //
336     // Then the Composite extension added a 32 bit depth Visual intended
337     // for Windows with an alpha channel, so bits not in the visual color
338     // masks were expected to be treated as alpha bits.
339     //
340     // Usually GLX counts only color bits in the Visual depth, but the
341     // depth of Composite's ARGB Visual includes alpha bits.  However,
342     // bits not in the color masks are not necessarily alpha bits because
343     // sometimes (NVIDIA) 32 bit Visuals are added for fbconfigs with 32
344     // bit BUFFER_SIZE but zero alpha bits and 24 color bits (NVIDIA
345     // again).
346     //
347     // This checks that the depth matches in one of the two ways.
348     // NVIDIA now forces format->depth == depth so only the first way
349     // is checked for NVIDIA
350     if (depth != format->depth &&
351         (mIsNVIDIA || depth != format->depth - alphaSize)) {
352       continue;
353     }
354 
355     // If all bits of the Pixmap are color bits and the Pixmap depth
356     // matches the depth of the fbconfig visual, then we can assume that
357     // the driver will do whatever is necessary to ensure that any
358     // GLXPixmap alpha bits are treated as set.  We can skip the
359     // ALPHA_SIZE check in this situation.  We need to skip this check for
360     // situations (ATI) where there are no fbconfigs without alpha bits.
361     //
362     // glXChooseFBConfig should prefer configs with smaller
363     // LOCAL_GLX_BUFFER_SIZE, so we should still get zero alpha bits if
364     // available, except perhaps with NVIDIA drivers where buffer size is
365     // not the specified sum of the component sizes.
366     if (haveNonColorBits) {
367       // There are bits in the Pixmap format that haven't been matched
368       // against the fbconfig visual.  These bits could either represent
369       // alpha or be unused, so just check that the number of alpha bits
370       // matches.
371       int size = 0;
372       sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_ALPHA_SIZE,
373                                      &size);
374       if (size != alphaSize) {
375         continue;
376       }
377     }
378 
379     matchIndex = i;
380     break;
381   }
382   if (matchIndex == -1) {
383     // GLX can't handle A8 surfaces, so this is not really unexpected. The
384     // caller should deal with this situation.
385     NS_WARNING_ASSERTION(
386         format->depth == 8,
387         "[GLX] Couldn't find a FBConfig matching Pixmap format");
388     return X11None;
389   }
390 
391   int pixmapAttribs[] = {LOCAL_GLX_TEXTURE_TARGET_EXT, LOCAL_GLX_TEXTURE_2D_EXT,
392                          LOCAL_GLX_TEXTURE_FORMAT_EXT,
393                          (alphaSize ? LOCAL_GLX_TEXTURE_FORMAT_RGBA_EXT
394                                     : LOCAL_GLX_TEXTURE_FORMAT_RGB_EXT),
395                          X11None};
396 
397   GLXPixmap glxpixmap =
398       fCreatePixmap(display, cfgs[matchIndex], xs->XDrawable(), pixmapAttribs);
399 
400   return glxpixmap;
401 }
402 
DestroyPixmap(Display * aDisplay,GLXPixmap aPixmap)403 void GLXLibrary::DestroyPixmap(Display* aDisplay, GLXPixmap aPixmap) {
404   if (!mUseTextureFromPixmap) {
405     return;
406   }
407 
408   fDestroyPixmap(aDisplay, aPixmap);
409 }
410 
BindTexImage(Display * aDisplay,GLXPixmap aPixmap)411 void GLXLibrary::BindTexImage(Display* aDisplay, GLXPixmap aPixmap) {
412   if (!mUseTextureFromPixmap) {
413     return;
414   }
415 
416   // Make sure all X drawing to the surface has finished before binding to a
417   // texture.
418   if (mClientIsMesa) {
419     // Using XSync instead of Mesa's glXWaitX, because its glxWaitX is a
420     // noop when direct rendering unless the current drawable is a
421     // single-buffer window.
422     FinishX(aDisplay);
423   } else {
424     fWaitX();
425   }
426   fBindTexImage(aDisplay, aPixmap, LOCAL_GLX_FRONT_LEFT_EXT, nullptr);
427 }
428 
ReleaseTexImage(Display * aDisplay,GLXPixmap aPixmap)429 void GLXLibrary::ReleaseTexImage(Display* aDisplay, GLXPixmap aPixmap) {
430   if (!mUseTextureFromPixmap) {
431     return;
432   }
433 
434   fReleaseTexImage(aDisplay, aPixmap, LOCAL_GLX_FRONT_LEFT_EXT);
435 }
436 
UpdateTexImage(Display * aDisplay,GLXPixmap aPixmap)437 void GLXLibrary::UpdateTexImage(Display* aDisplay, GLXPixmap aPixmap) {
438   // NVIDIA drivers don't require a rebind of the pixmap in order
439   // to display an updated image, and it's faster not to do it.
440   if (mIsNVIDIA) {
441     fWaitX();
442     return;
443   }
444 
445   ReleaseTexImage(aDisplay, aPixmap);
446   BindTexImage(aDisplay, aPixmap);
447 }
448 
449 static int (*sOldErrorHandler)(Display*, XErrorEvent*);
450 ScopedXErrorHandler::ErrorEvent sErrorEvent;
GLXErrorHandler(Display * display,XErrorEvent * ev)451 static int GLXErrorHandler(Display* display, XErrorEvent* ev) {
452   if (!sErrorEvent.mError.error_code) {
453     sErrorEvent.mError = *ev;
454   }
455   return 0;
456 }
457 
WrapperScope(const GLXLibrary & glx,const char * const funcName,Display * aDisplay)458 GLXLibrary::WrapperScope::WrapperScope(const GLXLibrary& glx,
459                                        const char* const funcName,
460                                        Display* aDisplay)
461     : mGlx(glx), mFuncName(funcName), mDisplay(aDisplay) {
462   if (mGlx.mDebug) {
463     sOldErrorHandler = XSetErrorHandler(GLXErrorHandler);
464   }
465 }
466 
~WrapperScope()467 GLXLibrary::WrapperScope::~WrapperScope() {
468   if (mGlx.mDebug) {
469     if (mDisplay) {
470       FinishX(mDisplay);
471     }
472     if (sErrorEvent.mError.error_code) {
473       char buffer[100] = {};
474       if (mDisplay) {
475         XGetErrorText(mDisplay, sErrorEvent.mError.error_code, buffer,
476                       sizeof(buffer));
477       } else {
478         SprintfLiteral(buffer, "%d", sErrorEvent.mError.error_code);
479       }
480       printf_stderr("X ERROR after %s: %s (%i) - Request: %i.%i, Serial: %lu",
481                     mFuncName, buffer, sErrorEvent.mError.error_code,
482                     sErrorEvent.mError.request_code,
483                     sErrorEvent.mError.minor_code, sErrorEvent.mError.serial);
484       MOZ_ASSERT_UNREACHABLE("AfterGLXCall sErrorEvent");
485     }
486     XSetErrorHandler(sOldErrorHandler);
487   }
488 }
489 
490 // Returns the GTK display if available; otherwise, if a display was
491 // previously opened by this method and is still open, returns a
492 // reference to it; otherwise, opens a new connection.  (The non-GTK
493 // cases are similar to what we do for EGL.)
GetDisplay()494 std::shared_ptr<XlibDisplay> GLXLibrary::GetDisplay() {
495   std::shared_ptr<XlibDisplay> display;
496 
497 #ifdef MOZ_WIDGET_GTK
498   static const bool kHaveGtk = !!gdk_display_get_default();
499   if (kHaveGtk) {
500     display = XlibDisplay::Borrow(DefaultXDisplay());
501   }
502 #endif
503   if (display) {
504     return display;
505   }
506 
507   auto ownDisplay = mOwnDisplay.Lock();
508   display = ownDisplay->lock();
509   if (display) {
510     return display;
511   }
512 
513   display = XlibDisplay::Open(nullptr);
514   if (NS_WARN_IF(!display)) {
515     return nullptr;
516   }
517   *ownDisplay = display;
518   return display;
519 }
520 
CreateGLContext(const GLContextDesc & desc,std::shared_ptr<XlibDisplay> display,GLXDrawable drawable,GLXFBConfig cfg,bool deleteDrawable,gfxXlibSurface * pixmap)521 already_AddRefed<GLContextGLX> GLContextGLX::CreateGLContext(
522     const GLContextDesc& desc, std::shared_ptr<XlibDisplay> display,
523     GLXDrawable drawable, GLXFBConfig cfg, bool deleteDrawable,
524     gfxXlibSurface* pixmap) {
525   GLXLibrary& glx = sGLXLibrary;
526 
527   int isDoubleBuffered = 0;
528   int err = glx.fGetFBConfigAttrib(*display, cfg, LOCAL_GLX_DOUBLEBUFFER,
529                                    &isDoubleBuffered);
530   if (LOCAL_GLX_BAD_ATTRIBUTE != err) {
531     if (ShouldSpew()) {
532       printf("[GLX] FBConfig is %sdouble-buffered\n",
533              isDoubleBuffered ? "" : "not ");
534     }
535   }
536 
537   if (!glx.HasCreateContextAttribs()) {
538     NS_WARNING("Cannot create GLContextGLX without glxCreateContextAttribs");
539     return nullptr;
540   }
541 
542   // -
543 
544   const auto CreateWithAttribs =
545       [&](const std::vector<int>& attribs) -> RefPtr<GLContextGLX> {
546     OffMainThreadScopedXErrorHandler handler;
547 
548     auto terminated = attribs;
549     terminated.push_back(0);
550 
551     // X Errors can happen even if this context creation returns non-null, and
552     // we should not try to use such contexts. (Errors may come from the
553     // distant server, or something)
554     const auto glxContext = glx.fCreateContextAttribs(
555         *display, cfg, nullptr, X11True, terminated.data());
556     if (!glxContext) return nullptr;
557     const RefPtr<GLContextGLX> ret =
558         new GLContextGLX(desc, display, drawable, glxContext, deleteDrawable,
559                          isDoubleBuffered, pixmap);
560     if (handler.SyncAndGetError(*display)) return nullptr;
561 
562     if (!ret->Init()) return nullptr;
563     if (handler.SyncAndGetError(*display)) return nullptr;
564 
565     return ret;
566   };
567 
568   // -
569 
570   RefPtr<GLContextGLX> glContext;
571 
572   std::vector<int> attribs;
573   attribs.insert(attribs.end(), {
574                                     LOCAL_GLX_RENDER_TYPE,
575                                     LOCAL_GLX_RGBA_TYPE,
576                                 });
577   if (glx.HasVideoMemoryPurge()) {
578     attribs.insert(attribs.end(),
579                    {
580                        LOCAL_GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV,
581                        LOCAL_GL_TRUE,
582                    });
583   }
584   const bool useCore =
585       !(desc.flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE);
586   if (useCore) {
587     attribs.insert(attribs.end(), {
588                                       LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB,
589                                       3,
590                                       LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB,
591                                       2,
592                                       LOCAL_GLX_CONTEXT_PROFILE_MASK_ARB,
593                                       LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
594                                   });
595   }
596 
597   if (glx.HasRobustness()) {
598     auto withRobustness = attribs;
599     withRobustness.insert(withRobustness.end(),
600                           {
601                               LOCAL_GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
602                               LOCAL_GLX_LOSE_CONTEXT_ON_RESET_ARB,
603                           });
604 
605     {
606       auto withRBAB = withRobustness;
607       withRBAB.insert(withRBAB.end(),
608                       {
609                           LOCAL_GLX_CONTEXT_FLAGS_ARB,
610                           LOCAL_GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
611                       });
612       if (!glContext) {
613         glContext = CreateWithAttribs(withRBAB);
614         if (!glContext) {
615           NS_WARNING("Failed to create+init GLContextGLX with RBAB");
616         }
617       }
618     }
619 
620     if (!glContext) {
621       glContext = CreateWithAttribs(withRobustness);
622       if (!glContext) {
623         NS_WARNING("Failed to create+init GLContextGLX with Robustness");
624       }
625     }
626   }
627 
628   if (!glContext) {
629     glContext = CreateWithAttribs(attribs);
630     if (!glContext) {
631       NS_WARNING("Failed to create+init GLContextGLX with required attribs");
632     }
633   }
634 
635   return glContext.forget();
636 }
637 
~GLContextGLX()638 GLContextGLX::~GLContextGLX() {
639   MarkDestroyed();
640 
641   // Wrapped context should not destroy glxContext/Surface
642   if (!mOwnsContext) {
643     return;
644   }
645 
646   // see bug 659842 comment 76
647 #ifdef DEBUG
648   bool success =
649 #endif
650       mGLX->fMakeCurrent(*mDisplay, X11None, nullptr);
651   MOZ_ASSERT(success,
652              "glXMakeCurrent failed to release GL context before we call "
653              "glXDestroyContext!");
654 
655   mGLX->fDestroyContext(*mDisplay, mContext);
656 
657   if (mDeleteDrawable) {
658     mGLX->fDestroyPixmap(*mDisplay, mDrawable);
659   }
660 }
661 
Init()662 bool GLContextGLX::Init() {
663   if (!GLContext::Init()) {
664     return false;
665   }
666 
667   // EXT_framebuffer_object is not supported on Core contexts
668   // so we'll also check for ARB_framebuffer_object
669   if (!IsExtensionSupported(EXT_framebuffer_object) &&
670       !IsSupported(GLFeature::framebuffer_object))
671     return false;
672 
673   return true;
674 }
675 
MakeCurrentImpl() const676 bool GLContextGLX::MakeCurrentImpl() const {
677   if (mGLX->IsMesa()) {
678     // Read into the event queue to ensure that Mesa receives a
679     // DRI2InvalidateBuffers event before drawing. See bug 1280653.
680     Unused << XPending(*mDisplay);
681   }
682 
683   const bool succeeded = mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext);
684   NS_ASSERTION(succeeded, "Failed to make GL context current!");
685 
686   if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
687     // Many GLX implementations default to blocking until the next
688     // VBlank when calling glXSwapBuffers. We want to run unthrottled
689     // in ASAP mode. See bug 1280744.
690     const bool isASAP = (StaticPrefs::layout_frame_rate() == 0);
691     mGLX->fSwapInterval(*mDisplay, mDrawable, isASAP ? 0 : 1);
692   }
693   return succeeded;
694 }
695 
IsCurrentImpl() const696 bool GLContextGLX::IsCurrentImpl() const {
697   return mGLX->fGetCurrentContext() == mContext;
698 }
699 
GetSymbolLoader() const700 Maybe<SymbolLoader> GLContextGLX::GetSymbolLoader() const {
701   const auto pfn = sGLXLibrary.GetGetProcAddress();
702   return Some(SymbolLoader(pfn));
703 }
704 
IsDoubleBuffered() const705 bool GLContextGLX::IsDoubleBuffered() const { return mDoubleBuffered; }
706 
SwapBuffers()707 bool GLContextGLX::SwapBuffers() {
708   if (!mDoubleBuffered) return false;
709   mGLX->fSwapBuffers(*mDisplay, mDrawable);
710   return true;
711 }
712 
GetBufferAge() const713 GLint GLContextGLX::GetBufferAge() const {
714   if (!sGLXLibrary.SupportsBufferAge()) {
715     return 0;
716   }
717 
718   GLuint result = 0;
719   mGLX->fQueryDrawable(*mDisplay, mDrawable, LOCAL_GLX_BACK_BUFFER_AGE_EXT,
720                        &result);
721   if (result > INT32_MAX) {
722     // If the result can't fit, just assume the buffer cannot be reused.
723     return 0;
724   }
725   return result;
726 }
727 
GetWSIInfo(nsCString * const out) const728 void GLContextGLX::GetWSIInfo(nsCString* const out) const {
729   int screen = DefaultScreen(mDisplay->get());
730 
731   int majorVersion, minorVersion;
732   sGLXLibrary.fQueryVersion(*mDisplay, &majorVersion, &minorVersion);
733 
734   out->Append(nsPrintfCString("GLX %u.%u", majorVersion, minorVersion));
735 
736   out->AppendLiteral("\nGLX_VENDOR(client): ");
737   out->Append(sGLXLibrary.fGetClientString(*mDisplay, LOCAL_GLX_VENDOR));
738 
739   out->AppendLiteral("\nGLX_VENDOR(server): ");
740   out->Append(
741       sGLXLibrary.fQueryServerString(*mDisplay, screen, LOCAL_GLX_VENDOR));
742 
743   out->AppendLiteral("\nExtensions: ");
744   out->Append(sGLXLibrary.fQueryExtensionsString(*mDisplay, screen));
745 }
746 
OverrideDrawable(GLXDrawable drawable)747 bool GLContextGLX::OverrideDrawable(GLXDrawable drawable) {
748   return mGLX->fMakeCurrent(*mDisplay, drawable, mContext);
749 }
750 
RestoreDrawable()751 bool GLContextGLX::RestoreDrawable() {
752   return mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext);
753 }
754 
GLContextGLX(const GLContextDesc & desc,std::shared_ptr<XlibDisplay> aDisplay,GLXDrawable aDrawable,GLXContext aContext,bool aDeleteDrawable,bool aDoubleBuffered,gfxXlibSurface * aPixmap)755 GLContextGLX::GLContextGLX(const GLContextDesc& desc,
756                            std::shared_ptr<XlibDisplay> aDisplay,
757                            GLXDrawable aDrawable, GLXContext aContext,
758                            bool aDeleteDrawable, bool aDoubleBuffered,
759                            gfxXlibSurface* aPixmap)
760     : GLContext(desc, nullptr),
761       mContext(aContext),
762       mDisplay(aDisplay),
763       mDrawable(aDrawable),
764       mDeleteDrawable(aDeleteDrawable),
765       mDoubleBuffered(aDoubleBuffered),
766       mGLX(&sGLXLibrary),
767       mPixmap(aPixmap) {}
768 
AreCompatibleVisuals(Visual * one,Visual * two)769 static bool AreCompatibleVisuals(Visual* one, Visual* two) {
770   if (one->c_class != two->c_class) {
771     return false;
772   }
773 
774   if (one->red_mask != two->red_mask || one->green_mask != two->green_mask ||
775       one->blue_mask != two->blue_mask) {
776     return false;
777   }
778 
779   if (one->bits_per_rgb != two->bits_per_rgb) {
780     return false;
781   }
782 
783   return true;
784 }
785 
CreateForWidget(Display * aXDisplay,Window aXWindow,bool aHardwareWebRender,bool aForceAccelerated)786 already_AddRefed<GLContext> CreateForWidget(Display* aXDisplay, Window aXWindow,
787                                             bool aHardwareWebRender,
788                                             bool aForceAccelerated) {
789   if (!sGLXLibrary.EnsureInitialized(aXDisplay)) {
790     return nullptr;
791   }
792 
793   // Currently, we take whatever Visual the window already has, and
794   // try to create an fbconfig for that visual.  This isn't
795   // necessarily what we want in the long run; an fbconfig may not
796   // be available for the existing visual, or if it is, the GL
797   // performance might be suboptimal.  But using the existing visual
798   // is a relatively safe intermediate step.
799 
800   if (!aXDisplay) {
801     NS_ERROR("X Display required for GLX Context provider");
802     return nullptr;
803   }
804 
805   int xscreen = DefaultScreen(aXDisplay);
806 
807   ScopedXFree<GLXFBConfig> cfgs;
808   GLXFBConfig config;
809   int visid;
810   if (!GLContextGLX::FindFBConfigForWindow(aXDisplay, xscreen, aXWindow, &cfgs,
811                                            &config, &visid,
812                                            aHardwareWebRender)) {
813     return nullptr;
814   }
815 
816   CreateContextFlags flags;
817   if (aHardwareWebRender) {
818     flags = CreateContextFlags::NONE;  // WR needs GL3.2+
819   } else {
820     flags = CreateContextFlags::REQUIRE_COMPAT_PROFILE;
821   }
822   return GLContextGLX::CreateGLContext({{flags}, false},
823                                        XlibDisplay::Borrow(aXDisplay), aXWindow,
824                                        config, false, nullptr);
825 }
826 
CreateForCompositorWidget(CompositorWidget * aCompositorWidget,bool aHardwareWebRender,bool aForceAccelerated)827 already_AddRefed<GLContext> GLContextProviderGLX::CreateForCompositorWidget(
828     CompositorWidget* aCompositorWidget, bool aHardwareWebRender,
829     bool aForceAccelerated) {
830   if (!aCompositorWidget) {
831     MOZ_ASSERT(false);
832     return nullptr;
833   }
834   GtkCompositorWidget* compWidget = aCompositorWidget->AsGTK();
835   MOZ_ASSERT(compWidget);
836 
837   return CreateForWidget(DefaultXDisplay(), compWidget->XWindow(),
838                          aHardwareWebRender, aForceAccelerated);
839 }
840 
ChooseConfig(GLXLibrary * glx,Display * display,int screen,ScopedXFree<GLXFBConfig> * const out_scopedConfigArr,GLXFBConfig * const out_config,int * const out_visid)841 static bool ChooseConfig(GLXLibrary* glx, Display* display, int screen,
842                          ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
843                          GLXFBConfig* const out_config, int* const out_visid) {
844   ScopedXFree<GLXFBConfig>& scopedConfigArr = *out_scopedConfigArr;
845 
846   const int attribs[] = {
847       LOCAL_GLX_RENDER_TYPE,
848       LOCAL_GLX_RGBA_BIT,
849       LOCAL_GLX_DRAWABLE_TYPE,
850       LOCAL_GLX_PIXMAP_BIT,
851       LOCAL_GLX_X_RENDERABLE,
852       X11True,
853       LOCAL_GLX_RED_SIZE,
854       8,
855       LOCAL_GLX_GREEN_SIZE,
856       8,
857       LOCAL_GLX_BLUE_SIZE,
858       8,
859       LOCAL_GLX_ALPHA_SIZE,
860       8,
861       LOCAL_GLX_DEPTH_SIZE,
862       0,
863       LOCAL_GLX_STENCIL_SIZE,
864       0,
865       0,
866   };
867 
868   int numConfigs = 0;
869   scopedConfigArr = glx->fChooseFBConfig(display, screen, attribs, &numConfigs);
870   if (!scopedConfigArr || !numConfigs) return false;
871 
872   // Issues with glxChooseFBConfig selection and sorting:
873   // * ALPHA_SIZE is sorted as 'largest total RGBA bits first'. If we don't
874   // request
875   //   alpha bits, we'll probably get RGBA anyways, since 32 is more than 24.
876   // * DEPTH_SIZE is sorted largest first, including for `0` inputs.
877   // * STENCIL_SIZE is smallest first, but it might return `8` even though we
878   // ask for
879   //   `0`.
880 
881   // For now, we don't care about these. We *will* care when we do XPixmap
882   // sharing.
883 
884   for (int i = 0; i < numConfigs; ++i) {
885     GLXFBConfig curConfig = scopedConfigArr[i];
886 
887     int visid;
888     if (glx->fGetFBConfigAttrib(display, curConfig, LOCAL_GLX_VISUAL_ID,
889                                 &visid) != Success) {
890       continue;
891     }
892 
893     if (!visid) continue;
894 
895     *out_config = curConfig;
896     *out_visid = visid;
897     return true;
898   }
899 
900   return false;
901 }
902 
FindVisual(Display * display,int screen,bool useWebRender,bool useAlpha,int * const out_visualId)903 bool GLContextGLX::FindVisual(Display* display, int screen, bool useWebRender,
904                               bool useAlpha, int* const out_visualId) {
905   if (!sGLXLibrary.EnsureInitialized(display)) {
906     return false;
907   }
908 
909   XVisualInfo visualTemplate;
910   visualTemplate.screen = screen;
911 
912   // Get all visuals of screen
913 
914   int visualsLen = 0;
915   XVisualInfo* xVisuals =
916       XGetVisualInfo(display, VisualScreenMask, &visualTemplate, &visualsLen);
917   if (!xVisuals) {
918     return false;
919   }
920   const Range<XVisualInfo> visualInfos(xVisuals, visualsLen);
921   auto cleanupVisuals = MakeScopeExit([&] { XFree(xVisuals); });
922 
923   // Get default visual info
924 
925   Visual* defaultVisual = DefaultVisual(display, screen);
926   const auto defaultVisualInfo = [&]() -> const XVisualInfo* {
927     for (const auto& cur : visualInfos) {
928       if (cur.visual == defaultVisual) {
929         return &cur;
930       }
931     }
932     return nullptr;
933   }();
934   if (!defaultVisualInfo) {
935     MOZ_ASSERT(false);
936     return false;
937   }
938 
939   const int bpp = useAlpha ? 32 : 24;
940   const int alphaSize = useAlpha ? 8 : 0;
941   const int depthSize = useWebRender ? 24 : 0;
942 
943   for (auto& cur : visualInfos) {
944     const auto fnConfigMatches = [&](const int pname, const int expected) {
945       int actual;
946       if (sGLXLibrary.fGetConfig(display, &cur, pname, &actual)) {
947         return false;
948       }
949       return actual == expected;
950     };
951 
952     // Check if visual is compatible.
953     if (cur.depth != bpp || cur.c_class != defaultVisualInfo->c_class) {
954       continue;
955     }
956 
957     // Check if visual is compatible to GL requests.
958     if (fnConfigMatches(LOCAL_GLX_USE_GL, 1) &&
959         fnConfigMatches(LOCAL_GLX_DOUBLEBUFFER, 1) &&
960         fnConfigMatches(LOCAL_GLX_RED_SIZE, 8) &&
961         fnConfigMatches(LOCAL_GLX_GREEN_SIZE, 8) &&
962         fnConfigMatches(LOCAL_GLX_BLUE_SIZE, 8) &&
963         fnConfigMatches(LOCAL_GLX_ALPHA_SIZE, alphaSize) &&
964         fnConfigMatches(LOCAL_GLX_DEPTH_SIZE, depthSize)) {
965       *out_visualId = cur.visualid;
966       return true;
967     }
968   }
969 
970   return false;
971 }
972 
FindFBConfigForWindow(Display * display,int screen,Window window,ScopedXFree<GLXFBConfig> * const out_scopedConfigArr,GLXFBConfig * const out_config,int * const out_visid,bool aWebRender)973 bool GLContextGLX::FindFBConfigForWindow(
974     Display* display, int screen, Window window,
975     ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
976     GLXFBConfig* const out_config, int* const out_visid, bool aWebRender) {
977   // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
978   // we could probably do this first and replace the glXGetFBConfigs
979   // with glXChooseConfigs.  Docs are sparklingly clear as always.
980   XWindowAttributes windowAttrs;
981   if (!XGetWindowAttributes(display, window, &windowAttrs)) {
982     NS_WARNING("[GLX] XGetWindowAttributes() failed");
983     return false;
984   }
985 
986   ScopedXFree<GLXFBConfig>& cfgs = *out_scopedConfigArr;
987   int numConfigs;
988   const int webrenderAttribs[] = {LOCAL_GLX_ALPHA_SIZE,
989                                   windowAttrs.depth == 32 ? 8 : 0,
990                                   LOCAL_GLX_DOUBLEBUFFER, X11True, 0};
991 
992   if (aWebRender) {
993     cfgs = sGLXLibrary.fChooseFBConfig(display, screen, webrenderAttribs,
994                                        &numConfigs);
995   } else {
996     cfgs = sGLXLibrary.fGetFBConfigs(display, screen, &numConfigs);
997   }
998 
999   if (!cfgs) {
1000     NS_WARNING("[GLX] glXGetFBConfigs() failed");
1001     return false;
1002   }
1003   NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
1004 
1005   const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
1006 #ifdef DEBUG
1007   printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
1008 #endif
1009 
1010   for (int i = 0; i < numConfigs; i++) {
1011     int visid = X11None;
1012     sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID,
1013                                    &visid);
1014     if (visid) {
1015       // WebRender compatible GLX visual is configured
1016       // at nsWindow::Create() by GLContextGLX::FindVisual(),
1017       // just reuse it here.
1018       if (windowVisualID == static_cast<VisualID>(visid)) {
1019         *out_config = cfgs[i];
1020         *out_visid = visid;
1021         return true;
1022       }
1023     }
1024   }
1025 
1026   // We don't have a frame buffer visual which matches the GLX visual
1027   // from GLContextGLX::FindVisual(). Let's try to find a near one and hope
1028   // we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there.
1029   for (int i = 0; i < numConfigs; i++) {
1030     int visid = X11None;
1031     sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID,
1032                                    &visid);
1033     if (visid) {
1034       int depth;
1035       Visual* visual;
1036       FindVisualAndDepth(display, visid, &visual, &depth);
1037       if (depth == windowAttrs.depth &&
1038           AreCompatibleVisuals(windowAttrs.visual, visual)) {
1039         *out_config = cfgs[i];
1040         *out_visid = visid;
1041         return true;
1042       }
1043     }
1044   }
1045 
1046   NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
1047   return false;
1048 }
1049 
CreateOffscreenPixmapContext(const GLContextCreateDesc & desc,const IntSize & size,nsACString * const out_failureId)1050 static already_AddRefed<GLContextGLX> CreateOffscreenPixmapContext(
1051     const GLContextCreateDesc& desc, const IntSize& size,
1052     nsACString* const out_failureId) {
1053   GLXLibrary* glx = &sGLXLibrary;
1054   auto display = glx->GetDisplay();
1055 
1056   if (!display || !glx->EnsureInitialized(*display)) return nullptr;
1057 
1058   int screen = DefaultScreen(display->get());
1059 
1060   ScopedXFree<GLXFBConfig> scopedConfigArr;
1061   GLXFBConfig config;
1062   int visid;
1063   if (!ChooseConfig(glx, *display, screen, &scopedConfigArr, &config, &visid)) {
1064     NS_WARNING("Failed to find a compatible config.");
1065     return nullptr;
1066   }
1067 
1068   Visual* visual;
1069   int depth;
1070   FindVisualAndDepth(*display, visid, &visual, &depth);
1071 
1072   OffMainThreadScopedXErrorHandler xErrorHandler;
1073   bool error = false;
1074 
1075   gfx::IntSize dummySize(16, 16);
1076   RefPtr<gfxXlibSurface> surface = gfxXlibSurface::Create(
1077       display, DefaultScreenOfDisplay(display->get()), visual, dummySize);
1078   if (surface->CairoStatus() != 0) {
1079     mozilla::Unused << xErrorHandler.SyncAndGetError(*display);
1080     return nullptr;
1081   }
1082 
1083   // Handle slightly different signature between glXCreatePixmap and
1084   // its pre-GLX-1.3 extension equivalent (though given the ABI, we
1085   // might not need to).
1086   const auto drawable = surface->XDrawable();
1087   const auto pixmap = glx->fCreatePixmap(*display, config, drawable, nullptr);
1088   if (pixmap == 0) {
1089     error = true;
1090   }
1091 
1092   bool serverError = xErrorHandler.SyncAndGetError(*display);
1093   if (error || serverError) return nullptr;
1094 
1095   auto fullDesc = GLContextDesc{desc};
1096   fullDesc.isOffscreen = true;
1097   return GLContextGLX::CreateGLContext(fullDesc, display, pixmap, config, true,
1098                                        surface);
1099 }
1100 
1101 /*static*/
CreateHeadless(const GLContextCreateDesc & desc,nsACString * const out_failureId)1102 already_AddRefed<GLContext> GLContextProviderGLX::CreateHeadless(
1103     const GLContextCreateDesc& desc, nsACString* const out_failureId) {
1104   IntSize dummySize = IntSize(16, 16);
1105   return CreateOffscreenPixmapContext(desc, dummySize, out_failureId);
1106 }
1107 
1108 /*static*/
GetGlobalContext()1109 GLContext* GLContextProviderGLX::GetGlobalContext() {
1110   // Context sharing not supported.
1111   return nullptr;
1112 }
1113 
1114 /*static*/
Shutdown()1115 void GLContextProviderGLX::Shutdown() {}
1116 
1117 }  // namespace mozilla::gl
1118