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