Bug 1508381 - remove now-unnecessary TASKCLUSTER_* variables r=tomprince
[gecko.git] / gfx / gl / GLContextProviderGLX.cpp
blob682e8a893d96cac498321ac5afb79cee90e89e72
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/. */
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
13 #include <X11/Xlib.h>
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/widget/CompositorWidget.h"
23 #include "mozilla/widget/GtkCompositorWidget.h"
24 #include "mozilla/Unused.h"
26 #include "prenv.h"
27 #include "GLContextProvider.h"
28 #include "GLLibraryLoader.h"
29 #include "nsDebug.h"
30 #include "nsIWidget.h"
31 #include "GLXLibrary.h"
32 #include "gfxXlibSurface.h"
33 #include "gfxContext.h"
34 #include "gfxEnv.h"
35 #include "gfxPlatform.h"
36 #include "GLContextGLX.h"
37 #include "gfxUtils.h"
38 #include "gfx2DGlue.h"
39 #include "GLScreenBuffer.h"
40 #include "gfxPrefs.h"
42 #include "gfxCrashReporterUtils.h"
44 #ifdef MOZ_WIDGET_GTK
45 # include "gfxPlatformGtk.h"
46 #endif
48 namespace mozilla {
49 namespace 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() {
63 if (mInitialized) {
64 return true;
67 // Don't repeatedly try to initialize.
68 if (mTriedInitializing) {
69 return false;
71 mTriedInitializing = true;
73 // Force enabling s3 texture compression. (Bug 774134)
74 PR_SetEnv("force_s3tc_enable=true");
76 if (!mOGLLibrary) {
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 #ifdef __OpenBSD__
84 libGLfilename = "libGL.so";
85 #endif
87 const bool forceFeatureReport = false;
88 ScopedGfxFeatureReporter reporter(libGLfilename, forceFeatureReport);
89 mOGLLibrary = PR_LoadLibrary(libGLfilename);
90 if (!mOGLLibrary) {
91 NS_WARNING("Couldn't load OpenGL shared library.");
92 return false;
94 reporter.SetSuccessful();
97 if (gfxEnv::GlxDebug()) {
98 mDebug = true;
101 #define SYMBOL(X) \
103 (PRFuncPtr*)&mSymbols.f##X, { \
104 { "glX" #X } \
107 #define END_OF_SYMBOLS \
109 nullptr, {} \
112 const SymLoadStruct symbols[] = {
113 /* functions that were in GLX 1.0 */
114 SYMBOL(DestroyContext),
115 SYMBOL(MakeCurrent),
116 SYMBOL(SwapBuffers),
117 SYMBOL(QueryVersion),
118 SYMBOL(GetConfig),
119 SYMBOL(GetCurrentContext),
120 SYMBOL(WaitGL),
121 SYMBOL(WaitX),
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"}}},
140 END_OF_SYMBOLS};
143 const SymbolLoader libLoader(*mOGLLibrary);
144 if (!libLoader.LoadSymbols(symbols)) {
145 NS_WARNING("Couldn't load required GLX symbols.");
146 return false;
149 const SymbolLoader pfnLoader(mSymbols.fGetProcAddress);
151 Display* display = DefaultXDisplay();
152 int screen = DefaultScreen(display);
155 int major, minor;
156 if (!fQueryVersion(display, &major, &minor) || major != 1 || minor < 3) {
157 NS_ERROR("GLX version older than 1.3. (released in 1998)");
158 return false;
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),
172 END_OF_SYMBOLS};
174 const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) {
175 if (pfnLoader.LoadSymbols(symbols)) return true;
177 ClearSymbols(symbols);
178 return false;
181 const char* clientVendor = fGetClientString(display, LOCAL_GLX_VENDOR);
182 const char* serverVendor =
183 fQueryServerString(display, screen, LOCAL_GLX_VENDOR);
184 const char* extensionsStr = fQueryExtensionsString(display, screen);
186 if (HasExtension(extensionsStr, "GLX_EXT_texture_from_pixmap") &&
187 fnLoadSymbols(symbols_texturefrompixmap)) {
188 mUseTextureFromPixmap = gfxPrefs::UseGLXTextureFromPixmap();
189 } else {
190 mUseTextureFromPixmap = false;
191 NS_WARNING("Texture from pixmap disabled");
194 if (HasExtension(extensionsStr, "GLX_ARB_create_context") &&
195 HasExtension(extensionsStr, "GLX_ARB_create_context_profile") &&
196 fnLoadSymbols(symbols_createcontext)) {
197 mHasCreateContextAttribs = true;
200 if (HasExtension(extensionsStr, "GLX_ARB_create_context_robustness")) {
201 mHasRobustness = true;
204 if (HasExtension(extensionsStr, "GLX_NV_robustness_video_memory_purge")) {
205 mHasVideoMemoryPurge = true;
208 if (HasExtension(extensionsStr, "GLX_SGI_video_sync") &&
209 fnLoadSymbols(symbols_videosync)) {
210 mHasVideoSync = true;
213 if (!HasExtension(extensionsStr, "GLX_EXT_swap_control") ||
214 !fnLoadSymbols(symbols_swapcontrol)) {
215 NS_WARNING(
216 "GLX_swap_control unsupported, ASAP mode may still block on buffer "
217 "swaps.");
220 mIsATI = serverVendor && DoesStringMatch(serverVendor, "ATI");
221 mIsNVIDIA =
222 serverVendor && DoesStringMatch(serverVendor, "NVIDIA Corporation");
223 mClientIsMesa = clientVendor && DoesStringMatch(clientVendor, "Mesa");
225 mInitialized = true;
227 return true;
230 bool GLXLibrary::SupportsTextureFromPixmap(gfxASurface* aSurface) {
231 if (!EnsureInitialized()) {
232 return false;
235 if (aSurface->GetType() != gfxSurfaceType::Xlib || !mUseTextureFromPixmap) {
236 return false;
239 return true;
242 bool GLXLibrary::SupportsVideoSync() {
243 if (!EnsureInitialized()) {
244 return false;
247 return mHasVideoSync;
250 GLXPixmap GLXLibrary::CreatePixmap(gfxASurface* aSurface) {
251 if (!SupportsTextureFromPixmap(aSurface)) {
252 return X11None;
255 gfxXlibSurface* xs = static_cast<gfxXlibSurface*>(aSurface);
256 const XRenderPictFormat* format = xs->XRenderFormat();
257 if (!format || format->type != PictTypeDirect) {
258 return X11None;
260 const XRenderDirectFormat& direct = format->direct;
261 int alphaSize = FloorLog2(direct.alphaMask + 1);
262 NS_ASSERTION((1 << alphaSize) - 1 == direct.alphaMask,
263 "Unexpected render format with non-adjacent alpha bits");
265 int attribs[] = {LOCAL_GLX_DOUBLEBUFFER,
266 False,
267 LOCAL_GLX_DRAWABLE_TYPE,
268 LOCAL_GLX_PIXMAP_BIT,
269 LOCAL_GLX_ALPHA_SIZE,
270 alphaSize,
271 (alphaSize ? LOCAL_GLX_BIND_TO_TEXTURE_RGBA_EXT
272 : LOCAL_GLX_BIND_TO_TEXTURE_RGB_EXT),
273 True,
274 LOCAL_GLX_RENDER_TYPE,
275 LOCAL_GLX_RGBA_BIT,
276 X11None};
278 int numConfigs = 0;
279 Display* display = xs->XDisplay();
280 int xscreen = DefaultScreen(display);
282 ScopedXFree<GLXFBConfig> cfgs(
283 fChooseFBConfig(display, xscreen, attribs, &numConfigs));
285 // Find an fbconfig that matches the pixel format used on the Pixmap.
286 int matchIndex = -1;
287 unsigned long redMask = static_cast<unsigned long>(direct.redMask)
288 << direct.red;
289 unsigned long greenMask = static_cast<unsigned long>(direct.greenMask)
290 << direct.green;
291 unsigned long blueMask = static_cast<unsigned long>(direct.blueMask)
292 << direct.blue;
293 // This is true if the Pixmap has bits for alpha or unused bits.
294 bool haveNonColorBits =
295 ~(redMask | greenMask | blueMask) != -1UL << format->depth;
297 for (int i = 0; i < numConfigs; i++) {
298 int id = X11None;
299 sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &id);
300 Visual* visual;
301 int depth;
302 FindVisualAndDepth(display, id, &visual, &depth);
303 if (!visual || visual->c_class != TrueColor ||
304 visual->red_mask != redMask || visual->green_mask != greenMask ||
305 visual->blue_mask != blueMask) {
306 continue;
309 // Historically Xlib Visuals did not try to represent an alpha channel
310 // and there was no means to use an alpha channel on a Pixmap. The
311 // Xlib Visual from the fbconfig was not intended to have any
312 // information about alpha bits.
314 // Since then, RENDER has added formats for 32 bit depth Pixmaps.
315 // Some of these formats have bits for alpha and some have unused
316 // bits.
318 // Then the Composite extension added a 32 bit depth Visual intended
319 // for Windows with an alpha channel, so bits not in the visual color
320 // masks were expected to be treated as alpha bits.
322 // Usually GLX counts only color bits in the Visual depth, but the
323 // depth of Composite's ARGB Visual includes alpha bits. However,
324 // bits not in the color masks are not necessarily alpha bits because
325 // sometimes (NVIDIA) 32 bit Visuals are added for fbconfigs with 32
326 // bit BUFFER_SIZE but zero alpha bits and 24 color bits (NVIDIA
327 // again).
329 // This checks that the depth matches in one of the two ways.
330 // NVIDIA now forces format->depth == depth so only the first way
331 // is checked for NVIDIA
332 if (depth != format->depth &&
333 (mIsNVIDIA || depth != format->depth - alphaSize)) {
334 continue;
337 // If all bits of the Pixmap are color bits and the Pixmap depth
338 // matches the depth of the fbconfig visual, then we can assume that
339 // the driver will do whatever is necessary to ensure that any
340 // GLXPixmap alpha bits are treated as set. We can skip the
341 // ALPHA_SIZE check in this situation. We need to skip this check for
342 // situations (ATI) where there are no fbconfigs without alpha bits.
344 // glXChooseFBConfig should prefer configs with smaller
345 // LOCAL_GLX_BUFFER_SIZE, so we should still get zero alpha bits if
346 // available, except perhaps with NVIDIA drivers where buffer size is
347 // not the specified sum of the component sizes.
348 if (haveNonColorBits) {
349 // There are bits in the Pixmap format that haven't been matched
350 // against the fbconfig visual. These bits could either represent
351 // alpha or be unused, so just check that the number of alpha bits
352 // matches.
353 int size = 0;
354 sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_ALPHA_SIZE,
355 &size);
356 if (size != alphaSize) {
357 continue;
361 matchIndex = i;
362 break;
364 if (matchIndex == -1) {
365 // GLX can't handle A8 surfaces, so this is not really unexpected. The
366 // caller should deal with this situation.
367 NS_WARNING_ASSERTION(
368 format->depth == 8,
369 "[GLX] Couldn't find a FBConfig matching Pixmap format");
370 return X11None;
373 int pixmapAttribs[] = {LOCAL_GLX_TEXTURE_TARGET_EXT, LOCAL_GLX_TEXTURE_2D_EXT,
374 LOCAL_GLX_TEXTURE_FORMAT_EXT,
375 (alphaSize ? LOCAL_GLX_TEXTURE_FORMAT_RGBA_EXT
376 : LOCAL_GLX_TEXTURE_FORMAT_RGB_EXT),
377 X11None};
379 GLXPixmap glxpixmap =
380 fCreatePixmap(display, cfgs[matchIndex], xs->XDrawable(), pixmapAttribs);
382 return glxpixmap;
385 void GLXLibrary::DestroyPixmap(Display* aDisplay, GLXPixmap aPixmap) {
386 if (!mUseTextureFromPixmap) {
387 return;
390 fDestroyPixmap(aDisplay, aPixmap);
393 void GLXLibrary::BindTexImage(Display* aDisplay, GLXPixmap aPixmap) {
394 if (!mUseTextureFromPixmap) {
395 return;
398 // Make sure all X drawing to the surface has finished before binding to a
399 // texture.
400 if (mClientIsMesa) {
401 // Using XSync instead of Mesa's glXWaitX, because its glxWaitX is a
402 // noop when direct rendering unless the current drawable is a
403 // single-buffer window.
404 FinishX(aDisplay);
405 } else {
406 fWaitX();
408 fBindTexImage(aDisplay, aPixmap, LOCAL_GLX_FRONT_LEFT_EXT, nullptr);
411 void GLXLibrary::ReleaseTexImage(Display* aDisplay, GLXPixmap aPixmap) {
412 if (!mUseTextureFromPixmap) {
413 return;
416 fReleaseTexImage(aDisplay, aPixmap, LOCAL_GLX_FRONT_LEFT_EXT);
419 void GLXLibrary::UpdateTexImage(Display* aDisplay, GLXPixmap aPixmap) {
420 // NVIDIA drivers don't require a rebind of the pixmap in order
421 // to display an updated image, and it's faster not to do it.
422 if (mIsNVIDIA) {
423 fWaitX();
424 return;
427 ReleaseTexImage(aDisplay, aPixmap);
428 BindTexImage(aDisplay, aPixmap);
431 static int (*sOldErrorHandler)(Display*, XErrorEvent*);
432 ScopedXErrorHandler::ErrorEvent sErrorEvent;
433 static int GLXErrorHandler(Display* display, XErrorEvent* ev) {
434 if (!sErrorEvent.mError.error_code) {
435 sErrorEvent.mError = *ev;
437 return 0;
440 void GLXLibrary::BeforeGLXCall() const {
441 if (mDebug) {
442 sOldErrorHandler = XSetErrorHandler(GLXErrorHandler);
446 void GLXLibrary::AfterGLXCall() const {
447 if (mDebug) {
448 FinishX(DefaultXDisplay());
449 if (sErrorEvent.mError.error_code) {
450 char buffer[2048];
451 XGetErrorText(DefaultXDisplay(), sErrorEvent.mError.error_code, buffer,
452 sizeof(buffer));
453 printf_stderr("X ERROR: %s (%i) - Request: %i.%i, Serial: %lu", buffer,
454 sErrorEvent.mError.error_code,
455 sErrorEvent.mError.request_code,
456 sErrorEvent.mError.minor_code, sErrorEvent.mError.serial);
457 MOZ_ASSERT_UNREACHABLE("AfterGLXCall sErrorEvent");
459 XSetErrorHandler(sOldErrorHandler);
463 already_AddRefed<GLContextGLX> GLContextGLX::CreateGLContext(
464 CreateContextFlags flags, const SurfaceCaps& caps, bool isOffscreen,
465 Display* display, GLXDrawable drawable, GLXFBConfig cfg,
466 bool deleteDrawable, gfxXlibSurface* pixmap) {
467 GLXLibrary& glx = sGLXLibrary;
469 int db = 0;
470 int err = glx.fGetFBConfigAttrib(display, cfg, LOCAL_GLX_DOUBLEBUFFER, &db);
471 if (LOCAL_GLX_BAD_ATTRIBUTE != err) {
472 if (ShouldSpew()) {
473 printf("[GLX] FBConfig is %sdouble-buffered\n", db ? "" : "not ");
477 GLXContext context;
478 RefPtr<GLContextGLX> glContext;
479 bool error;
481 OffMainThreadScopedXErrorHandler xErrorHandler;
483 do {
484 error = false;
486 if (glx.HasCreateContextAttribs()) {
487 AutoTArray<int, 13> attrib_list;
488 if (glx.HasRobustness()) {
489 const int robust_attribs[] = {
490 LOCAL_GLX_CONTEXT_FLAGS_ARB,
491 LOCAL_GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,
492 LOCAL_GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
493 LOCAL_GLX_LOSE_CONTEXT_ON_RESET_ARB,
495 attrib_list.AppendElements(robust_attribs,
496 MOZ_ARRAY_LENGTH(robust_attribs));
498 if (glx.HasVideoMemoryPurge()) {
499 const int memory_purge_attribs[] = {
500 LOCAL_GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV,
501 LOCAL_GL_TRUE,
503 attrib_list.AppendElements(memory_purge_attribs,
504 MOZ_ARRAY_LENGTH(memory_purge_attribs));
506 if (!(flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE)) {
507 int core_attribs[] = {
508 LOCAL_GLX_CONTEXT_MAJOR_VERSION_ARB,
510 LOCAL_GLX_CONTEXT_MINOR_VERSION_ARB,
512 LOCAL_GLX_CONTEXT_PROFILE_MASK_ARB,
513 LOCAL_GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
515 attrib_list.AppendElements(core_attribs,
516 MOZ_ARRAY_LENGTH(core_attribs));
518 attrib_list.AppendElement(0);
520 context = glx.fCreateContextAttribs(display, cfg, nullptr, True,
521 attrib_list.Elements());
522 } else {
523 context = glx.fCreateNewContext(display, cfg, LOCAL_GLX_RGBA_TYPE,
524 nullptr, True);
527 if (context) {
528 glContext = new GLContextGLX(flags, caps, isOffscreen, display, drawable,
529 context, deleteDrawable, db, pixmap);
530 if (!glContext->Init()) error = true;
531 } else {
532 error = true;
535 error |= xErrorHandler.SyncAndGetError(display);
537 if (error) {
538 NS_WARNING("Failed to create GLXContext!");
539 glContext = nullptr; // note: this must be done while the graceful X
540 // error handler is set, because glxMakeCurrent can
541 // give a GLXBadDrawable error
544 return glContext.forget();
545 } while (true);
548 GLContextGLX::~GLContextGLX() {
549 MarkDestroyed();
551 // Wrapped context should not destroy glxContext/Surface
552 if (!mOwnsContext) {
553 return;
556 // see bug 659842 comment 76
557 #ifdef DEBUG
558 bool success =
559 #endif
560 mGLX->fMakeCurrent(mDisplay, X11None, nullptr);
561 MOZ_ASSERT(success,
562 "glXMakeCurrent failed to release GL context before we call "
563 "glXDestroyContext!");
565 mGLX->fDestroyContext(mDisplay, mContext);
567 if (mDeleteDrawable) {
568 mGLX->fDestroyPixmap(mDisplay, mDrawable);
572 bool GLContextGLX::Init() {
573 if (!GLContext::Init()) {
574 return false;
577 // EXT_framebuffer_object is not supported on Core contexts
578 // so we'll also check for ARB_framebuffer_object
579 if (!IsExtensionSupported(EXT_framebuffer_object) &&
580 !IsSupported(GLFeature::framebuffer_object))
581 return false;
583 return true;
586 bool GLContextGLX::MakeCurrentImpl() const {
587 if (mGLX->IsMesa()) {
588 // Read into the event queue to ensure that Mesa receives a
589 // DRI2InvalidateBuffers event before drawing. See bug 1280653.
590 Unused << XPending(mDisplay);
593 const bool succeeded = mGLX->fMakeCurrent(mDisplay, mDrawable, mContext);
594 NS_ASSERTION(succeeded, "Failed to make GL context current!");
596 if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
597 // Many GLX implementations default to blocking until the next
598 // VBlank when calling glXSwapBuffers. We want to run unthrottled
599 // in ASAP mode. See bug 1280744.
600 const bool isASAP = (gfxPrefs::LayoutFrameRate() == 0);
601 mGLX->fSwapInterval(mDisplay, mDrawable, isASAP ? 0 : 1);
603 return succeeded;
606 bool GLContextGLX::IsCurrentImpl() const {
607 return mGLX->fGetCurrentContext() == mContext;
610 Maybe<SymbolLoader> GLContextGLX::GetSymbolLoader() const {
611 const auto pfn = sGLXLibrary.GetGetProcAddress();
612 return Some(SymbolLoader(pfn));
615 bool GLContextGLX::IsDoubleBuffered() const { return mDoubleBuffered; }
617 bool GLContextGLX::SwapBuffers() {
618 if (!mDoubleBuffered) return false;
619 mGLX->fSwapBuffers(mDisplay, mDrawable);
620 return true;
623 void GLContextGLX::GetWSIInfo(nsCString* const out) const {
624 Display* display = DefaultXDisplay();
625 int screen = DefaultScreen(display);
627 int majorVersion, minorVersion;
628 sGLXLibrary.fQueryVersion(display, &majorVersion, &minorVersion);
630 out->Append(nsPrintfCString("GLX %u.%u", majorVersion, minorVersion));
632 out->AppendLiteral("\nGLX_VENDOR(client): ");
633 out->Append(sGLXLibrary.fGetClientString(display, LOCAL_GLX_VENDOR));
635 out->AppendLiteral("\nGLX_VENDOR(server): ");
636 out->Append(
637 sGLXLibrary.fQueryServerString(display, screen, LOCAL_GLX_VENDOR));
639 out->AppendLiteral("\nExtensions: ");
640 out->Append(sGLXLibrary.fQueryExtensionsString(display, screen));
643 bool GLContextGLX::OverrideDrawable(GLXDrawable drawable) {
644 if (Screen()) Screen()->AssureBlitted();
645 Bool result = mGLX->fMakeCurrent(mDisplay, drawable, mContext);
646 return result;
649 bool GLContextGLX::RestoreDrawable() {
650 return mGLX->fMakeCurrent(mDisplay, mDrawable, mContext);
653 GLContextGLX::GLContextGLX(CreateContextFlags flags, const SurfaceCaps& caps,
654 bool isOffscreen, Display* aDisplay,
655 GLXDrawable aDrawable, GLXContext aContext,
656 bool aDeleteDrawable, bool aDoubleBuffered,
657 gfxXlibSurface* aPixmap)
658 : GLContext(flags, caps, nullptr, isOffscreen),
659 mContext(aContext),
660 mDisplay(aDisplay),
661 mDrawable(aDrawable),
662 mDeleteDrawable(aDeleteDrawable),
663 mDoubleBuffered(aDoubleBuffered),
664 mGLX(&sGLXLibrary),
665 mPixmap(aPixmap) {}
667 static bool AreCompatibleVisuals(Visual* one, Visual* two) {
668 if (one->c_class != two->c_class) {
669 return false;
672 if (one->red_mask != two->red_mask || one->green_mask != two->green_mask ||
673 one->blue_mask != two->blue_mask) {
674 return false;
677 if (one->bits_per_rgb != two->bits_per_rgb) {
678 return false;
681 return true;
684 already_AddRefed<GLContext> GLContextProviderGLX::CreateWrappingExisting(
685 void* aContext, void* aSurface) {
686 if (!sGLXLibrary.EnsureInitialized()) {
687 return nullptr;
690 if (aContext && aSurface) {
691 SurfaceCaps caps = SurfaceCaps::Any();
692 RefPtr<GLContextGLX> glContext =
693 new GLContextGLX(CreateContextFlags::NONE, caps,
694 false, // Offscreen
695 (Display*)DefaultXDisplay(), // Display
696 (GLXDrawable)aSurface, (GLXContext)aContext,
697 false, // aDeleteDrawable,
698 true, (gfxXlibSurface*)nullptr);
700 glContext->mOwnsContext = false;
701 return glContext.forget();
704 return nullptr;
707 already_AddRefed<GLContext> CreateForWidget(Display* aXDisplay, Window aXWindow,
708 bool aWebRender,
709 bool aForceAccelerated) {
710 if (!sGLXLibrary.EnsureInitialized()) {
711 return nullptr;
714 // Currently, we take whatever Visual the window already has, and
715 // try to create an fbconfig for that visual. This isn't
716 // necessarily what we want in the long run; an fbconfig may not
717 // be available for the existing visual, or if it is, the GL
718 // performance might be suboptimal. But using the existing visual
719 // is a relatively safe intermediate step.
721 if (!aXDisplay) {
722 NS_ERROR("X Display required for GLX Context provider");
723 return nullptr;
726 int xscreen = DefaultScreen(aXDisplay);
728 ScopedXFree<GLXFBConfig> cfgs;
729 GLXFBConfig config;
730 int visid;
731 if (!GLContextGLX::FindFBConfigForWindow(aXDisplay, xscreen, aXWindow, &cfgs,
732 &config, &visid, aWebRender)) {
733 return nullptr;
736 CreateContextFlags flags;
737 if (aWebRender) {
738 flags = CreateContextFlags::NONE; // WR needs GL3.2+
739 } else {
740 flags = CreateContextFlags::REQUIRE_COMPAT_PROFILE;
742 return GLContextGLX::CreateGLContext(flags, SurfaceCaps::Any(), false,
743 aXDisplay, aXWindow, config, false,
744 nullptr);
747 already_AddRefed<GLContext> GLContextProviderGLX::CreateForCompositorWidget(
748 CompositorWidget* aCompositorWidget, bool aWebRender,
749 bool aForceAccelerated) {
750 if (!aCompositorWidget) {
751 MOZ_ASSERT(false);
752 return nullptr;
754 GtkCompositorWidget* compWidget = aCompositorWidget->AsX11();
755 MOZ_ASSERT(compWidget);
757 return CreateForWidget(compWidget->XDisplay(), compWidget->XWindow(),
758 aWebRender, aForceAccelerated);
761 already_AddRefed<GLContext> GLContextProviderGLX::CreateForWindow(
762 nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated) {
763 Display* display =
764 (Display*)aWidget->GetNativeData(NS_NATIVE_COMPOSITOR_DISPLAY);
765 Window window = GET_NATIVE_WINDOW(aWidget);
767 return CreateForWidget(display, window, aWebRender, aForceAccelerated);
770 static bool ChooseConfig(GLXLibrary* glx, Display* display, int screen,
771 const SurfaceCaps& minCaps,
772 ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
773 GLXFBConfig* const out_config, int* const out_visid) {
774 ScopedXFree<GLXFBConfig>& scopedConfigArr = *out_scopedConfigArr;
776 if (minCaps.antialias) return false;
778 int attribs[] = {LOCAL_GLX_DRAWABLE_TYPE,
779 LOCAL_GLX_PIXMAP_BIT,
780 LOCAL_GLX_X_RENDERABLE,
781 True,
782 LOCAL_GLX_RED_SIZE,
784 LOCAL_GLX_GREEN_SIZE,
786 LOCAL_GLX_BLUE_SIZE,
788 LOCAL_GLX_ALPHA_SIZE,
789 minCaps.alpha ? 8 : 0,
790 LOCAL_GLX_DEPTH_SIZE,
791 minCaps.depth ? 16 : 0,
792 LOCAL_GLX_STENCIL_SIZE,
793 minCaps.stencil ? 8 : 0,
796 int numConfigs = 0;
797 scopedConfigArr = glx->fChooseFBConfig(display, screen, attribs, &numConfigs);
798 if (!scopedConfigArr || !numConfigs) return false;
800 // Issues with glxChooseFBConfig selection and sorting:
801 // * ALPHA_SIZE is sorted as 'largest total RGBA bits first'. If we don't
802 // request
803 // alpha bits, we'll probably get RGBA anyways, since 32 is more than 24.
804 // * DEPTH_SIZE is sorted largest first, including for `0` inputs.
805 // * STENCIL_SIZE is smallest first, but it might return `8` even though we
806 // ask for
807 // `0`.
809 // For now, we don't care about these. We *will* care when we do XPixmap
810 // sharing.
812 for (int i = 0; i < numConfigs; ++i) {
813 GLXFBConfig curConfig = scopedConfigArr[i];
815 int visid;
816 if (glx->fGetFBConfigAttrib(display, curConfig, LOCAL_GLX_VISUAL_ID,
817 &visid) != Success) {
818 continue;
821 if (!visid) continue;
823 *out_config = curConfig;
824 *out_visid = visid;
825 return true;
828 return false;
831 bool GLContextGLX::FindVisual(Display* display, int screen, bool useWebRender,
832 bool useAlpha, int* const out_visualId) {
833 if (!sGLXLibrary.EnsureInitialized()) {
834 return false;
837 XVisualInfo visualTemplate;
838 visualTemplate.screen = screen;
840 // Get all visuals of screen
842 int visualsLen = 0;
843 XVisualInfo* xVisuals =
844 XGetVisualInfo(display, VisualScreenMask, &visualTemplate, &visualsLen);
845 if (!xVisuals) {
846 return false;
848 const Range<XVisualInfo> visualInfos(xVisuals, visualsLen);
849 auto cleanupVisuals = MakeScopeExit([&] { XFree(xVisuals); });
851 // Get default visual info
853 Visual* defaultVisual = DefaultVisual(display, screen);
854 const auto defaultVisualInfo = [&]() -> const XVisualInfo* {
855 for (const auto& cur : visualInfos) {
856 if (cur.visual == defaultVisual) {
857 return &cur;
860 return nullptr;
861 }();
862 if (!defaultVisualInfo) {
863 MOZ_ASSERT(false);
864 return false;
867 const int bpp = useAlpha ? 32 : 24;
868 const int alphaSize = useAlpha ? 8 : 0;
869 const int depthSize = useWebRender ? 24 : 0;
871 for (auto& cur : visualInfos) {
872 const auto fnConfigMatches = [&](const int pname, const int expected) {
873 int actual;
874 if (sGLXLibrary.fGetConfig(display, &cur, pname, &actual)) {
875 return false;
877 return actual == expected;
880 // Check if visual is compatible.
881 if (cur.depth != bpp || cur.c_class != defaultVisualInfo->c_class) {
882 continue;
885 // Check if visual is compatible to GL requests.
886 if (fnConfigMatches(LOCAL_GLX_USE_GL, 1) &&
887 fnConfigMatches(LOCAL_GLX_DOUBLEBUFFER, 1) &&
888 fnConfigMatches(LOCAL_GLX_RED_SIZE, 8) &&
889 fnConfigMatches(LOCAL_GLX_GREEN_SIZE, 8) &&
890 fnConfigMatches(LOCAL_GLX_BLUE_SIZE, 8) &&
891 fnConfigMatches(LOCAL_GLX_ALPHA_SIZE, alphaSize) &&
892 fnConfigMatches(LOCAL_GLX_DEPTH_SIZE, depthSize)) {
893 *out_visualId = cur.visualid;
894 return true;
898 return false;
901 bool GLContextGLX::FindFBConfigForWindow(
902 Display* display, int screen, Window window,
903 ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
904 GLXFBConfig* const out_config, int* const out_visid, bool aWebRender) {
905 // XXX the visual ID is almost certainly the LOCAL_GLX_FBCONFIG_ID, so
906 // we could probably do this first and replace the glXGetFBConfigs
907 // with glXChooseConfigs. Docs are sparklingly clear as always.
908 XWindowAttributes windowAttrs;
909 if (!XGetWindowAttributes(display, window, &windowAttrs)) {
910 NS_WARNING("[GLX] XGetWindowAttributes() failed");
911 return false;
914 ScopedXFree<GLXFBConfig>& cfgs = *out_scopedConfigArr;
915 int numConfigs;
916 const int webrenderAttribs[] = {LOCAL_GLX_ALPHA_SIZE,
917 windowAttrs.depth == 32 ? 8 : 0,
918 LOCAL_GLX_DEPTH_SIZE,
920 LOCAL_GLX_DOUBLEBUFFER,
921 True,
924 if (aWebRender) {
925 cfgs = sGLXLibrary.fChooseFBConfig(display, screen, webrenderAttribs,
926 &numConfigs);
927 } else {
928 cfgs = sGLXLibrary.fGetFBConfigs(display, screen, &numConfigs);
931 if (!cfgs) {
932 NS_WARNING("[GLX] glXGetFBConfigs() failed");
933 return false;
935 NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
937 const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
938 #ifdef DEBUG
939 printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
940 #endif
942 for (int i = 0; i < numConfigs; i++) {
943 int visid = X11None;
944 sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID,
945 &visid);
946 if (visid) {
947 // WebRender compatible GLX visual is configured
948 // at nsWindow::Create() by GLContextGLX::FindVisual(),
949 // just reuse it here.
950 if (windowVisualID == static_cast<VisualID>(visid)) {
951 *out_config = cfgs[i];
952 *out_visid = visid;
953 return true;
958 // We don't have a frame buffer visual which matches the GLX visual
959 // from GLContextGLX::FindVisual(). Let's try to find a near one and hope
960 // we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there.
961 for (int i = 0; i < numConfigs; i++) {
962 int visid = X11None;
963 sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID,
964 &visid);
965 if (visid) {
966 int depth;
967 Visual* visual;
968 FindVisualAndDepth(display, visid, &visual, &depth);
969 if (depth == windowAttrs.depth &&
970 AreCompatibleVisuals(windowAttrs.visual, visual)) {
971 *out_config = cfgs[i];
972 *out_visid = visid;
973 return true;
978 NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
979 return false;
982 static already_AddRefed<GLContextGLX> CreateOffscreenPixmapContext(
983 CreateContextFlags flags, const IntSize& size, const SurfaceCaps& minCaps,
984 nsACString* const out_failureId) {
985 GLXLibrary* glx = &sGLXLibrary;
986 if (!glx->EnsureInitialized()) return nullptr;
988 Display* display = DefaultXDisplay();
989 int screen = DefaultScreen(display);
991 ScopedXFree<GLXFBConfig> scopedConfigArr;
992 GLXFBConfig config;
993 int visid;
994 if (!ChooseConfig(glx, display, screen, minCaps, &scopedConfigArr, &config,
995 &visid)) {
996 NS_WARNING("Failed to find a compatible config.");
997 return nullptr;
1000 Visual* visual;
1001 int depth;
1002 FindVisualAndDepth(display, visid, &visual, &depth);
1004 OffMainThreadScopedXErrorHandler xErrorHandler;
1005 bool error = false;
1007 gfx::IntSize dummySize(16, 16);
1008 RefPtr<gfxXlibSurface> surface = gfxXlibSurface::Create(
1009 DefaultScreenOfDisplay(display), visual, dummySize);
1010 if (surface->CairoStatus() != 0) {
1011 mozilla::Unused << xErrorHandler.SyncAndGetError(display);
1012 return nullptr;
1015 // Handle slightly different signature between glXCreatePixmap and
1016 // its pre-GLX-1.3 extension equivalent (though given the ABI, we
1017 // might not need to).
1018 const auto drawable = surface->XDrawable();
1019 const auto pixmap = glx->fCreatePixmap(display, config, drawable, nullptr);
1020 if (pixmap == 0) {
1021 error = true;
1024 bool serverError = xErrorHandler.SyncAndGetError(display);
1025 if (error || serverError) return nullptr;
1027 return GLContextGLX::CreateGLContext(flags, minCaps, true, display, pixmap,
1028 config, true, surface);
1031 /*static*/
1032 already_AddRefed<GLContext> GLContextProviderGLX::CreateHeadless(
1033 CreateContextFlags flags, nsACString* const out_failureId) {
1034 IntSize dummySize = IntSize(16, 16);
1035 SurfaceCaps dummyCaps = SurfaceCaps::Any();
1036 return CreateOffscreenPixmapContext(flags, dummySize, dummyCaps,
1037 out_failureId);
1040 /*static*/
1041 already_AddRefed<GLContext> GLContextProviderGLX::CreateOffscreen(
1042 const IntSize& size, const SurfaceCaps& minCaps, CreateContextFlags flags,
1043 nsACString* const out_failureId) {
1044 SurfaceCaps minBackbufferCaps = minCaps;
1045 if (minCaps.antialias) {
1046 minBackbufferCaps.antialias = false;
1047 minBackbufferCaps.depth = false;
1048 minBackbufferCaps.stencil = false;
1051 RefPtr<GLContext> gl;
1052 gl = CreateOffscreenPixmapContext(flags, size, minBackbufferCaps,
1053 out_failureId);
1054 if (!gl) return nullptr;
1056 if (!gl->InitOffscreen(size, minCaps)) {
1057 *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_GLX_INIT");
1058 return nullptr;
1061 return gl.forget();
1064 /*static*/
1065 GLContext* GLContextProviderGLX::GetGlobalContext() {
1066 // Context sharing not supported.
1067 return nullptr;
1070 /*static*/
1071 void GLContextProviderGLX::Shutdown() {}
1073 } /* namespace gl */
1074 } /* namespace mozilla */