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