GLLibraryEGL.cpp (34152B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "GLLibraryEGL.h" 6 7 #include "gfxConfig.h" 8 #include "gfxCrashReporterUtils.h" 9 #include "gfxEnv.h" 10 #include "gfxUtils.h" 11 #include "mozilla/Preferences.h" 12 #include "mozilla/Assertions.h" 13 #include "mozilla/gfx/gfxVars.h" 14 #include "mozilla/gfx/Logging.h" 15 #include "mozilla/glean/DomCanvasMetrics.h" 16 #include "mozilla/Tokenizer.h" 17 #include "mozilla/ScopeExit.h" 18 #include "mozilla/StaticPrefs_gfx.h" 19 #include "mozilla/StaticPrefs_webgl.h" 20 #include "nsDirectoryServiceDefs.h" 21 #include "nsDirectoryServiceUtils.h" 22 #include "nsPrintfCString.h" 23 #ifdef XP_WIN 24 # include "mozilla/gfx/DeviceManagerDx.h" 25 # include "nsWindowsHelpers.h" 26 # include "prerror.h" 27 28 # include <d3d11.h> 29 #endif 30 #include "OGLShaderProgram.h" 31 #include "prenv.h" 32 #include "prsystem.h" 33 #include "GLContext.h" 34 #include "GLContextProvider.h" 35 #include "GLLibraryLoader.h" 36 #include "GLReadTexImageHelper.h" 37 #include "ScopedGLHelpers.h" 38 #ifdef MOZ_WIDGET_GTK 39 # include "mozilla/WidgetUtilsGtk.h" 40 # include "mozilla/widget/DMABufDevice.h" 41 # ifdef MOZ_WAYLAND 42 # include "mozilla/widget/nsWaylandDisplay.h" 43 # endif // MOZ_WIDGET_GTK 44 # include <gdk/gdk.h> 45 #endif // MOZ_WAYLAND 46 47 #include <mutex> // for call_once 48 49 namespace mozilla { 50 namespace gl { 51 52 StaticMutex GLLibraryEGL::sMutex; 53 StaticRefPtr<GLLibraryEGL> GLLibraryEGL::sInstance; 54 55 // should match the order of EGLExtensions, and be null-terminated. 56 static const char* sEGLLibraryExtensionNames[] = { 57 "EGL_ANDROID_get_native_client_buffer", 58 "EGL_ANGLE_device_creation", 59 "EGL_ANGLE_device_creation_d3d11", 60 "EGL_ANGLE_platform_angle", 61 "EGL_ANGLE_platform_angle_d3d", 62 "EGL_EXT_device_enumeration", 63 "EGL_EXT_device_query", 64 "EGL_EXT_platform_device", 65 "EGL_MESA_platform_surfaceless"}; 66 67 // should match the order of EGLExtensions, and be null-terminated. 68 static const char* sEGLExtensionNames[] = { 69 "EGL_KHR_image_base", 70 "EGL_KHR_image_pixmap", 71 "EGL_KHR_gl_texture_2D_image", 72 "EGL_ANGLE_surface_d3d_texture_2d_share_handle", 73 "EGL_EXT_create_context_robustness", 74 "EGL_KHR_image", 75 "EGL_KHR_fence_sync", 76 "EGL_KHR_wait_sync", 77 "EGL_ANDROID_native_fence_sync", 78 "EGL_ANDROID_image_crop", 79 "EGL_ANGLE_d3d_share_handle_client_buffer", 80 "EGL_KHR_create_context", 81 "EGL_KHR_stream", 82 "EGL_KHR_stream_consumer_gltexture", 83 "EGL_NV_stream_consumer_gltexture_yuv", 84 "EGL_ANGLE_stream_producer_d3d_texture", 85 "EGL_KHR_surfaceless_context", 86 "EGL_KHR_create_context_no_error", 87 "EGL_MOZ_create_context_provoking_vertex_dont_care", 88 "EGL_EXT_swap_buffers_with_damage", 89 "EGL_KHR_swap_buffers_with_damage", 90 "EGL_EXT_buffer_age", 91 "EGL_KHR_partial_update", 92 "EGL_NV_robustness_video_memory_purge", 93 "EGL_EXT_image_dma_buf_import", 94 "EGL_EXT_image_dma_buf_import_modifiers", 95 "EGL_MESA_image_dma_buf_export", 96 "EGL_KHR_no_config_context", 97 }; 98 99 PRLibrary* LoadApitraceLibrary() { 100 const char* path = nullptr; 101 102 #ifdef ANDROID 103 // We only need to explicitly dlopen egltrace 104 // on android as we can use LD_PRELOAD or other tricks 105 // on other platforms. We look for it in /data/local 106 // as that's writeable by all users. 107 path = "/data/local/tmp/egltrace.so"; 108 #endif 109 if (!path) return nullptr; 110 111 // Initialization of gfx prefs here is only needed during the unit tests... 112 if (!StaticPrefs::gfx_apitrace_enabled_AtStartup()) { 113 return nullptr; 114 } 115 116 static PRLibrary* sApitraceLibrary = nullptr; 117 if (sApitraceLibrary) return sApitraceLibrary; 118 119 nsAutoCString logFile; 120 Preferences::GetCString("gfx.apitrace.logfile", logFile); 121 if (logFile.IsEmpty()) { 122 logFile = "firefox.trace"; 123 } 124 125 // The firefox process can't write to /data/local, but it can write 126 // to $GRE_HOME/ 127 nsAutoCString logPath; 128 logPath.AppendPrintf("%s/%s", getenv("GRE_HOME"), logFile.get()); 129 130 #ifndef XP_WIN // Windows is missing setenv and forbids PR_LoadLibrary. 131 // apitrace uses the TRACE_FILE environment variable to determine where 132 // to log trace output to 133 printf_stderr("Logging GL tracing output to %s", logPath.get()); 134 setenv("TRACE_FILE", logPath.get(), false); 135 136 printf_stderr("Attempting load of %s\n", path); 137 sApitraceLibrary = PR_LoadLibrary(path); 138 #endif 139 140 return sApitraceLibrary; 141 } 142 143 #ifdef XP_WIN 144 // see the comment in GLLibraryEGL::EnsureInitialized() for the rationale here. 145 static PRLibrary* LoadLibraryForEGLOnWindows(const nsAString& filename) { 146 nsAutoString path(gfx::gfxVars::GREDirectory()); 147 path.Append(PR_GetDirectorySeparator()); 148 path.Append(filename); 149 150 PRLibSpec lspec; 151 lspec.type = PR_LibSpec_PathnameU; 152 lspec.value.pathname_u = path.get(); 153 PRLibrary* lib = PR_LoadLibraryWithFlags(lspec, PR_LD_LAZY | PR_LD_LOCAL); 154 if (!lib) { 155 gfxCriticalNote << "Failed to load " << path.get() << " " << PR_GetError() 156 << " " << PR_GetOSError(); 157 } 158 return lib; 159 } 160 161 #endif // XP_WIN 162 163 static std::shared_ptr<EglDisplay> GetAndInitDisplay( 164 GLLibraryEGL& egl, void* displayType, 165 const StaticMutexAutoLock& aProofOfLock) { 166 const auto display = egl.fGetDisplay(displayType); 167 if (!display) return nullptr; 168 return EglDisplay::Create(egl, display, false, aProofOfLock); 169 } 170 171 #ifdef MOZ_WIDGET_GTK 172 static std::shared_ptr<EglDisplay> GetAndInitDeviceDisplay( 173 GLLibraryEGL& egl, const StaticMutexAutoLock& aProofOfLock) { 174 nsAutoCString drmRenderDevice(gfx::gfxVars::DrmRenderDevice()); 175 if (drmRenderDevice.IsEmpty() || 176 !egl.IsExtensionSupported(EGLLibExtension::EXT_platform_device) || 177 !egl.IsExtensionSupported(EGLLibExtension::EXT_device_enumeration)) { 178 return nullptr; 179 } 180 181 EGLint maxDevices; 182 if (!egl.fQueryDevicesEXT(0, nullptr, &maxDevices)) { 183 return nullptr; 184 } 185 186 std::vector<EGLDeviceEXT> devices(maxDevices); 187 EGLint numDevices; 188 if (!egl.fQueryDevicesEXT(devices.size(), devices.data(), &numDevices)) { 189 return nullptr; 190 } 191 devices.resize(numDevices); 192 193 EGLDisplay display = EGL_NO_DISPLAY; 194 for (const auto& device : devices) { 195 const char* renderNodeString = 196 egl.fQueryDeviceStringEXT(device, LOCAL_EGL_DRM_RENDER_NODE_FILE_EXT); 197 if (renderNodeString && 198 strcmp(renderNodeString, drmRenderDevice.get()) == 0) { 199 const EGLAttrib attrib_list[] = {LOCAL_EGL_NONE}; 200 display = egl.fGetPlatformDisplay(LOCAL_EGL_PLATFORM_DEVICE_EXT, device, 201 attrib_list); 202 break; 203 } 204 } 205 if (!display) { 206 return nullptr; 207 } 208 209 return EglDisplay::Create(egl, display, true, aProofOfLock); 210 } 211 212 static std::shared_ptr<EglDisplay> GetAndInitSoftwareDisplay( 213 GLLibraryEGL& egl, const StaticMutexAutoLock& aProofOfLock) { 214 if (!egl.IsExtensionSupported(EGLLibExtension::EXT_platform_device) || 215 !egl.IsExtensionSupported(EGLLibExtension::EXT_device_enumeration)) { 216 return nullptr; 217 } 218 219 EGLint maxDevices; 220 if (!egl.fQueryDevicesEXT(0, nullptr, &maxDevices)) { 221 return nullptr; 222 } 223 224 std::vector<EGLDeviceEXT> devices(maxDevices); 225 EGLint numDevices; 226 if (!egl.fQueryDevicesEXT(devices.size(), devices.data(), &numDevices)) { 227 return nullptr; 228 } 229 devices.resize(numDevices); 230 231 EGLDisplay display = EGL_NO_DISPLAY; 232 for (const auto& device : devices) { 233 const char* renderNodeString = 234 egl.fQueryDeviceStringEXT(device, LOCAL_EGL_DRM_RENDER_NODE_FILE_EXT); 235 // We are looking for a device with no file 236 if (!renderNodeString || *renderNodeString == 0) { 237 const EGLAttrib attrib_list[] = {LOCAL_EGL_NONE}; 238 display = egl.fGetPlatformDisplay(LOCAL_EGL_PLATFORM_DEVICE_EXT, device, 239 attrib_list); 240 break; 241 } 242 } 243 if (!display) { 244 return nullptr; 245 } 246 247 return EglDisplay::Create(egl, display, true, aProofOfLock); 248 } 249 250 static std::shared_ptr<EglDisplay> GetAndInitSurfacelessDisplay( 251 GLLibraryEGL& egl, const StaticMutexAutoLock& aProofOfLock) { 252 if (!egl.IsExtensionSupported(EGLLibExtension::MESA_platform_surfaceless)) { 253 return nullptr; 254 } 255 256 const EGLAttrib attrib_list[] = {LOCAL_EGL_NONE}; 257 const EGLDisplay display = egl.fGetPlatformDisplay( 258 LOCAL_EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, attrib_list); 259 if (display == EGL_NO_DISPLAY) { 260 return nullptr; 261 } 262 return EglDisplay::Create(egl, display, true, aProofOfLock); 263 } 264 #endif 265 266 static auto EglDebugLayersEnabled() { 267 EGLAttrib ret = LOCAL_EGL_FALSE; 268 #ifdef XP_WIN 269 if (StaticPrefs::gfx_direct3d11_enable_debug_layer_AtStartup()) { 270 ret = LOCAL_EGL_TRUE; 271 } 272 #endif 273 return ret; 274 } 275 276 static std::shared_ptr<EglDisplay> GetAndInitWARPDisplay( 277 GLLibraryEGL& egl, void* displayType, 278 const StaticMutexAutoLock& aProofOfLock) { 279 const EGLAttrib attrib_list[] = { 280 LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, 281 LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE, 282 LOCAL_EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE, 283 EglDebugLayersEnabled(), 284 // Requires: 285 LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE, 286 LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, LOCAL_EGL_NONE}; 287 const EGLDisplay display = egl.fGetPlatformDisplay( 288 LOCAL_EGL_PLATFORM_ANGLE_ANGLE, displayType, attrib_list); 289 290 if (display == EGL_NO_DISPLAY) { 291 const EGLint err = egl.fGetError(); 292 if (err != LOCAL_EGL_SUCCESS) { 293 gfxCriticalError() << "Unexpected GL error: " << gfx::hexa(err); 294 MOZ_CRASH("GFX: Unexpected GL error."); 295 } 296 return nullptr; 297 } 298 299 return EglDisplay::Create(egl, display, true, aProofOfLock); 300 } 301 302 std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplay( 303 ID3D11Device* const d3d11Device) { 304 StaticMutexAutoLock lock(sMutex); 305 EGLDeviceEXT eglDevice = 306 fCreateDeviceANGLE(LOCAL_EGL_D3D11_DEVICE_ANGLE, d3d11Device, nullptr); 307 if (!eglDevice) { 308 gfxCriticalNote << "Failed to get EGLDeviceEXT of D3D11Device"; 309 return nullptr; 310 } 311 const char* features[] = {"allowES3OnFL10_0", nullptr}; 312 // Create an EGLDisplay using the EGLDevice 313 const EGLAttrib attrib_list[] = {LOCAL_EGL_FEATURE_OVERRIDES_ENABLED_ANGLE, 314 reinterpret_cast<EGLAttrib>(features), 315 LOCAL_EGL_NONE}; 316 const auto display = fGetPlatformDisplay(LOCAL_EGL_PLATFORM_DEVICE_EXT, 317 eglDevice, attrib_list); 318 if (!display) { 319 gfxCriticalNote << "Failed to get EGLDisplay of D3D11Device"; 320 return nullptr; 321 } 322 323 if (!display) { 324 const EGLint err = fGetError(); 325 if (err != LOCAL_EGL_SUCCESS) { 326 gfxCriticalError() << "Unexpected GL error: " << gfx::hexa(err); 327 MOZ_CRASH("GFX: Unexpected GL error."); 328 } 329 return nullptr; 330 } 331 332 const auto ret = EglDisplay::Create(*this, display, false, lock); 333 334 if (!ret) { 335 const EGLint err = fGetError(); 336 if (err != LOCAL_EGL_SUCCESS) { 337 gfxCriticalError() 338 << "Failed to initialize EGLDisplay for WebRender error: " 339 << gfx::hexa(err); 340 } 341 return nullptr; 342 } 343 return ret; 344 } 345 346 static bool IsAccelAngleSupported(nsACString* const out_failureId) { 347 if (!gfx::gfxVars::AllowWebglAccelAngle()) { 348 if (out_failureId->IsEmpty()) { 349 *out_failureId = "FEATURE_FAILURE_ACCL_ANGLE_NOT_OK"_ns; 350 } 351 return false; 352 } 353 return true; 354 } 355 356 class AngleErrorReporting { 357 public: 358 AngleErrorReporting() : mFailureId(nullptr) { 359 // No static constructor 360 } 361 362 void SetFailureId(nsACString* const aFailureId) { mFailureId = aFailureId; } 363 364 void logError(const char* errorMessage) { 365 if (!mFailureId) { 366 return; 367 } 368 369 nsCString str(errorMessage); 370 Tokenizer tokenizer(str); 371 372 // Parse "ANGLE Display::initialize error " << error.getID() << ": " 373 // << error.getMessage() 374 nsCString currWord; 375 Tokenizer::Token intToken; 376 if (tokenizer.CheckWord("ANGLE") && tokenizer.CheckWhite() && 377 tokenizer.CheckWord("Display") && tokenizer.CheckChar(':') && 378 tokenizer.CheckChar(':') && tokenizer.CheckWord("initialize") && 379 tokenizer.CheckWhite() && tokenizer.CheckWord("error") && 380 tokenizer.CheckWhite() && 381 tokenizer.Check(Tokenizer::TOKEN_INTEGER, intToken)) { 382 *mFailureId = "FAILURE_ID_ANGLE_ID_"; 383 mFailureId->AppendPrintf("%" PRIu64, intToken.AsInteger()); 384 } else { 385 *mFailureId = "FAILURE_ID_ANGLE_UNKNOWN"; 386 } 387 } 388 389 private: 390 nsACString* mFailureId; 391 }; 392 393 MOZ_RUNINIT AngleErrorReporting gAngleErrorReporter; 394 395 static std::shared_ptr<EglDisplay> GetAndInitDisplayForAccelANGLE( 396 GLLibraryEGL& egl, nsACString* const out_failureId, 397 const StaticMutexAutoLock& aProofOfLock) { 398 gfx::FeatureState& d3d11ANGLE = 399 gfx::gfxConfig::GetFeature(gfx::Feature::D3D11_HW_ANGLE); 400 401 if (!StaticPrefs::webgl_angle_try_d3d11()) { 402 d3d11ANGLE.UserDisable("User disabled D3D11 ANGLE by pref", 403 "FAILURE_ID_ANGLE_PREF"_ns); 404 } 405 if (StaticPrefs::webgl_angle_force_d3d11()) { 406 d3d11ANGLE.UserForceEnable( 407 "User force-enabled D3D11 ANGLE on disabled hardware"); 408 } 409 gAngleErrorReporter.SetFailureId(out_failureId); 410 411 auto guardShutdown = mozilla::MakeScopeExit([&] { 412 gAngleErrorReporter.SetFailureId(nullptr); 413 // NOTE: Ideally we should be calling ANGLEPlatformShutdown after the 414 // ANGLE display is destroyed. However gAngleErrorReporter 415 // will live longer than the ANGLE display so we're fine. 416 }); 417 418 if (gfx::gfxConfig::IsForcedOnByUser(gfx::Feature::D3D11_HW_ANGLE)) { 419 return GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE, 420 aProofOfLock); 421 } 422 423 std::shared_ptr<EglDisplay> ret; 424 if (d3d11ANGLE.IsEnabled()) { 425 ret = GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE, 426 aProofOfLock); 427 } 428 429 if (!ret) { 430 ret = GetAndInitDisplay(egl, EGL_DEFAULT_DISPLAY, aProofOfLock); 431 } 432 433 if (!ret && out_failureId->IsEmpty()) { 434 *out_failureId = "FEATURE_FAILURE_ACCL_ANGLE_NO_DISP"_ns; 435 } 436 437 return ret; 438 } 439 440 // - 441 442 #if defined(XP_UNIX) 443 # define GLES2_LIB "libGLESv2.so" 444 # define GLES2_LIB2 "libGLESv2.so.2" 445 # define GL_LIB "libGL.so" 446 # define GL_LIB2 "libGL.so.1" 447 #elif defined(XP_WIN) 448 # define GLES2_LIB "libGLESv2.dll" 449 #else 450 # error "Platform not recognized" 451 #endif 452 453 Maybe<SymbolLoader> GLLibraryEGL::GetSymbolLoader() const { 454 auto ret = SymbolLoader(mSymbols.fGetProcAddress); 455 ret.mLib = mGLLibrary; 456 return Some(ret); 457 } 458 459 // - 460 461 /* static */ 462 RefPtr<GLLibraryEGL> GLLibraryEGL::Get(nsACString* const out_failureId) { 463 StaticMutexAutoLock lock(sMutex); 464 if (!sInstance) { 465 sInstance = new GLLibraryEGL; 466 if (NS_WARN_IF(!sInstance->Init(out_failureId))) { 467 sInstance = nullptr; 468 } 469 } 470 return sInstance; 471 } 472 473 /* static */ void GLLibraryEGL::Shutdown() { 474 StaticMutexAutoLock lock(sMutex); 475 sInstance = nullptr; 476 } 477 478 bool GLLibraryEGL::Init(nsACString* const out_failureId) { 479 MOZ_RELEASE_ASSERT(!mSymbols.fTerminate); 480 481 mozilla::ScopedGfxFeatureReporter reporter("EGL"); 482 483 #ifdef XP_WIN 484 if (!mEGLLibrary) { 485 // On Windows, the GLESv2, EGL and DXSDK libraries are shipped with libxul 486 // and we should look for them there. We have to load the libs in this 487 // order, because libEGL.dll depends on libGLESv2.dll which depends on the 488 // DXSDK libraries. This matters especially for WebRT apps which are in a 489 // different directory. See bug 760323 and bug 749459 490 491 // Also note that we intentionally leak the libs we load. 492 493 do { 494 // Windows 8.1+ has d3dcompiler_47.dll in the system directory. 495 if (LoadLibrarySystem32(L"d3dcompiler_47.dll")) break; 496 497 MOZ_ASSERT(false, "d3dcompiler DLL loading failed."); 498 } while (false); 499 500 mGLLibrary = LoadLibraryForEGLOnWindows(u"libGLESv2.dll"_ns); 501 502 mEGLLibrary = LoadLibraryForEGLOnWindows(u"libEGL.dll"_ns); 503 } 504 505 #else // !Windows 506 507 // On non-Windows (Android) we use system copies of libEGL. We look for 508 // the APITrace lib, libEGL.so, and libEGL.so.1 in that order. 509 510 # if defined(ANDROID) 511 if (!mEGLLibrary) mEGLLibrary = LoadApitraceLibrary(); 512 # endif 513 514 if (!mEGLLibrary) { 515 mEGLLibrary = PR_LoadLibrary("libEGL.so"); 516 } 517 # if defined(XP_UNIX) 518 if (!mEGLLibrary) { 519 mEGLLibrary = PR_LoadLibrary("libEGL.so.1"); 520 } 521 # endif 522 523 # ifdef APITRACE_LIB 524 if (!mGLLibrary) { 525 mGLLibrary = PR_LoadLibrary(APITRACE_LIB); 526 } 527 # endif 528 529 # ifdef GL_LIB 530 if (!mGLLibrary) { 531 mGLLibrary = PR_LoadLibrary(GL_LIB); 532 } 533 # endif 534 535 # ifdef GL_LIB2 536 if (!mGLLibrary) { 537 mGLLibrary = PR_LoadLibrary(GL_LIB2); 538 } 539 # endif 540 541 if (!mGLLibrary) { 542 mGLLibrary = PR_LoadLibrary(GLES2_LIB); 543 } 544 545 # ifdef GLES2_LIB2 546 if (!mGLLibrary) { 547 mGLLibrary = PR_LoadLibrary(GLES2_LIB2); 548 } 549 # endif 550 551 #endif // !Windows 552 553 if (!mEGLLibrary || !mGLLibrary) { 554 NS_WARNING("Couldn't load EGL LIB."); 555 *out_failureId = "FEATURE_FAILURE_EGL_LOAD_3"_ns; 556 return false; 557 } 558 559 #define SYMBOL(X) \ 560 { \ 561 (PRFuncPtr*)&mSymbols.f##X, { \ 562 { \ 563 "egl" #X \ 564 } \ 565 } \ 566 } 567 #define END_OF_SYMBOLS \ 568 { \ 569 nullptr, {} \ 570 } 571 572 SymLoadStruct earlySymbols[] = {SYMBOL(GetDisplay), 573 SYMBOL(Terminate), 574 SYMBOL(GetCurrentSurface), 575 SYMBOL(GetCurrentContext), 576 SYMBOL(MakeCurrent), 577 SYMBOL(DestroyContext), 578 SYMBOL(CreateContext), 579 SYMBOL(DestroySurface), 580 SYMBOL(CreateWindowSurface), 581 SYMBOL(CreatePbufferSurface), 582 SYMBOL(CreatePbufferFromClientBuffer), 583 SYMBOL(CreatePixmapSurface), 584 SYMBOL(BindAPI), 585 SYMBOL(Initialize), 586 SYMBOL(ChooseConfig), 587 SYMBOL(GetError), 588 SYMBOL(GetConfigs), 589 SYMBOL(GetConfigAttrib), 590 SYMBOL(WaitNative), 591 SYMBOL(GetProcAddress), 592 SYMBOL(SwapBuffers), 593 SYMBOL(CopyBuffers), 594 SYMBOL(QueryString), 595 SYMBOL(QueryContext), 596 SYMBOL(BindTexImage), 597 SYMBOL(ReleaseTexImage), 598 SYMBOL(SwapInterval), 599 SYMBOL(QuerySurface), 600 END_OF_SYMBOLS}; 601 602 { 603 const SymbolLoader libLoader(*mEGLLibrary); 604 if (!libLoader.LoadSymbols(earlySymbols)) { 605 NS_WARNING( 606 "Couldn't find required entry points in EGL library (early init)"); 607 *out_failureId = "FEATURE_FAILURE_EGL_SYM"_ns; 608 return false; 609 } 610 } 611 612 { 613 const char internalFuncName[] = 614 "_Z35eglQueryStringImplementationANDROIDPvi"; 615 const auto& internalFunc = 616 PR_FindFunctionSymbol(mEGLLibrary, internalFuncName); 617 if (internalFunc) { 618 *(PRFuncPtr*)&mSymbols.fQueryString = internalFunc; 619 } 620 } 621 622 // - 623 624 InitLibExtensions(); 625 626 const SymbolLoader pfnLoader(mSymbols.fGetProcAddress); 627 628 const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) { 629 const bool shouldWarn = gfxEnv::MOZ_GL_SPEW(); 630 if (pfnLoader.LoadSymbols(symbols, shouldWarn)) return true; 631 632 ClearSymbols(symbols); 633 return false; 634 }; 635 636 // Check the ANGLE support the system has 637 mIsANGLE = IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle); 638 639 // Client exts are ready. (But not display exts!) 640 641 if (mIsANGLE) { 642 MOZ_ASSERT(IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d)); 643 const SymLoadStruct angleSymbols[] = {SYMBOL(GetPlatformDisplay), 644 END_OF_SYMBOLS}; 645 if (!fnLoadSymbols(angleSymbols)) { 646 gfxCriticalError() << "Failed to load ANGLE symbols!"; 647 return false; 648 } 649 MOZ_ASSERT(IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d)); 650 const SymLoadStruct createDeviceSymbols[] = { 651 SYMBOL(CreateDeviceANGLE), SYMBOL(ReleaseDeviceANGLE), END_OF_SYMBOLS}; 652 if (!fnLoadSymbols(createDeviceSymbols)) { 653 NS_ERROR( 654 "EGL supports ANGLE_device_creation without exposing its functions!"); 655 MarkExtensionUnsupported(EGLLibExtension::ANGLE_device_creation); 656 } 657 } 658 659 // ANDROID_get_native_client_buffer isn't necessarily enumerated in lib exts, 660 // but it is one. 661 { 662 const SymLoadStruct symbols[] = {SYMBOL(GetNativeClientBufferANDROID), 663 END_OF_SYMBOLS}; 664 if (fnLoadSymbols(symbols)) { 665 mAvailableExtensions[UnderlyingValue( 666 EGLLibExtension::ANDROID_get_native_client_buffer)] = true; 667 } 668 } 669 670 // - 671 // Load possible display ext symbols. 672 673 { 674 const SymLoadStruct symbols[] = {SYMBOL(QuerySurfacePointerANGLE), 675 END_OF_SYMBOLS}; 676 (void)fnLoadSymbols(symbols); 677 } 678 { 679 const SymLoadStruct symbols[] = { 680 SYMBOL(CreateSyncKHR), SYMBOL(DestroySyncKHR), 681 SYMBOL(ClientWaitSyncKHR), SYMBOL(GetSyncAttribKHR), END_OF_SYMBOLS}; 682 (void)fnLoadSymbols(symbols); 683 } 684 { 685 const SymLoadStruct symbols[] = {SYMBOL(CreateImageKHR), 686 SYMBOL(DestroyImageKHR), END_OF_SYMBOLS}; 687 (void)fnLoadSymbols(symbols); 688 } 689 { 690 const SymLoadStruct symbols[] = {SYMBOL(WaitSyncKHR), END_OF_SYMBOLS}; 691 (void)fnLoadSymbols(symbols); 692 } 693 { 694 const SymLoadStruct symbols[] = {SYMBOL(DupNativeFenceFDANDROID), 695 END_OF_SYMBOLS}; 696 (void)fnLoadSymbols(symbols); 697 } 698 { 699 const SymLoadStruct symbols[] = {SYMBOL(CreateStreamKHR), 700 SYMBOL(DestroyStreamKHR), 701 SYMBOL(QueryStreamKHR), END_OF_SYMBOLS}; 702 (void)fnLoadSymbols(symbols); 703 } 704 { 705 const SymLoadStruct symbols[] = {SYMBOL(StreamConsumerGLTextureExternalKHR), 706 SYMBOL(StreamConsumerAcquireKHR), 707 SYMBOL(StreamConsumerReleaseKHR), 708 END_OF_SYMBOLS}; 709 (void)fnLoadSymbols(symbols); 710 } 711 { 712 const SymLoadStruct symbols[] = { 713 SYMBOL(QueryDisplayAttribEXT), SYMBOL(QueryDeviceAttribEXT), 714 SYMBOL(QueryDeviceStringEXT), END_OF_SYMBOLS}; 715 (void)fnLoadSymbols(symbols); 716 } 717 { 718 const SymLoadStruct symbols[] = { 719 SYMBOL(StreamConsumerGLTextureExternalAttribsNV), END_OF_SYMBOLS}; 720 (void)fnLoadSymbols(symbols); 721 } 722 { 723 const SymLoadStruct symbols[] = { 724 SYMBOL(CreateStreamProducerD3DTextureANGLE), 725 SYMBOL(StreamPostD3DTextureANGLE), END_OF_SYMBOLS}; 726 (void)fnLoadSymbols(symbols); 727 } 728 { 729 const SymLoadStruct symbols[] = { 730 {(PRFuncPtr*)&mSymbols.fSwapBuffersWithDamage, 731 {{"eglSwapBuffersWithDamageEXT"}}}, 732 END_OF_SYMBOLS}; 733 (void)fnLoadSymbols(symbols); 734 } 735 { 736 const SymLoadStruct symbols[] = { 737 {(PRFuncPtr*)&mSymbols.fSwapBuffersWithDamage, 738 {{"eglSwapBuffersWithDamageKHR"}}}, 739 END_OF_SYMBOLS}; 740 (void)fnLoadSymbols(symbols); 741 } 742 { 743 const SymLoadStruct symbols[] = { 744 {(PRFuncPtr*)&mSymbols.fSetDamageRegion, {{"eglSetDamageRegionKHR"}}}, 745 END_OF_SYMBOLS}; 746 (void)fnLoadSymbols(symbols); 747 } 748 { 749 const SymLoadStruct symbols[] = {SYMBOL(GetPlatformDisplay), 750 END_OF_SYMBOLS}; 751 (void)fnLoadSymbols(symbols); 752 } 753 { 754 const SymLoadStruct symbols[] = {SYMBOL(ExportDMABUFImageQueryMESA), 755 SYMBOL(ExportDMABUFImageMESA), 756 END_OF_SYMBOLS}; 757 (void)fnLoadSymbols(symbols); 758 } 759 { 760 const SymLoadStruct symbols[] = {SYMBOL(QueryDevicesEXT), END_OF_SYMBOLS}; 761 (void)fnLoadSymbols(symbols); 762 } 763 764 return true; 765 } 766 767 // - 768 769 template <size_t N> 770 static void MarkExtensions(const char* rawExtString, bool shouldDumpExts, 771 const char* extType, const char* const (&names)[N], 772 std::bitset<N>* const out) { 773 MOZ_ASSERT(rawExtString); 774 775 const nsDependentCString extString(rawExtString); 776 777 std::vector<nsCString> extList; 778 SplitByChar(extString, ' ', &extList); 779 780 if (shouldDumpExts) { 781 printf_stderr("%u EGL %s extensions: (*: recognized)\n", 782 (uint32_t)extList.size(), extType); 783 } 784 785 MarkBitfieldByStrings(extList, shouldDumpExts, names, out); 786 } 787 788 // - 789 790 // static 791 std::shared_ptr<EglDisplay> EglDisplay::Create( 792 GLLibraryEGL& lib, const EGLDisplay display, const bool isWarp, 793 const StaticMutexAutoLock& aProofOfLock) { 794 // Retrieve the EglDisplay if it already exists 795 { 796 const auto itr = lib.mActiveDisplays.find(display); 797 if (itr != lib.mActiveDisplays.end()) { 798 const auto ret = itr->second.lock(); 799 if (ret) { 800 return ret; 801 } 802 } 803 } 804 805 if (!lib.fInitialize(display, nullptr, nullptr)) { 806 return nullptr; 807 } 808 809 static std::once_flag sMesaLeakFlag; 810 std::call_once(sMesaLeakFlag, MesaMemoryLeakWorkaround); 811 812 const auto ret = 813 std::make_shared<EglDisplay>(PrivateUseOnly{}, lib, display, isWarp); 814 lib.mActiveDisplays.insert({display, ret}); 815 return ret; 816 } 817 818 EglDisplay::EglDisplay(const PrivateUseOnly&, GLLibraryEGL& lib, 819 const EGLDisplay disp, const bool isWarp) 820 : mLib(&lib), mDisplay(disp), mIsWARP(isWarp) { 821 const bool shouldDumpExts = GLContext::ShouldDumpExts(); 822 823 auto rawExtString = 824 (const char*)mLib->fQueryString(mDisplay, LOCAL_EGL_EXTENSIONS); 825 if (!rawExtString) { 826 NS_WARNING("Failed to query EGL display extensions!."); 827 rawExtString = ""; 828 } 829 MarkExtensions(rawExtString, shouldDumpExts, "display", sEGLExtensionNames, 830 &mAvailableExtensions); 831 832 // - 833 834 if (!HasKHRImageBase()) { 835 MarkExtensionUnsupported(EGLExtension::KHR_image_pixmap); 836 } 837 838 if (IsExtensionSupported(EGLExtension::KHR_surfaceless_context)) { 839 const auto vendor = 840 (const char*)mLib->fQueryString(mDisplay, LOCAL_EGL_VENDOR); 841 842 // Bug 1464610: Mali T720 (Amazon Fire 8 HD) claims to support this 843 // extension, but if you actually eglMakeCurrent() with EGL_NO_SURFACE, it 844 // fails to render anything when a real surface is provided later on. We 845 // only have the EGL vendor available here, so just avoid using this 846 // extension on all Mali devices. 847 if (vendor && (strcmp(vendor, "ARM") == 0)) { 848 MarkExtensionUnsupported(EGLExtension::KHR_surfaceless_context); 849 } 850 } 851 852 // ANDROID_native_fence_sync isn't necessarily enumerated in display ext, 853 // but it is one. 854 if (mLib->mSymbols.fDupNativeFenceFDANDROID) { 855 mAvailableExtensions[UnderlyingValue( 856 EGLExtension::ANDROID_native_fence_sync)] = true; 857 } 858 } 859 860 EglDisplay::~EglDisplay() { 861 StaticMutexAutoLock lock(GLLibraryEGL::sMutex); 862 fTerminate(); 863 mLib->mActiveDisplays.erase(mDisplay); 864 } 865 866 // - 867 868 std::shared_ptr<EglDisplay> GLLibraryEGL::DefaultDisplay( 869 nsACString* const out_failureId) { 870 StaticMutexAutoLock lock(sMutex); 871 auto ret = mDefaultDisplay.lock(); 872 if (ret) return ret; 873 874 ret = CreateDisplayLocked(false, false, out_failureId, lock); 875 mDefaultDisplay = ret; 876 return ret; 877 } 878 879 std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplay( 880 const bool forceAccel, const bool forceSoftware, 881 nsACString* const out_failureId) { 882 StaticMutexAutoLock lock(sMutex); 883 return CreateDisplayLocked(forceAccel, forceSoftware, out_failureId, lock); 884 } 885 886 std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplayLocked( 887 const bool forceAccel, const bool forceSoftware, 888 nsACString* const out_failureId, const StaticMutexAutoLock& aProofOfLock) { 889 std::shared_ptr<EglDisplay> ret; 890 891 if (IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d)) { 892 nsCString accelAngleFailureId; 893 bool accelAngleSupport = IsAccelAngleSupported(&accelAngleFailureId); 894 bool shouldTryAccel = (forceAccel || accelAngleSupport) && !forceSoftware; 895 bool shouldTryWARP = !forceAccel; // Only if ANGLE not supported or fails 896 897 // If WARP preferred, will override ANGLE support 898 if (StaticPrefs::webgl_angle_force_warp()) { 899 shouldTryWARP = true; 900 shouldTryAccel = false; 901 if (accelAngleFailureId.IsEmpty()) { 902 accelAngleFailureId = "FEATURE_FAILURE_FORCE_WARP"_ns; 903 } 904 } 905 906 // Hardware accelerated ANGLE path (supported or force accel) 907 if (shouldTryAccel) { 908 ret = GetAndInitDisplayForAccelANGLE(*this, out_failureId, aProofOfLock); 909 } 910 911 // Report the acceleration status to telemetry 912 if (!ret) { 913 if (accelAngleFailureId.IsEmpty()) { 914 glean::canvas::webgl_accl_failure_id 915 .Get("FEATURE_FAILURE_ACCL_ANGLE_UNKNOWN"_ns) 916 .Add(1); 917 } else { 918 glean::canvas::webgl_accl_failure_id.Get(accelAngleFailureId).Add(1); 919 } 920 } else { 921 glean::canvas::webgl_accl_failure_id.Get("SUCCESS"_ns).Add(1); 922 } 923 924 // Fallback to a WARP display if ANGLE fails, or if WARP is forced 925 if (!ret && shouldTryWARP) { 926 ret = GetAndInitWARPDisplay(*this, EGL_DEFAULT_DISPLAY, aProofOfLock); 927 if (!ret) { 928 if (out_failureId->IsEmpty()) { 929 *out_failureId = "FEATURE_FAILURE_WARP_FALLBACK"_ns; 930 } 931 NS_ERROR("Fallback WARP context failed to initialize."); 932 return nullptr; 933 } 934 } 935 } else { 936 void* nativeDisplay = EGL_DEFAULT_DISPLAY; 937 #ifdef MOZ_WIDGET_GTK 938 if (!ret && (!gfx::gfxVars::WebglUseHardware() || forceSoftware)) { 939 // Initialize a swrast egl device such as llvmpipe 940 ret = GetAndInitSoftwareDisplay(*this, aProofOfLock); 941 } 942 // Initialize the display the normal way 943 if (!ret && !gdk_display_get_default() && !forceSoftware) { 944 ret = GetAndInitDeviceDisplay(*this, aProofOfLock); 945 if (!ret) { 946 ret = GetAndInitSurfacelessDisplay(*this, aProofOfLock); 947 } 948 } 949 # ifdef MOZ_WAYLAND 950 else if (!ret && widget::GdkIsWaylandDisplay() && !forceSoftware) { 951 // Wayland does not support EGL_DEFAULT_DISPLAY 952 nativeDisplay = widget::WaylandDisplayGetWLDisplay(); 953 if (!nativeDisplay) { 954 NS_WARNING("Failed to get wl_display."); 955 return nullptr; 956 } 957 } 958 # endif 959 #endif 960 if (!ret && !forceSoftware) { 961 ret = GetAndInitDisplay(*this, nativeDisplay, aProofOfLock); 962 } 963 } 964 965 if (!ret) { 966 if (out_failureId->IsEmpty()) { 967 *out_failureId = "FEATURE_FAILURE_NO_DISPLAY"_ns; 968 } 969 NS_WARNING("Failed to initialize a display."); 970 return nullptr; 971 } 972 973 return ret; 974 } 975 976 void GLLibraryEGL::InitLibExtensions() { 977 const bool shouldDumpExts = GLContext::ShouldDumpExts(); 978 979 const char* rawExtString = nullptr; 980 981 #ifndef ANDROID 982 // Bug 1209612: Crashes on a number of android drivers. 983 // Ideally we would only blocklist this there, but for now we don't need the 984 // client extension list on ANDROID (we mostly need it on ANGLE), and we'd 985 // rather not crash. 986 rawExtString = (const char*)fQueryString(nullptr, LOCAL_EGL_EXTENSIONS); 987 #endif 988 989 if (!rawExtString) { 990 if (shouldDumpExts) { 991 printf_stderr("No EGL lib extensions.\n"); 992 } 993 return; 994 } 995 996 MarkExtensions(rawExtString, shouldDumpExts, "lib", sEGLLibraryExtensionNames, 997 &mAvailableExtensions); 998 } 999 1000 void EglDisplay::DumpEGLConfig(EGLConfig cfg) const { 1001 #define ATTR(_x) \ 1002 do { \ 1003 int attrval = 0; \ 1004 mLib->fGetConfigAttrib(mDisplay, cfg, LOCAL_EGL_##_x, &attrval); \ 1005 const auto err = mLib->fGetError(); \ 1006 if (err != 0x3000) { \ 1007 printf_stderr(" %s: ERROR (0x%04x)\n", #_x, err); \ 1008 } else { \ 1009 printf_stderr(" %s: %d (0x%04x)\n", #_x, attrval, attrval); \ 1010 } \ 1011 } while (0) 1012 1013 printf_stderr("EGL Config: %d [%p]\n", (int)(intptr_t)cfg, cfg); 1014 1015 ATTR(BUFFER_SIZE); 1016 ATTR(ALPHA_SIZE); 1017 ATTR(BLUE_SIZE); 1018 ATTR(GREEN_SIZE); 1019 ATTR(RED_SIZE); 1020 ATTR(DEPTH_SIZE); 1021 ATTR(STENCIL_SIZE); 1022 ATTR(CONFIG_CAVEAT); 1023 ATTR(CONFIG_ID); 1024 ATTR(LEVEL); 1025 ATTR(MAX_PBUFFER_HEIGHT); 1026 ATTR(MAX_PBUFFER_PIXELS); 1027 ATTR(MAX_PBUFFER_WIDTH); 1028 ATTR(NATIVE_RENDERABLE); 1029 ATTR(NATIVE_VISUAL_ID); 1030 ATTR(NATIVE_VISUAL_TYPE); 1031 ATTR(PRESERVED_RESOURCES); 1032 ATTR(SAMPLES); 1033 ATTR(SAMPLE_BUFFERS); 1034 ATTR(SURFACE_TYPE); 1035 ATTR(TRANSPARENT_TYPE); 1036 ATTR(TRANSPARENT_RED_VALUE); 1037 ATTR(TRANSPARENT_GREEN_VALUE); 1038 ATTR(TRANSPARENT_BLUE_VALUE); 1039 ATTR(BIND_TO_TEXTURE_RGB); 1040 ATTR(BIND_TO_TEXTURE_RGBA); 1041 ATTR(MIN_SWAP_INTERVAL); 1042 ATTR(MAX_SWAP_INTERVAL); 1043 ATTR(LUMINANCE_SIZE); 1044 ATTR(ALPHA_MASK_SIZE); 1045 ATTR(COLOR_BUFFER_TYPE); 1046 ATTR(RENDERABLE_TYPE); 1047 ATTR(CONFORMANT); 1048 1049 #undef ATTR 1050 } 1051 1052 void EglDisplay::DumpEGLConfigs() const { 1053 int nc = 0; 1054 mLib->fGetConfigs(mDisplay, nullptr, 0, &nc); 1055 std::vector<EGLConfig> ec(nc); 1056 mLib->fGetConfigs(mDisplay, ec.data(), ec.size(), &nc); 1057 1058 for (int i = 0; i < nc; ++i) { 1059 printf_stderr("========= EGL Config %d ========\n", i); 1060 DumpEGLConfig(ec[i]); 1061 } 1062 } 1063 1064 static bool ShouldTrace() { 1065 static bool ret = gfxEnv::MOZ_GL_DEBUG_VERBOSE(); 1066 return ret; 1067 } 1068 1069 void BeforeEGLCall(const char* glFunction) { 1070 if (ShouldTrace()) { 1071 printf_stderr("[egl] > %s\n", glFunction); 1072 } 1073 } 1074 1075 void AfterEGLCall(const char* glFunction) { 1076 if (ShouldTrace()) { 1077 printf_stderr("[egl] < %s\n", glFunction); 1078 } 1079 } 1080 1081 } /* namespace gl */ 1082 } /* namespace mozilla */