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/. */
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())
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())
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( \
30 # ifdef MOZ_WIDGET_ANDROID
31 # include <android/native_window.h>
32 # include <android/native_window_jni.h>
33 # include "mozilla/widget/AndroidCompositorWidget.h"
36 # define GLES2_LIB "libGLESv2.so"
37 # define GLES2_LIB2 "libGLESv2.so.2"
40 # include "mozilla/widget/WinCompositorWidget.h"
43 # define GLES2_LIB "libGLESv2.dll"
45 # ifndef WIN32_LEAN_AND_MEAN
46 # define WIN32_LEAN_AND_MEAN 1
51 # error "Platform not recognized"
54 #include "gfxCrashReporterUtils.h"
55 #include "gfxFailure.h"
56 #include "gfxPlatform.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"
73 #include "nsIWidget.h"
74 #include "nsThreadUtils.h"
75 #include "ScopedGLHelpers.h"
77 #if defined(MOZ_WIDGET_GTK)
78 # include "mozilla/widget/GtkCompositorWidget.h"
79 # if defined(MOZ_WAYLAND)
80 # include <gdk/gdkwayland.h>
81 # include <wayland-egl.h>
82 # include "mozilla/WidgetUtilsGtk.h"
83 # include "mozilla/widget/nsWaylandDisplay.h"
89 using namespace mozilla::gfx
;
94 using namespace mozilla::widget
;
96 #if defined(MOZ_WAYLAND)
97 class WaylandOffscreenGLSurface
{
99 WaylandOffscreenGLSurface(struct wl_surface
* aWaylandSurface
,
100 struct wl_egl_window
* aEGLWindow
);
101 ~WaylandOffscreenGLSurface();
104 struct wl_surface
* mWaylandSurface
= nullptr;
105 struct wl_egl_window
* mEGLWindow
= nullptr;
108 static nsTHashMap
<nsPtrHashKey
<void>, WaylandOffscreenGLSurface
*>
109 sWaylandOffscreenGLSurfaces
;
111 void DeleteWaylandOffscreenGLSurface(EGLSurface surface
) {
112 auto entry
= sWaylandOffscreenGLSurfaces
.Lookup(surface
);
120 static bool CreateConfigScreen(EglDisplay
&, EGLConfig
* const aConfig
,
121 const bool aEnableDepthBuffer
,
122 const bool aUseGles
);
124 // append three zeros at the end of attribs list to work around
125 // EGL implementation bugs that iterate until they find 0, instead of
126 // EGL_NONE. See bug 948406.
127 #define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
128 LOCAL_EGL_NONE, 0, 0, 0
130 static EGLint kTerminationAttribs
[] = {
131 EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
};
133 static int next_power_of_two(int v
) {
145 static bool is_power_of_two(int v
) {
146 NS_ASSERTION(v
>= 0, "bad value");
148 if (v
== 0) return true;
150 return (v
& (v
- 1)) == 0;
153 static EGLSurface
CreateFallbackSurface(EglDisplay
& egl
,
154 const EGLConfig
& config
) {
155 if (egl
.IsExtensionSupported(EGLExtension::KHR_surfaceless_context
)) {
156 // We don't need a PBuffer surface in this case
157 return EGL_NO_SURFACE
;
160 std::vector
<EGLint
> pbattrs
;
161 pbattrs
.push_back(LOCAL_EGL_WIDTH
);
162 pbattrs
.push_back(1);
163 pbattrs
.push_back(LOCAL_EGL_HEIGHT
);
164 pbattrs
.push_back(1);
166 for (const auto& cur
: kTerminationAttribs
) {
167 pbattrs
.push_back(cur
);
170 EGLSurface surface
= egl
.fCreatePbufferSurface(config
, pbattrs
.data());
172 MOZ_CRASH("Failed to create fallback EGLSurface");
178 static EGLSurface
CreateSurfaceFromNativeWindow(
179 EglDisplay
& egl
, const EGLNativeWindowType window
, const EGLConfig config
) {
181 EGLSurface newSurface
= EGL_NO_SURFACE
;
183 #ifdef MOZ_WIDGET_ANDROID
184 JNIEnv
* const env
= jni::GetEnvForThread();
185 ANativeWindow
* const nativeWindow
=
186 ANativeWindow_fromSurface(env
, reinterpret_cast<jobject
>(window
));
188 gfxCriticalNote
<< "Failed to obtain native window from Surface";
189 return EGL_NO_SURFACE
;
191 const auto& display
= egl
.mLib
->fGetDisplay(EGL_DEFAULT_DISPLAY
);
192 newSurface
= egl
.mLib
->fCreateWindowSurface(display
, config
, nativeWindow
, 0);
193 ANativeWindow_release(nativeWindow
);
195 newSurface
= egl
.fCreateWindowSurface(config
, window
, 0);
198 const auto err
= egl
.mLib
->fGetError();
199 gfxCriticalNote
<< "Failed to create EGLSurface!: " << gfx::hexa(err
);
204 /* GLContextEGLFactory class was added as a friend of GLContextEGL
205 * so that it could access GLContextEGL::CreateGLContext. This was
206 * done so that a new function would not need to be added to the shared
207 * GLContextProvider interface.
209 class GLContextEGLFactory
{
211 static already_AddRefed
<GLContext
> Create(EGLNativeWindowType aWindow
,
212 bool aHardwareWebRender
);
213 static already_AddRefed
<GLContext
> CreateImpl(EGLNativeWindowType aWindow
,
214 bool aHardwareWebRender
,
218 GLContextEGLFactory() = default;
219 ~GLContextEGLFactory() = default;
222 already_AddRefed
<GLContext
> GLContextEGLFactory::CreateImpl(
223 EGLNativeWindowType aWindow
, bool aHardwareWebRender
, bool aUseGles
) {
225 const auto lib
= GLLibraryEGL::Get(&failureId
);
227 gfxCriticalNote
<< "Failed[3] to load EGL library: " << failureId
.get();
230 const auto egl
= lib
->CreateDisplay(true, &failureId
);
232 gfxCriticalNote
<< "Failed[3] to create EGL library display: "
237 bool doubleBuffered
= true;
240 if (aHardwareWebRender
&& egl
->mLib
->IsANGLE()) {
241 // Force enable alpha channel to make sure ANGLE use correct framebuffer
244 if (!CreateConfig(*egl
, &config
, bpp
, false, aUseGles
)) {
245 gfxCriticalNote
<< "Failed to create EGLConfig for WebRender ANGLE!";
248 } else if (kIsLinux
) {
250 if (!CreateConfig(*egl
, &config
, bpp
, false, aUseGles
)) {
251 gfxCriticalNote
<< "Failed to create EGLConfig for WebRender!";
255 if (!CreateConfigScreen(*egl
, &config
,
256 /* aEnableDepthBuffer */ false, aUseGles
)) {
257 gfxCriticalNote
<< "Failed to create EGLConfig!";
262 EGLSurface surface
= EGL_NO_SURFACE
;
264 surface
= mozilla::gl::CreateSurfaceFromNativeWindow(*egl
, aWindow
, config
);
270 CreateContextFlags flags
= CreateContextFlags::NONE
;
271 if (aHardwareWebRender
&&
272 StaticPrefs::gfx_webrender_prefer_robustness_AtStartup()) {
273 flags
|= CreateContextFlags::PREFER_ROBUSTNESS
;
275 if (aHardwareWebRender
&& aUseGles
) {
276 flags
|= CreateContextFlags::PREFER_ES3
;
278 if (!aHardwareWebRender
) {
279 flags
|= CreateContextFlags::REQUIRE_COMPAT_PROFILE
;
282 const auto desc
= GLContextDesc
{{flags
}, false};
283 RefPtr
<GLContextEGL
> gl
= GLContextEGL::CreateGLContext(
284 egl
, desc
, config
, surface
, aUseGles
, config
, &failureId
);
286 const auto err
= egl
->mLib
->fGetError();
287 gfxCriticalNote
<< "Failed to create EGLContext!: " << gfx::hexa(err
);
288 GLContextEGL::DestroySurface(*egl
, surface
);
293 gl
->SetIsDoubleBuffered(doubleBuffered
);
295 #ifdef MOZ_WIDGET_GTK
297 const int interval
= gfxVars::SwapIntervalEGL() ? 1 : 0;
298 egl
->fSwapInterval(interval
);
301 if (aHardwareWebRender
&& egl
->mLib
->IsANGLE()) {
302 MOZ_ASSERT(doubleBuffered
);
303 const int interval
= gfxVars::SwapIntervalEGL() ? 1 : 0;
304 egl
->fSwapInterval(interval
);
309 already_AddRefed
<GLContext
> GLContextEGLFactory::Create(
310 EGLNativeWindowType aWindow
, bool aHardwareWebRender
) {
312 #if defined(MOZ_WIDGET_ANDROID)
315 preferGles
= StaticPrefs::gfx_egl_prefer_gles_enabled_AtStartup();
316 #endif // defined(MOZ_WIDGET_ANDROID)
318 RefPtr
<GLContext
> glContext
=
319 CreateImpl(aWindow
, aHardwareWebRender
, preferGles
);
320 #if !defined(MOZ_WIDGET_ANDROID)
322 glContext
= CreateImpl(aWindow
, aHardwareWebRender
, !preferGles
);
324 #endif // !defined(MOZ_WIDGET_ANDROID)
325 return glContext
.forget();
329 EGLSurface
GLContextEGL::CreateEGLSurfaceForCompositorWidget(
330 widget::CompositorWidget
* aCompositorWidget
, const EGLConfig aConfig
) {
331 nsCString discardFailureId
;
332 const auto egl
= DefaultEglDisplay(&discardFailureId
);
334 gfxCriticalNote
<< "Failed to load EGL library 6!";
335 return EGL_NO_SURFACE
;
338 MOZ_ASSERT(aCompositorWidget
);
340 // RenderCompositorEGL does not like EGL_NO_SURFACE as it fallbacks
341 // to SW rendering or claims itself as paused.
342 // In case we're missing valid native window because aCompositorWidget hidden,
343 // just create a fallback EGLSurface.
344 // Actual EGLSurface will be created by widget code later when
345 // aCompositorWidget becomes visible.
346 if (widget::GdkIsWaylandDisplay() && aCompositorWidget
->IsHidden()) {
347 mozilla::gfx::IntSize
pbSize(16, 16);
348 return CreateWaylandOffscreenSurface(*egl
, aConfig
, pbSize
);
351 EGLNativeWindowType window
=
352 GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget
);
354 gfxCriticalNote
<< "window is null";
355 return EGL_NO_SURFACE
;
358 return mozilla::gl::CreateSurfaceFromNativeWindow(*egl
, window
, aConfig
);
361 GLContextEGL::GLContextEGL(const std::shared_ptr
<EglDisplay
> egl
,
362 const GLContextDesc
& desc
, EGLConfig surfaceConfig
,
363 EGLSurface surface
, EGLContext context
)
364 : GLContext(desc
, nullptr, false),
366 mSurfaceConfig(surfaceConfig
),
369 mFallbackSurface(CreateFallbackSurface(*mEgl
, mSurfaceConfig
)) {
371 printf_stderr("Initializing context %p surface %p on display %p\n", mContext
,
372 mSurface
, mEgl
->mDisplay
);
376 void GLContextEGL::OnMarkDestroyed() {
377 if (mSurfaceOverride
!= EGL_NO_SURFACE
) {
378 SetEGLSurfaceOverride(EGL_NO_SURFACE
);
382 GLContextEGL::~GLContextEGL() {
385 // Wrapped context should not destroy eglContext/Surface
391 printf_stderr("Destroying context %p surface %p on display %p\n", mContext
,
392 mSurface
, mEgl
->mDisplay
);
395 mEgl
->fDestroyContext(mContext
);
397 DestroySurface(*mEgl
, mSurface
);
398 DestroySurface(*mEgl
, mFallbackSurface
);
401 bool GLContextEGL::Init() {
402 if (!GLContext::Init()) return false;
404 bool current
= MakeCurrent();
406 gfx::LogFailure("Couldn't get device attachments for device."_ns
);
411 mEgl
->HasKHRImageBase() &&
412 mEgl
->IsExtensionSupported(EGLExtension::KHR_gl_texture_2D_image
) &&
413 IsExtensionSupported(OES_EGL_image
);
418 bool GLContextEGL::BindTexImage() {
419 if (!mSurface
) return false;
421 if (mBound
&& !ReleaseTexImage()) return false;
424 mEgl
->fBindTexImage((EGLSurface
)mSurface
, LOCAL_EGL_BACK_BUFFER
);
425 if (success
== LOCAL_EGL_FALSE
) return false;
431 bool GLContextEGL::ReleaseTexImage() {
432 if (!mBound
) return true;
434 if (!mSurface
) return false;
437 success
= mEgl
->fReleaseTexImage((EGLSurface
)mSurface
, LOCAL_EGL_BACK_BUFFER
);
438 if (success
== LOCAL_EGL_FALSE
) return false;
444 void GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf
) {
445 mSurfaceOverride
= surf
;
446 DebugOnly
<bool> ok
= MakeCurrent(true);
450 bool GLContextEGL::MakeCurrentImpl() const {
452 (mSurfaceOverride
!= EGL_NO_SURFACE
) ? mSurfaceOverride
: mSurface
;
454 surface
= mFallbackSurface
;
457 const bool succeeded
= mEgl
->fMakeCurrent(surface
, surface
, mContext
);
459 const auto eglError
= mEgl
->mLib
->fGetError();
460 if (eglError
== LOCAL_EGL_CONTEXT_LOST
) {
461 OnContextLostError();
463 NS_WARNING("Failed to make GL context current!");
465 printf_stderr("EGL Error: 0x%04x\n", eglError
);
473 bool GLContextEGL::IsCurrentImpl() const {
474 return mEgl
->mLib
->fGetCurrentContext() == mContext
;
477 bool GLContextEGL::RenewSurface(CompositorWidget
* aWidget
) {
481 // unconditionally release the surface and create a new one. Don't try to
482 // optimize this away. If we get here, then by definition we know that we want
483 // to get a new surface.
487 EGLNativeWindowType nativeWindow
=
488 GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget
);
490 // In case we're missing native window on Wayland CompositorWidget is hidden.
491 // Don't create a fallback EGL surface but fails here.
492 // We need to repeat RenewSurface() when native window is available
493 // (CompositorWidget becomes visible).
494 if (GdkIsWaylandDisplay()) {
495 NS_WARNING("Failed to get native window");
500 mSurface
= mozilla::gl::CreateSurfaceFromNativeWindow(*mEgl
, nativeWindow
,
503 NS_WARNING("Failed to create EGLSurface from native window");
507 const bool ok
= MakeCurrent(true);
509 #ifdef MOZ_WIDGET_GTK
511 const int interval
= gfxVars::SwapIntervalEGL() ? 1 : 0;
512 mEgl
->fSwapInterval(interval
);
518 void GLContextEGL::ReleaseSurface() {
520 DestroySurface(*mEgl
, mSurface
);
522 if (mSurface
== mSurfaceOverride
) {
523 mSurfaceOverride
= EGL_NO_SURFACE
;
525 mSurface
= EGL_NO_SURFACE
;
528 Maybe
<SymbolLoader
> GLContextEGL::GetSymbolLoader() const {
529 return mEgl
->mLib
->GetSymbolLoader();
532 bool GLContextEGL::SwapBuffers() {
534 mSurfaceOverride
!= EGL_NO_SURFACE
? mSurfaceOverride
: mSurface
;
536 if ((mEgl
->IsExtensionSupported(
537 EGLExtension::EXT_swap_buffers_with_damage
) ||
538 mEgl
->IsExtensionSupported(
539 EGLExtension::KHR_swap_buffers_with_damage
))) {
540 std::vector
<EGLint
> rects
;
541 for (auto iter
= mDamageRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
542 const IntRect
& r
= iter
.Get();
543 rects
.push_back(r
.X());
544 rects
.push_back(r
.Y());
545 rects
.push_back(r
.Width());
546 rects
.push_back(r
.Height());
548 mDamageRegion
.SetEmpty();
549 return mEgl
->fSwapBuffersWithDamage(surface
, rects
.data(),
552 return mEgl
->fSwapBuffers(surface
);
558 void GLContextEGL::SetDamage(const nsIntRegion
& aDamageRegion
) {
559 mDamageRegion
= aDamageRegion
;
562 void GLContextEGL::GetWSIInfo(nsCString
* const out
) const {
563 out
->AppendLiteral("EGL_VENDOR: ");
565 (const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
, LOCAL_EGL_VENDOR
));
567 out
->AppendLiteral("\nEGL_VERSION: ");
569 (const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
, LOCAL_EGL_VERSION
));
571 out
->AppendLiteral("\nEGL_EXTENSIONS: ");
572 out
->Append((const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
,
573 LOCAL_EGL_EXTENSIONS
));
575 #ifndef ANDROID // This query will crash some old android.
576 out
->AppendLiteral("\nEGL_EXTENSIONS(nullptr): ");
578 (const char*)mEgl
->mLib
->fQueryString(nullptr, LOCAL_EGL_EXTENSIONS
));
582 bool GLContextEGL::HasExtBufferAge() const {
583 return mEgl
->IsExtensionSupported(EGLExtension::EXT_buffer_age
);
586 bool GLContextEGL::HasKhrPartialUpdate() const {
587 return mEgl
->IsExtensionSupported(EGLExtension::KHR_partial_update
);
590 GLint
GLContextEGL::GetBufferAge() const {
592 mSurfaceOverride
!= EGL_NO_SURFACE
? mSurfaceOverride
: mSurface
;
594 if (surface
&& (HasExtBufferAge() || HasKhrPartialUpdate())) {
596 mEgl
->fQuerySurface(surface
, LOCAL_EGL_BUFFER_AGE_EXT
, &result
);
603 #define LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ 0x6000
605 RefPtr
<GLContextEGL
> GLContextEGL::CreateGLContext(
606 const std::shared_ptr
<EglDisplay
> egl
, const GLContextDesc
& desc
,
607 EGLConfig surfaceConfig
, EGLSurface surface
, const bool useGles
,
608 EGLConfig contextConfig
, nsACString
* const out_failureId
) {
609 const auto& flags
= desc
.flags
;
611 std::vector
<EGLint
> required_attribs
;
614 // TODO: This fBindAPI could be more thread-safe
615 if (egl
->mLib
->fBindAPI(LOCAL_EGL_OPENGL_ES_API
) == LOCAL_EGL_FALSE
) {
616 *out_failureId
= "FEATURE_FAILURE_EGL_ES"_ns
;
617 NS_WARNING("Failed to bind API to GLES!");
620 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
621 if (flags
& CreateContextFlags::PREFER_ES3
) {
622 required_attribs
.push_back(3);
624 required_attribs
.push_back(2);
627 if (egl
->mLib
->fBindAPI(LOCAL_EGL_OPENGL_API
) == LOCAL_EGL_FALSE
) {
628 *out_failureId
= "FEATURE_FAILURE_EGL"_ns
;
629 NS_WARNING("Failed to bind API to GL!");
632 if (flags
& CreateContextFlags::REQUIRE_COMPAT_PROFILE
) {
633 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK
);
634 required_attribs
.push_back(
635 LOCAL_EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT
);
636 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
637 required_attribs
.push_back(2);
639 // !REQUIRE_COMPAT_PROFILE means core profle.
640 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK
);
641 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT
);
642 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
643 required_attribs
.push_back(3);
644 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MINOR_VERSION
);
645 required_attribs
.push_back(2);
649 if ((flags
& CreateContextFlags::PREFER_EXACT_VERSION
) &&
650 egl
->mLib
->IsANGLE()) {
651 required_attribs
.push_back(
652 LOCAL_EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE
);
653 required_attribs
.push_back(LOCAL_EGL_FALSE
);
656 const auto debugFlags
= GLContext::ChooseDebugFlags(flags
);
657 if (!debugFlags
&& flags
& CreateContextFlags::NO_VALIDATION
&&
658 egl
->IsExtensionSupported(EGLExtension::KHR_create_context_no_error
)) {
659 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_NO_ERROR_KHR
);
660 required_attribs
.push_back(LOCAL_EGL_TRUE
);
663 if (flags
& CreateContextFlags::PROVOKING_VERTEX_DONT_CARE
&&
664 egl
->IsExtensionSupported(
665 EGLExtension::MOZ_create_context_provoking_vertex_dont_care
)) {
666 required_attribs
.push_back(
667 LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ
);
668 required_attribs
.push_back(LOCAL_EGL_TRUE
);
671 std::vector
<EGLint
> ext_robustness_attribs
;
672 std::vector
<EGLint
> ext_rbab_attribs
; // RBAB: Robust Buffer Access Behavior
673 std::vector
<EGLint
> khr_robustness_attribs
;
674 std::vector
<EGLint
> khr_rbab_attribs
; // RBAB: Robust Buffer Access Behavior
675 if (flags
& CreateContextFlags::PREFER_ROBUSTNESS
) {
676 std::vector
<EGLint
> base_robustness_attribs
= required_attribs
;
677 if (egl
->IsExtensionSupported(
678 EGLExtension::NV_robustness_video_memory_purge
)) {
679 base_robustness_attribs
.push_back(
680 LOCAL_EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
);
681 base_robustness_attribs
.push_back(LOCAL_EGL_TRUE
);
684 if (egl
->IsExtensionSupported(
685 EGLExtension::EXT_create_context_robustness
)) {
686 ext_robustness_attribs
= base_robustness_attribs
;
687 ext_robustness_attribs
.push_back(
688 LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT
);
689 ext_robustness_attribs
.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT
);
691 if (gfxVars::AllowEglRbab()) {
692 ext_rbab_attribs
= ext_robustness_attribs
;
693 ext_rbab_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT
);
694 ext_rbab_attribs
.push_back(LOCAL_EGL_TRUE
);
698 if (egl
->IsExtensionSupported(EGLExtension::KHR_create_context
)) {
699 khr_robustness_attribs
= base_robustness_attribs
;
700 khr_robustness_attribs
.push_back(
701 LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR
);
702 khr_robustness_attribs
.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_KHR
);
704 khr_rbab_attribs
= khr_robustness_attribs
;
705 khr_rbab_attribs
.push_back(LOCAL_EGL_CONTEXT_FLAGS_KHR
);
706 khr_rbab_attribs
.push_back(
707 LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR
);
711 const auto fnCreate
= [&](const std::vector
<EGLint
>& attribs
) {
712 auto terminated_attribs
= attribs
;
714 for (const auto& cur
: kTerminationAttribs
) {
715 terminated_attribs
.push_back(cur
);
718 return egl
->fCreateContext(contextConfig
, EGL_NO_CONTEXT
,
719 terminated_attribs
.data());
724 if (!khr_rbab_attribs
.empty()) {
725 context
= fnCreate(khr_rbab_attribs
);
727 NS_WARNING("Failed to create EGLContext with khr_rbab_attribs");
730 if (!ext_rbab_attribs
.empty()) {
731 context
= fnCreate(ext_rbab_attribs
);
733 NS_WARNING("Failed to create EGLContext with ext_rbab_attribs");
736 if (!khr_robustness_attribs
.empty()) {
737 context
= fnCreate(khr_robustness_attribs
);
739 NS_WARNING("Failed to create EGLContext with khr_robustness_attribs");
742 if (!ext_robustness_attribs
.empty()) {
743 context
= fnCreate(ext_robustness_attribs
);
745 NS_WARNING("Failed to create EGLContext with ext_robustness_attribs");
748 context
= fnCreate(required_attribs
);
750 NS_WARNING("Failed to create EGLContext with required_attribs");
752 *out_failureId
= "FEATURE_FAILURE_EGL_CREATE"_ns
;
757 RefPtr
<GLContextEGL
> glContext
=
758 new GLContextEGL(egl
, desc
, surfaceConfig
, surface
, context
);
759 if (!glContext
->Init()) {
760 *out_failureId
= "FEATURE_FAILURE_EGL_INIT"_ns
;
764 if (GLContext::ShouldSpew()) {
765 printf_stderr("new GLContextEGL %p on EGLDisplay %p\n", glContext
.get(),
773 EGLSurface
GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
774 EglDisplay
& egl
, EGLConfig config
, EGLenum bindToTextureFormat
,
775 mozilla::gfx::IntSize
& pbsize
) {
776 nsTArray
<EGLint
> pbattrs(16);
777 EGLSurface surface
= nullptr;
779 TRY_AGAIN_POWER_OF_TWO
:
781 pbattrs
.AppendElement(LOCAL_EGL_WIDTH
);
782 pbattrs
.AppendElement(pbsize
.width
);
783 pbattrs
.AppendElement(LOCAL_EGL_HEIGHT
);
784 pbattrs
.AppendElement(pbsize
.height
);
786 if (bindToTextureFormat
!= LOCAL_EGL_NONE
) {
787 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_TARGET
);
788 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_2D
);
790 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_FORMAT
);
791 pbattrs
.AppendElement(bindToTextureFormat
);
794 for (const auto& cur
: kTerminationAttribs
) {
795 pbattrs
.AppendElement(cur
);
798 surface
= egl
.fCreatePbufferSurface(config
, &pbattrs
[0]);
800 if (!is_power_of_two(pbsize
.width
) || !is_power_of_two(pbsize
.height
)) {
801 if (!is_power_of_two(pbsize
.width
))
802 pbsize
.width
= next_power_of_two(pbsize
.width
);
803 if (!is_power_of_two(pbsize
.height
))
804 pbsize
.height
= next_power_of_two(pbsize
.height
);
806 NS_WARNING("Failed to create pbuffer, trying power of two dims");
807 goto TRY_AGAIN_POWER_OF_TWO
;
810 NS_WARNING("Failed to create pbuffer surface");
817 #if defined(MOZ_WAYLAND)
818 WaylandOffscreenGLSurface::WaylandOffscreenGLSurface(
819 struct wl_surface
* aWaylandSurface
, struct wl_egl_window
* aEGLWindow
)
820 : mWaylandSurface(aWaylandSurface
), mEGLWindow(aEGLWindow
) {}
822 WaylandOffscreenGLSurface::~WaylandOffscreenGLSurface() {
824 wl_egl_window_destroy(mEGLWindow
);
826 if (mWaylandSurface
) {
827 wl_surface_destroy(mWaylandSurface
);
832 EGLSurface
GLContextEGL::CreateWaylandOffscreenSurface(
833 EglDisplay
& egl
, EGLConfig config
, mozilla::gfx::IntSize
& pbsize
) {
834 wl_egl_window
* eglwindow
= nullptr;
836 struct wl_compositor
* compositor
=
837 gdk_wayland_display_get_wl_compositor(gdk_display_get_default());
838 struct wl_surface
* wlsurface
= wl_compositor_create_surface(compositor
);
839 eglwindow
= wl_egl_window_create(wlsurface
, pbsize
.width
, pbsize
.height
);
840 if (!eglwindow
) return nullptr;
842 const auto surface
= egl
.fCreateWindowSurface(
843 config
, reinterpret_cast<EGLNativeWindowType
>(eglwindow
), 0);
845 MOZ_DIAGNOSTIC_ASSERT(!sWaylandOffscreenGLSurfaces
.Contains(surface
));
846 sWaylandOffscreenGLSurfaces
.LookupOrInsert(
847 surface
, new WaylandOffscreenGLSurface(wlsurface
, eglwindow
));
853 static const EGLint kEGLConfigAttribsRGB16
[] = {
854 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
855 LOCAL_EGL_RED_SIZE
, 5,
856 LOCAL_EGL_GREEN_SIZE
, 6,
857 LOCAL_EGL_BLUE_SIZE
, 5,
858 LOCAL_EGL_ALPHA_SIZE
, 0};
860 static const EGLint kEGLConfigAttribsRGB24
[] = {
861 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
862 LOCAL_EGL_RED_SIZE
, 8,
863 LOCAL_EGL_GREEN_SIZE
, 8,
864 LOCAL_EGL_BLUE_SIZE
, 8,
865 LOCAL_EGL_ALPHA_SIZE
, 0};
867 static const EGLint kEGLConfigAttribsRGBA32
[] = {
868 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
869 LOCAL_EGL_RED_SIZE
, 8,
870 LOCAL_EGL_GREEN_SIZE
, 8,
871 LOCAL_EGL_BLUE_SIZE
, 8,
872 LOCAL_EGL_ALPHA_SIZE
, 8};
874 bool CreateConfig(EglDisplay
& aEgl
, EGLConfig
* aConfig
, int32_t aDepth
,
875 bool aEnableDepthBuffer
, bool aUseGles
, bool aAllowFallback
) {
876 EGLConfig configs
[64];
877 std::vector
<EGLint
> attribs
;
878 EGLint ncfg
= ArrayLength(configs
);
882 for (const auto& cur
: kEGLConfigAttribsRGB16
) {
883 attribs
.push_back(cur
);
887 for (const auto& cur
: kEGLConfigAttribsRGB24
) {
888 attribs
.push_back(cur
);
892 for (const auto& cur
: kEGLConfigAttribsRGBA32
) {
893 attribs
.push_back(cur
);
897 NS_ERROR("Unknown pixel depth");
902 attribs
.push_back(LOCAL_EGL_RENDERABLE_TYPE
);
903 attribs
.push_back(LOCAL_EGL_OPENGL_ES2_BIT
);
905 for (const auto& cur
: kTerminationAttribs
) {
906 attribs
.push_back(cur
);
909 if (!aEgl
.fChooseConfig(attribs
.data(), configs
, ncfg
, &ncfg
) || ncfg
< 1) {
913 Maybe
<EGLConfig
> fallbackConfig
;
915 for (int j
= 0; j
< ncfg
; ++j
) {
916 EGLConfig config
= configs
[j
];
918 if (aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_RED_SIZE
, &r
) &&
919 aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_GREEN_SIZE
, &g
) &&
920 aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_BLUE_SIZE
, &b
) &&
921 aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_ALPHA_SIZE
, &a
) &&
922 ((aDepth
== 16 && r
== 5 && g
== 6 && b
== 5) ||
923 (aDepth
== 24 && r
== 8 && g
== 8 && b
== 8) ||
924 (aDepth
== 32 && r
== 8 && g
== 8 && b
== 8 && a
== 8))) {
926 if (aEnableDepthBuffer
) {
927 if (!aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_DEPTH_SIZE
, &z
) ||
933 if (GdkIsX11Display()) {
935 if (!aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_NATIVE_VISUAL_ID
,
940 XVisualInfo visual_info_template
, *visual_info
;
943 visual_info_template
.visualid
= configVisualID
;
945 XGetVisualInfo(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
946 VisualIDMask
, &visual_info_template
, &num_visuals
);
948 if (!visual_info
|| visual_info
->depth
!= aDepth
) {
949 if (aAllowFallback
&& !fallbackConfig
) {
950 fallbackConfig
= Some(config
);
961 if (kIsLinux
&& fallbackConfig
) {
962 *aConfig
= fallbackConfig
.value();
969 // Return true if a suitable EGLConfig was found and pass it out
970 // through aConfig. Return false otherwise.
972 // NB: It's entirely legal for the returned EGLConfig to be valid yet
973 // have the value null.
974 static bool CreateConfigScreen(EglDisplay
& egl
, EGLConfig
* const aConfig
,
975 const bool aEnableDepthBuffer
,
976 const bool aUseGles
) {
977 int32_t depth
= gfxVars::ScreenDepth();
978 if (CreateConfig(egl
, aConfig
, depth
, aEnableDepthBuffer
, aUseGles
)) {
981 #ifdef MOZ_WIDGET_ANDROID
983 // Android doesn't always support 16 bit so also try 24 bit
985 return CreateConfig(egl
, aConfig
, 24, aEnableDepthBuffer
, aUseGles
);
988 // Some devices that have 24 bit screens only support 16 bit OpenGL?
990 return CreateConfig(egl
, aConfig
, 16, aEnableDepthBuffer
, aUseGles
);
996 already_AddRefed
<GLContext
> GLContextProviderEGL::CreateForCompositorWidget(
997 CompositorWidget
* aCompositorWidget
, bool aHardwareWebRender
,
998 bool /*aForceAccelerated*/) {
999 EGLNativeWindowType window
= nullptr;
1000 if (aCompositorWidget
) {
1001 window
= GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget
);
1003 return GLContextEGLFactory::Create(window
, aHardwareWebRender
);
1006 EGLSurface
GLContextEGL::CreateCompatibleSurface(void* aWindow
) const {
1007 MOZ_ASSERT(aWindow
);
1008 MOZ_RELEASE_ASSERT(mSurfaceConfig
!= EGL_NO_CONFIG
);
1010 // NOTE: aWindow is an ANativeWindow
1011 EGLSurface surface
= mEgl
->fCreateWindowSurface(
1012 mSurfaceConfig
, reinterpret_cast<EGLNativeWindowType
>(aWindow
), nullptr);
1014 gfxCriticalError() << "CreateCompatibleSurface failed: "
1015 << hexa(GetError());
1020 static void FillContextAttribs(bool es3
, bool useGles
, nsTArray
<EGLint
>* out
) {
1021 out
->AppendElement(LOCAL_EGL_SURFACE_TYPE
);
1023 if (GdkIsWaylandDisplay()) {
1024 // Wayland on desktop does not support PBuffer or FBO.
1025 // We create a dummy wl_egl_window instead.
1026 out
->AppendElement(LOCAL_EGL_WINDOW_BIT
);
1030 out
->AppendElement(LOCAL_EGL_PBUFFER_BIT
);
1034 out
->AppendElement(LOCAL_EGL_RENDERABLE_TYPE
);
1036 out
->AppendElement(LOCAL_EGL_OPENGL_ES3_BIT_KHR
);
1038 out
->AppendElement(LOCAL_EGL_OPENGL_ES2_BIT
);
1042 out
->AppendElement(LOCAL_EGL_RED_SIZE
);
1043 out
->AppendElement(8);
1045 out
->AppendElement(LOCAL_EGL_GREEN_SIZE
);
1046 out
->AppendElement(8);
1048 out
->AppendElement(LOCAL_EGL_BLUE_SIZE
);
1049 out
->AppendElement(8);
1051 out
->AppendElement(LOCAL_EGL_ALPHA_SIZE
);
1052 out
->AppendElement(8);
1054 out
->AppendElement(LOCAL_EGL_DEPTH_SIZE
);
1055 out
->AppendElement(0);
1057 out
->AppendElement(LOCAL_EGL_STENCIL_SIZE
);
1058 out
->AppendElement(0);
1060 // EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
1061 out
->AppendElement(LOCAL_EGL_NONE
);
1062 out
->AppendElement(0);
1064 out
->AppendElement(0);
1065 out
->AppendElement(0);
1069 /// Useful for debugging, but normally unused.
1070 static GLint GetAttrib(GLLibraryEGL* egl, EGLConfig config, EGLint attrib) {
1072 egl->fGetConfigAttrib(config, attrib, &bits);
1073 MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
1079 static EGLConfig
ChooseConfig(EglDisplay
& egl
, const GLContextCreateDesc
& desc
,
1080 const bool useGles
) {
1081 nsTArray
<EGLint
> configAttribList
;
1082 FillContextAttribs(bool(desc
.flags
& CreateContextFlags::PREFER_ES3
), useGles
,
1085 const EGLint
* configAttribs
= configAttribList
.Elements();
1087 // The sorting dictated by the spec for eglChooseConfig reasonably assures
1088 // that a reasonable 'best' config is on top.
1089 const EGLint kMaxConfigs
= 1;
1090 EGLConfig configs
[kMaxConfigs
];
1091 EGLint foundConfigs
= 0;
1092 if (!egl
.fChooseConfig(configAttribs
, configs
, kMaxConfigs
, &foundConfigs
) ||
1093 foundConfigs
== 0) {
1094 return EGL_NO_CONFIG
;
1097 EGLConfig config
= configs
[0];
1103 bool GLContextEGL::FindVisual(int* const out_visualId
) {
1104 nsCString discardFailureId
;
1105 const auto egl
= DefaultEglDisplay(&discardFailureId
);
1108 << "GLContextEGL::FindVisual(): Failed to load EGL library!";
1114 if (!CreateConfig(*egl
, &config
, bpp
, /* aEnableDepthBuffer */ false,
1115 /* aUseGles */ false, /* aAllowFallback */ false)) {
1116 // We are on a buggy driver. Do not return a visual so a fallback path can
1117 // be used. See https://gitlab.freedesktop.org/mesa/mesa/-/issues/149
1120 if (egl
->fGetConfigAttrib(config
, LOCAL_EGL_NATIVE_VISUAL_ID
, out_visualId
)) {
1128 RefPtr
<GLContextEGL
> GLContextEGL::CreateWithoutSurface(
1129 const std::shared_ptr
<EglDisplay
> egl
, const GLContextCreateDesc
& desc
,
1130 nsACString
* const out_failureId
) {
1131 const auto WithUseGles
= [&](const bool useGles
) -> RefPtr
<GLContextEGL
> {
1132 #ifdef MOZ_WIDGET_GTK
1133 // First try creating a context with no config and no surface, this is what
1134 // we really want, and seems to be the only way to make selecting software
1135 // Mesa init properly when it's not the first device.
1136 if (egl
->IsExtensionSupported(EGLExtension::KHR_no_config_context
) &&
1137 egl
->IsExtensionSupported(EGLExtension::KHR_surfaceless_context
)) {
1138 // These extensions have been supported by mesa and nvidia drivers
1139 // since 2014 or earlier, this is the preferred code path
1140 auto fullDesc
= GLContextDesc
{desc
};
1141 fullDesc
.isOffscreen
= true;
1142 RefPtr
<GLContextEGL
> gl
= GLContextEGL::CreateGLContext(
1143 egl
, fullDesc
, EGL_NO_CONFIG
, EGL_NO_SURFACE
, useGles
, EGL_NO_CONFIG
,
1149 "Failed to create GLContext with no config and no surface, will try "
1154 const EGLConfig surfaceConfig
= ChooseConfig(*egl
, desc
, useGles
);
1155 if (surfaceConfig
== EGL_NO_CONFIG
) {
1156 *out_failureId
= "FEATURE_FAILURE_EGL_NO_CONFIG"_ns
;
1157 NS_WARNING("Failed to find a compatible config.");
1161 if (GLContext::ShouldSpew()) {
1162 egl
->DumpEGLConfig(surfaceConfig
);
1164 const EGLConfig contextConfig
=
1165 egl
->IsExtensionSupported(EGLExtension::KHR_no_config_context
)
1169 auto dummySize
= mozilla::gfx::IntSize
{16, 16};
1170 EGLSurface surface
= nullptr;
1172 if (GdkIsWaylandDisplay()) {
1173 surface
= GLContextEGL::CreateWaylandOffscreenSurface(*egl
, surfaceConfig
,
1178 surface
= GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
1179 *egl
, surfaceConfig
, LOCAL_EGL_NONE
, dummySize
);
1182 *out_failureId
= "FEATURE_FAILURE_EGL_POT"_ns
;
1183 NS_WARNING("Failed to create PBuffer for context!");
1187 auto fullDesc
= GLContextDesc
{desc
};
1188 fullDesc
.isOffscreen
= true;
1189 RefPtr
<GLContextEGL
> gl
=
1190 GLContextEGL::CreateGLContext(egl
, fullDesc
, surfaceConfig
, surface
,
1191 useGles
, contextConfig
, out_failureId
);
1193 NS_WARNING("Failed to create GLContext from PBuffer");
1194 egl
->fDestroySurface(surface
);
1195 #if defined(MOZ_WAYLAND)
1196 DeleteWaylandOffscreenGLSurface(surface
);
1205 #if defined(MOZ_WIDGET_ANDROID)
1208 preferGles
= StaticPrefs::gfx_egl_prefer_gles_enabled_AtStartup();
1209 #endif // defined(MOZ_WIDGET_ANDROID)
1210 RefPtr
<GLContextEGL
> gl
= WithUseGles(preferGles
);
1211 #if !defined(MOZ_WIDGET_ANDROID)
1213 gl
= WithUseGles(!preferGles
);
1215 #endif // !defined(MOZ_WIDGET_ANDROID)
1220 void GLContextEGL::DestroySurface(EglDisplay
& aEgl
, const EGLSurface aSurface
) {
1221 if (aSurface
!= EGL_NO_SURFACE
) {
1222 if (!aEgl
.fMakeCurrent(EGL_NO_SURFACE
, EGL_NO_SURFACE
, EGL_NO_CONTEXT
)) {
1223 const EGLint err
= aEgl
.mLib
->fGetError();
1224 gfxCriticalNote
<< "Error in eglMakeCurrent: " << gfx::hexa(err
);
1226 if (!aEgl
.fDestroySurface(aSurface
)) {
1227 const EGLint err
= aEgl
.mLib
->fGetError();
1228 gfxCriticalNote
<< "Error in eglDestroySurface: " << gfx::hexa(err
);
1230 #if defined(MOZ_WAYLAND)
1231 DeleteWaylandOffscreenGLSurface(aSurface
);
1237 already_AddRefed
<GLContext
> GLContextProviderEGL::CreateHeadless(
1238 const GLContextCreateDesc
& desc
, nsACString
* const out_failureId
) {
1239 const auto display
= DefaultEglDisplay(out_failureId
);
1243 auto ret
= GLContextEGL::CreateWithoutSurface(display
, desc
, out_failureId
);
1244 return ret
.forget();
1247 // Don't want a global context on Android as 1) share groups across 2 threads
1248 // fail on many Tegra drivers (bug 759225) and 2) some mobile devices have a
1249 // very strict limit on global number of GL contexts (bug 754257) and 3) each
1250 // EGL context eats 750k on B2G (bug 813783)
1252 GLContext
* GLContextProviderEGL::GetGlobalContext() { return nullptr; }
1256 /*static*/ void GLContextProviderEGL::Shutdown() { GLLibraryEGL::Shutdown(); }
1258 } /* namespace gl */
1259 } /* namespace mozilla */
1261 #undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS