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