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_createcontext[] = {
168 SYMBOL(CreateContextAttribsARB), END_OF_SYMBOLS};
169
170 const SymLoadStruct symbols_videosync[] = {
171 SYMBOL(GetVideoSyncSGI), SYMBOL(WaitVideoSyncSGI), END_OF_SYMBOLS};
172
173 const SymLoadStruct symbols_swapcontrol[] = {SYMBOL(SwapIntervalEXT),
174 END_OF_SYMBOLS};
175
176 const SymLoadStruct symbols_querydrawable[] = {SYMBOL(QueryDrawable),
177 END_OF_SYMBOLS};
178
179 const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) {
180 if (pfnLoader.LoadSymbols(symbols)) return true;
181
182 ClearSymbols(symbols);
183 return false;
184 };
185
186 const char* clientVendor = fGetClientString(aDisplay, LOCAL_GLX_VENDOR);
187 const char* serverVendor =
188 fQueryServerString(aDisplay, screen, LOCAL_GLX_VENDOR);
189 const char* extensionsStr = fQueryExtensionsString(aDisplay, screen);
190
191 if (HasExtension(extensionsStr, "GLX_ARB_create_context") &&
192 HasExtension(extensionsStr, "GLX_ARB_create_context_profile") &&
193 fnLoadSymbols(symbols_createcontext)) {
194 mHasCreateContextAttribs = true;
195 }
196
197 if (HasExtension(extensionsStr, "GLX_ARB_create_context_robustness")) {
198 mHasRobustness = true;
199 }
200
201 if (HasExtension(extensionsStr, "GLX_NV_robustness_video_memory_purge")) {
202 mHasVideoMemoryPurge = true;
203 }
204
205 if (HasExtension(extensionsStr, "GLX_SGI_video_sync") &&
206 fnLoadSymbols(symbols_videosync)) {
207 mHasVideoSync = true;
208 }
209
210 if (!HasExtension(extensionsStr, "GLX_EXT_swap_control") ||
211 !fnLoadSymbols(symbols_swapcontrol)) {
212 NS_WARNING(
213 "GLX_swap_control unsupported, ASAP mode may still block on buffer "
214 "swaps.");
215 }
216
217 if (HasExtension(extensionsStr, "GLX_EXT_buffer_age") &&
218 fnLoadSymbols(symbols_querydrawable)) {
219 mHasBufferAge = true;
220 }
221
222 mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI");
223 mIsNVIDIA =
224 serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation");
225 mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa");
226
227 mInitialized = true;
228
229 // This needs to be after `fQueryServerString` is called so that the
230 // driver is loaded.
231 MesaMemoryLeakWorkaround();
232
233 return true;
234 }
235
SupportsVideoSync(Display * aDisplay)236 bool GLXLibrary::SupportsVideoSync(Display* aDisplay) {
237 if (!EnsureInitialized(aDisplay)) {
238 return false;
239 }
240
241 return mHasVideoSync;
242 }
243
244 static int (*sOldErrorHandler)(Display*, XErrorEvent*);
245 ScopedXErrorHandler::ErrorEvent sErrorEvent;
GLXErrorHandler(Display * display,XErrorEvent * ev)246 static int GLXErrorHandler(Display* display, XErrorEvent* ev) {
247 if (!sErrorEvent.mError.error_code) {
248 sErrorEvent.mError = *ev;
249 }
250 return 0;
251 }
252
WrapperScope(const GLXLibrary & glx,const char * const funcName,Display * aDisplay)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
~WrapperScope()262 GLXLibrary::WrapperScope::~WrapperScope() {
263 if (mGlx.mDebug) {
264 if (mDisplay) {
265 FinishX(mDisplay);
266 }
267 if (sErrorEvent.mError.error_code) {
268 char buffer[100] = {};
269 if (mDisplay) {
270 XGetErrorText(mDisplay, sErrorEvent.mError.error_code, buffer,
271 sizeof(buffer));
272 } else {
273 SprintfLiteral(buffer, "%d", sErrorEvent.mError.error_code);
274 }
275 printf_stderr("X ERROR after %s: %s (%i) - Request: %i.%i, Serial: %lu",
276 mFuncName, buffer, sErrorEvent.mError.error_code,
277 sErrorEvent.mError.request_code,
278 sErrorEvent.mError.minor_code, sErrorEvent.mError.serial);
279 MOZ_ASSERT_UNREACHABLE("AfterGLXCall sErrorEvent");
280 }
281 XSetErrorHandler(sOldErrorHandler);
282 }
283 }
284
285 // Returns the GTK display if available; otherwise, if a display was
286 // previously opened by this method and is still open, returns a
287 // reference to it; otherwise, opens a new connection. (The non-GTK
288 // cases are similar to what we do for EGL.)
GetDisplay()289 std::shared_ptr<XlibDisplay> GLXLibrary::GetDisplay() {
290 std::shared_ptr<XlibDisplay> display;
291
292 #ifdef MOZ_WIDGET_GTK
293 static const bool kHaveGtk = !!gdk_display_get_default();
294 if (kHaveGtk) {
295 display = XlibDisplay::Borrow(DefaultXDisplay());
296 }
297 #endif
298 if (display) {
299 return display;
300 }
301
302 auto ownDisplay = mOwnDisplay.Lock();
303 display = ownDisplay->lock();
304 if (display) {
305 return display;
306 }
307
308 display = XlibDisplay::Open(nullptr);
309 if (NS_WARN_IF(!display)) {
310 return nullptr;
311 }
312 *ownDisplay = display;
313 return display;
314 }
315
CreateGLContext(const GLContextDesc & desc,std::shared_ptr<XlibDisplay> display,GLXDrawable drawable,GLXFBConfig cfg,bool deleteDrawable,gfxXlibSurface * pixmap)316 already_AddRefed<GLContextGLX> GLContextGLX::CreateGLContext(
317 const GLContextDesc& desc, std::shared_ptr<XlibDisplay> display,
318 GLXDrawable drawable, GLXFBConfig cfg, bool deleteDrawable,
319 gfxXlibSurface* pixmap) {
320 GLXLibrary& glx = sGLXLibrary;
321
322 int isDoubleBuffered = 0;
323 int err = glx.fGetFBConfigAttrib(*display, cfg, LOCAL_GLX_DOUBLEBUFFER,
324 &isDoubleBuffered);
325 if (LOCAL_GLX_BAD_ATTRIBUTE != err) {
326 if (ShouldSpew()) {
327 printf("[GLX] FBConfig is %sdouble-buffered\n",
328 isDoubleBuffered ? "" : "not ");
329 }
330 }
331
332 if (!glx.HasCreateContextAttribs()) {
333 NS_WARNING("Cannot create GLContextGLX without glxCreateContextAttribs");
334 return nullptr;
335 }
336
337 // -
338
339 const auto CreateWithAttribs =
340 [&](const std::vector<int>& attribs) -> RefPtr<GLContextGLX> {
341 OffMainThreadScopedXErrorHandler handler;
342
343 auto terminated = attribs;
344 terminated.push_back(0);
345
346 // X Errors can happen even if this context creation returns non-null, and
347 // we should not try to use such contexts. (Errors may come from the
348 // distant server, or something)
349 const auto glxContext = glx.fCreateContextAttribs(
350 *display, cfg, nullptr, X11True, terminated.data());
351 if (!glxContext) return nullptr;
352 const RefPtr<GLContextGLX> ret =
353 new GLContextGLX(desc, display, drawable, glxContext, deleteDrawable,
354 isDoubleBuffered, pixmap);
355 if (handler.SyncAndGetError(*display)) return nullptr;
356
357 if (!ret->Init()) return nullptr;
358 if (handler.SyncAndGetError(*display)) return nullptr;
359
360 return ret;
361 };
362
363 // -
364
365 RefPtr<GLContextGLX> glContext;
366
367 std::vector<int> attribs;
368 attribs.insert(attribs.end(), {
369 LOCAL_GLX_RENDER_TYPE,
370 LOCAL_GLX_RGBA_TYPE,
371 });
372 if (glx.HasVideoMemoryPurge()) {
373 attribs.insert(attribs.end(),
374 {
375 LOCAL_GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV,
376 LOCAL_GL_TRUE,
377 });
378 }
379 const bool useCore =
380 !(desc.flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE);
381 if (useCore) {
382 attribs.insert(attribs.end(), {
383 LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB,
384 3,
385 LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB,
386 2,
387 LOCAL_GLX_CONTEXT_PROFILE_MASK_ARB,
388 LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
389 });
390 }
391
392 if (glx.HasRobustness()) {
393 auto withRobustness = attribs;
394 withRobustness.insert(withRobustness.end(),
395 {
396 LOCAL_GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
397 LOCAL_GLX_LOSE_CONTEXT_ON_RESET_ARB,
398 });
399
400 {
401 auto withRBAB = withRobustness;
402 withRBAB.insert(withRBAB.end(),
403 {
404 LOCAL_GLX_CONTEXT_FLAGS_ARB,
405 LOCAL_GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
406 });
407 if (!glContext) {
408 glContext = CreateWithAttribs(withRBAB);
409 if (!glContext) {
410 NS_WARNING("Failed to create+init GLContextGLX with RBAB");
411 }
412 }
413 }
414
415 if (!glContext) {
416 glContext = CreateWithAttribs(withRobustness);
417 if (!glContext) {
418 NS_WARNING("Failed to create+init GLContextGLX with Robustness");
419 }
420 }
421 }
422
423 if (!glContext) {
424 glContext = CreateWithAttribs(attribs);
425 if (!glContext) {
426 NS_WARNING("Failed to create+init GLContextGLX with required attribs");
427 }
428 }
429
430 return glContext.forget();
431 }
432
~GLContextGLX()433 GLContextGLX::~GLContextGLX() {
434 MarkDestroyed();
435
436 // Wrapped context should not destroy glxContext/Surface
437 if (!mOwnsContext) {
438 return;
439 }
440
441 // see bug 659842 comment 76
442 #ifdef DEBUG
443 bool success =
444 #endif
445 mGLX->fMakeCurrent(*mDisplay, X11None, nullptr);
446 MOZ_ASSERT(success,
447 "glXMakeCurrent failed to release GL context before we call "
448 "glXDestroyContext!");
449
450 mGLX->fDestroyContext(*mDisplay, mContext);
451
452 if (mDeleteDrawable) {
453 mGLX->fDestroyPixmap(*mDisplay, mDrawable);
454 }
455 }
456
Init()457 bool GLContextGLX::Init() {
458 if (!GLContext::Init()) {
459 return false;
460 }
461
462 // EXT_framebuffer_object is not supported on Core contexts
463 // so we'll also check for ARB_framebuffer_object
464 if (!IsExtensionSupported(EXT_framebuffer_object) &&
465 !IsSupported(GLFeature::framebuffer_object))
466 return false;
467
468 return true;
469 }
470
MakeCurrentImpl() const471 bool GLContextGLX::MakeCurrentImpl() const {
472 if (mGLX->IsMesa()) {
473 // Read into the event queue to ensure that Mesa receives a
474 // DRI2InvalidateBuffers event before drawing. See bug 1280653.
475 Unused << XPending(*mDisplay);
476 }
477
478 const bool succeeded = mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext);
479 NS_ASSERTION(succeeded, "Failed to make GL context current!");
480
481 if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
482 // Many GLX implementations default to blocking until the next
483 // VBlank when calling glXSwapBuffers. We want to run unthrottled
484 // in ASAP mode. See bug 1280744.
485 const bool swapInterval = gfxVars::SwapIntervalGLX();
486 const bool isASAP = (StaticPrefs::layout_frame_rate() == 0);
487 const int interval = (swapInterval && !isASAP) ? 1 : 0;
488 mGLX->fSwapInterval(*mDisplay, mDrawable, interval);
489 }
490 return succeeded;
491 }
492
IsCurrentImpl() const493 bool GLContextGLX::IsCurrentImpl() const {
494 return mGLX->fGetCurrentContext() == mContext;
495 }
496
GetSymbolLoader() const497 Maybe<SymbolLoader> GLContextGLX::GetSymbolLoader() const {
498 const auto pfn = sGLXLibrary.GetGetProcAddress();
499 return Some(SymbolLoader(pfn));
500 }
501
IsDoubleBuffered() const502 bool GLContextGLX::IsDoubleBuffered() const { return mDoubleBuffered; }
503
SwapBuffers()504 bool GLContextGLX::SwapBuffers() {
505 if (!mDoubleBuffered) return false;
506 mGLX->fSwapBuffers(*mDisplay, mDrawable);
507 return true;
508 }
509
GetBufferAge() const510 GLint GLContextGLX::GetBufferAge() const {
511 if (!sGLXLibrary.SupportsBufferAge()) {
512 return 0;
513 }
514
515 GLuint result = 0;
516 mGLX->fQueryDrawable(*mDisplay, mDrawable, LOCAL_GLX_BACK_BUFFER_AGE_EXT,
517 &result);
518 if (result > INT32_MAX) {
519 // If the result can't fit, just assume the buffer cannot be reused.
520 return 0;
521 }
522 return result;
523 }
524
GetWSIInfo(nsCString * const out) const525 void GLContextGLX::GetWSIInfo(nsCString* const out) const {
526 int screen = DefaultScreen(mDisplay->get());
527
528 int majorVersion, minorVersion;
529 sGLXLibrary.fQueryVersion(*mDisplay, &majorVersion, &minorVersion);
530
531 out->Append(nsPrintfCString("GLX %u.%u", majorVersion, minorVersion));
532
533 out->AppendLiteral("\nGLX_VENDOR(client): ");
534 out->Append(sGLXLibrary.fGetClientString(*mDisplay, LOCAL_GLX_VENDOR));
535
536 out->AppendLiteral("\nGLX_VENDOR(server): ");
537 out->Append(
538 sGLXLibrary.fQueryServerString(*mDisplay, screen, LOCAL_GLX_VENDOR));
539
540 out->AppendLiteral("\nExtensions: ");
541 out->Append(sGLXLibrary.fQueryExtensionsString(*mDisplay, screen));
542 }
543
OverrideDrawable(GLXDrawable drawable)544 bool GLContextGLX::OverrideDrawable(GLXDrawable drawable) {
545 return mGLX->fMakeCurrent(*mDisplay, drawable, mContext);
546 }
547
RestoreDrawable()548 bool GLContextGLX::RestoreDrawable() {
549 return mGLX->fMakeCurrent(*mDisplay, mDrawable, mContext);
550 }
551
GLContextGLX(const GLContextDesc & desc,std::shared_ptr<XlibDisplay> aDisplay,GLXDrawable aDrawable,GLXContext aContext,bool aDeleteDrawable,bool aDoubleBuffered,gfxXlibSurface * aPixmap)552 GLContextGLX::GLContextGLX(const GLContextDesc& desc,
553 std::shared_ptr<XlibDisplay> aDisplay,
554 GLXDrawable aDrawable, GLXContext aContext,
555 bool aDeleteDrawable, bool aDoubleBuffered,
556 gfxXlibSurface* aPixmap)
557 : GLContext(desc, nullptr),
558 mContext(aContext),
559 mDisplay(aDisplay),
560 mDrawable(aDrawable),
561 mDeleteDrawable(aDeleteDrawable),
562 mDoubleBuffered(aDoubleBuffered),
563 mGLX(&sGLXLibrary),
564 mPixmap(aPixmap) {}
565
AreCompatibleVisuals(Visual * one,Visual * two)566 static bool AreCompatibleVisuals(Visual* one, Visual* two) {
567 if (one->c_class != two->c_class) {
568 return false;
569 }
570
571 if (one->red_mask != two->red_mask || one->green_mask != two->green_mask ||
572 one->blue_mask != two->blue_mask) {
573 return false;
574 }
575
576 if (one->bits_per_rgb != two->bits_per_rgb) {
577 return false;
578 }
579
580 return true;
581 }
582
CreateForWidget(Display * aXDisplay,Window aXWindow,bool aHardwareWebRender,bool aForceAccelerated)583 already_AddRefed<GLContext> CreateForWidget(Display* aXDisplay, Window aXWindow,
584 bool aHardwareWebRender,
585 bool aForceAccelerated) {
586 if (!sGLXLibrary.EnsureInitialized(aXDisplay)) {
587 return nullptr;
588 }
589
590 // Currently, we take whatever Visual the window already has, and
591 // try to create an fbconfig for that visual. This isn't
592 // necessarily what we want in the long run; an fbconfig may not
593 // be available for the existing visual, or if it is, the GL
594 // performance might be suboptimal. But using the existing visual
595 // is a relatively safe intermediate step.
596
597 if (!aXDisplay) {
598 NS_ERROR("X Display required for GLX Context provider");
599 return nullptr;
600 }
601
602 if (!aXWindow) {
603 NS_ERROR("X window required for GLX Context provider");
604 return nullptr;
605 }
606
607 int xscreen = DefaultScreen(aXDisplay);
608
609 ScopedXFree<GLXFBConfig> cfgs;
610 GLXFBConfig config;
611 int visid;
612 if (!GLContextGLX::FindFBConfigForWindow(aXDisplay, xscreen, aXWindow, &cfgs,
613 &config, &visid,
614 aHardwareWebRender)) {
615 return nullptr;
616 }
617
618 CreateContextFlags flags;
619 if (aHardwareWebRender) {
620 flags = CreateContextFlags::NONE; // WR needs GL3.2+
621 } else {
622 flags = CreateContextFlags::REQUIRE_COMPAT_PROFILE;
623 }
624 return GLContextGLX::CreateGLContext({{flags}, false},
625 XlibDisplay::Borrow(aXDisplay), aXWindow,
626 config, false, nullptr);
627 }
628
CreateForCompositorWidget(CompositorWidget * aCompositorWidget,bool aHardwareWebRender,bool aForceAccelerated)629 already_AddRefed<GLContext> GLContextProviderGLX::CreateForCompositorWidget(
630 CompositorWidget* aCompositorWidget, bool aHardwareWebRender,
631 bool aForceAccelerated) {
632 if (!aCompositorWidget) {
633 MOZ_ASSERT(false);
634 return nullptr;
635 }
636 GtkCompositorWidget* compWidget = aCompositorWidget->AsGTK();
637 MOZ_ASSERT(compWidget);
638
639 return CreateForWidget(DefaultXDisplay(), compWidget->XWindow(),
640 aHardwareWebRender, aForceAccelerated);
641 }
642
ChooseConfig(GLXLibrary * glx,Display * display,int screen,ScopedXFree<GLXFBConfig> * const out_scopedConfigArr,GLXFBConfig * const out_config,int * const out_visid)643 static bool ChooseConfig(GLXLibrary* glx, Display* display, int screen,
644 ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
645 GLXFBConfig* const out_config, int* const out_visid) {
646 ScopedXFree<GLXFBConfig>& scopedConfigArr = *out_scopedConfigArr;
647
648 const int attribs[] = {
649 LOCAL_GLX_RENDER_TYPE,
650 LOCAL_GLX_RGBA_BIT,
651 LOCAL_GLX_DRAWABLE_TYPE,
652 LOCAL_GLX_PIXMAP_BIT,
653 LOCAL_GLX_X_RENDERABLE,
654 X11True,
655 LOCAL_GLX_RED_SIZE,
656 8,
657 LOCAL_GLX_GREEN_SIZE,
658 8,
659 LOCAL_GLX_BLUE_SIZE,
660 8,
661 LOCAL_GLX_ALPHA_SIZE,
662 8,
663 LOCAL_GLX_DEPTH_SIZE,
664 0,
665 LOCAL_GLX_STENCIL_SIZE,
666 0,
667 0,
668 };
669
670 int numConfigs = 0;
671 scopedConfigArr = glx->fChooseFBConfig(display, screen, attribs, &numConfigs);
672 if (!scopedConfigArr || !numConfigs) return false;
673
674 // Issues with glxChooseFBConfig selection and sorting:
675 // * ALPHA_SIZE is sorted as 'largest total RGBA bits first'. If we don't
676 // request
677 // alpha bits, we'll probably get RGBA anyways, since 32 is more than 24.
678 // * DEPTH_SIZE is sorted largest first, including for `0` inputs.
679 // * STENCIL_SIZE is smallest first, but it might return `8` even though we
680 // ask for
681 // `0`.
682
683 // For now, we don't care about these. We *will* care when we do XPixmap
684 // sharing.
685
686 for (int i = 0; i < numConfigs; ++i) {
687 GLXFBConfig curConfig = scopedConfigArr[i];
688
689 int visid;
690 if (glx->fGetFBConfigAttrib(display, curConfig, LOCAL_GLX_VISUAL_ID,
691 &visid) != Success) {
692 continue;
693 }
694
695 if (!visid) continue;
696
697 *out_config = curConfig;
698 *out_visid = visid;
699 return true;
700 }
701
702 return false;
703 }
704
FindVisual(Display * display,int screen,int * const out_visualId)705 bool GLContextGLX::FindVisual(Display* display, int screen,
706 int* const out_visualId) {
707 if (!sGLXLibrary.EnsureInitialized(display)) {
708 return false;
709 }
710
711 XVisualInfo visualTemplate;
712 visualTemplate.screen = screen;
713
714 // Get all visuals of screen
715
716 int visualsLen = 0;
717 XVisualInfo* xVisuals =
718 XGetVisualInfo(display, VisualScreenMask, &visualTemplate, &visualsLen);
719 if (!xVisuals) {
720 return false;
721 }
722 const Range<XVisualInfo> visualInfos(xVisuals, visualsLen);
723 auto cleanupVisuals = MakeScopeExit([&] { XFree(xVisuals); });
724
725 // Get default visual info
726
727 Visual* defaultVisual = DefaultVisual(display, screen);
728 const auto defaultVisualInfo = [&]() -> const XVisualInfo* {
729 for (const auto& cur : visualInfos) {
730 if (cur.visual == defaultVisual) {
731 return &cur;
732 }
733 }
734 return nullptr;
735 }();
736 if (!defaultVisualInfo) {
737 MOZ_ASSERT(false);
738 return false;
739 }
740
741 const int bpp = 32;
742
743 for (auto& cur : visualInfos) {
744 const auto fnConfigMatches = [&](const int pname, const int expected) {
745 int actual;
746 if (sGLXLibrary.fGetConfig(display, &cur, pname, &actual)) {
747 return false;
748 }
749 return actual == expected;
750 };
751
752 // Check if visual is compatible.
753 if (cur.depth != bpp || cur.c_class != defaultVisualInfo->c_class) {
754 continue;
755 }
756
757 // Check if visual is compatible to GL requests.
758 if (fnConfigMatches(LOCAL_GLX_USE_GL, 1) &&
759 fnConfigMatches(LOCAL_GLX_DOUBLEBUFFER, 1) &&
760 fnConfigMatches(LOCAL_GLX_RED_SIZE, 8) &&
761 fnConfigMatches(LOCAL_GLX_GREEN_SIZE, 8) &&
762 fnConfigMatches(LOCAL_GLX_BLUE_SIZE, 8) &&
763 fnConfigMatches(LOCAL_GLX_ALPHA_SIZE, 8)) {
764 *out_visualId = cur.visualid;
765 return true;
766 }
767 }
768
769 return false;
770 }
771
FindFBConfigForWindow(Display * display,int screen,Window window,ScopedXFree<GLXFBConfig> * const out_scopedConfigArr,GLXFBConfig * const out_config,int * const out_visid,bool aWebRender)772 bool GLContextGLX::FindFBConfigForWindow(
773 Display* display, int screen, Window window,
774 ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
775 GLXFBConfig* const out_config, int* const out_visid, bool aWebRender) {
776 // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
777 // we could probably do this first and replace the glXGetFBConfigs
778 // with glXChooseConfigs. Docs are sparklingly clear as always.
779 XWindowAttributes windowAttrs;
780 if (!XGetWindowAttributes(display, window, &windowAttrs)) {
781 NS_WARNING("[GLX] XGetWindowAttributes() failed");
782 return false;
783 }
784
785 ScopedXFree<GLXFBConfig>& cfgs = *out_scopedConfigArr;
786 int numConfigs;
787 const int webrenderAttribs[] = {LOCAL_GLX_ALPHA_SIZE,
788 windowAttrs.depth == 32 ? 8 : 0,
789 LOCAL_GLX_DOUBLEBUFFER, X11True, 0};
790
791 if (aWebRender) {
792 cfgs = sGLXLibrary.fChooseFBConfig(display, screen, webrenderAttribs,
793 &numConfigs);
794 } else {
795 cfgs = sGLXLibrary.fGetFBConfigs(display, screen, &numConfigs);
796 }
797
798 if (!cfgs) {
799 NS_WARNING("[GLX] glXGetFBConfigs() failed");
800 return false;
801 }
802 NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
803
804 const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
805 #ifdef DEBUG
806 printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
807 #endif
808
809 for (int i = 0; i < numConfigs; i++) {
810 int visid = X11None;
811 sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID,
812 &visid);
813 if (visid) {
814 // WebRender compatible GLX visual is configured
815 // at nsWindow::Create() by GLContextGLX::FindVisual(),
816 // just reuse it here.
817 if (windowVisualID == static_cast<VisualID>(visid)) {
818 *out_config = cfgs[i];
819 *out_visid = visid;
820 return true;
821 }
822 }
823 }
824
825 // We don't have a frame buffer visual which matches the GLX visual
826 // from GLContextGLX::FindVisual(). Let's try to find a near one and hope
827 // we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there.
828 for (int i = 0; i < numConfigs; i++) {
829 int visid = X11None;
830 sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID,
831 &visid);
832 if (visid) {
833 int depth;
834 Visual* visual;
835 FindVisualAndDepth(display, visid, &visual, &depth);
836 if (depth == windowAttrs.depth &&
837 AreCompatibleVisuals(windowAttrs.visual, visual)) {
838 *out_config = cfgs[i];
839 *out_visid = visid;
840 return true;
841 }
842 }
843 }
844
845 NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
846 return false;
847 }
848
CreateOffscreenPixmapContext(const GLContextCreateDesc & desc,const IntSize & size,nsACString * const out_failureId)849 static already_AddRefed<GLContextGLX> CreateOffscreenPixmapContext(
850 const GLContextCreateDesc& desc, const IntSize& size,
851 nsACString* const out_failureId) {
852 GLXLibrary* glx = &sGLXLibrary;
853 auto display = glx->GetDisplay();
854
855 if (!display || !glx->EnsureInitialized(*display)) return nullptr;
856
857 int screen = DefaultScreen(display->get());
858
859 ScopedXFree<GLXFBConfig> scopedConfigArr;
860 GLXFBConfig config;
861 int visid;
862 if (!ChooseConfig(glx, *display, screen, &scopedConfigArr, &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 OffMainThreadScopedXErrorHandler xErrorHandler;
872 bool error = false;
873
874 gfx::IntSize dummySize(16, 16);
875 RefPtr<gfxXlibSurface> surface = gfxXlibSurface::Create(
876 display, DefaultScreenOfDisplay(display->get()), visual, dummySize);
877 if (surface->CairoStatus() != 0) {
878 mozilla::Unused << xErrorHandler.SyncAndGetError(*display);
879 return nullptr;
880 }
881
882 // Handle slightly different signature between glXCreatePixmap and
883 // its pre-GLX-1.3 extension equivalent (though given the ABI, we
884 // might not need to).
885 const auto drawable = surface->XDrawable();
886 const auto pixmap = glx->fCreatePixmap(*display, config, drawable, nullptr);
887 if (pixmap == 0) {
888 error = true;
889 }
890
891 bool serverError = xErrorHandler.SyncAndGetError(*display);
892 if (error || serverError) return nullptr;
893
894 auto fullDesc = GLContextDesc{desc};
895 fullDesc.isOffscreen = true;
896 return GLContextGLX::CreateGLContext(fullDesc, display, pixmap, config, true,
897 surface);
898 }
899
900 /*static*/
CreateHeadless(const GLContextCreateDesc & desc,nsACString * const out_failureId)901 already_AddRefed<GLContext> GLContextProviderGLX::CreateHeadless(
902 const GLContextCreateDesc& desc, nsACString* const out_failureId) {
903 IntSize dummySize = IntSize(16, 16);
904 return CreateOffscreenPixmapContext(desc, dummySize, out_failureId);
905 }
906
907 /*static*/
GetGlobalContext()908 GLContext* GLContextProviderGLX::GetGlobalContext() {
909 // Context sharing not supported.
910 return nullptr;
911 }
912
913 /*static*/
Shutdown()914 void GLContextProviderGLX::Shutdown() {}
915
916 } // namespace mozilla::gl
917