Bug 1771374 - Fix lint warnings. r=gfx-reviewers,aosmond
[gecko.git] / gfx / gl / GLLibraryEGL.cpp
blobe52a8bf755eb4eef7d6ccffb1856c2584eecd082
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"
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 # ifdef MOZ_WAYLAND
41 # include "mozilla/widget/nsWaylandDisplay.h"
42 # include "mozilla/widget/DMABufLibWrapper.h"
43 # endif // MOZ_WIDGET_GTK
44 # include <gdk/gdk.h>
45 #endif // MOZ_WAYLAND
47 #include <mutex> // for call_once
49 namespace mozilla {
50 namespace gl {
52 StaticMutex GLLibraryEGL::sMutex;
53 StaticRefPtr<GLLibraryEGL> GLLibraryEGL::sInstance;
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"};
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"};
97 PRLibrary* LoadApitraceLibrary() {
98 const char* path = nullptr;
100 #ifdef ANDROID
101 // We only need to explicitly dlopen egltrace
102 // on android as we can use LD_PRELOAD or other tricks
103 // on other platforms. We look for it in /data/local
104 // as that's writeable by all users.
105 path = "/data/local/tmp/egltrace.so";
106 #endif
107 if (!path) return nullptr;
109 // Initialization of gfx prefs here is only needed during the unit tests...
110 if (!StaticPrefs::gfx_apitrace_enabled_AtStartup()) {
111 return nullptr;
114 static PRLibrary* sApitraceLibrary = nullptr;
115 if (sApitraceLibrary) return sApitraceLibrary;
117 nsAutoCString logFile;
118 Preferences::GetCString("gfx.apitrace.logfile", logFile);
119 if (logFile.IsEmpty()) {
120 logFile = "firefox.trace";
123 // The firefox process can't write to /data/local, but it can write
124 // to $GRE_HOME/
125 nsAutoCString logPath;
126 logPath.AppendPrintf("%s/%s", getenv("GRE_HOME"), logFile.get());
128 #ifndef XP_WIN // Windows is missing setenv and forbids PR_LoadLibrary.
129 // apitrace uses the TRACE_FILE environment variable to determine where
130 // to log trace output to
131 printf_stderr("Logging GL tracing output to %s", logPath.get());
132 setenv("TRACE_FILE", logPath.get(), false);
134 printf_stderr("Attempting load of %s\n", path);
135 sApitraceLibrary = PR_LoadLibrary(path);
136 #endif
138 return sApitraceLibrary;
141 #ifdef XP_WIN
142 // see the comment in GLLibraryEGL::EnsureInitialized() for the rationale here.
143 static PRLibrary* LoadLibraryForEGLOnWindows(const nsAString& filename) {
144 nsAutoString path(gfx::gfxVars::GREDirectory());
145 path.Append(PR_GetDirectorySeparator());
146 path.Append(filename);
148 PRLibSpec lspec;
149 lspec.type = PR_LibSpec_PathnameU;
150 lspec.value.pathname_u = path.get();
151 return PR_LoadLibraryWithFlags(lspec, PR_LD_LAZY | PR_LD_LOCAL);
154 #endif // XP_WIN
156 static std::shared_ptr<EglDisplay> GetAndInitDisplay(
157 GLLibraryEGL& egl, void* displayType,
158 const StaticMutexAutoLock& aProofOfLock) {
159 const auto display = egl.fGetDisplay(displayType);
160 if (!display) return nullptr;
161 return EglDisplay::Create(egl, display, false, aProofOfLock);
164 #ifdef MOZ_WAYLAND
165 static std::shared_ptr<EglDisplay> GetAndInitDeviceDisplay(
166 GLLibraryEGL& egl, const StaticMutexAutoLock& aProofOfLock) {
167 nsAutoCString drmRenderDevice(gfx::gfxVars::DrmRenderDevice());
168 if (drmRenderDevice.IsEmpty() ||
169 !egl.IsExtensionSupported(EGLLibExtension::EXT_platform_device) ||
170 !egl.IsExtensionSupported(EGLLibExtension::EXT_device_enumeration)) {
171 return nullptr;
174 EGLint maxDevices;
175 if (!egl.fQueryDevicesEXT(0, nullptr, &maxDevices)) {
176 return nullptr;
179 std::vector<EGLDeviceEXT> devices(maxDevices);
180 EGLint numDevices;
181 if (!egl.fQueryDevicesEXT(devices.size(), devices.data(), &numDevices)) {
182 return nullptr;
184 devices.resize(numDevices);
186 EGLDisplay display = EGL_NO_DISPLAY;
187 for (const auto& device : devices) {
188 const char* renderNodeString =
189 egl.fQueryDeviceStringEXT(device, LOCAL_EGL_DRM_RENDER_NODE_FILE_EXT);
190 if (renderNodeString &&
191 strcmp(renderNodeString, drmRenderDevice.get()) == 0) {
192 const EGLAttrib attrib_list[] = {LOCAL_EGL_NONE};
193 display = egl.fGetPlatformDisplay(LOCAL_EGL_PLATFORM_DEVICE_EXT, device,
194 attrib_list);
195 break;
198 if (!display) {
199 return nullptr;
202 return EglDisplay::Create(egl, display, true, aProofOfLock);
205 static std::shared_ptr<EglDisplay> GetAndInitSurfacelessDisplay(
206 GLLibraryEGL& egl, const StaticMutexAutoLock& aProofOfLock) {
207 if (!egl.IsExtensionSupported(EGLLibExtension::MESA_platform_surfaceless)) {
208 return nullptr;
211 const EGLAttrib attrib_list[] = {LOCAL_EGL_NONE};
212 const EGLDisplay display = egl.fGetPlatformDisplay(
213 LOCAL_EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, attrib_list);
214 if (display == EGL_NO_DISPLAY) {
215 return nullptr;
217 return EglDisplay::Create(egl, display, true, aProofOfLock);
219 #endif
221 static std::shared_ptr<EglDisplay> GetAndInitWARPDisplay(
222 GLLibraryEGL& egl, void* displayType,
223 const StaticMutexAutoLock& aProofOfLock) {
224 const EGLAttrib attrib_list[] = {
225 LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
226 LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE,
227 // Requires:
228 LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
229 LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, LOCAL_EGL_NONE};
230 const EGLDisplay display = egl.fGetPlatformDisplay(
231 LOCAL_EGL_PLATFORM_ANGLE_ANGLE, displayType, attrib_list);
233 if (display == EGL_NO_DISPLAY) {
234 const EGLint err = egl.fGetError();
235 if (err != LOCAL_EGL_SUCCESS) {
236 gfxCriticalError() << "Unexpected GL error: " << gfx::hexa(err);
237 MOZ_CRASH("GFX: Unexpected GL error.");
239 return nullptr;
242 return EglDisplay::Create(egl, display, true, aProofOfLock);
245 std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplay(
246 ID3D11Device* const d3d11Device) {
247 StaticMutexAutoLock lock(sMutex);
248 EGLDeviceEXT eglDevice =
249 fCreateDeviceANGLE(LOCAL_EGL_D3D11_DEVICE_ANGLE, d3d11Device, nullptr);
250 if (!eglDevice) {
251 gfxCriticalNote << "Failed to get EGLDeviceEXT of D3D11Device";
252 return nullptr;
254 const char* features[] = {"allowES3OnFL10_0", nullptr};
255 // Create an EGLDisplay using the EGLDevice
256 const EGLAttrib attrib_list[] = {LOCAL_EGL_FEATURE_OVERRIDES_ENABLED_ANGLE,
257 reinterpret_cast<EGLAttrib>(features),
258 LOCAL_EGL_NONE};
259 const auto display = fGetPlatformDisplay(LOCAL_EGL_PLATFORM_DEVICE_EXT,
260 eglDevice, attrib_list);
261 if (!display) {
262 gfxCriticalNote << "Failed to get EGLDisplay of D3D11Device";
263 return nullptr;
266 if (!display) {
267 const EGLint err = fGetError();
268 if (err != LOCAL_EGL_SUCCESS) {
269 gfxCriticalError() << "Unexpected GL error: " << gfx::hexa(err);
270 MOZ_CRASH("GFX: Unexpected GL error.");
272 return nullptr;
275 const auto ret = EglDisplay::Create(*this, display, false, lock);
277 if (!ret) {
278 const EGLint err = fGetError();
279 if (err != LOCAL_EGL_SUCCESS) {
280 gfxCriticalError()
281 << "Failed to initialize EGLDisplay for WebRender error: "
282 << gfx::hexa(err);
284 return nullptr;
286 return ret;
289 static bool IsAccelAngleSupported(nsACString* const out_failureId) {
290 if (!gfx::gfxVars::AllowWebglAccelAngle()) {
291 if (out_failureId->IsEmpty()) {
292 *out_failureId = "FEATURE_FAILURE_ACCL_ANGLE_NOT_OK"_ns;
294 return false;
296 return true;
299 class AngleErrorReporting {
300 public:
301 AngleErrorReporting() : mFailureId(nullptr) {
302 // No static constructor
305 void SetFailureId(nsACString* const aFailureId) { mFailureId = aFailureId; }
307 void logError(const char* errorMessage) {
308 if (!mFailureId) {
309 return;
312 nsCString str(errorMessage);
313 Tokenizer tokenizer(str);
315 // Parse "ANGLE Display::initialize error " << error.getID() << ": "
316 // << error.getMessage()
317 nsCString currWord;
318 Tokenizer::Token intToken;
319 if (tokenizer.CheckWord("ANGLE") && tokenizer.CheckWhite() &&
320 tokenizer.CheckWord("Display") && tokenizer.CheckChar(':') &&
321 tokenizer.CheckChar(':') && tokenizer.CheckWord("initialize") &&
322 tokenizer.CheckWhite() && tokenizer.CheckWord("error") &&
323 tokenizer.CheckWhite() &&
324 tokenizer.Check(Tokenizer::TOKEN_INTEGER, intToken)) {
325 *mFailureId = "FAILURE_ID_ANGLE_ID_";
326 mFailureId->AppendPrintf("%" PRIu64, intToken.AsInteger());
327 } else {
328 *mFailureId = "FAILURE_ID_ANGLE_UNKNOWN";
332 private:
333 nsACString* mFailureId;
336 AngleErrorReporting gAngleErrorReporter;
338 static std::shared_ptr<EglDisplay> GetAndInitDisplayForAccelANGLE(
339 GLLibraryEGL& egl, nsACString* const out_failureId,
340 const StaticMutexAutoLock& aProofOfLock) {
341 gfx::FeatureState& d3d11ANGLE =
342 gfx::gfxConfig::GetFeature(gfx::Feature::D3D11_HW_ANGLE);
344 if (!StaticPrefs::webgl_angle_try_d3d11()) {
345 d3d11ANGLE.UserDisable("User disabled D3D11 ANGLE by pref",
346 "FAILURE_ID_ANGLE_PREF"_ns);
348 if (StaticPrefs::webgl_angle_force_d3d11()) {
349 d3d11ANGLE.UserForceEnable(
350 "User force-enabled D3D11 ANGLE on disabled hardware");
352 gAngleErrorReporter.SetFailureId(out_failureId);
354 auto guardShutdown = mozilla::MakeScopeExit([&] {
355 gAngleErrorReporter.SetFailureId(nullptr);
356 // NOTE: Ideally we should be calling ANGLEPlatformShutdown after the
357 // ANGLE display is destroyed. However gAngleErrorReporter
358 // will live longer than the ANGLE display so we're fine.
361 if (gfx::gfxConfig::IsForcedOnByUser(gfx::Feature::D3D11_HW_ANGLE)) {
362 return GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE,
363 aProofOfLock);
366 std::shared_ptr<EglDisplay> ret;
367 if (d3d11ANGLE.IsEnabled()) {
368 ret = GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE,
369 aProofOfLock);
372 if (!ret) {
373 ret = GetAndInitDisplay(egl, EGL_DEFAULT_DISPLAY, aProofOfLock);
376 if (!ret && out_failureId->IsEmpty()) {
377 *out_failureId = "FEATURE_FAILURE_ACCL_ANGLE_NO_DISP"_ns;
380 return ret;
383 // -
385 #if defined(XP_UNIX)
386 # define GLES2_LIB "libGLESv2.so"
387 # define GLES2_LIB2 "libGLESv2.so.2"
388 # define GL_LIB "libGL.so"
389 # define GL_LIB2 "libGL.so.1"
390 #elif defined(XP_WIN)
391 # define GLES2_LIB "libGLESv2.dll"
392 #else
393 # error "Platform not recognized"
394 #endif
396 Maybe<SymbolLoader> GLLibraryEGL::GetSymbolLoader() const {
397 auto ret = SymbolLoader(mSymbols.fGetProcAddress);
398 ret.mLib = mGLLibrary;
399 return Some(ret);
402 // -
404 /* static */
405 RefPtr<GLLibraryEGL> GLLibraryEGL::Get(nsACString* const out_failureId) {
406 StaticMutexAutoLock lock(sMutex);
407 if (!sInstance) {
408 sInstance = new GLLibraryEGL;
409 if (NS_WARN_IF(!sInstance->Init(out_failureId))) {
410 sInstance = nullptr;
413 return sInstance;
416 /* static */ void GLLibraryEGL::Shutdown() {
417 StaticMutexAutoLock lock(sMutex);
418 sInstance = nullptr;
421 bool GLLibraryEGL::Init(nsACString* const out_failureId) {
422 MOZ_RELEASE_ASSERT(!mSymbols.fTerminate);
424 mozilla::ScopedGfxFeatureReporter reporter("EGL");
426 #ifdef XP_WIN
427 if (!mEGLLibrary) {
428 // On Windows, the GLESv2, EGL and DXSDK libraries are shipped with libxul
429 // and we should look for them there. We have to load the libs in this
430 // order, because libEGL.dll depends on libGLESv2.dll which depends on the
431 // DXSDK libraries. This matters especially for WebRT apps which are in a
432 // different directory. See bug 760323 and bug 749459
434 // Also note that we intentionally leak the libs we load.
436 do {
437 // Windows 8.1+ has d3dcompiler_47.dll in the system directory.
438 // Try it first. Note that _46 will never be in the system
439 // directory. So there is no point trying _46 in the system
440 // directory.
442 if (LoadLibrarySystem32(L"d3dcompiler_47.dll")) break;
444 # ifdef MOZ_D3DCOMPILER_VISTA_DLL
445 if (LoadLibraryForEGLOnWindows(NS_LITERAL_STRING_FROM_CSTRING(
446 MOZ_STRINGIFY(MOZ_D3DCOMPILER_VISTA_DLL))))
447 break;
448 # endif
450 MOZ_ASSERT(false, "d3dcompiler DLL loading failed.");
451 } while (false);
453 mGLLibrary = LoadLibraryForEGLOnWindows(u"libGLESv2.dll"_ns);
455 mEGLLibrary = LoadLibraryForEGLOnWindows(u"libEGL.dll"_ns);
458 #else // !Windows
460 // On non-Windows (Android) we use system copies of libEGL. We look for
461 // the APITrace lib, libEGL.so, and libEGL.so.1 in that order.
463 # if defined(ANDROID)
464 if (!mEGLLibrary) mEGLLibrary = LoadApitraceLibrary();
465 # endif
467 if (!mEGLLibrary) {
468 mEGLLibrary = PR_LoadLibrary("libEGL.so");
470 # if defined(XP_UNIX)
471 if (!mEGLLibrary) {
472 mEGLLibrary = PR_LoadLibrary("libEGL.so.1");
474 # endif
476 # ifdef APITRACE_LIB
477 if (!mGLLibrary) {
478 mGLLibrary = PR_LoadLibrary(APITRACE_LIB);
480 # endif
482 # ifdef GL_LIB
483 if (!mGLLibrary) {
484 mGLLibrary = PR_LoadLibrary(GL_LIB);
486 # endif
488 # ifdef GL_LIB2
489 if (!mGLLibrary) {
490 mGLLibrary = PR_LoadLibrary(GL_LIB2);
492 # endif
494 if (!mGLLibrary) {
495 mGLLibrary = PR_LoadLibrary(GLES2_LIB);
498 # ifdef GLES2_LIB2
499 if (!mGLLibrary) {
500 mGLLibrary = PR_LoadLibrary(GLES2_LIB2);
502 # endif
504 #endif // !Windows
506 if (!mEGLLibrary || !mGLLibrary) {
507 NS_WARNING("Couldn't load EGL LIB.");
508 *out_failureId = "FEATURE_FAILURE_EGL_LOAD_3"_ns;
509 return false;
512 #define SYMBOL(X) \
514 (PRFuncPtr*)&mSymbols.f##X, { \
515 { "egl" #X } \
518 #define END_OF_SYMBOLS \
520 nullptr, {} \
523 SymLoadStruct earlySymbols[] = {SYMBOL(GetDisplay),
524 SYMBOL(Terminate),
525 SYMBOL(GetCurrentSurface),
526 SYMBOL(GetCurrentContext),
527 SYMBOL(MakeCurrent),
528 SYMBOL(DestroyContext),
529 SYMBOL(CreateContext),
530 SYMBOL(DestroySurface),
531 SYMBOL(CreateWindowSurface),
532 SYMBOL(CreatePbufferSurface),
533 SYMBOL(CreatePbufferFromClientBuffer),
534 SYMBOL(CreatePixmapSurface),
535 SYMBOL(BindAPI),
536 SYMBOL(Initialize),
537 SYMBOL(ChooseConfig),
538 SYMBOL(GetError),
539 SYMBOL(GetConfigs),
540 SYMBOL(GetConfigAttrib),
541 SYMBOL(WaitNative),
542 SYMBOL(GetProcAddress),
543 SYMBOL(SwapBuffers),
544 SYMBOL(CopyBuffers),
545 SYMBOL(QueryString),
546 SYMBOL(QueryContext),
547 SYMBOL(BindTexImage),
548 SYMBOL(ReleaseTexImage),
549 SYMBOL(SwapInterval),
550 SYMBOL(QuerySurface),
551 END_OF_SYMBOLS};
554 const SymbolLoader libLoader(*mEGLLibrary);
555 if (!libLoader.LoadSymbols(earlySymbols)) {
556 NS_WARNING(
557 "Couldn't find required entry points in EGL library (early init)");
558 *out_failureId = "FEATURE_FAILURE_EGL_SYM"_ns;
559 return false;
564 const char internalFuncName[] =
565 "_Z35eglQueryStringImplementationANDROIDPvi";
566 const auto& internalFunc =
567 PR_FindFunctionSymbol(mEGLLibrary, internalFuncName);
568 if (internalFunc) {
569 *(PRFuncPtr*)&mSymbols.fQueryString = internalFunc;
573 // -
575 InitLibExtensions();
577 const SymbolLoader pfnLoader(mSymbols.fGetProcAddress);
579 const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) {
580 const bool shouldWarn = gfxEnv::GlSpew();
581 if (pfnLoader.LoadSymbols(symbols, shouldWarn)) return true;
583 ClearSymbols(symbols);
584 return false;
587 // Check the ANGLE support the system has
588 mIsANGLE = IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle);
590 // Client exts are ready. (But not display exts!)
592 if (mIsANGLE) {
593 MOZ_ASSERT(IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d));
594 const SymLoadStruct angleSymbols[] = {SYMBOL(GetPlatformDisplay),
595 END_OF_SYMBOLS};
596 if (!fnLoadSymbols(angleSymbols)) {
597 gfxCriticalError() << "Failed to load ANGLE symbols!";
598 return false;
600 MOZ_ASSERT(IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d));
601 const SymLoadStruct createDeviceSymbols[] = {
602 SYMBOL(CreateDeviceANGLE), SYMBOL(ReleaseDeviceANGLE), END_OF_SYMBOLS};
603 if (!fnLoadSymbols(createDeviceSymbols)) {
604 NS_ERROR(
605 "EGL supports ANGLE_device_creation without exposing its functions!");
606 MarkExtensionUnsupported(EGLLibExtension::ANGLE_device_creation);
610 // ANDROID_get_native_client_buffer isn't necessarily enumerated in lib exts,
611 // but it is one.
613 const SymLoadStruct symbols[] = {SYMBOL(GetNativeClientBufferANDROID),
614 END_OF_SYMBOLS};
615 if (fnLoadSymbols(symbols)) {
616 mAvailableExtensions[UnderlyingValue(
617 EGLLibExtension::ANDROID_get_native_client_buffer)] = true;
621 // -
622 // Load possible display ext symbols.
625 const SymLoadStruct symbols[] = {SYMBOL(QuerySurfacePointerANGLE),
626 END_OF_SYMBOLS};
627 (void)fnLoadSymbols(symbols);
630 const SymLoadStruct symbols[] = {
631 SYMBOL(CreateSyncKHR), SYMBOL(DestroySyncKHR),
632 SYMBOL(ClientWaitSyncKHR), SYMBOL(GetSyncAttribKHR), END_OF_SYMBOLS};
633 (void)fnLoadSymbols(symbols);
636 const SymLoadStruct symbols[] = {SYMBOL(CreateImageKHR),
637 SYMBOL(DestroyImageKHR), END_OF_SYMBOLS};
638 (void)fnLoadSymbols(symbols);
641 const SymLoadStruct symbols[] = {SYMBOL(WaitSyncKHR), END_OF_SYMBOLS};
642 (void)fnLoadSymbols(symbols);
645 const SymLoadStruct symbols[] = {SYMBOL(DupNativeFenceFDANDROID),
646 END_OF_SYMBOLS};
647 (void)fnLoadSymbols(symbols);
650 const SymLoadStruct symbols[] = {SYMBOL(CreateStreamKHR),
651 SYMBOL(DestroyStreamKHR),
652 SYMBOL(QueryStreamKHR), END_OF_SYMBOLS};
653 (void)fnLoadSymbols(symbols);
656 const SymLoadStruct symbols[] = {SYMBOL(StreamConsumerGLTextureExternalKHR),
657 SYMBOL(StreamConsumerAcquireKHR),
658 SYMBOL(StreamConsumerReleaseKHR),
659 END_OF_SYMBOLS};
660 (void)fnLoadSymbols(symbols);
663 const SymLoadStruct symbols[] = {
664 SYMBOL(QueryDisplayAttribEXT), SYMBOL(QueryDeviceAttribEXT),
665 SYMBOL(QueryDeviceStringEXT), END_OF_SYMBOLS};
666 (void)fnLoadSymbols(symbols);
669 const SymLoadStruct symbols[] = {
670 SYMBOL(StreamConsumerGLTextureExternalAttribsNV), END_OF_SYMBOLS};
671 (void)fnLoadSymbols(symbols);
674 const SymLoadStruct symbols[] = {
675 SYMBOL(CreateStreamProducerD3DTextureANGLE),
676 SYMBOL(StreamPostD3DTextureANGLE), END_OF_SYMBOLS};
677 (void)fnLoadSymbols(symbols);
680 const SymLoadStruct symbols[] = {
681 {(PRFuncPtr*)&mSymbols.fSwapBuffersWithDamage,
682 {{"eglSwapBuffersWithDamageEXT"}}},
683 END_OF_SYMBOLS};
684 (void)fnLoadSymbols(symbols);
687 const SymLoadStruct symbols[] = {
688 {(PRFuncPtr*)&mSymbols.fSwapBuffersWithDamage,
689 {{"eglSwapBuffersWithDamageKHR"}}},
690 END_OF_SYMBOLS};
691 (void)fnLoadSymbols(symbols);
694 const SymLoadStruct symbols[] = {
695 {(PRFuncPtr*)&mSymbols.fSetDamageRegion, {{"eglSetDamageRegionKHR"}}},
696 END_OF_SYMBOLS};
697 (void)fnLoadSymbols(symbols);
700 const SymLoadStruct symbols[] = {SYMBOL(GetPlatformDisplay),
701 END_OF_SYMBOLS};
702 (void)fnLoadSymbols(symbols);
705 const SymLoadStruct symbols[] = {SYMBOL(ExportDMABUFImageQueryMESA),
706 SYMBOL(ExportDMABUFImageMESA),
707 END_OF_SYMBOLS};
708 (void)fnLoadSymbols(symbols);
711 const SymLoadStruct symbols[] = {SYMBOL(QueryDevicesEXT), END_OF_SYMBOLS};
712 (void)fnLoadSymbols(symbols);
715 return true;
718 // -
720 template <size_t N>
721 static void MarkExtensions(const char* rawExtString, bool shouldDumpExts,
722 const char* extType, const char* const (&names)[N],
723 std::bitset<N>* const out) {
724 MOZ_ASSERT(rawExtString);
726 const nsDependentCString extString(rawExtString);
728 std::vector<nsCString> extList;
729 SplitByChar(extString, ' ', &extList);
731 if (shouldDumpExts) {
732 printf_stderr("%u EGL %s extensions: (*: recognized)\n",
733 (uint32_t)extList.size(), extType);
736 MarkBitfieldByStrings(extList, shouldDumpExts, names, out);
739 // -
741 // static
742 std::shared_ptr<EglDisplay> EglDisplay::Create(
743 GLLibraryEGL& lib, const EGLDisplay display, const bool isWarp,
744 const StaticMutexAutoLock& aProofOfLock) {
745 // Retrieve the EglDisplay if it already exists
747 const auto itr = lib.mActiveDisplays.find(display);
748 if (itr != lib.mActiveDisplays.end()) {
749 const auto ret = itr->second.lock();
750 if (ret) {
751 return ret;
756 if (!lib.fInitialize(display, nullptr, nullptr)) {
757 return nullptr;
760 static std::once_flag sMesaLeakFlag;
761 std::call_once(sMesaLeakFlag, MesaMemoryLeakWorkaround);
763 const auto ret =
764 std::make_shared<EglDisplay>(PrivateUseOnly{}, lib, display, isWarp);
765 lib.mActiveDisplays.insert({display, ret});
766 return ret;
769 EglDisplay::EglDisplay(const PrivateUseOnly&, GLLibraryEGL& lib,
770 const EGLDisplay disp, const bool isWarp)
771 : mLib(&lib), mDisplay(disp), mIsWARP(isWarp) {
772 const bool shouldDumpExts = GLContext::ShouldDumpExts();
774 auto rawExtString =
775 (const char*)mLib->fQueryString(mDisplay, LOCAL_EGL_EXTENSIONS);
776 if (!rawExtString) {
777 NS_WARNING("Failed to query EGL display extensions!.");
778 rawExtString = "";
780 MarkExtensions(rawExtString, shouldDumpExts, "display", sEGLExtensionNames,
781 &mAvailableExtensions);
783 // -
785 if (!HasKHRImageBase()) {
786 MarkExtensionUnsupported(EGLExtension::KHR_image_pixmap);
789 if (IsExtensionSupported(EGLExtension::KHR_surfaceless_context)) {
790 const auto vendor =
791 (const char*)mLib->fQueryString(mDisplay, LOCAL_EGL_VENDOR);
793 // Bug 1464610: Mali T720 (Amazon Fire 8 HD) claims to support this
794 // extension, but if you actually eglMakeCurrent() with EGL_NO_SURFACE, it
795 // fails to render anything when a real surface is provided later on. We
796 // only have the EGL vendor available here, so just avoid using this
797 // extension on all Mali devices.
798 if (vendor && (strcmp(vendor, "ARM") == 0)) {
799 MarkExtensionUnsupported(EGLExtension::KHR_surfaceless_context);
803 // ANDROID_native_fence_sync isn't necessarily enumerated in display ext,
804 // but it is one.
805 if (mLib->mSymbols.fDupNativeFenceFDANDROID) {
806 mAvailableExtensions[UnderlyingValue(
807 EGLExtension::ANDROID_native_fence_sync)] = true;
811 EglDisplay::~EglDisplay() {
812 StaticMutexAutoLock lock(GLLibraryEGL::sMutex);
813 fTerminate();
814 mLib->mActiveDisplays.erase(mDisplay);
817 // -
819 std::shared_ptr<EglDisplay> GLLibraryEGL::DefaultDisplay(
820 nsACString* const out_failureId) {
821 StaticMutexAutoLock lock(sMutex);
822 auto ret = mDefaultDisplay.lock();
823 if (ret) return ret;
825 ret = CreateDisplayLocked(false, out_failureId, lock);
826 mDefaultDisplay = ret;
827 return ret;
830 std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplay(
831 const bool forceAccel, nsACString* const out_failureId) {
832 StaticMutexAutoLock lock(sMutex);
833 return CreateDisplayLocked(forceAccel, out_failureId, lock);
836 std::shared_ptr<EglDisplay> GLLibraryEGL::CreateDisplayLocked(
837 const bool forceAccel, nsACString* const out_failureId,
838 const StaticMutexAutoLock& aProofOfLock) {
839 std::shared_ptr<EglDisplay> ret;
841 if (IsExtensionSupported(EGLLibExtension::ANGLE_platform_angle_d3d)) {
842 nsCString accelAngleFailureId;
843 bool accelAngleSupport = IsAccelAngleSupported(&accelAngleFailureId);
844 bool shouldTryAccel = forceAccel || accelAngleSupport;
845 bool shouldTryWARP = !forceAccel; // Only if ANGLE not supported or fails
847 // If WARP preferred, will override ANGLE support
848 if (StaticPrefs::webgl_angle_force_warp()) {
849 shouldTryWARP = true;
850 shouldTryAccel = false;
851 if (accelAngleFailureId.IsEmpty()) {
852 accelAngleFailureId = "FEATURE_FAILURE_FORCE_WARP"_ns;
856 // Hardware accelerated ANGLE path (supported or force accel)
857 if (shouldTryAccel) {
858 ret = GetAndInitDisplayForAccelANGLE(*this, out_failureId, aProofOfLock);
861 // Report the acceleration status to telemetry
862 if (!ret) {
863 if (accelAngleFailureId.IsEmpty()) {
864 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID,
865 "FEATURE_FAILURE_ACCL_ANGLE_UNKNOWN"_ns);
866 } else {
867 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID,
868 accelAngleFailureId);
870 } else {
871 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID,
872 "SUCCESS"_ns);
875 // Fallback to a WARP display if ANGLE fails, or if WARP is forced
876 if (!ret && shouldTryWARP) {
877 ret = GetAndInitWARPDisplay(*this, EGL_DEFAULT_DISPLAY, aProofOfLock);
878 if (!ret) {
879 if (out_failureId->IsEmpty()) {
880 *out_failureId = "FEATURE_FAILURE_WARP_FALLBACK"_ns;
882 NS_ERROR("Fallback WARP context failed to initialize.");
883 return nullptr;
886 } else {
887 void* nativeDisplay = EGL_DEFAULT_DISPLAY;
888 #ifdef MOZ_WAYLAND
889 GdkDisplay* gdkDisplay = gdk_display_get_default();
890 if (!gdkDisplay) {
891 ret = GetAndInitDeviceDisplay(*this, aProofOfLock);
892 if (!ret) {
893 ret = GetAndInitSurfacelessDisplay(*this, aProofOfLock);
895 } else if (widget::GdkIsWaylandDisplay(gdkDisplay)) {
896 // Wayland does not support EGL_DEFAULT_DISPLAY
897 nativeDisplay = widget::WaylandDisplayGetWLDisplay(gdkDisplay);
898 if (!nativeDisplay) {
899 NS_WARNING("Failed to get wl_display.");
900 return nullptr;
903 #endif
904 if (!ret) {
905 ret = GetAndInitDisplay(*this, nativeDisplay, aProofOfLock);
909 if (!ret) {
910 if (out_failureId->IsEmpty()) {
911 *out_failureId = "FEATURE_FAILURE_NO_DISPLAY"_ns;
913 NS_WARNING("Failed to initialize a display.");
914 return nullptr;
917 return ret;
920 void GLLibraryEGL::InitLibExtensions() {
921 const bool shouldDumpExts = GLContext::ShouldDumpExts();
923 const char* rawExtString = nullptr;
925 #ifndef ANDROID
926 // Bug 1209612: Crashes on a number of android drivers.
927 // Ideally we would only blocklist this there, but for now we don't need the
928 // client extension list on ANDROID (we mostly need it on ANGLE), and we'd
929 // rather not crash.
930 rawExtString = (const char*)fQueryString(nullptr, LOCAL_EGL_EXTENSIONS);
931 #endif
933 if (!rawExtString) {
934 if (shouldDumpExts) {
935 printf_stderr("No EGL lib extensions.\n");
937 return;
940 MarkExtensions(rawExtString, shouldDumpExts, "lib", sEGLLibraryExtensionNames,
941 &mAvailableExtensions);
944 void EglDisplay::DumpEGLConfig(EGLConfig cfg) const {
945 #define ATTR(_x) \
946 do { \
947 int attrval = 0; \
948 mLib->fGetConfigAttrib(mDisplay, cfg, LOCAL_EGL_##_x, &attrval); \
949 const auto err = mLib->fGetError(); \
950 if (err != 0x3000) { \
951 printf_stderr(" %s: ERROR (0x%04x)\n", #_x, err); \
952 } else { \
953 printf_stderr(" %s: %d (0x%04x)\n", #_x, attrval, attrval); \
955 } while (0)
957 printf_stderr("EGL Config: %d [%p]\n", (int)(intptr_t)cfg, cfg);
959 ATTR(BUFFER_SIZE);
960 ATTR(ALPHA_SIZE);
961 ATTR(BLUE_SIZE);
962 ATTR(GREEN_SIZE);
963 ATTR(RED_SIZE);
964 ATTR(DEPTH_SIZE);
965 ATTR(STENCIL_SIZE);
966 ATTR(CONFIG_CAVEAT);
967 ATTR(CONFIG_ID);
968 ATTR(LEVEL);
969 ATTR(MAX_PBUFFER_HEIGHT);
970 ATTR(MAX_PBUFFER_PIXELS);
971 ATTR(MAX_PBUFFER_WIDTH);
972 ATTR(NATIVE_RENDERABLE);
973 ATTR(NATIVE_VISUAL_ID);
974 ATTR(NATIVE_VISUAL_TYPE);
975 ATTR(PRESERVED_RESOURCES);
976 ATTR(SAMPLES);
977 ATTR(SAMPLE_BUFFERS);
978 ATTR(SURFACE_TYPE);
979 ATTR(TRANSPARENT_TYPE);
980 ATTR(TRANSPARENT_RED_VALUE);
981 ATTR(TRANSPARENT_GREEN_VALUE);
982 ATTR(TRANSPARENT_BLUE_VALUE);
983 ATTR(BIND_TO_TEXTURE_RGB);
984 ATTR(BIND_TO_TEXTURE_RGBA);
985 ATTR(MIN_SWAP_INTERVAL);
986 ATTR(MAX_SWAP_INTERVAL);
987 ATTR(LUMINANCE_SIZE);
988 ATTR(ALPHA_MASK_SIZE);
989 ATTR(COLOR_BUFFER_TYPE);
990 ATTR(RENDERABLE_TYPE);
991 ATTR(CONFORMANT);
993 #undef ATTR
996 void EglDisplay::DumpEGLConfigs() const {
997 int nc = 0;
998 mLib->fGetConfigs(mDisplay, nullptr, 0, &nc);
999 std::vector<EGLConfig> ec(nc);
1000 mLib->fGetConfigs(mDisplay, ec.data(), ec.size(), &nc);
1002 for (int i = 0; i < nc; ++i) {
1003 printf_stderr("========= EGL Config %d ========\n", i);
1004 DumpEGLConfig(ec[i]);
1008 static bool ShouldTrace() {
1009 static bool ret = gfxEnv::GlDebugVerbose();
1010 return ret;
1013 void BeforeEGLCall(const char* glFunction) {
1014 if (ShouldTrace()) {
1015 printf_stderr("[egl] > %s\n", glFunction);
1019 void AfterEGLCall(const char* glFunction) {
1020 if (ShouldTrace()) {
1021 printf_stderr("[egl] < %s\n", glFunction);
1025 } /* namespace gl */
1026 } /* namespace mozilla */