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/. */
9 # define GET_NATIVE_WINDOW(aWidget) \
10 GDK_WINDOW_XID((GdkWindow*)aWidget->GetNativeData(NS_NATIVE_WINDOW))
14 #include <X11/Xutil.h>
15 #include "X11UndefineNone.h"
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/StaticPrefs_gfx.h"
23 #include "mozilla/StaticPrefs_layout.h"
24 #include "mozilla/widget/CompositorWidget.h"
25 #include "mozilla/widget/GtkCompositorWidget.h"
26 #include "mozilla/Unused.h"
29 #include "GLContextProvider.h"
30 #include "GLLibraryLoader.h"
32 #include "nsIWidget.h"
33 #include "GLXLibrary.h"
34 #include "gfxXlibSurface.h"
35 #include "gfxContext.h"
37 #include "gfxPlatform.h"
38 #include "GLContextGLX.h"
40 #include "gfx2DGlue.h"
41 #include "GLScreenBuffer.h"
43 #include "gfxCrashReporterUtils.h"
46 # include "gfxPlatformGtk.h"
49 namespace mozilla::gl
{
51 using namespace mozilla::gfx
;
52 using namespace mozilla::widget
;
54 GLXLibrary sGLXLibrary
;
56 static inline bool HasExtension(const char* aExtensions
,
57 const char* aRequiredExtension
) {
58 return GLContext::ListHasExtension(
59 reinterpret_cast<const GLubyte
*>(aExtensions
), aRequiredExtension
);
62 bool GLXLibrary::EnsureInitialized() {
67 // Don't repeatedly try to initialize.
68 if (mTriedInitializing
) {
71 mTriedInitializing
= true;
73 // Force enabling s3 texture compression. (Bug 774134)
74 PR_SetEnv("force_s3tc_enable=true");
77 // see e.g. bug 608526: it is intrinsically interesting to know whether we
78 // have dynamically linked to libGL.so.1 because at least the NVIDIA
79 // implementation requires an executable stack, which causes mprotect calls,
80 // which trigger glibc bug
81 // http://sourceware.org/bugzilla/show_bug.cgi?id=12225
82 const char* libGLfilename
= "libGL.so.1";
83 #if defined(__OpenBSD__) || defined(__NetBSD__)
84 libGLfilename
= "libGL.so";
87 const bool forceFeatureReport
= false;
88 ScopedGfxFeatureReporter
reporter(libGLfilename
, forceFeatureReport
);
89 mOGLLibrary
= PR_LoadLibrary(libGLfilename
);
91 NS_WARNING("Couldn't load OpenGL shared library.");
94 reporter
.SetSuccessful();
97 if (gfxEnv::GlxDebug()) {
103 (PRFuncPtr*)&mSymbols.f##X, { \
107 #define END_OF_SYMBOLS \
112 const SymLoadStruct symbols
[] = {
113 /* functions that were in GLX 1.0 */
114 SYMBOL(DestroyContext
),
117 SYMBOL(QueryVersion
),
119 SYMBOL(GetCurrentContext
),
123 /* functions introduced in GLX 1.1 */
124 SYMBOL(QueryExtensionsString
),
125 SYMBOL(GetClientString
),
126 SYMBOL(QueryServerString
),
128 /* functions introduced in GLX 1.3 */
129 SYMBOL(ChooseFBConfig
),
130 SYMBOL(ChooseVisual
),
131 SYMBOL(GetFBConfigAttrib
),
132 SYMBOL(GetFBConfigs
),
133 SYMBOL(CreatePixmap
),
134 SYMBOL(DestroyPixmap
),
135 SYMBOL(CreateNewContext
),
137 // Core in GLX 1.4, ARB extension before.
138 {(PRFuncPtr
*)&mSymbols
.fGetProcAddress
,
139 {{"glXGetProcAddress", "glXGetProcAddressARB"}}},
143 const SymbolLoader
libLoader(*mOGLLibrary
);
144 if (!libLoader
.LoadSymbols(symbols
)) {
145 NS_WARNING("Couldn't load required GLX symbols.");
149 const SymbolLoader
pfnLoader(mSymbols
.fGetProcAddress
);
151 Display
* display
= DefaultXDisplay();
152 int screen
= DefaultScreen(display
);
156 if (!fQueryVersion(display
, &major
, &minor
) || major
!= 1 || minor
< 3) {
157 NS_ERROR("GLX version older than 1.3. (released in 1998)");
162 const SymLoadStruct symbols_texturefrompixmap
[] = {
163 SYMBOL(BindTexImageEXT
), SYMBOL(ReleaseTexImageEXT
), END_OF_SYMBOLS
};
165 const SymLoadStruct symbols_createcontext
[] = {
166 SYMBOL(CreateContextAttribsARB
), END_OF_SYMBOLS
};
168 const SymLoadStruct symbols_videosync
[] = {
169 SYMBOL(GetVideoSyncSGI
), SYMBOL(WaitVideoSyncSGI
), END_OF_SYMBOLS
};
171 const SymLoadStruct symbols_swapcontrol
[] = {SYMBOL(SwapIntervalEXT
),
174 const SymLoadStruct symbols_querydrawable
[] = {SYMBOL(QueryDrawable
),
177 const auto fnLoadSymbols
= [&](const SymLoadStruct
* symbols
) {
178 if (pfnLoader
.LoadSymbols(symbols
)) return true;
180 ClearSymbols(symbols
);
184 const char* clientVendor
= fGetClientString(display
, LOCAL_GLX_VENDOR
);
185 const char* serverVendor
=
186 fQueryServerString(display
, screen
, LOCAL_GLX_VENDOR
);
187 const char* extensionsStr
= fQueryExtensionsString(display
, screen
);
189 if (HasExtension(extensionsStr
, "GLX_EXT_texture_from_pixmap") &&
190 fnLoadSymbols(symbols_texturefrompixmap
)) {
191 mUseTextureFromPixmap
= StaticPrefs::gfx_use_glx_texture_from_pixmap();
193 mUseTextureFromPixmap
= false;
194 NS_WARNING("Texture from pixmap disabled");
197 if (HasExtension(extensionsStr
, "GLX_ARB_create_context") &&
198 HasExtension(extensionsStr
, "GLX_ARB_create_context_profile") &&
199 fnLoadSymbols(symbols_createcontext
)) {
200 mHasCreateContextAttribs
= true;
203 if (HasExtension(extensionsStr
, "GLX_ARB_create_context_robustness")) {
204 mHasRobustness
= true;
207 if (HasExtension(extensionsStr
, "GLX_NV_robustness_video_memory_purge")) {
208 mHasVideoMemoryPurge
= true;
211 if (HasExtension(extensionsStr
, "GLX_SGI_video_sync") &&
212 fnLoadSymbols(symbols_videosync
)) {
213 mHasVideoSync
= true;
216 if (!HasExtension(extensionsStr
, "GLX_EXT_swap_control") ||
217 !fnLoadSymbols(symbols_swapcontrol
)) {
219 "GLX_swap_control unsupported, ASAP mode may still block on buffer "
223 if (HasExtension(extensionsStr
, "GLX_EXT_buffer_age") &&
224 fnLoadSymbols(symbols_querydrawable
)) {
225 mHasBufferAge
= true;
228 mIsATI
= serverVendor
&& DoesStringMatch(serverVendor
, "ATI");
230 serverVendor
&& DoesStringMatch(serverVendor
, "NVIDIA Corporation");
231 mClientIsMesa
= clientVendor
&& DoesStringMatch(clientVendor
, "Mesa");
235 // This needs to be after `fQueryServerString` is called so that the
237 MesaMemoryLeakWorkaround();
242 bool GLXLibrary::SupportsTextureFromPixmap(gfxASurface
* aSurface
) {
243 if (!EnsureInitialized()) {
247 if (aSurface
->GetType() != gfxSurfaceType::Xlib
|| !mUseTextureFromPixmap
) {
254 bool GLXLibrary::SupportsVideoSync() {
255 if (!EnsureInitialized()) {
259 return mHasVideoSync
;
262 GLXPixmap
GLXLibrary::CreatePixmap(gfxASurface
* aSurface
) {
263 if (!SupportsTextureFromPixmap(aSurface
)) {
267 gfxXlibSurface
* xs
= static_cast<gfxXlibSurface
*>(aSurface
);
268 const XRenderPictFormat
* format
= xs
->XRenderFormat();
269 if (!format
|| format
->type
!= PictTypeDirect
) {
272 const XRenderDirectFormat
& direct
= format
->direct
;
273 int alphaSize
= FloorLog2(direct
.alphaMask
+ 1);
274 NS_ASSERTION((1 << alphaSize
) - 1 == direct
.alphaMask
,
275 "Unexpected render format with non-adjacent alpha bits");
277 int attribs
[] = {LOCAL_GLX_DOUBLEBUFFER
,
279 LOCAL_GLX_DRAWABLE_TYPE
,
280 LOCAL_GLX_PIXMAP_BIT
,
281 LOCAL_GLX_ALPHA_SIZE
,
283 (alphaSize
? LOCAL_GLX_BIND_TO_TEXTURE_RGBA_EXT
284 : LOCAL_GLX_BIND_TO_TEXTURE_RGB_EXT
),
286 LOCAL_GLX_RENDER_TYPE
,
291 Display
* display
= xs
->XDisplay();
292 int xscreen
= DefaultScreen(display
);
294 ScopedXFree
<GLXFBConfig
> cfgs(
295 fChooseFBConfig(display
, xscreen
, attribs
, &numConfigs
));
297 // Find an fbconfig that matches the pixel format used on the Pixmap.
299 unsigned long redMask
= static_cast<unsigned long>(direct
.redMask
)
301 unsigned long greenMask
= static_cast<unsigned long>(direct
.greenMask
)
303 unsigned long blueMask
= static_cast<unsigned long>(direct
.blueMask
)
305 // This is true if the Pixmap has bits for alpha or unused bits.
306 bool haveNonColorBits
=
307 ~(redMask
| greenMask
| blueMask
) != -1UL << format
->depth
;
309 for (int i
= 0; i
< numConfigs
; i
++) {
311 sGLXLibrary
.fGetFBConfigAttrib(display
, cfgs
[i
], LOCAL_GLX_VISUAL_ID
, &id
);
314 FindVisualAndDepth(display
, id
, &visual
, &depth
);
315 if (!visual
|| visual
->c_class
!= TrueColor
||
316 visual
->red_mask
!= redMask
|| visual
->green_mask
!= greenMask
||
317 visual
->blue_mask
!= blueMask
) {
321 // Historically Xlib Visuals did not try to represent an alpha channel
322 // and there was no means to use an alpha channel on a Pixmap. The
323 // Xlib Visual from the fbconfig was not intended to have any
324 // information about alpha bits.
326 // Since then, RENDER has added formats for 32 bit depth Pixmaps.
327 // Some of these formats have bits for alpha and some have unused
330 // Then the Composite extension added a 32 bit depth Visual intended
331 // for Windows with an alpha channel, so bits not in the visual color
332 // masks were expected to be treated as alpha bits.
334 // Usually GLX counts only color bits in the Visual depth, but the
335 // depth of Composite's ARGB Visual includes alpha bits. However,
336 // bits not in the color masks are not necessarily alpha bits because
337 // sometimes (NVIDIA) 32 bit Visuals are added for fbconfigs with 32
338 // bit BUFFER_SIZE but zero alpha bits and 24 color bits (NVIDIA
341 // This checks that the depth matches in one of the two ways.
342 // NVIDIA now forces format->depth == depth so only the first way
343 // is checked for NVIDIA
344 if (depth
!= format
->depth
&&
345 (mIsNVIDIA
|| depth
!= format
->depth
- alphaSize
)) {
349 // If all bits of the Pixmap are color bits and the Pixmap depth
350 // matches the depth of the fbconfig visual, then we can assume that
351 // the driver will do whatever is necessary to ensure that any
352 // GLXPixmap alpha bits are treated as set. We can skip the
353 // ALPHA_SIZE check in this situation. We need to skip this check for
354 // situations (ATI) where there are no fbconfigs without alpha bits.
356 // glXChooseFBConfig should prefer configs with smaller
357 // LOCAL_GLX_BUFFER_SIZE, so we should still get zero alpha bits if
358 // available, except perhaps with NVIDIA drivers where buffer size is
359 // not the specified sum of the component sizes.
360 if (haveNonColorBits
) {
361 // There are bits in the Pixmap format that haven't been matched
362 // against the fbconfig visual. These bits could either represent
363 // alpha or be unused, so just check that the number of alpha bits
366 sGLXLibrary
.fGetFBConfigAttrib(display
, cfgs
[i
], LOCAL_GLX_ALPHA_SIZE
,
368 if (size
!= alphaSize
) {
376 if (matchIndex
== -1) {
377 // GLX can't handle A8 surfaces, so this is not really unexpected. The
378 // caller should deal with this situation.
379 NS_WARNING_ASSERTION(
381 "[GLX] Couldn't find a FBConfig matching Pixmap format");
385 int pixmapAttribs
[] = {LOCAL_GLX_TEXTURE_TARGET_EXT
, LOCAL_GLX_TEXTURE_2D_EXT
,
386 LOCAL_GLX_TEXTURE_FORMAT_EXT
,
387 (alphaSize
? LOCAL_GLX_TEXTURE_FORMAT_RGBA_EXT
388 : LOCAL_GLX_TEXTURE_FORMAT_RGB_EXT
),
391 GLXPixmap glxpixmap
=
392 fCreatePixmap(display
, cfgs
[matchIndex
], xs
->XDrawable(), pixmapAttribs
);
397 void GLXLibrary::DestroyPixmap(Display
* aDisplay
, GLXPixmap aPixmap
) {
398 if (!mUseTextureFromPixmap
) {
402 fDestroyPixmap(aDisplay
, aPixmap
);
405 void GLXLibrary::BindTexImage(Display
* aDisplay
, GLXPixmap aPixmap
) {
406 if (!mUseTextureFromPixmap
) {
410 // Make sure all X drawing to the surface has finished before binding to a
413 // Using XSync instead of Mesa's glXWaitX, because its glxWaitX is a
414 // noop when direct rendering unless the current drawable is a
415 // single-buffer window.
420 fBindTexImage(aDisplay
, aPixmap
, LOCAL_GLX_FRONT_LEFT_EXT
, nullptr);
423 void GLXLibrary::ReleaseTexImage(Display
* aDisplay
, GLXPixmap aPixmap
) {
424 if (!mUseTextureFromPixmap
) {
428 fReleaseTexImage(aDisplay
, aPixmap
, LOCAL_GLX_FRONT_LEFT_EXT
);
431 void GLXLibrary::UpdateTexImage(Display
* aDisplay
, GLXPixmap aPixmap
) {
432 // NVIDIA drivers don't require a rebind of the pixmap in order
433 // to display an updated image, and it's faster not to do it.
439 ReleaseTexImage(aDisplay
, aPixmap
);
440 BindTexImage(aDisplay
, aPixmap
);
443 static int (*sOldErrorHandler
)(Display
*, XErrorEvent
*);
444 ScopedXErrorHandler::ErrorEvent sErrorEvent
;
445 static int GLXErrorHandler(Display
* display
, XErrorEvent
* ev
) {
446 if (!sErrorEvent
.mError
.error_code
) {
447 sErrorEvent
.mError
= *ev
;
452 GLXLibrary::WrapperScope::WrapperScope(const GLXLibrary
& glx
,
453 const char* const funcName
)
454 : mGlx(glx
), mFuncName(funcName
) {
456 sOldErrorHandler
= XSetErrorHandler(GLXErrorHandler
);
460 GLXLibrary::WrapperScope::~WrapperScope() {
462 FinishX(DefaultXDisplay());
463 if (sErrorEvent
.mError
.error_code
) {
464 char buffer
[100] = {};
465 XGetErrorText(DefaultXDisplay(), sErrorEvent
.mError
.error_code
, buffer
,
467 printf_stderr("X ERROR after %s: %s (%i) - Request: %i.%i, Serial: %lu",
468 mFuncName
, buffer
, sErrorEvent
.mError
.error_code
,
469 sErrorEvent
.mError
.request_code
,
470 sErrorEvent
.mError
.minor_code
, sErrorEvent
.mError
.serial
);
471 MOZ_ASSERT_UNREACHABLE("AfterGLXCall sErrorEvent");
473 XSetErrorHandler(sOldErrorHandler
);
477 already_AddRefed
<GLContextGLX
> GLContextGLX::CreateGLContext(
478 const GLContextDesc
& desc
, Display
* display
, GLXDrawable drawable
,
479 GLXFBConfig cfg
, bool deleteDrawable
, gfxXlibSurface
* pixmap
) {
480 GLXLibrary
& glx
= sGLXLibrary
;
482 int isDoubleBuffered
= 0;
483 int err
= glx
.fGetFBConfigAttrib(display
, cfg
, LOCAL_GLX_DOUBLEBUFFER
,
485 if (LOCAL_GLX_BAD_ATTRIBUTE
!= err
) {
487 printf("[GLX] FBConfig is %sdouble-buffered\n",
488 isDoubleBuffered
? "" : "not ");
492 if (!glx
.HasCreateContextAttribs()) {
493 NS_WARNING("Cannot create GLContextGLX without glxCreateContextAttribs");
499 const auto CreateWithAttribs
=
500 [&](const std::vector
<int>& attribs
) -> RefPtr
<GLContextGLX
> {
501 OffMainThreadScopedXErrorHandler handler
;
503 auto terminated
= attribs
;
504 terminated
.push_back(0);
506 // X Errors can happen even if this context creation returns non-null, and
507 // we should not try to use such contexts. (Errors may come from the
508 // distant server, or something)
509 const auto glxContext
= glx
.fCreateContextAttribs(
510 display
, cfg
, nullptr, X11True
, terminated
.data());
511 if (!glxContext
) return nullptr;
512 const RefPtr
<GLContextGLX
> ret
=
513 new GLContextGLX(desc
, display
, drawable
, glxContext
, deleteDrawable
,
514 isDoubleBuffered
, pixmap
);
515 if (handler
.SyncAndGetError(display
)) return nullptr;
517 if (!ret
->Init()) return nullptr;
518 if (handler
.SyncAndGetError(display
)) return nullptr;
525 RefPtr
<GLContextGLX
> glContext
;
527 std::vector
<int> attribs
;
528 attribs
.insert(attribs
.end(), {
529 LOCAL_GLX_RENDER_TYPE
,
532 if (glx
.HasVideoMemoryPurge()) {
533 attribs
.insert(attribs
.end(),
535 LOCAL_GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
,
540 !(desc
.flags
& CreateContextFlags::REQUIRE_COMPAT_PROFILE
);
542 attribs
.insert(attribs
.end(), {
543 LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB
,
545 LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB
,
547 LOCAL_GLX_CONTEXT_PROFILE_MASK_ARB
,
548 LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB
,
552 if (glx
.HasRobustness()) {
553 auto withRobustness
= attribs
;
554 withRobustness
.insert(withRobustness
.end(),
556 LOCAL_GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB
,
557 LOCAL_GLX_LOSE_CONTEXT_ON_RESET_ARB
,
561 auto withRBAB
= withRobustness
;
562 withRBAB
.insert(withRBAB
.end(),
564 LOCAL_GLX_CONTEXT_FLAGS_ARB
,
565 LOCAL_GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB
,
568 glContext
= CreateWithAttribs(withRBAB
);
570 NS_WARNING("Failed to create+init GLContextGLX with RBAB");
576 glContext
= CreateWithAttribs(withRobustness
);
578 NS_WARNING("Failed to create+init GLContextGLX with Robustness");
584 glContext
= CreateWithAttribs(attribs
);
586 NS_WARNING("Failed to create+init GLContextGLX with required attribs");
590 return glContext
.forget();
593 GLContextGLX::~GLContextGLX() {
596 // Wrapped context should not destroy glxContext/Surface
601 // see bug 659842 comment 76
605 mGLX
->fMakeCurrent(mDisplay
, X11None
, nullptr);
607 "glXMakeCurrent failed to release GL context before we call "
608 "glXDestroyContext!");
610 mGLX
->fDestroyContext(mDisplay
, mContext
);
612 if (mDeleteDrawable
) {
613 mGLX
->fDestroyPixmap(mDisplay
, mDrawable
);
617 bool GLContextGLX::Init() {
618 if (!GLContext::Init()) {
622 // EXT_framebuffer_object is not supported on Core contexts
623 // so we'll also check for ARB_framebuffer_object
624 if (!IsExtensionSupported(EXT_framebuffer_object
) &&
625 !IsSupported(GLFeature::framebuffer_object
))
631 bool GLContextGLX::MakeCurrentImpl() const {
632 if (mGLX
->IsMesa()) {
633 // Read into the event queue to ensure that Mesa receives a
634 // DRI2InvalidateBuffers event before drawing. See bug 1280653.
635 Unused
<< XPending(mDisplay
);
638 const bool succeeded
= mGLX
->fMakeCurrent(mDisplay
, mDrawable
, mContext
);
639 NS_ASSERTION(succeeded
, "Failed to make GL context current!");
641 if (!IsOffscreen() && mGLX
->SupportsSwapControl()) {
642 // Many GLX implementations default to blocking until the next
643 // VBlank when calling glXSwapBuffers. We want to run unthrottled
644 // in ASAP mode. See bug 1280744.
645 const bool isASAP
= (StaticPrefs::layout_frame_rate() == 0);
646 mGLX
->fSwapInterval(mDisplay
, mDrawable
, isASAP
? 0 : 1);
651 bool GLContextGLX::IsCurrentImpl() const {
652 return mGLX
->fGetCurrentContext() == mContext
;
655 Maybe
<SymbolLoader
> GLContextGLX::GetSymbolLoader() const {
656 const auto pfn
= sGLXLibrary
.GetGetProcAddress();
657 return Some(SymbolLoader(pfn
));
660 bool GLContextGLX::IsDoubleBuffered() const { return mDoubleBuffered
; }
662 bool GLContextGLX::SwapBuffers() {
663 if (!mDoubleBuffered
) return false;
664 mGLX
->fSwapBuffers(mDisplay
, mDrawable
);
668 GLint
GLContextGLX::GetBufferAge() const {
669 if (!sGLXLibrary
.SupportsBufferAge()) {
674 mGLX
->fQueryDrawable(mDisplay
, mDrawable
, LOCAL_GLX_BACK_BUFFER_AGE_EXT
,
676 if (result
> INT32_MAX
) {
677 // If the result can't fit, just assume the buffer cannot be reused.
683 void GLContextGLX::GetWSIInfo(nsCString
* const out
) const {
684 Display
* display
= DefaultXDisplay();
685 int screen
= DefaultScreen(display
);
687 int majorVersion
, minorVersion
;
688 sGLXLibrary
.fQueryVersion(display
, &majorVersion
, &minorVersion
);
690 out
->Append(nsPrintfCString("GLX %u.%u", majorVersion
, minorVersion
));
692 out
->AppendLiteral("\nGLX_VENDOR(client): ");
693 out
->Append(sGLXLibrary
.fGetClientString(display
, LOCAL_GLX_VENDOR
));
695 out
->AppendLiteral("\nGLX_VENDOR(server): ");
697 sGLXLibrary
.fQueryServerString(display
, screen
, LOCAL_GLX_VENDOR
));
699 out
->AppendLiteral("\nExtensions: ");
700 out
->Append(sGLXLibrary
.fQueryExtensionsString(display
, screen
));
703 bool GLContextGLX::OverrideDrawable(GLXDrawable drawable
) {
704 return mGLX
->fMakeCurrent(mDisplay
, drawable
, mContext
);
707 bool GLContextGLX::RestoreDrawable() {
708 return mGLX
->fMakeCurrent(mDisplay
, mDrawable
, mContext
);
711 GLContextGLX::GLContextGLX(const GLContextDesc
& desc
, Display
* aDisplay
,
712 GLXDrawable aDrawable
, GLXContext aContext
,
713 bool aDeleteDrawable
, bool aDoubleBuffered
,
714 gfxXlibSurface
* aPixmap
)
715 : GLContext(desc
, nullptr),
718 mDrawable(aDrawable
),
719 mDeleteDrawable(aDeleteDrawable
),
720 mDoubleBuffered(aDoubleBuffered
),
724 static bool AreCompatibleVisuals(Visual
* one
, Visual
* two
) {
725 if (one
->c_class
!= two
->c_class
) {
729 if (one
->red_mask
!= two
->red_mask
|| one
->green_mask
!= two
->green_mask
||
730 one
->blue_mask
!= two
->blue_mask
) {
734 if (one
->bits_per_rgb
!= two
->bits_per_rgb
) {
741 already_AddRefed
<GLContext
> CreateForWidget(Display
* aXDisplay
, Window aXWindow
,
742 bool aHardwareWebRender
,
743 bool aForceAccelerated
) {
744 if (!sGLXLibrary
.EnsureInitialized()) {
748 // Currently, we take whatever Visual the window already has, and
749 // try to create an fbconfig for that visual. This isn't
750 // necessarily what we want in the long run; an fbconfig may not
751 // be available for the existing visual, or if it is, the GL
752 // performance might be suboptimal. But using the existing visual
753 // is a relatively safe intermediate step.
756 NS_ERROR("X Display required for GLX Context provider");
760 int xscreen
= DefaultScreen(aXDisplay
);
762 ScopedXFree
<GLXFBConfig
> cfgs
;
765 if (!GLContextGLX::FindFBConfigForWindow(aXDisplay
, xscreen
, aXWindow
, &cfgs
,
767 aHardwareWebRender
)) {
771 CreateContextFlags flags
;
772 if (aHardwareWebRender
) {
773 flags
= CreateContextFlags::NONE
; // WR needs GL3.2+
775 flags
= CreateContextFlags::REQUIRE_COMPAT_PROFILE
;
777 return GLContextGLX::CreateGLContext({{flags
}, false}, aXDisplay
, aXWindow
,
778 config
, false, nullptr);
781 already_AddRefed
<GLContext
> GLContextProviderGLX::CreateForCompositorWidget(
782 CompositorWidget
* aCompositorWidget
, bool aHardwareWebRender
,
783 bool aForceAccelerated
) {
784 if (!aCompositorWidget
) {
788 GtkCompositorWidget
* compWidget
= aCompositorWidget
->AsGTK();
789 MOZ_ASSERT(compWidget
);
791 return CreateForWidget(DefaultXDisplay(), compWidget
->XWindow(),
792 aHardwareWebRender
, aForceAccelerated
);
795 static bool ChooseConfig(GLXLibrary
* glx
, Display
* display
, int screen
,
796 ScopedXFree
<GLXFBConfig
>* const out_scopedConfigArr
,
797 GLXFBConfig
* const out_config
, int* const out_visid
) {
798 ScopedXFree
<GLXFBConfig
>& scopedConfigArr
= *out_scopedConfigArr
;
800 const int attribs
[] = {
801 LOCAL_GLX_RENDER_TYPE
,
803 LOCAL_GLX_DRAWABLE_TYPE
,
804 LOCAL_GLX_PIXMAP_BIT
,
805 LOCAL_GLX_X_RENDERABLE
,
809 LOCAL_GLX_GREEN_SIZE
,
813 LOCAL_GLX_ALPHA_SIZE
,
815 LOCAL_GLX_DEPTH_SIZE
,
817 LOCAL_GLX_STENCIL_SIZE
,
823 scopedConfigArr
= glx
->fChooseFBConfig(display
, screen
, attribs
, &numConfigs
);
824 if (!scopedConfigArr
|| !numConfigs
) return false;
826 // Issues with glxChooseFBConfig selection and sorting:
827 // * ALPHA_SIZE is sorted as 'largest total RGBA bits first'. If we don't
829 // alpha bits, we'll probably get RGBA anyways, since 32 is more than 24.
830 // * DEPTH_SIZE is sorted largest first, including for `0` inputs.
831 // * STENCIL_SIZE is smallest first, but it might return `8` even though we
835 // For now, we don't care about these. We *will* care when we do XPixmap
838 for (int i
= 0; i
< numConfigs
; ++i
) {
839 GLXFBConfig curConfig
= scopedConfigArr
[i
];
842 if (glx
->fGetFBConfigAttrib(display
, curConfig
, LOCAL_GLX_VISUAL_ID
,
843 &visid
) != Success
) {
847 if (!visid
) continue;
849 *out_config
= curConfig
;
857 bool GLContextGLX::FindVisual(Display
* display
, int screen
, bool useWebRender
,
858 bool useAlpha
, int* const out_visualId
) {
859 if (!sGLXLibrary
.EnsureInitialized()) {
863 XVisualInfo visualTemplate
;
864 visualTemplate
.screen
= screen
;
866 // Get all visuals of screen
869 XVisualInfo
* xVisuals
=
870 XGetVisualInfo(display
, VisualScreenMask
, &visualTemplate
, &visualsLen
);
874 const Range
<XVisualInfo
> visualInfos(xVisuals
, visualsLen
);
875 auto cleanupVisuals
= MakeScopeExit([&] { XFree(xVisuals
); });
877 // Get default visual info
879 Visual
* defaultVisual
= DefaultVisual(display
, screen
);
880 const auto defaultVisualInfo
= [&]() -> const XVisualInfo
* {
881 for (const auto& cur
: visualInfos
) {
882 if (cur
.visual
== defaultVisual
) {
888 if (!defaultVisualInfo
) {
893 const int bpp
= useAlpha
? 32 : 24;
894 const int alphaSize
= useAlpha
? 8 : 0;
895 const int depthSize
= useWebRender
? 24 : 0;
897 for (auto& cur
: visualInfos
) {
898 const auto fnConfigMatches
= [&](const int pname
, const int expected
) {
900 if (sGLXLibrary
.fGetConfig(display
, &cur
, pname
, &actual
)) {
903 return actual
== expected
;
906 // Check if visual is compatible.
907 if (cur
.depth
!= bpp
|| cur
.c_class
!= defaultVisualInfo
->c_class
) {
911 // Check if visual is compatible to GL requests.
912 if (fnConfigMatches(LOCAL_GLX_USE_GL
, 1) &&
913 fnConfigMatches(LOCAL_GLX_DOUBLEBUFFER
, 1) &&
914 fnConfigMatches(LOCAL_GLX_RED_SIZE
, 8) &&
915 fnConfigMatches(LOCAL_GLX_GREEN_SIZE
, 8) &&
916 fnConfigMatches(LOCAL_GLX_BLUE_SIZE
, 8) &&
917 fnConfigMatches(LOCAL_GLX_ALPHA_SIZE
, alphaSize
) &&
918 fnConfigMatches(LOCAL_GLX_DEPTH_SIZE
, depthSize
)) {
919 *out_visualId
= cur
.visualid
;
927 bool GLContextGLX::FindFBConfigForWindow(
928 Display
* display
, int screen
, Window window
,
929 ScopedXFree
<GLXFBConfig
>* const out_scopedConfigArr
,
930 GLXFBConfig
* const out_config
, int* const out_visid
, bool aWebRender
) {
931 // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
932 // we could probably do this first and replace the glXGetFBConfigs
933 // with glXChooseConfigs. Docs are sparklingly clear as always.
934 XWindowAttributes windowAttrs
;
935 if (!XGetWindowAttributes(display
, window
, &windowAttrs
)) {
936 NS_WARNING("[GLX] XGetWindowAttributes() failed");
940 ScopedXFree
<GLXFBConfig
>& cfgs
= *out_scopedConfigArr
;
942 const int webrenderAttribs
[] = {LOCAL_GLX_ALPHA_SIZE
,
943 windowAttrs
.depth
== 32 ? 8 : 0,
944 LOCAL_GLX_DOUBLEBUFFER
, X11True
, 0};
947 cfgs
= sGLXLibrary
.fChooseFBConfig(display
, screen
, webrenderAttribs
,
950 cfgs
= sGLXLibrary
.fGetFBConfigs(display
, screen
, &numConfigs
);
954 NS_WARNING("[GLX] glXGetFBConfigs() failed");
957 NS_ASSERTION(numConfigs
> 0, "No FBConfigs found!");
959 const VisualID windowVisualID
= XVisualIDFromVisual(windowAttrs
.visual
);
961 printf("[GLX] window %lx has VisualID 0x%lx\n", window
, windowVisualID
);
964 for (int i
= 0; i
< numConfigs
; i
++) {
966 sGLXLibrary
.fGetFBConfigAttrib(display
, cfgs
[i
], LOCAL_GLX_VISUAL_ID
,
969 // WebRender compatible GLX visual is configured
970 // at nsWindow::Create() by GLContextGLX::FindVisual(),
971 // just reuse it here.
972 if (windowVisualID
== static_cast<VisualID
>(visid
)) {
973 *out_config
= cfgs
[i
];
980 // We don't have a frame buffer visual which matches the GLX visual
981 // from GLContextGLX::FindVisual(). Let's try to find a near one and hope
982 // we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there.
983 for (int i
= 0; i
< numConfigs
; i
++) {
985 sGLXLibrary
.fGetFBConfigAttrib(display
, cfgs
[i
], LOCAL_GLX_VISUAL_ID
,
990 FindVisualAndDepth(display
, visid
, &visual
, &depth
);
991 if (depth
== windowAttrs
.depth
&&
992 AreCompatibleVisuals(windowAttrs
.visual
, visual
)) {
993 *out_config
= cfgs
[i
];
1000 NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
1004 static already_AddRefed
<GLContextGLX
> CreateOffscreenPixmapContext(
1005 const GLContextCreateDesc
& desc
, const IntSize
& size
,
1006 nsACString
* const out_failureId
) {
1007 GLXLibrary
* glx
= &sGLXLibrary
;
1008 if (!glx
->EnsureInitialized()) return nullptr;
1010 Display
* display
= DefaultXDisplay();
1011 int screen
= DefaultScreen(display
);
1013 ScopedXFree
<GLXFBConfig
> scopedConfigArr
;
1016 if (!ChooseConfig(glx
, display
, screen
, &scopedConfigArr
, &config
, &visid
)) {
1017 NS_WARNING("Failed to find a compatible config.");
1023 FindVisualAndDepth(display
, visid
, &visual
, &depth
);
1025 OffMainThreadScopedXErrorHandler xErrorHandler
;
1028 gfx::IntSize
dummySize(16, 16);
1029 RefPtr
<gfxXlibSurface
> surface
= gfxXlibSurface::Create(
1030 DefaultScreenOfDisplay(display
), visual
, dummySize
);
1031 if (surface
->CairoStatus() != 0) {
1032 mozilla::Unused
<< xErrorHandler
.SyncAndGetError(display
);
1036 // Handle slightly different signature between glXCreatePixmap and
1037 // its pre-GLX-1.3 extension equivalent (though given the ABI, we
1038 // might not need to).
1039 const auto drawable
= surface
->XDrawable();
1040 const auto pixmap
= glx
->fCreatePixmap(display
, config
, drawable
, nullptr);
1045 bool serverError
= xErrorHandler
.SyncAndGetError(display
);
1046 if (error
|| serverError
) return nullptr;
1048 auto fullDesc
= GLContextDesc
{desc
};
1049 fullDesc
.isOffscreen
= true;
1050 return GLContextGLX::CreateGLContext(fullDesc
, display
, pixmap
, config
, true,
1055 already_AddRefed
<GLContext
> GLContextProviderGLX::CreateHeadless(
1056 const GLContextCreateDesc
& desc
, nsACString
* const out_failureId
) {
1057 IntSize dummySize
= IntSize(16, 16);
1058 return CreateOffscreenPixmapContext(desc
, dummySize
, out_failureId
);
1062 GLContext
* GLContextProviderGLX::GetGlobalContext() {
1063 // Context sharing not supported.
1068 void GLContextProviderGLX::Shutdown() {}
1070 } // namespace mozilla::gl