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