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