1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #if defined(MOZ_WIDGET_GTK)
7 #  define GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget) \
8     ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_EGL_WINDOW))
9 #  define GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget) \
10     (aWidget->AsGTK()->GetEGLNativeWindow())
11 #elif defined(MOZ_WIDGET_ANDROID)
12 #  define GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget) \
13     ((EGLNativeWindowType)aWidget->GetNativeData(NS_JAVA_SURFACE))
14 #  define GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget) \
15     (aWidget->AsAndroid()->GetEGLNativeWindow())
16 #elif defined(XP_WIN)
17 #  define GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget) \
18     ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW))
19 #  define GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget) \
20     ((EGLNativeWindowType)aWidget->AsWindows()->GetHwnd())
21 #else
22 #  define GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget) \
23     ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW))
24 #  define GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget)     \
25     ((EGLNativeWindowType)aWidget->RealWidget()->GetNativeData( \
26         NS_NATIVE_WINDOW))
27 #endif
28 
29 #if defined(XP_UNIX)
30 #  ifdef MOZ_WIDGET_ANDROID
31 #    include <android/native_window.h>
32 #    include <android/native_window_jni.h>
33 #    include "mozilla/widget/AndroidCompositorWidget.h"
34 #  endif
35 
36 #  define GLES2_LIB "libGLESv2.so"
37 #  define GLES2_LIB2 "libGLESv2.so.2"
38 
39 #elif defined(XP_WIN)
40 #  include "mozilla/widget/WinCompositorWidget.h"
41 #  include "nsIFile.h"
42 
43 #  define GLES2_LIB "libGLESv2.dll"
44 
45 #  ifndef WIN32_LEAN_AND_MEAN
46 #    define WIN32_LEAN_AND_MEAN 1
47 #  endif
48 
49 #  include <windows.h>
50 #else
51 #  error "Platform not recognized"
52 #endif
53 
54 #include "gfxCrashReporterUtils.h"
55 #include "gfxFailure.h"
56 #include "gfxPlatform.h"
57 #include "gfxUtils.h"
58 #include "GLBlitHelper.h"
59 #include "GLContextEGL.h"
60 #include "GLContextProvider.h"
61 #include "GLLibraryEGL.h"
62 #include "GLLibraryLoader.h"
63 #include "mozilla/ArrayUtils.h"
64 #include "mozilla/Preferences.h"
65 #include "mozilla/Services.h"
66 #include "mozilla/StaticPrefs_gfx.h"
67 #include "mozilla/gfx/gfxVars.h"
68 #include "mozilla/gfx/BuildConstants.h"
69 #include "mozilla/gfx/Logging.h"
70 #include "mozilla/layers/CompositorOptions.h"
71 #include "mozilla/widget/CompositorWidget.h"
72 #include "nsDebug.h"
73 #include "nsIWidget.h"
74 #include "nsThreadUtils.h"
75 #include "ScopedGLHelpers.h"
76 
77 #if defined(MOZ_WIDGET_GTK)
78 #  include "mozilla/widget/GtkCompositorWidget.h"
79 #  include "mozilla/WidgetUtilsGtk.h"
80 #  if defined(MOZ_WAYLAND)
81 #    include <gdk/gdkwayland.h>
82 #    include <wayland-egl.h>
83 #    define MOZ_GTK_WAYLAND 1
84 #  endif
85 #endif
86 
87 struct wl_egl_window;
88 
89 using namespace mozilla::gfx;
90 
91 namespace mozilla {
92 namespace gl {
93 
94 using namespace mozilla::widget;
95 
96 #if defined(MOZ_WAYLAND)
97 class WaylandGLSurface {
98  public:
99   WaylandGLSurface(struct wl_surface* aWaylandSurface,
100                    struct wl_egl_window* aEGLWindow);
101   ~WaylandGLSurface();
102 
103  private:
104   struct wl_surface* mWaylandSurface;
105   struct wl_egl_window* mEGLWindow;
106 };
107 
108 static nsTHashMap<nsPtrHashKey<void>, WaylandGLSurface*> sWaylandGLSurface;
109 
DeleteWaylandGLSurface(EGLSurface surface)110 void DeleteWaylandGLSurface(EGLSurface surface) {
111 #  ifdef MOZ_GTK_WAYLAND
112   // We're running on Wayland which means our EGLSurface may
113   // have attached Wayland backend data which must be released.
114   if (GdkIsWaylandDisplay()) {
115     auto entry = sWaylandGLSurface.Lookup(surface);
116     if (entry) {
117       delete entry.Data();
118       entry.Remove();
119     }
120   }
121 #  endif
122 }
123 #endif
124 
125 static bool CreateConfigScreen(EglDisplay&, EGLConfig* const aConfig,
126                                const bool aEnableDepthBuffer,
127                                const bool aUseGles);
128 
129 // append three zeros at the end of attribs list to work around
130 // EGL implementation bugs that iterate until they find 0, instead of
131 // EGL_NONE. See bug 948406.
132 #define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
133   LOCAL_EGL_NONE, 0, 0, 0
134 
135 static EGLint kTerminationAttribs[] = {
136     EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS};
137 
next_power_of_two(int v)138 static int next_power_of_two(int v) {
139   v--;
140   v |= v >> 1;
141   v |= v >> 2;
142   v |= v >> 4;
143   v |= v >> 8;
144   v |= v >> 16;
145   v++;
146 
147   return v;
148 }
149 
is_power_of_two(int v)150 static bool is_power_of_two(int v) {
151   NS_ASSERTION(v >= 0, "bad value");
152 
153   if (v == 0) return true;
154 
155   return (v & (v - 1)) == 0;
156 }
157 
DestroySurface(EglDisplay & egl,const EGLSurface oldSurface)158 static void DestroySurface(EglDisplay& egl, const EGLSurface oldSurface) {
159   if (oldSurface != EGL_NO_SURFACE) {
160     // TODO: This breaks TLS MakeCurrent caching.
161     egl.fMakeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
162     egl.fDestroySurface(oldSurface);
163 #if defined(MOZ_WAYLAND)
164     DeleteWaylandGLSurface(oldSurface);
165 #endif
166   }
167 }
168 
CreateFallbackSurface(EglDisplay & egl,const EGLConfig & config)169 static EGLSurface CreateFallbackSurface(EglDisplay& egl,
170                                         const EGLConfig& config) {
171   if (egl.IsExtensionSupported(EGLExtension::KHR_surfaceless_context)) {
172     // We don't need a PBuffer surface in this case
173     return EGL_NO_SURFACE;
174   }
175 
176   std::vector<EGLint> pbattrs;
177   pbattrs.push_back(LOCAL_EGL_WIDTH);
178   pbattrs.push_back(1);
179   pbattrs.push_back(LOCAL_EGL_HEIGHT);
180   pbattrs.push_back(1);
181 
182   for (const auto& cur : kTerminationAttribs) {
183     pbattrs.push_back(cur);
184   }
185 
186   EGLSurface surface = egl.fCreatePbufferSurface(config, pbattrs.data());
187   if (!surface) {
188     MOZ_CRASH("Failed to create fallback EGLSurface");
189   }
190 
191   return surface;
192 }
193 
CreateSurfaceFromNativeWindow(EglDisplay & egl,const EGLNativeWindowType window,const EGLConfig config)194 static EGLSurface CreateSurfaceFromNativeWindow(
195     EglDisplay& egl, const EGLNativeWindowType window, const EGLConfig config) {
196   MOZ_ASSERT(window);
197   EGLSurface newSurface = EGL_NO_SURFACE;
198 
199 #ifdef MOZ_WIDGET_ANDROID
200   JNIEnv* const env = jni::GetEnvForThread();
201   ANativeWindow* const nativeWindow =
202       ANativeWindow_fromSurface(env, reinterpret_cast<jobject>(window));
203   if (!nativeWindow) {
204     return EGL_NO_SURFACE;
205   }
206   const auto& display = egl.mLib->fGetDisplay(EGL_DEFAULT_DISPLAY);
207   newSurface = egl.mLib->fCreateWindowSurface(display, config, nativeWindow, 0);
208   ANativeWindow_release(nativeWindow);
209 #else
210   newSurface = egl.fCreateWindowSurface(config, window, 0);
211   if (!newSurface) {
212     const auto err = egl.mLib->fGetError();
213     gfxCriticalNote << "Failed to create EGLSurface!: " << gfx::hexa(err);
214   }
215 #endif
216   return newSurface;
217 }
218 
219 /* GLContextEGLFactory class was added as a friend of GLContextEGL
220  * so that it could access  GLContextEGL::CreateGLContext. This was
221  * done so that a new function would not need to be added to the shared
222  * GLContextProvider interface.
223  */
224 class GLContextEGLFactory {
225  public:
226   static already_AddRefed<GLContext> Create(EGLNativeWindowType aWindow,
227                                             bool aHardwareWebRender);
228   static already_AddRefed<GLContext> CreateImpl(EGLNativeWindowType aWindow,
229                                                 bool aHardwareWebRender,
230                                                 bool aUseGles);
231 
232  private:
233   GLContextEGLFactory() = default;
234   ~GLContextEGLFactory() = default;
235 };
236 
CreateImpl(EGLNativeWindowType aWindow,bool aHardwareWebRender,bool aUseGles)237 already_AddRefed<GLContext> GLContextEGLFactory::CreateImpl(
238     EGLNativeWindowType aWindow, bool aHardwareWebRender, bool aUseGles) {
239   nsCString failureId;
240   const auto lib = gl::DefaultEglLibrary(&failureId);
241   if (!lib) {
242     gfxCriticalNote << "Failed[3] to load EGL library: " << failureId.get();
243     return nullptr;
244   }
245   const auto egl = lib->CreateDisplay(true, &failureId);
246   if (!egl) {
247     gfxCriticalNote << "Failed[3] to create EGL library  display: "
248                     << failureId.get();
249     return nullptr;
250   }
251 
252   bool doubleBuffered = true;
253 
254   EGLConfig config;
255   if (aHardwareWebRender && egl->mLib->IsANGLE()) {
256     // Force enable alpha channel to make sure ANGLE use correct framebuffer
257     // formart
258     const int bpp = 32;
259     if (!CreateConfig(*egl, &config, bpp, false, aUseGles)) {
260       gfxCriticalNote << "Failed to create EGLConfig for WebRender ANGLE!";
261       return nullptr;
262     }
263   } else if (kIsWayland || kIsX11) {
264     const int bpp = 32;
265     if (!CreateConfig(*egl, &config, bpp, false, aUseGles)) {
266       gfxCriticalNote << "Failed to create EGLConfig for WebRender!";
267       return nullptr;
268     }
269   } else {
270     if (!CreateConfigScreen(*egl, &config,
271                             /* aEnableDepthBuffer */ false, aUseGles)) {
272       gfxCriticalNote << "Failed to create EGLConfig!";
273       return nullptr;
274     }
275   }
276 
277   EGLSurface surface = EGL_NO_SURFACE;
278   if (aWindow) {
279     surface = mozilla::gl::CreateSurfaceFromNativeWindow(*egl, aWindow, config);
280     if (!surface) {
281       return nullptr;
282     }
283   }
284 
285   CreateContextFlags flags = CreateContextFlags::NONE;
286   if (aHardwareWebRender &&
287       StaticPrefs::gfx_webrender_prefer_robustness_AtStartup()) {
288     flags |= CreateContextFlags::PREFER_ROBUSTNESS;
289   }
290   if (aHardwareWebRender && aUseGles) {
291     flags |= CreateContextFlags::PREFER_ES3;
292   }
293   if (!aHardwareWebRender) {
294     flags |= CreateContextFlags::REQUIRE_COMPAT_PROFILE;
295   }
296 
297   const auto desc = GLContextDesc{{flags}, false};
298   RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(
299       egl, desc, config, surface, aUseGles, &failureId);
300   if (!gl) {
301     const auto err = egl->mLib->fGetError();
302     gfxCriticalNote << "Failed to create EGLContext!: " << gfx::hexa(err);
303     mozilla::gl::DestroySurface(*egl, surface);
304     return nullptr;
305   }
306 
307   gl->MakeCurrent();
308   gl->SetIsDoubleBuffered(doubleBuffered);
309 
310 #ifdef MOZ_WIDGET_GTK
311   if (surface) {
312     const int interval = gfxVars::SwapIntervalEGL() ? 1 : 0;
313     egl->fSwapInterval(interval);
314   }
315 #endif
316   if (aHardwareWebRender && egl->mLib->IsANGLE()) {
317     MOZ_ASSERT(doubleBuffered);
318     const int interval = gfxVars::SwapIntervalEGL() ? 1 : 0;
319     egl->fSwapInterval(interval);
320   }
321   return gl.forget();
322 }
323 
Create(EGLNativeWindowType aWindow,bool aHardwareWebRender)324 already_AddRefed<GLContext> GLContextEGLFactory::Create(
325     EGLNativeWindowType aWindow, bool aHardwareWebRender) {
326   bool preferGles;
327 #if defined(MOZ_WIDGET_ANDROID)
328   preferGles = true;
329 #else
330   preferGles = StaticPrefs::gfx_egl_prefer_gles_enabled_AtStartup();
331 #endif  // defined(MOZ_WIDGET_ANDROID)
332 
333   RefPtr<GLContext> glContext =
334       CreateImpl(aWindow, aHardwareWebRender, preferGles);
335 #if !defined(MOZ_WIDGET_ANDROID)
336   if (!glContext) {
337     glContext = CreateImpl(aWindow, aHardwareWebRender, !preferGles);
338   }
339 #endif  // !defined(MOZ_WIDGET_ANDROID)
340   return glContext.forget();
341 }
342 
343 /* static */
CreateEGLSurfaceForCompositorWidget(widget::CompositorWidget * aCompositorWidget,const EGLConfig aConfig)344 EGLSurface GLContextEGL::CreateEGLSurfaceForCompositorWidget(
345     widget::CompositorWidget* aCompositorWidget, const EGLConfig aConfig) {
346   nsCString discardFailureId;
347   const auto egl = DefaultEglDisplay(&discardFailureId);
348   if (!egl) {
349     gfxCriticalNote << "Failed to load EGL library 6!";
350     return EGL_NO_SURFACE;
351   }
352 
353   MOZ_ASSERT(aCompositorWidget);
354   EGLNativeWindowType window =
355       GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget);
356   if (!window) {
357     gfxCriticalNote << "window is null";
358     return EGL_NO_SURFACE;
359   }
360 
361   return mozilla::gl::CreateSurfaceFromNativeWindow(*egl, window, aConfig);
362 }
363 
GLContextEGL(const std::shared_ptr<EglDisplay> egl,const GLContextDesc & desc,EGLConfig config,EGLSurface surface,EGLContext context)364 GLContextEGL::GLContextEGL(const std::shared_ptr<EglDisplay> egl,
365                            const GLContextDesc& desc, EGLConfig config,
366                            EGLSurface surface, EGLContext context)
367     : GLContext(desc, nullptr, false),
368       mEgl(egl),
369       mConfig(config),
370       mContext(context),
371       mSurface(surface),
372       mFallbackSurface(CreateFallbackSurface(*mEgl, mConfig)) {
373 #ifdef DEBUG
374   printf_stderr("Initializing context %p surface %p on display %p\n", mContext,
375                 mSurface, mEgl->mDisplay);
376 #endif
377 }
378 
OnMarkDestroyed()379 void GLContextEGL::OnMarkDestroyed() {
380   if (mSurfaceOverride != EGL_NO_SURFACE) {
381     SetEGLSurfaceOverride(EGL_NO_SURFACE);
382   }
383 }
384 
~GLContextEGL()385 GLContextEGL::~GLContextEGL() {
386   MarkDestroyed();
387 
388   // Wrapped context should not destroy eglContext/Surface
389   if (!mOwnsContext) {
390     return;
391   }
392 
393 #ifdef DEBUG
394   printf_stderr("Destroying context %p surface %p on display %p\n", mContext,
395                 mSurface, mEgl->mDisplay);
396 #endif
397 
398   mEgl->fDestroyContext(mContext);
399 
400   mozilla::gl::DestroySurface(*mEgl, mSurface);
401   mozilla::gl::DestroySurface(*mEgl, mFallbackSurface);
402 }
403 
Init()404 bool GLContextEGL::Init() {
405   if (!GLContext::Init()) return false;
406 
407   bool current = MakeCurrent();
408   if (!current) {
409     gfx::LogFailure("Couldn't get device attachments for device."_ns);
410     return false;
411   }
412 
413   mShareWithEGLImage =
414       mEgl->HasKHRImageBase() &&
415       mEgl->IsExtensionSupported(EGLExtension::KHR_gl_texture_2D_image) &&
416       IsExtensionSupported(OES_EGL_image);
417 
418   return true;
419 }
420 
BindTexImage()421 bool GLContextEGL::BindTexImage() {
422   if (!mSurface) return false;
423 
424   if (mBound && !ReleaseTexImage()) return false;
425 
426   EGLBoolean success =
427       mEgl->fBindTexImage((EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER);
428   if (success == LOCAL_EGL_FALSE) return false;
429 
430   mBound = true;
431   return true;
432 }
433 
ReleaseTexImage()434 bool GLContextEGL::ReleaseTexImage() {
435   if (!mBound) return true;
436 
437   if (!mSurface) return false;
438 
439   EGLBoolean success;
440   success = mEgl->fReleaseTexImage((EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER);
441   if (success == LOCAL_EGL_FALSE) return false;
442 
443   mBound = false;
444   return true;
445 }
446 
SetEGLSurfaceOverride(EGLSurface surf)447 void GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf) {
448   mSurfaceOverride = surf;
449   DebugOnly<bool> ok = MakeCurrent(true);
450   MOZ_ASSERT(ok);
451 }
452 
MakeCurrentImpl() const453 bool GLContextEGL::MakeCurrentImpl() const {
454   EGLSurface surface =
455       (mSurfaceOverride != EGL_NO_SURFACE) ? mSurfaceOverride : mSurface;
456   if (!surface) {
457     surface = mFallbackSurface;
458   }
459 
460   const bool succeeded = mEgl->fMakeCurrent(surface, surface, mContext);
461   if (!succeeded) {
462     const auto eglError = mEgl->mLib->fGetError();
463     if (eglError == LOCAL_EGL_CONTEXT_LOST) {
464       OnContextLostError();
465     } else {
466       NS_WARNING("Failed to make GL context current!");
467 #ifdef DEBUG
468       printf_stderr("EGL Error: 0x%04x\n", eglError);
469 #endif
470     }
471   }
472 
473   return succeeded;
474 }
475 
IsCurrentImpl() const476 bool GLContextEGL::IsCurrentImpl() const {
477   return mEgl->mLib->fGetCurrentContext() == mContext;
478 }
479 
RenewSurface(CompositorWidget * aWidget)480 bool GLContextEGL::RenewSurface(CompositorWidget* aWidget) {
481   if (!mOwnsContext) {
482     return false;
483   }
484   // unconditionally release the surface and create a new one. Don't try to
485   // optimize this away. If we get here, then by definition we know that we want
486   // to get a new surface.
487   ReleaseSurface();
488   MOZ_ASSERT(aWidget);
489 
490   EGLNativeWindowType nativeWindow =
491       GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget);
492   if (nativeWindow) {
493     mSurface = mozilla::gl::CreateSurfaceFromNativeWindow(*mEgl, nativeWindow,
494                                                           mConfig);
495     if (!mSurface) {
496       NS_WARNING("Failed to create EGLSurface from native window");
497       return false;
498     }
499   }
500   const bool ok = MakeCurrent(true);
501   MOZ_ASSERT(ok);
502 #ifdef MOZ_WIDGET_GTK
503   if (mSurface) {
504     const int interval = gfxVars::SwapIntervalEGL() ? 1 : 0;
505     mEgl->fSwapInterval(interval);
506   }
507 #endif
508   return ok;
509 }
510 
ReleaseSurface()511 void GLContextEGL::ReleaseSurface() {
512   if (mOwnsContext) {
513     mozilla::gl::DestroySurface(*mEgl, mSurface);
514   }
515   if (mSurface == mSurfaceOverride) {
516     mSurfaceOverride = EGL_NO_SURFACE;
517   }
518   mSurface = EGL_NO_SURFACE;
519 }
520 
GetSymbolLoader() const521 Maybe<SymbolLoader> GLContextEGL::GetSymbolLoader() const {
522   return mEgl->mLib->GetSymbolLoader();
523 }
524 
SwapBuffers()525 bool GLContextEGL::SwapBuffers() {
526   EGLSurface surface =
527       mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface;
528   if (surface) {
529     if ((mEgl->IsExtensionSupported(
530              EGLExtension::EXT_swap_buffers_with_damage) ||
531          mEgl->IsExtensionSupported(
532              EGLExtension::KHR_swap_buffers_with_damage))) {
533       std::vector<EGLint> rects;
534       for (auto iter = mDamageRegion.RectIter(); !iter.Done(); iter.Next()) {
535         const IntRect& r = iter.Get();
536         rects.push_back(r.X());
537         rects.push_back(r.Y());
538         rects.push_back(r.Width());
539         rects.push_back(r.Height());
540       }
541       mDamageRegion.SetEmpty();
542       return mEgl->fSwapBuffersWithDamage(surface, rects.data(),
543                                           rects.size() / 4);
544     }
545     return mEgl->fSwapBuffers(surface);
546   } else {
547     return false;
548   }
549 }
550 
SetDamage(const nsIntRegion & aDamageRegion)551 void GLContextEGL::SetDamage(const nsIntRegion& aDamageRegion) {
552   mDamageRegion = aDamageRegion;
553 }
554 
GetWSIInfo(nsCString * const out) const555 void GLContextEGL::GetWSIInfo(nsCString* const out) const {
556   out->AppendLiteral("EGL_VENDOR: ");
557   out->Append(
558       (const char*)mEgl->mLib->fQueryString(mEgl->mDisplay, LOCAL_EGL_VENDOR));
559 
560   out->AppendLiteral("\nEGL_VERSION: ");
561   out->Append(
562       (const char*)mEgl->mLib->fQueryString(mEgl->mDisplay, LOCAL_EGL_VERSION));
563 
564   out->AppendLiteral("\nEGL_EXTENSIONS: ");
565   out->Append((const char*)mEgl->mLib->fQueryString(mEgl->mDisplay,
566                                                     LOCAL_EGL_EXTENSIONS));
567 
568 #ifndef ANDROID  // This query will crash some old android.
569   out->AppendLiteral("\nEGL_EXTENSIONS(nullptr): ");
570   out->Append(
571       (const char*)mEgl->mLib->fQueryString(nullptr, LOCAL_EGL_EXTENSIONS));
572 #endif
573 }
574 
HasExtBufferAge() const575 bool GLContextEGL::HasExtBufferAge() const {
576   return mEgl->IsExtensionSupported(EGLExtension::EXT_buffer_age);
577 }
578 
HasKhrPartialUpdate() const579 bool GLContextEGL::HasKhrPartialUpdate() const {
580   return mEgl->IsExtensionSupported(EGLExtension::KHR_partial_update);
581 }
582 
GetBufferAge() const583 GLint GLContextEGL::GetBufferAge() const {
584   EGLSurface surface =
585       mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface;
586 
587   if (surface && (HasExtBufferAge() || HasKhrPartialUpdate())) {
588     EGLint result;
589     mEgl->fQuerySurface(surface, LOCAL_EGL_BUFFER_AGE_EXT, &result);
590     return result;
591   }
592 
593   return 0;
594 }
595 
596 #define LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ 0x6000
597 
CreateGLContext(const std::shared_ptr<EglDisplay> egl,const GLContextDesc & desc,EGLConfig config,EGLSurface surface,const bool useGles,nsACString * const out_failureId)598 RefPtr<GLContextEGL> GLContextEGL::CreateGLContext(
599     const std::shared_ptr<EglDisplay> egl, const GLContextDesc& desc,
600     EGLConfig config, EGLSurface surface, const bool useGles,
601     nsACString* const out_failureId) {
602   const auto& flags = desc.flags;
603 
604   std::vector<EGLint> required_attribs;
605 
606   if (useGles) {
607     // TODO: This fBindAPI could be more thread-safe
608     if (egl->mLib->fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) {
609       *out_failureId = "FEATURE_FAILURE_EGL_ES"_ns;
610       NS_WARNING("Failed to bind API to GLES!");
611       return nullptr;
612     }
613     required_attribs.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION);
614     if (flags & CreateContextFlags::PREFER_ES3) {
615       required_attribs.push_back(3);
616     } else {
617       required_attribs.push_back(2);
618     }
619   } else {
620     if (egl->mLib->fBindAPI(LOCAL_EGL_OPENGL_API) == LOCAL_EGL_FALSE) {
621       *out_failureId = "FEATURE_FAILURE_EGL"_ns;
622       NS_WARNING("Failed to bind API to GL!");
623       return nullptr;
624     }
625     if (flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE) {
626       required_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK);
627       required_attribs.push_back(
628           LOCAL_EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
629       required_attribs.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION);
630       required_attribs.push_back(2);
631     } else {
632       // !REQUIRE_COMPAT_PROFILE means core profle.
633       required_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK);
634       required_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
635       required_attribs.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION);
636       required_attribs.push_back(3);
637       required_attribs.push_back(LOCAL_EGL_CONTEXT_MINOR_VERSION);
638       required_attribs.push_back(2);
639     }
640   }
641 
642   if ((flags & CreateContextFlags::PREFER_EXACT_VERSION) &&
643       egl->mLib->IsANGLE()) {
644     required_attribs.push_back(
645         LOCAL_EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE);
646     required_attribs.push_back(LOCAL_EGL_FALSE);
647   }
648 
649   const auto debugFlags = GLContext::ChooseDebugFlags(flags);
650   if (!debugFlags && flags & CreateContextFlags::NO_VALIDATION &&
651       egl->IsExtensionSupported(EGLExtension::KHR_create_context_no_error)) {
652     required_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_NO_ERROR_KHR);
653     required_attribs.push_back(LOCAL_EGL_TRUE);
654   }
655 
656   if (flags & CreateContextFlags::PROVOKING_VERTEX_DONT_CARE &&
657       egl->IsExtensionSupported(
658           EGLExtension::MOZ_create_context_provoking_vertex_dont_care)) {
659     required_attribs.push_back(
660         LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ);
661     required_attribs.push_back(LOCAL_EGL_TRUE);
662   }
663 
664   std::vector<EGLint> ext_robustness_attribs;
665   std::vector<EGLint> ext_rbab_attribs;  // RBAB: Robust Buffer Access Behavior
666   std::vector<EGLint> khr_robustness_attribs;
667   std::vector<EGLint> khr_rbab_attribs;  // RBAB: Robust Buffer Access Behavior
668   if (flags & CreateContextFlags::PREFER_ROBUSTNESS) {
669     std::vector<EGLint> base_robustness_attribs = required_attribs;
670     if (egl->IsExtensionSupported(
671             EGLExtension::NV_robustness_video_memory_purge)) {
672       base_robustness_attribs.push_back(
673           LOCAL_EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV);
674       base_robustness_attribs.push_back(LOCAL_EGL_TRUE);
675     }
676 
677     if (egl->IsExtensionSupported(
678             EGLExtension::EXT_create_context_robustness)) {
679       ext_robustness_attribs = base_robustness_attribs;
680       ext_robustness_attribs.push_back(
681           LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
682       ext_robustness_attribs.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT);
683 
684       if (gfxVars::AllowEglRbab()) {
685         ext_rbab_attribs = ext_robustness_attribs;
686         ext_rbab_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
687         ext_rbab_attribs.push_back(LOCAL_EGL_TRUE);
688       }
689     }
690 
691     if (egl->IsExtensionSupported(EGLExtension::KHR_create_context)) {
692       khr_robustness_attribs = base_robustness_attribs;
693       khr_robustness_attribs.push_back(
694           LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR);
695       khr_robustness_attribs.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_KHR);
696 
697       khr_rbab_attribs = khr_robustness_attribs;
698       khr_rbab_attribs.push_back(LOCAL_EGL_CONTEXT_FLAGS_KHR);
699       khr_rbab_attribs.push_back(
700           LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR);
701     }
702   }
703 
704   const auto fnCreate = [&](const std::vector<EGLint>& attribs) {
705     auto terminated_attribs = attribs;
706 
707     for (const auto& cur : kTerminationAttribs) {
708       terminated_attribs.push_back(cur);
709     }
710 
711     return egl->fCreateContext(config, EGL_NO_CONTEXT,
712                                terminated_attribs.data());
713   };
714 
715   EGLContext context;
716   do {
717     if (!khr_rbab_attribs.empty()) {
718       context = fnCreate(khr_rbab_attribs);
719       if (context) break;
720       NS_WARNING("Failed to create EGLContext with khr_rbab_attribs");
721     }
722 
723     if (!ext_rbab_attribs.empty()) {
724       context = fnCreate(ext_rbab_attribs);
725       if (context) break;
726       NS_WARNING("Failed to create EGLContext with ext_rbab_attribs");
727     }
728 
729     if (!khr_robustness_attribs.empty()) {
730       context = fnCreate(khr_robustness_attribs);
731       if (context) break;
732       NS_WARNING("Failed to create EGLContext with khr_robustness_attribs");
733     }
734 
735     if (!ext_robustness_attribs.empty()) {
736       context = fnCreate(ext_robustness_attribs);
737       if (context) break;
738       NS_WARNING("Failed to create EGLContext with ext_robustness_attribs");
739     }
740 
741     context = fnCreate(required_attribs);
742     if (context) break;
743     NS_WARNING("Failed to create EGLContext with required_attribs");
744 
745     *out_failureId = "FEATURE_FAILURE_EGL_CREATE"_ns;
746     return nullptr;
747   } while (false);
748   MOZ_ASSERT(context);
749 
750   RefPtr<GLContextEGL> glContext =
751       new GLContextEGL(egl, desc, config, surface, context);
752   if (!glContext->Init()) {
753     *out_failureId = "FEATURE_FAILURE_EGL_INIT"_ns;
754     return nullptr;
755   }
756 
757   if (GLContext::ShouldSpew()) {
758     printf_stderr("new GLContextEGL %p on EGLDisplay %p\n", glContext.get(),
759                   egl->mDisplay);
760   }
761 
762   return glContext;
763 }
764 
765 // static
CreatePBufferSurfaceTryingPowerOfTwo(EglDisplay & egl,EGLConfig config,EGLenum bindToTextureFormat,mozilla::gfx::IntSize & pbsize)766 EGLSurface GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
767     EglDisplay& egl, EGLConfig config, EGLenum bindToTextureFormat,
768     mozilla::gfx::IntSize& pbsize) {
769   nsTArray<EGLint> pbattrs(16);
770   EGLSurface surface = nullptr;
771 
772 TRY_AGAIN_POWER_OF_TWO:
773   pbattrs.Clear();
774   pbattrs.AppendElement(LOCAL_EGL_WIDTH);
775   pbattrs.AppendElement(pbsize.width);
776   pbattrs.AppendElement(LOCAL_EGL_HEIGHT);
777   pbattrs.AppendElement(pbsize.height);
778 
779   if (bindToTextureFormat != LOCAL_EGL_NONE) {
780     pbattrs.AppendElement(LOCAL_EGL_TEXTURE_TARGET);
781     pbattrs.AppendElement(LOCAL_EGL_TEXTURE_2D);
782 
783     pbattrs.AppendElement(LOCAL_EGL_TEXTURE_FORMAT);
784     pbattrs.AppendElement(bindToTextureFormat);
785   }
786 
787   for (const auto& cur : kTerminationAttribs) {
788     pbattrs.AppendElement(cur);
789   }
790 
791   surface = egl.fCreatePbufferSurface(config, &pbattrs[0]);
792   if (!surface) {
793     if (!is_power_of_two(pbsize.width) || !is_power_of_two(pbsize.height)) {
794       if (!is_power_of_two(pbsize.width))
795         pbsize.width = next_power_of_two(pbsize.width);
796       if (!is_power_of_two(pbsize.height))
797         pbsize.height = next_power_of_two(pbsize.height);
798 
799       NS_WARNING("Failed to create pbuffer, trying power of two dims");
800       goto TRY_AGAIN_POWER_OF_TWO;
801     }
802 
803     NS_WARNING("Failed to create pbuffer surface");
804     return nullptr;
805   }
806 
807   return surface;
808 }
809 
810 #if defined(MOZ_WAYLAND)
WaylandGLSurface(struct wl_surface * aWaylandSurface,struct wl_egl_window * aEGLWindow)811 WaylandGLSurface::WaylandGLSurface(struct wl_surface* aWaylandSurface,
812                                    struct wl_egl_window* aEGLWindow)
813     : mWaylandSurface(aWaylandSurface), mEGLWindow(aEGLWindow) {}
814 
~WaylandGLSurface()815 WaylandGLSurface::~WaylandGLSurface() {
816   wl_egl_window_destroy(mEGLWindow);
817   wl_surface_destroy(mWaylandSurface);
818 }
819 #endif
820 
821 // static
CreateWaylandBufferSurface(EglDisplay & egl,EGLConfig config,mozilla::gfx::IntSize & pbsize)822 EGLSurface GLContextEGL::CreateWaylandBufferSurface(
823     EglDisplay& egl, EGLConfig config, mozilla::gfx::IntSize& pbsize) {
824   wl_egl_window* eglwindow = nullptr;
825 
826 #ifdef MOZ_GTK_WAYLAND
827   struct wl_compositor* compositor =
828       gdk_wayland_display_get_wl_compositor(gdk_display_get_default());
829   struct wl_surface* wlsurface = wl_compositor_create_surface(compositor);
830   eglwindow = wl_egl_window_create(wlsurface, pbsize.width, pbsize.height);
831 #endif
832   if (!eglwindow) return nullptr;
833 
834   const auto surface = egl.fCreateWindowSurface(
835       config, reinterpret_cast<EGLNativeWindowType>(eglwindow), 0);
836   if (surface) {
837 #ifdef MOZ_GTK_WAYLAND
838     MOZ_ASSERT(!sWaylandGLSurface.Contains(surface));
839     sWaylandGLSurface.LookupOrInsert(
840         surface, new WaylandGLSurface(wlsurface, eglwindow));
841 #endif
842   }
843 
844   return surface;
845 }
846 
847 static const EGLint kEGLConfigAttribsRGB16[] = {
848     LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
849     LOCAL_EGL_RED_SIZE,     5,
850     LOCAL_EGL_GREEN_SIZE,   6,
851     LOCAL_EGL_BLUE_SIZE,    5,
852     LOCAL_EGL_ALPHA_SIZE,   0};
853 
854 static const EGLint kEGLConfigAttribsRGB24[] = {
855     LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
856     LOCAL_EGL_RED_SIZE,     8,
857     LOCAL_EGL_GREEN_SIZE,   8,
858     LOCAL_EGL_BLUE_SIZE,    8,
859     LOCAL_EGL_ALPHA_SIZE,   0};
860 
861 static const EGLint kEGLConfigAttribsRGBA32[] = {
862     LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
863     LOCAL_EGL_RED_SIZE,     8,
864     LOCAL_EGL_GREEN_SIZE,   8,
865     LOCAL_EGL_BLUE_SIZE,    8,
866     LOCAL_EGL_ALPHA_SIZE,   8};
867 
CreateConfig(EglDisplay & aEgl,EGLConfig * aConfig,int32_t aDepth,bool aEnableDepthBuffer,bool aUseGles,bool aAllowFallback)868 bool CreateConfig(EglDisplay& aEgl, EGLConfig* aConfig, int32_t aDepth,
869                   bool aEnableDepthBuffer, bool aUseGles, bool aAllowFallback) {
870   EGLConfig configs[64];
871   std::vector<EGLint> attribs;
872   EGLint ncfg = ArrayLength(configs);
873 
874   switch (aDepth) {
875     case 16:
876       for (const auto& cur : kEGLConfigAttribsRGB16) {
877         attribs.push_back(cur);
878       }
879       break;
880     case 24:
881       for (const auto& cur : kEGLConfigAttribsRGB24) {
882         attribs.push_back(cur);
883       }
884       break;
885     case 32:
886       for (const auto& cur : kEGLConfigAttribsRGBA32) {
887         attribs.push_back(cur);
888       }
889       break;
890     default:
891       NS_ERROR("Unknown pixel depth");
892       return false;
893   }
894 
895   if (aUseGles) {
896     attribs.push_back(LOCAL_EGL_RENDERABLE_TYPE);
897     attribs.push_back(LOCAL_EGL_OPENGL_ES2_BIT);
898   }
899   for (const auto& cur : kTerminationAttribs) {
900     attribs.push_back(cur);
901   }
902 
903   if (!aEgl.fChooseConfig(attribs.data(), configs, ncfg, &ncfg) || ncfg < 1) {
904     return false;
905   }
906 
907   Maybe<EGLConfig> fallbackConfig;
908 
909   for (int j = 0; j < ncfg; ++j) {
910     EGLConfig config = configs[j];
911     EGLint r, g, b, a;
912     if (aEgl.fGetConfigAttrib(config, LOCAL_EGL_RED_SIZE, &r) &&
913         aEgl.fGetConfigAttrib(config, LOCAL_EGL_GREEN_SIZE, &g) &&
914         aEgl.fGetConfigAttrib(config, LOCAL_EGL_BLUE_SIZE, &b) &&
915         aEgl.fGetConfigAttrib(config, LOCAL_EGL_ALPHA_SIZE, &a) &&
916         ((aDepth == 16 && r == 5 && g == 6 && b == 5) ||
917          (aDepth == 24 && r == 8 && g == 8 && b == 8) ||
918          (aDepth == 32 && r == 8 && g == 8 && b == 8 && a == 8))) {
919       EGLint z;
920       if (aEnableDepthBuffer) {
921         if (!aEgl.fGetConfigAttrib(config, LOCAL_EGL_DEPTH_SIZE, &z) ||
922             z != 24) {
923           continue;
924         }
925       }
926 #ifdef MOZ_X11
927       if (GdkIsX11Display()) {
928         int configVisualID;
929         if (!aEgl.fGetConfigAttrib(config, LOCAL_EGL_NATIVE_VISUAL_ID,
930                                    &configVisualID)) {
931           continue;
932         }
933 
934         XVisualInfo visual_info_template, *visual_info;
935         int num_visuals;
936 
937         visual_info_template.visualid = configVisualID;
938         visual_info =
939             XGetVisualInfo(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
940                            VisualIDMask, &visual_info_template, &num_visuals);
941 
942         if (!visual_info || visual_info->depth != aDepth) {
943           if (aAllowFallback && !fallbackConfig) {
944             fallbackConfig = Some(config);
945           }
946           continue;
947         }
948       }
949 #endif
950       *aConfig = config;
951       return true;
952     }
953   }
954 
955   if (kIsX11 && fallbackConfig) {
956     *aConfig = fallbackConfig.value();
957     return true;
958   }
959 
960   return false;
961 }
962 
963 // Return true if a suitable EGLConfig was found and pass it out
964 // through aConfig.  Return false otherwise.
965 //
966 // NB: It's entirely legal for the returned EGLConfig to be valid yet
967 // have the value null.
CreateConfigScreen(EglDisplay & egl,EGLConfig * const aConfig,const bool aEnableDepthBuffer,const bool aUseGles)968 static bool CreateConfigScreen(EglDisplay& egl, EGLConfig* const aConfig,
969                                const bool aEnableDepthBuffer,
970                                const bool aUseGles) {
971   int32_t depth = gfxVars::ScreenDepth();
972   if (CreateConfig(egl, aConfig, depth, aEnableDepthBuffer, aUseGles)) {
973     return true;
974   }
975 #ifdef MOZ_WIDGET_ANDROID
976   // Bug 736005
977   // Android doesn't always support 16 bit so also try 24 bit
978   if (depth == 16) {
979     return CreateConfig(egl, aConfig, 24, aEnableDepthBuffer, aUseGles);
980   }
981   // Bug 970096
982   // Some devices that have 24 bit screens only support 16 bit OpenGL?
983   if (depth == 24) {
984     return CreateConfig(egl, aConfig, 16, aEnableDepthBuffer, aUseGles);
985   }
986 #endif
987   return false;
988 }
989 
CreateForCompositorWidget(CompositorWidget * aCompositorWidget,bool aHardwareWebRender,bool)990 already_AddRefed<GLContext> GLContextProviderEGL::CreateForCompositorWidget(
991     CompositorWidget* aCompositorWidget, bool aHardwareWebRender,
992     bool /*aForceAccelerated*/) {
993   EGLNativeWindowType window = nullptr;
994   if (aCompositorWidget) {
995     window = GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget);
996   }
997   return GLContextEGLFactory::Create(window, aHardwareWebRender);
998 }
999 
CreateCompatibleSurface(void * aWindow) const1000 EGLSurface GLContextEGL::CreateCompatibleSurface(void* aWindow) const {
1001   MOZ_ASSERT(aWindow);
1002   MOZ_RELEASE_ASSERT(mConfig != EGL_NO_CONFIG);
1003 
1004   // NOTE: aWindow is an ANativeWindow
1005   EGLSurface surface = mEgl->fCreateWindowSurface(
1006       mConfig, reinterpret_cast<EGLNativeWindowType>(aWindow), nullptr);
1007   if (!surface) {
1008     gfxCriticalError() << "CreateCompatibleSurface failed: "
1009                        << hexa(GetError());
1010   }
1011   return surface;
1012 }
1013 
FillContextAttribs(bool es3,bool useGles,nsTArray<EGLint> * out)1014 static void FillContextAttribs(bool es3, bool useGles, nsTArray<EGLint>* out) {
1015   out->AppendElement(LOCAL_EGL_SURFACE_TYPE);
1016 #ifdef MOZ_GTK_WAYLAND
1017   if (GdkIsWaylandDisplay()) {
1018     // Wayland on desktop does not support PBuffer or FBO.
1019     // We create a dummy wl_egl_window instead.
1020     out->AppendElement(LOCAL_EGL_WINDOW_BIT);
1021   } else
1022 #endif
1023   {
1024     out->AppendElement(LOCAL_EGL_PBUFFER_BIT);
1025   }
1026 
1027   if (useGles) {
1028     out->AppendElement(LOCAL_EGL_RENDERABLE_TYPE);
1029     if (es3) {
1030       out->AppendElement(LOCAL_EGL_OPENGL_ES3_BIT_KHR);
1031     } else {
1032       out->AppendElement(LOCAL_EGL_OPENGL_ES2_BIT);
1033     }
1034   }
1035 
1036   out->AppendElement(LOCAL_EGL_RED_SIZE);
1037   out->AppendElement(8);
1038 
1039   out->AppendElement(LOCAL_EGL_GREEN_SIZE);
1040   out->AppendElement(8);
1041 
1042   out->AppendElement(LOCAL_EGL_BLUE_SIZE);
1043   out->AppendElement(8);
1044 
1045   out->AppendElement(LOCAL_EGL_ALPHA_SIZE);
1046   out->AppendElement(8);
1047 
1048   out->AppendElement(LOCAL_EGL_DEPTH_SIZE);
1049   out->AppendElement(0);
1050 
1051   out->AppendElement(LOCAL_EGL_STENCIL_SIZE);
1052   out->AppendElement(0);
1053 
1054   // EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
1055   out->AppendElement(LOCAL_EGL_NONE);
1056   out->AppendElement(0);
1057 
1058   out->AppendElement(0);
1059   out->AppendElement(0);
1060 }
1061 
1062 /*
1063 /// Useful for debugging, but normally unused.
1064 static GLint GetAttrib(GLLibraryEGL* egl, EGLConfig config, EGLint attrib) {
1065   EGLint bits = 0;
1066   egl->fGetConfigAttrib(config, attrib, &bits);
1067   MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
1068 
1069   return bits;
1070 }
1071 */
1072 
ChooseConfig(EglDisplay & egl,const GLContextCreateDesc & desc,const bool useGles)1073 static EGLConfig ChooseConfig(EglDisplay& egl, const GLContextCreateDesc& desc,
1074                               const bool useGles) {
1075   nsTArray<EGLint> configAttribList;
1076   FillContextAttribs(bool(desc.flags & CreateContextFlags::PREFER_ES3), useGles,
1077                      &configAttribList);
1078 
1079   const EGLint* configAttribs = configAttribList.Elements();
1080 
1081   // The sorting dictated by the spec for eglChooseConfig reasonably assures
1082   // that a reasonable 'best' config is on top.
1083   const EGLint kMaxConfigs = 1;
1084   EGLConfig configs[kMaxConfigs];
1085   EGLint foundConfigs = 0;
1086   if (!egl.fChooseConfig(configAttribs, configs, kMaxConfigs, &foundConfigs) ||
1087       foundConfigs == 0) {
1088     return EGL_NO_CONFIG;
1089   }
1090 
1091   EGLConfig config = configs[0];
1092   return config;
1093 }
1094 
1095 #ifdef MOZ_X11
1096 /* static */
FindVisual(int * const out_visualId)1097 bool GLContextEGL::FindVisual(int* const out_visualId) {
1098   nsCString discardFailureId;
1099   const auto egl = DefaultEglDisplay(&discardFailureId);
1100   if (!egl) {
1101     gfxCriticalNote
1102         << "GLContextEGL::FindVisual(): Failed to load EGL library!";
1103     return false;
1104   }
1105 
1106   EGLConfig config;
1107   const int bpp = 32;
1108   if (!CreateConfig(*egl, &config, bpp, /* aEnableDepthBuffer */ false,
1109                     /* aUseGles */ false, /* aAllowFallback */ false)) {
1110     // We are on a buggy driver. Do not return a visual so a fallback path can
1111     // be used. See https://gitlab.freedesktop.org/mesa/mesa/-/issues/149
1112     return false;
1113   }
1114   if (egl->fGetConfigAttrib(config, LOCAL_EGL_NATIVE_VISUAL_ID, out_visualId)) {
1115     return true;
1116   }
1117   return false;
1118 }
1119 #endif
1120 
1121 /*static*/
CreateEGLPBufferOffscreenContextImpl(const std::shared_ptr<EglDisplay> egl,const GLContextCreateDesc & desc,const mozilla::gfx::IntSize & size,const bool useGles,nsACString * const out_failureId)1122 RefPtr<GLContextEGL> GLContextEGL::CreateEGLPBufferOffscreenContextImpl(
1123     const std::shared_ptr<EglDisplay> egl, const GLContextCreateDesc& desc,
1124     const mozilla::gfx::IntSize& size, const bool useGles,
1125     nsACString* const out_failureId) {
1126   const EGLConfig config = ChooseConfig(*egl, desc, useGles);
1127   if (config == EGL_NO_CONFIG) {
1128     *out_failureId = "FEATURE_FAILURE_EGL_NO_CONFIG"_ns;
1129     NS_WARNING("Failed to find a compatible config.");
1130     return nullptr;
1131   }
1132 
1133   if (GLContext::ShouldSpew()) {
1134     egl->DumpEGLConfig(config);
1135   }
1136 
1137   mozilla::gfx::IntSize pbSize(size);
1138   EGLSurface surface = nullptr;
1139 #ifdef MOZ_GTK_WAYLAND
1140   if (GdkIsWaylandDisplay()) {
1141     surface = GLContextEGL::CreateWaylandBufferSurface(*egl, config, pbSize);
1142   } else
1143 #endif
1144   {
1145     surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
1146         *egl, config, LOCAL_EGL_NONE, pbSize);
1147   }
1148   if (!surface) {
1149     *out_failureId = "FEATURE_FAILURE_EGL_POT"_ns;
1150     NS_WARNING("Failed to create PBuffer for context!");
1151     return nullptr;
1152   }
1153 
1154   auto fullDesc = GLContextDesc{desc};
1155   fullDesc.isOffscreen = true;
1156   RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(
1157       egl, fullDesc, config, surface, useGles, out_failureId);
1158   if (!gl) {
1159     NS_WARNING("Failed to create GLContext from PBuffer");
1160     egl->fDestroySurface(surface);
1161 #if defined(MOZ_WAYLAND)
1162     DeleteWaylandGLSurface(surface);
1163 #endif
1164     return nullptr;
1165   }
1166 
1167   return gl;
1168 }
1169 
1170 /*static*/
CreateEGLPBufferOffscreenContext(const std::shared_ptr<EglDisplay> display,const GLContextCreateDesc & desc,const mozilla::gfx::IntSize & size,nsACString * const out_failureId)1171 RefPtr<GLContextEGL> GLContextEGL::CreateEGLPBufferOffscreenContext(
1172     const std::shared_ptr<EglDisplay> display, const GLContextCreateDesc& desc,
1173     const mozilla::gfx::IntSize& size, nsACString* const out_failureId) {
1174   bool preferGles;
1175 #if defined(MOZ_WIDGET_ANDROID)
1176   preferGles = true;
1177 #else
1178   preferGles = StaticPrefs::gfx_egl_prefer_gles_enabled_AtStartup();
1179 #endif  // defined(MOZ_WIDGET_ANDROID)
1180 
1181   RefPtr<GLContextEGL> gl = CreateEGLPBufferOffscreenContextImpl(
1182       display, desc, size, preferGles, out_failureId);
1183 #if !defined(MOZ_WIDGET_ANDROID)
1184   if (!gl) {
1185     gl = CreateEGLPBufferOffscreenContextImpl(display, desc, size, !preferGles,
1186                                               out_failureId);
1187   }
1188 #endif  // !defined(MOZ_WIDGET_ANDROID)
1189   return gl;
1190 }
1191 
1192 /*static*/
CreateHeadless(const GLContextCreateDesc & desc,nsACString * const out_failureId)1193 already_AddRefed<GLContext> GLContextProviderEGL::CreateHeadless(
1194     const GLContextCreateDesc& desc, nsACString* const out_failureId) {
1195   const auto display = DefaultEglDisplay(out_failureId);
1196   if (!display) {
1197     return nullptr;
1198   }
1199   mozilla::gfx::IntSize dummySize = mozilla::gfx::IntSize(16, 16);
1200   auto ret = GLContextEGL::CreateEGLPBufferOffscreenContext(
1201       display, desc, dummySize, out_failureId);
1202   return ret.forget();
1203 }
1204 
1205 // Don't want a global context on Android as 1) share groups across 2 threads
1206 // fail on many Tegra drivers (bug 759225) and 2) some mobile devices have a
1207 // very strict limit on global number of GL contexts (bug 754257) and 3) each
1208 // EGL context eats 750k on B2G (bug 813783)
1209 /*static*/
GetGlobalContext()1210 GLContext* GLContextProviderEGL::GetGlobalContext() { return nullptr; }
1211 
1212 // -
1213 
1214 static StaticMutex sMutex;
1215 static StaticRefPtr<GLLibraryEGL> gDefaultEglLibrary;
1216 
DefaultEglLibrary(nsACString * const out_failureId)1217 RefPtr<GLLibraryEGL> DefaultEglLibrary(nsACString* const out_failureId) {
1218   StaticMutexAutoLock lock(sMutex);
1219   if (!gDefaultEglLibrary) {
1220     gDefaultEglLibrary = GLLibraryEGL::Create(out_failureId);
1221     if (!gDefaultEglLibrary) {
1222       NS_WARNING("GLLibraryEGL::Create failed");
1223     }
1224   }
1225   return gDefaultEglLibrary.get();
1226 }
1227 
1228 // -
1229 
1230 /*static*/
Shutdown()1231 void GLContextProviderEGL::Shutdown() {
1232   StaticMutexAutoLock lock(sMutex);
1233   if (!gDefaultEglLibrary) {
1234     return;
1235   }
1236   gDefaultEglLibrary = nullptr;
1237 }
1238 
1239 } /* namespace gl */
1240 } /* namespace mozilla */
1241 
1242 #undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
1243