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
);
339 EGLNativeWindowType window
=
340 GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget
);
342 #ifdef MOZ_WIDGET_GTK
343 // RenderCompositorEGL does not like EGL_NO_SURFACE as it fallbacks
344 // to SW rendering or claims itself as paused.
345 // In case we're missing valid native window because aCompositorWidget
346 // hidden, just create a fallback EGLSurface. Actual EGLSurface will be
347 // created by widget code later when aCompositorWidget becomes visible.
348 mozilla::gfx::IntSize
pbSize(16, 16);
350 if (GdkIsWaylandDisplay()) {
351 return CreateWaylandOffscreenSurface(*egl
, aConfig
, pbSize
);
355 return CreatePBufferSurfaceTryingPowerOfTwo(*egl
, aConfig
, LOCAL_EGL_NONE
,
359 gfxCriticalNote
<< "window is null";
360 return EGL_NO_SURFACE
;
364 return mozilla::gl::CreateSurfaceFromNativeWindow(*egl
, window
, aConfig
);
367 GLContextEGL::GLContextEGL(const std::shared_ptr
<EglDisplay
> egl
,
368 const GLContextDesc
& desc
, EGLConfig surfaceConfig
,
369 EGLSurface surface
, EGLContext context
)
370 : GLContext(desc
, nullptr, false),
372 mSurfaceConfig(surfaceConfig
),
375 mFallbackSurface(CreateFallbackSurface(*mEgl
, mSurfaceConfig
)) {
377 printf_stderr("Initializing context %p surface %p on display %p\n", mContext
,
378 mSurface
, mEgl
->mDisplay
);
382 void GLContextEGL::OnMarkDestroyed() {
383 if (mSurfaceOverride
!= EGL_NO_SURFACE
) {
384 SetEGLSurfaceOverride(EGL_NO_SURFACE
);
388 GLContextEGL::~GLContextEGL() {
391 // Wrapped context should not destroy eglContext/Surface
397 printf_stderr("Destroying context %p surface %p on display %p\n", mContext
,
398 mSurface
, mEgl
->mDisplay
);
401 mEgl
->fDestroyContext(mContext
);
403 DestroySurface(*mEgl
, mSurface
);
404 DestroySurface(*mEgl
, mFallbackSurface
);
407 bool GLContextEGL::Init() {
408 if (!GLContext::Init()) return false;
410 bool current
= MakeCurrent();
412 gfx::LogFailure("Couldn't get device attachments for device."_ns
);
417 mEgl
->HasKHRImageBase() &&
418 mEgl
->IsExtensionSupported(EGLExtension::KHR_gl_texture_2D_image
) &&
419 IsExtensionSupported(OES_EGL_image
);
424 bool GLContextEGL::BindTexImage() {
425 if (!mSurface
) return false;
427 if (mBound
&& !ReleaseTexImage()) return false;
430 mEgl
->fBindTexImage((EGLSurface
)mSurface
, LOCAL_EGL_BACK_BUFFER
);
431 if (success
== LOCAL_EGL_FALSE
) return false;
437 bool GLContextEGL::ReleaseTexImage() {
438 if (!mBound
) return true;
440 if (!mSurface
) return false;
443 success
= mEgl
->fReleaseTexImage((EGLSurface
)mSurface
, LOCAL_EGL_BACK_BUFFER
);
444 if (success
== LOCAL_EGL_FALSE
) return false;
450 void GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf
) {
451 mSurfaceOverride
= surf
;
452 DebugOnly
<bool> ok
= MakeCurrent(true);
456 bool GLContextEGL::MakeCurrentImpl() const {
458 (mSurfaceOverride
!= EGL_NO_SURFACE
) ? mSurfaceOverride
: mSurface
;
460 surface
= mFallbackSurface
;
463 const bool succeeded
= mEgl
->fMakeCurrent(surface
, surface
, mContext
);
465 const auto eglError
= mEgl
->mLib
->fGetError();
466 if (eglError
== LOCAL_EGL_CONTEXT_LOST
) {
467 OnContextLostError();
469 NS_WARNING("Failed to make GL context current!");
471 printf_stderr("EGL Error: 0x%04x\n", eglError
);
479 bool GLContextEGL::IsCurrentImpl() const {
480 return mEgl
->mLib
->fGetCurrentContext() == mContext
;
483 bool GLContextEGL::RenewSurface(CompositorWidget
* aWidget
) {
487 // unconditionally release the surface and create a new one. Don't try to
488 // optimize this away. If we get here, then by definition we know that we want
489 // to get a new surface.
493 EGLNativeWindowType nativeWindow
=
494 GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget
);
496 mSurface
= mozilla::gl::CreateSurfaceFromNativeWindow(*mEgl
, nativeWindow
,
499 NS_WARNING("Failed to create EGLSurface from native window");
503 const bool ok
= MakeCurrent(true);
505 #ifdef MOZ_WIDGET_GTK
507 const int interval
= gfxVars::SwapIntervalEGL() ? 1 : 0;
508 mEgl
->fSwapInterval(interval
);
514 void GLContextEGL::ReleaseSurface() {
516 DestroySurface(*mEgl
, mSurface
);
518 if (mSurface
== mSurfaceOverride
) {
519 mSurfaceOverride
= EGL_NO_SURFACE
;
521 mSurface
= EGL_NO_SURFACE
;
524 Maybe
<SymbolLoader
> GLContextEGL::GetSymbolLoader() const {
525 return mEgl
->mLib
->GetSymbolLoader();
528 bool GLContextEGL::SwapBuffers() {
530 mSurfaceOverride
!= EGL_NO_SURFACE
? mSurfaceOverride
: mSurface
;
532 if ((mEgl
->IsExtensionSupported(
533 EGLExtension::EXT_swap_buffers_with_damage
) ||
534 mEgl
->IsExtensionSupported(
535 EGLExtension::KHR_swap_buffers_with_damage
))) {
536 std::vector
<EGLint
> rects
;
537 for (auto iter
= mDamageRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
538 const IntRect
& r
= iter
.Get();
539 rects
.push_back(r
.X());
540 rects
.push_back(r
.Y());
541 rects
.push_back(r
.Width());
542 rects
.push_back(r
.Height());
544 mDamageRegion
.SetEmpty();
545 return mEgl
->fSwapBuffersWithDamage(surface
, rects
.data(),
548 return mEgl
->fSwapBuffers(surface
);
554 void GLContextEGL::SetDamage(const nsIntRegion
& aDamageRegion
) {
555 mDamageRegion
= aDamageRegion
;
558 void GLContextEGL::GetWSIInfo(nsCString
* const out
) const {
559 out
->AppendLiteral("EGL_VENDOR: ");
561 (const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
, LOCAL_EGL_VENDOR
));
563 out
->AppendLiteral("\nEGL_VERSION: ");
565 (const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
, LOCAL_EGL_VERSION
));
567 out
->AppendLiteral("\nEGL_EXTENSIONS: ");
568 out
->Append((const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
,
569 LOCAL_EGL_EXTENSIONS
));
571 #ifndef ANDROID // This query will crash some old android.
572 out
->AppendLiteral("\nEGL_EXTENSIONS(nullptr): ");
574 (const char*)mEgl
->mLib
->fQueryString(nullptr, LOCAL_EGL_EXTENSIONS
));
578 bool GLContextEGL::HasExtBufferAge() const {
579 return mEgl
->IsExtensionSupported(EGLExtension::EXT_buffer_age
);
582 bool GLContextEGL::HasKhrPartialUpdate() const {
583 return mEgl
->IsExtensionSupported(EGLExtension::KHR_partial_update
);
586 GLint
GLContextEGL::GetBufferAge() const {
588 mSurfaceOverride
!= EGL_NO_SURFACE
? mSurfaceOverride
: mSurface
;
590 if (surface
&& (HasExtBufferAge() || HasKhrPartialUpdate())) {
592 mEgl
->fQuerySurface(surface
, LOCAL_EGL_BUFFER_AGE_EXT
, &result
);
599 #define LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ 0x6000
601 RefPtr
<GLContextEGL
> GLContextEGL::CreateGLContext(
602 const std::shared_ptr
<EglDisplay
> egl
, const GLContextDesc
& desc
,
603 EGLConfig surfaceConfig
, EGLSurface surface
, const bool useGles
,
604 EGLConfig contextConfig
, nsACString
* const out_failureId
) {
605 const auto& flags
= desc
.flags
;
607 std::vector
<EGLint
> required_attribs
;
610 // TODO: This fBindAPI could be more thread-safe
611 if (egl
->mLib
->fBindAPI(LOCAL_EGL_OPENGL_ES_API
) == LOCAL_EGL_FALSE
) {
612 *out_failureId
= "FEATURE_FAILURE_EGL_ES"_ns
;
613 NS_WARNING("Failed to bind API to GLES!");
616 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
617 if (flags
& CreateContextFlags::PREFER_ES3
) {
618 required_attribs
.push_back(3);
620 required_attribs
.push_back(2);
623 if (egl
->mLib
->fBindAPI(LOCAL_EGL_OPENGL_API
) == LOCAL_EGL_FALSE
) {
624 *out_failureId
= "FEATURE_FAILURE_EGL"_ns
;
625 NS_WARNING("Failed to bind API to GL!");
628 if (flags
& CreateContextFlags::REQUIRE_COMPAT_PROFILE
) {
629 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK
);
630 required_attribs
.push_back(
631 LOCAL_EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT
);
632 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
633 required_attribs
.push_back(2);
635 // !REQUIRE_COMPAT_PROFILE means core profle.
636 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK
);
637 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT
);
638 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
639 required_attribs
.push_back(3);
640 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MINOR_VERSION
);
641 required_attribs
.push_back(2);
645 if ((flags
& CreateContextFlags::PREFER_EXACT_VERSION
) &&
646 egl
->mLib
->IsANGLE()) {
647 required_attribs
.push_back(
648 LOCAL_EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE
);
649 required_attribs
.push_back(LOCAL_EGL_FALSE
);
652 const auto debugFlags
= GLContext::ChooseDebugFlags(flags
);
653 if (!debugFlags
&& flags
& CreateContextFlags::NO_VALIDATION
&&
654 egl
->IsExtensionSupported(EGLExtension::KHR_create_context_no_error
)) {
655 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_NO_ERROR_KHR
);
656 required_attribs
.push_back(LOCAL_EGL_TRUE
);
659 if (flags
& CreateContextFlags::PROVOKING_VERTEX_DONT_CARE
&&
660 egl
->IsExtensionSupported(
661 EGLExtension::MOZ_create_context_provoking_vertex_dont_care
)) {
662 required_attribs
.push_back(
663 LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ
);
664 required_attribs
.push_back(LOCAL_EGL_TRUE
);
667 std::vector
<EGLint
> ext_robustness_attribs
;
668 std::vector
<EGLint
> ext_rbab_attribs
; // RBAB: Robust Buffer Access Behavior
669 std::vector
<EGLint
> khr_robustness_attribs
;
670 std::vector
<EGLint
> khr_rbab_attribs
; // RBAB: Robust Buffer Access Behavior
671 if (flags
& CreateContextFlags::PREFER_ROBUSTNESS
) {
672 std::vector
<EGLint
> base_robustness_attribs
= required_attribs
;
673 if (egl
->IsExtensionSupported(
674 EGLExtension::NV_robustness_video_memory_purge
)) {
675 base_robustness_attribs
.push_back(
676 LOCAL_EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
);
677 base_robustness_attribs
.push_back(LOCAL_EGL_TRUE
);
680 if (egl
->IsExtensionSupported(
681 EGLExtension::EXT_create_context_robustness
)) {
682 ext_robustness_attribs
= base_robustness_attribs
;
683 ext_robustness_attribs
.push_back(
684 LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT
);
685 ext_robustness_attribs
.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT
);
687 if (gfxVars::AllowEglRbab()) {
688 ext_rbab_attribs
= ext_robustness_attribs
;
689 ext_rbab_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT
);
690 ext_rbab_attribs
.push_back(LOCAL_EGL_TRUE
);
694 if (egl
->IsExtensionSupported(EGLExtension::KHR_create_context
)) {
695 khr_robustness_attribs
= base_robustness_attribs
;
696 khr_robustness_attribs
.push_back(
697 LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR
);
698 khr_robustness_attribs
.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_KHR
);
700 khr_rbab_attribs
= khr_robustness_attribs
;
701 khr_rbab_attribs
.push_back(LOCAL_EGL_CONTEXT_FLAGS_KHR
);
702 khr_rbab_attribs
.push_back(
703 LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR
);
707 const auto fnCreate
= [&](const std::vector
<EGLint
>& attribs
) {
708 auto terminated_attribs
= attribs
;
710 for (const auto& cur
: kTerminationAttribs
) {
711 terminated_attribs
.push_back(cur
);
714 return egl
->fCreateContext(contextConfig
, EGL_NO_CONTEXT
,
715 terminated_attribs
.data());
720 if (!khr_rbab_attribs
.empty()) {
721 context
= fnCreate(khr_rbab_attribs
);
723 NS_WARNING("Failed to create EGLContext with khr_rbab_attribs");
726 if (!ext_rbab_attribs
.empty()) {
727 context
= fnCreate(ext_rbab_attribs
);
729 NS_WARNING("Failed to create EGLContext with ext_rbab_attribs");
732 if (!khr_robustness_attribs
.empty()) {
733 context
= fnCreate(khr_robustness_attribs
);
735 NS_WARNING("Failed to create EGLContext with khr_robustness_attribs");
738 if (!ext_robustness_attribs
.empty()) {
739 context
= fnCreate(ext_robustness_attribs
);
741 NS_WARNING("Failed to create EGLContext with ext_robustness_attribs");
744 context
= fnCreate(required_attribs
);
746 NS_WARNING("Failed to create EGLContext with required_attribs");
748 *out_failureId
= "FEATURE_FAILURE_EGL_CREATE"_ns
;
753 RefPtr
<GLContextEGL
> glContext
=
754 new GLContextEGL(egl
, desc
, surfaceConfig
, surface
, context
);
755 if (!glContext
->Init()) {
756 *out_failureId
= "FEATURE_FAILURE_EGL_INIT"_ns
;
760 if (GLContext::ShouldSpew()) {
761 printf_stderr("new GLContextEGL %p on EGLDisplay %p\n", glContext
.get(),
769 EGLSurface
GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
770 EglDisplay
& egl
, EGLConfig config
, EGLenum bindToTextureFormat
,
771 mozilla::gfx::IntSize
& pbsize
) {
772 nsTArray
<EGLint
> pbattrs(16);
773 EGLSurface surface
= nullptr;
775 TRY_AGAIN_POWER_OF_TWO
:
777 pbattrs
.AppendElement(LOCAL_EGL_WIDTH
);
778 pbattrs
.AppendElement(pbsize
.width
);
779 pbattrs
.AppendElement(LOCAL_EGL_HEIGHT
);
780 pbattrs
.AppendElement(pbsize
.height
);
782 if (bindToTextureFormat
!= LOCAL_EGL_NONE
) {
783 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_TARGET
);
784 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_2D
);
786 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_FORMAT
);
787 pbattrs
.AppendElement(bindToTextureFormat
);
790 for (const auto& cur
: kTerminationAttribs
) {
791 pbattrs
.AppendElement(cur
);
794 surface
= egl
.fCreatePbufferSurface(config
, &pbattrs
[0]);
796 if (!is_power_of_two(pbsize
.width
) || !is_power_of_two(pbsize
.height
)) {
797 if (!is_power_of_two(pbsize
.width
))
798 pbsize
.width
= next_power_of_two(pbsize
.width
);
799 if (!is_power_of_two(pbsize
.height
))
800 pbsize
.height
= next_power_of_two(pbsize
.height
);
802 NS_WARNING("Failed to create pbuffer, trying power of two dims");
803 goto TRY_AGAIN_POWER_OF_TWO
;
806 NS_WARNING("Failed to create pbuffer surface");
813 #if defined(MOZ_WAYLAND)
814 WaylandOffscreenGLSurface::WaylandOffscreenGLSurface(
815 struct wl_surface
* aWaylandSurface
, struct wl_egl_window
* aEGLWindow
)
816 : mWaylandSurface(aWaylandSurface
), mEGLWindow(aEGLWindow
) {}
818 WaylandOffscreenGLSurface::~WaylandOffscreenGLSurface() {
820 wl_egl_window_destroy(mEGLWindow
);
822 if (mWaylandSurface
) {
823 wl_surface_destroy(mWaylandSurface
);
828 EGLSurface
GLContextEGL::CreateWaylandOffscreenSurface(
829 EglDisplay
& egl
, EGLConfig config
, mozilla::gfx::IntSize
& pbsize
) {
830 wl_egl_window
* eglwindow
= nullptr;
832 struct wl_compositor
* compositor
=
833 gdk_wayland_display_get_wl_compositor(gdk_display_get_default());
834 struct wl_surface
* wlsurface
= wl_compositor_create_surface(compositor
);
835 eglwindow
= wl_egl_window_create(wlsurface
, pbsize
.width
, pbsize
.height
);
836 if (!eglwindow
) return nullptr;
838 const auto surface
= egl
.fCreateWindowSurface(
839 config
, reinterpret_cast<EGLNativeWindowType
>(eglwindow
), 0);
841 MOZ_DIAGNOSTIC_ASSERT(!sWaylandOffscreenGLSurfaces
.Contains(surface
));
842 sWaylandOffscreenGLSurfaces
.LookupOrInsert(
843 surface
, new WaylandOffscreenGLSurface(wlsurface
, eglwindow
));
849 static const EGLint kEGLConfigAttribsRGB16
[] = {
850 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
851 LOCAL_EGL_RED_SIZE
, 5,
852 LOCAL_EGL_GREEN_SIZE
, 6,
853 LOCAL_EGL_BLUE_SIZE
, 5,
854 LOCAL_EGL_ALPHA_SIZE
, 0};
856 static const EGLint kEGLConfigAttribsRGB24
[] = {
857 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
858 LOCAL_EGL_RED_SIZE
, 8,
859 LOCAL_EGL_GREEN_SIZE
, 8,
860 LOCAL_EGL_BLUE_SIZE
, 8,
861 LOCAL_EGL_ALPHA_SIZE
, 0};
863 static const EGLint kEGLConfigAttribsRGBA32
[] = {
864 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
865 LOCAL_EGL_RED_SIZE
, 8,
866 LOCAL_EGL_GREEN_SIZE
, 8,
867 LOCAL_EGL_BLUE_SIZE
, 8,
868 LOCAL_EGL_ALPHA_SIZE
, 8};
870 bool CreateConfig(EglDisplay
& aEgl
, EGLConfig
* aConfig
, int32_t aDepth
,
871 bool aEnableDepthBuffer
, bool aUseGles
, bool aAllowFallback
) {
872 EGLConfig configs
[64];
873 std::vector
<EGLint
> attribs
;
874 EGLint ncfg
= ArrayLength(configs
);
878 for (const auto& cur
: kEGLConfigAttribsRGB16
) {
879 attribs
.push_back(cur
);
883 for (const auto& cur
: kEGLConfigAttribsRGB24
) {
884 attribs
.push_back(cur
);
888 for (const auto& cur
: kEGLConfigAttribsRGBA32
) {
889 attribs
.push_back(cur
);
893 NS_ERROR("Unknown pixel depth");
898 attribs
.push_back(LOCAL_EGL_RENDERABLE_TYPE
);
899 attribs
.push_back(LOCAL_EGL_OPENGL_ES2_BIT
);
901 for (const auto& cur
: kTerminationAttribs
) {
902 attribs
.push_back(cur
);
905 if (!aEgl
.fChooseConfig(attribs
.data(), configs
, ncfg
, &ncfg
) || ncfg
< 1) {
909 Maybe
<EGLConfig
> fallbackConfig
;
911 for (int j
= 0; j
< ncfg
; ++j
) {
912 EGLConfig config
= configs
[j
];
914 if (aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_RED_SIZE
, &r
) &&
915 aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_GREEN_SIZE
, &g
) &&
916 aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_BLUE_SIZE
, &b
) &&
917 aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_ALPHA_SIZE
, &a
) &&
918 ((aDepth
== 16 && r
== 5 && g
== 6 && b
== 5) ||
919 (aDepth
== 24 && r
== 8 && g
== 8 && b
== 8) ||
920 (aDepth
== 32 && r
== 8 && g
== 8 && b
== 8 && a
== 8))) {
922 if (aEnableDepthBuffer
) {
923 if (!aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_DEPTH_SIZE
, &z
) ||
929 if (GdkIsX11Display()) {
931 if (!aEgl
.fGetConfigAttrib(config
, LOCAL_EGL_NATIVE_VISUAL_ID
,
936 XVisualInfo visual_info_template
, *visual_info
;
939 visual_info_template
.visualid
= configVisualID
;
941 XGetVisualInfo(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
942 VisualIDMask
, &visual_info_template
, &num_visuals
);
944 if (!visual_info
|| visual_info
->depth
!= aDepth
) {
945 if (aAllowFallback
&& !fallbackConfig
) {
946 fallbackConfig
= Some(config
);
957 if (kIsLinux
&& fallbackConfig
) {
958 *aConfig
= fallbackConfig
.value();
965 // Return true if a suitable EGLConfig was found and pass it out
966 // through aConfig. Return false otherwise.
968 // NB: It's entirely legal for the returned EGLConfig to be valid yet
969 // have the value null.
970 static bool CreateConfigScreen(EglDisplay
& egl
, EGLConfig
* const aConfig
,
971 const bool aEnableDepthBuffer
,
972 const bool aUseGles
) {
973 int32_t depth
= gfxVars::PrimaryScreenDepth();
974 if (CreateConfig(egl
, aConfig
, depth
, aEnableDepthBuffer
, aUseGles
)) {
977 #ifdef MOZ_WIDGET_ANDROID
979 // Android doesn't always support 16 bit so also try 24 bit
981 return CreateConfig(egl
, aConfig
, 24, aEnableDepthBuffer
, aUseGles
);
984 // Some devices that have 24 bit screens only support 16 bit OpenGL?
986 return CreateConfig(egl
, aConfig
, 16, aEnableDepthBuffer
, aUseGles
);
992 already_AddRefed
<GLContext
> GLContextProviderEGL::CreateForCompositorWidget(
993 CompositorWidget
* aCompositorWidget
, bool aHardwareWebRender
,
994 bool /*aForceAccelerated*/) {
995 EGLNativeWindowType window
= nullptr;
996 if (aCompositorWidget
) {
997 window
= GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget
);
999 return GLContextEGLFactory::Create(window
, aHardwareWebRender
);
1002 EGLSurface
GLContextEGL::CreateCompatibleSurface(void* aWindow
) const {
1003 MOZ_ASSERT(aWindow
);
1004 MOZ_RELEASE_ASSERT(mSurfaceConfig
!= EGL_NO_CONFIG
);
1006 // NOTE: aWindow is an ANativeWindow
1007 EGLSurface surface
= mEgl
->fCreateWindowSurface(
1008 mSurfaceConfig
, reinterpret_cast<EGLNativeWindowType
>(aWindow
), nullptr);
1010 gfxCriticalError() << "CreateCompatibleSurface failed: "
1011 << hexa(GetError());
1016 static void FillContextAttribs(bool es3
, bool useGles
, nsTArray
<EGLint
>* out
) {
1017 out
->AppendElement(LOCAL_EGL_SURFACE_TYPE
);
1019 if (GdkIsWaylandDisplay()) {
1020 // Wayland on desktop does not support PBuffer or FBO.
1021 // We create a dummy wl_egl_window instead.
1022 out
->AppendElement(LOCAL_EGL_WINDOW_BIT
);
1026 out
->AppendElement(LOCAL_EGL_PBUFFER_BIT
);
1030 out
->AppendElement(LOCAL_EGL_RENDERABLE_TYPE
);
1032 out
->AppendElement(LOCAL_EGL_OPENGL_ES3_BIT_KHR
);
1034 out
->AppendElement(LOCAL_EGL_OPENGL_ES2_BIT
);
1038 out
->AppendElement(LOCAL_EGL_RED_SIZE
);
1039 out
->AppendElement(8);
1041 out
->AppendElement(LOCAL_EGL_GREEN_SIZE
);
1042 out
->AppendElement(8);
1044 out
->AppendElement(LOCAL_EGL_BLUE_SIZE
);
1045 out
->AppendElement(8);
1047 out
->AppendElement(LOCAL_EGL_ALPHA_SIZE
);
1048 out
->AppendElement(8);
1050 out
->AppendElement(LOCAL_EGL_DEPTH_SIZE
);
1051 out
->AppendElement(0);
1053 out
->AppendElement(LOCAL_EGL_STENCIL_SIZE
);
1054 out
->AppendElement(0);
1056 // EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
1057 out
->AppendElement(LOCAL_EGL_NONE
);
1058 out
->AppendElement(0);
1060 out
->AppendElement(0);
1061 out
->AppendElement(0);
1065 /// Useful for debugging, but normally unused.
1066 static GLint GetAttrib(GLLibraryEGL* egl, EGLConfig config, EGLint attrib) {
1068 egl->fGetConfigAttrib(config, attrib, &bits);
1069 MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
1075 static EGLConfig
ChooseConfig(EglDisplay
& egl
, const GLContextCreateDesc
& desc
,
1076 const bool useGles
) {
1077 nsTArray
<EGLint
> configAttribList
;
1078 FillContextAttribs(bool(desc
.flags
& CreateContextFlags::PREFER_ES3
), useGles
,
1081 const EGLint
* configAttribs
= configAttribList
.Elements();
1083 // The sorting dictated by the spec for eglChooseConfig reasonably assures
1084 // that a reasonable 'best' config is on top.
1085 const EGLint kMaxConfigs
= 1;
1086 EGLConfig configs
[kMaxConfigs
];
1087 EGLint foundConfigs
= 0;
1088 if (!egl
.fChooseConfig(configAttribs
, configs
, kMaxConfigs
, &foundConfigs
) ||
1089 foundConfigs
== 0) {
1090 return EGL_NO_CONFIG
;
1093 EGLConfig config
= configs
[0];
1099 bool GLContextEGL::FindVisual(int* const out_visualId
) {
1100 nsCString discardFailureId
;
1101 const auto egl
= DefaultEglDisplay(&discardFailureId
);
1104 << "GLContextEGL::FindVisual(): Failed to load EGL library!";
1110 if (!CreateConfig(*egl
, &config
, bpp
, /* aEnableDepthBuffer */ false,
1111 /* aUseGles */ false, /* aAllowFallback */ false)) {
1112 // We are on a buggy driver. Do not return a visual so a fallback path can
1113 // be used. See https://gitlab.freedesktop.org/mesa/mesa/-/issues/149
1116 if (egl
->fGetConfigAttrib(config
, LOCAL_EGL_NATIVE_VISUAL_ID
, out_visualId
)) {
1124 RefPtr
<GLContextEGL
> GLContextEGL::CreateWithoutSurface(
1125 const std::shared_ptr
<EglDisplay
> egl
, const GLContextCreateDesc
& desc
,
1126 nsACString
* const out_failureId
) {
1127 const auto WithUseGles
= [&](const bool useGles
) -> RefPtr
<GLContextEGL
> {
1128 #ifdef MOZ_WIDGET_GTK
1129 // First try creating a context with no config and no surface, this is what
1130 // we really want, and seems to be the only way to make selecting software
1131 // Mesa init properly when it's not the first device.
1132 if (egl
->IsExtensionSupported(EGLExtension::KHR_no_config_context
) &&
1133 egl
->IsExtensionSupported(EGLExtension::KHR_surfaceless_context
)) {
1134 // These extensions have been supported by mesa and nvidia drivers
1135 // since 2014 or earlier, this is the preferred code path
1136 auto fullDesc
= GLContextDesc
{desc
};
1137 fullDesc
.isOffscreen
= true;
1138 RefPtr
<GLContextEGL
> gl
= GLContextEGL::CreateGLContext(
1139 egl
, fullDesc
, EGL_NO_CONFIG
, EGL_NO_SURFACE
, useGles
, EGL_NO_CONFIG
,
1145 "Failed to create GLContext with no config and no surface, will try "
1150 const EGLConfig surfaceConfig
= ChooseConfig(*egl
, desc
, useGles
);
1151 if (surfaceConfig
== EGL_NO_CONFIG
) {
1152 *out_failureId
= "FEATURE_FAILURE_EGL_NO_CONFIG"_ns
;
1153 NS_WARNING("Failed to find a compatible config.");
1157 if (GLContext::ShouldSpew()) {
1158 egl
->DumpEGLConfig(surfaceConfig
);
1160 const EGLConfig contextConfig
=
1161 egl
->IsExtensionSupported(EGLExtension::KHR_no_config_context
)
1165 auto dummySize
= mozilla::gfx::IntSize
{16, 16};
1166 EGLSurface surface
= nullptr;
1168 if (GdkIsWaylandDisplay()) {
1169 surface
= GLContextEGL::CreateWaylandOffscreenSurface(*egl
, surfaceConfig
,
1174 surface
= GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
1175 *egl
, surfaceConfig
, LOCAL_EGL_NONE
, dummySize
);
1178 *out_failureId
= "FEATURE_FAILURE_EGL_POT"_ns
;
1179 NS_WARNING("Failed to create PBuffer for context!");
1183 auto fullDesc
= GLContextDesc
{desc
};
1184 fullDesc
.isOffscreen
= true;
1185 RefPtr
<GLContextEGL
> gl
=
1186 GLContextEGL::CreateGLContext(egl
, fullDesc
, surfaceConfig
, surface
,
1187 useGles
, contextConfig
, out_failureId
);
1189 NS_WARNING("Failed to create GLContext from PBuffer");
1190 egl
->fDestroySurface(surface
);
1191 #if defined(MOZ_WAYLAND)
1192 DeleteWaylandOffscreenGLSurface(surface
);
1201 #if defined(MOZ_WIDGET_ANDROID)
1204 preferGles
= StaticPrefs::gfx_egl_prefer_gles_enabled_AtStartup();
1205 #endif // defined(MOZ_WIDGET_ANDROID)
1206 RefPtr
<GLContextEGL
> gl
= WithUseGles(preferGles
);
1207 #if !defined(MOZ_WIDGET_ANDROID)
1209 gl
= WithUseGles(!preferGles
);
1211 #endif // !defined(MOZ_WIDGET_ANDROID)
1216 void GLContextEGL::DestroySurface(EglDisplay
& aEgl
, const EGLSurface aSurface
) {
1217 if (aSurface
!= EGL_NO_SURFACE
) {
1218 if (!aEgl
.fMakeCurrent(EGL_NO_SURFACE
, EGL_NO_SURFACE
, EGL_NO_CONTEXT
)) {
1219 const EGLint err
= aEgl
.mLib
->fGetError();
1220 gfxCriticalNote
<< "Error in eglMakeCurrent: " << gfx::hexa(err
);
1222 if (!aEgl
.fDestroySurface(aSurface
)) {
1223 const EGLint err
= aEgl
.mLib
->fGetError();
1224 gfxCriticalNote
<< "Error in eglDestroySurface: " << gfx::hexa(err
);
1226 #if defined(MOZ_WAYLAND)
1227 DeleteWaylandOffscreenGLSurface(aSurface
);
1233 already_AddRefed
<GLContext
> GLContextProviderEGL::CreateHeadless(
1234 const GLContextCreateDesc
& desc
, nsACString
* const out_failureId
) {
1235 const auto display
= DefaultEglDisplay(out_failureId
);
1239 auto ret
= GLContextEGL::CreateWithoutSurface(display
, desc
, out_failureId
);
1240 return ret
.forget();
1243 // Don't want a global context on Android as 1) share groups across 2 threads
1244 // fail on many Tegra drivers (bug 759225) and 2) some mobile devices have a
1245 // very strict limit on global number of GL contexts (bug 754257) and 3) each
1246 // EGL context eats 750k on B2G (bug 813783)
1248 GLContext
* GLContextProviderEGL::GetGlobalContext() { return nullptr; }
1252 /*static*/ void GLContextProviderEGL::Shutdown() { GLLibraryEGL::Shutdown(); }
1254 } /* namespace gl */
1255 } /* namespace mozilla */
1257 #undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS