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 "gfxASurface.h"
55 #include "gfxCrashReporterUtils.h"
56 #include "gfxFailure.h"
57 #include "gfxPlatform.h"
59 #include "GLBlitHelper.h"
60 #include "GLContextEGL.h"
61 #include "GLContextProvider.h"
62 #include "GLLibraryEGL.h"
63 #include "GLLibraryLoader.h"
64 #include "mozilla/ArrayUtils.h"
65 #include "mozilla/Preferences.h"
66 #include "mozilla/Services.h"
67 #include "mozilla/StaticPrefs_gfx.h"
68 #include "mozilla/gfx/gfxVars.h"
69 #include "mozilla/gfx/BuildConstants.h"
70 #include "mozilla/gfx/Logging.h"
71 #include "mozilla/layers/CompositorOptions.h"
72 #include "mozilla/widget/CompositorWidget.h"
74 #include "nsIWidget.h"
75 #include "nsThreadUtils.h"
76 #include "ScopedGLHelpers.h"
78 #if defined(MOZ_WIDGET_GTK)
79 # include "mozilla/widget/GtkCompositorWidget.h"
80 # include "mozilla/WidgetUtilsGtk.h"
81 # if defined(MOZ_WAYLAND)
82 # include <gdk/gdkwayland.h>
83 # include <wayland-egl.h>
84 # define MOZ_GTK_WAYLAND 1
90 using namespace mozilla::gfx
;
95 using namespace mozilla::widget
;
97 #if defined(MOZ_WAYLAND)
98 class WaylandGLSurface
{
100 WaylandGLSurface(struct wl_surface
* aWaylandSurface
,
101 struct wl_egl_window
* aEGLWindow
);
105 struct wl_surface
* mWaylandSurface
;
106 struct wl_egl_window
* mEGLWindow
;
109 static nsTHashMap
<nsPtrHashKey
<void>, WaylandGLSurface
*> sWaylandGLSurface
;
111 void DeleteWaylandGLSurface(EGLSurface surface
) {
112 # ifdef MOZ_GTK_WAYLAND
113 // We're running on Wayland which means our EGLSurface may
114 // have attached Wayland backend data which must be released.
115 if (GdkIsWaylandDisplay()) {
116 auto entry
= sWaylandGLSurface
.Lookup(surface
);
126 static bool CreateConfigScreen(EglDisplay
&, EGLConfig
* const aConfig
,
127 const bool aEnableDepthBuffer
,
128 const bool aUseGles
, int aVisual
= 0);
130 // append three zeros at the end of attribs list to work around
131 // EGL implementation bugs that iterate until they find 0, instead of
132 // EGL_NONE. See bug 948406.
133 #define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
134 LOCAL_EGL_NONE, 0, 0, 0
136 static EGLint kTerminationAttribs
[] = {
137 EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
};
139 static int next_power_of_two(int v
) {
151 static bool is_power_of_two(int v
) {
152 NS_ASSERTION(v
>= 0, "bad value");
154 if (v
== 0) return true;
156 return (v
& (v
- 1)) == 0;
159 static void DestroySurface(EglDisplay
& egl
, const EGLSurface oldSurface
) {
160 if (oldSurface
!= EGL_NO_SURFACE
) {
161 // TODO: This breaks TLS MakeCurrent caching.
162 egl
.fMakeCurrent(EGL_NO_SURFACE
, EGL_NO_SURFACE
, EGL_NO_CONTEXT
);
163 egl
.fDestroySurface(oldSurface
);
164 #if defined(MOZ_WAYLAND)
165 DeleteWaylandGLSurface(oldSurface
);
170 static EGLSurface
CreateFallbackSurface(EglDisplay
& egl
,
171 const EGLConfig
& config
) {
172 if (egl
.IsExtensionSupported(EGLExtension::KHR_surfaceless_context
)) {
173 // We don't need a PBuffer surface in this case
174 return EGL_NO_SURFACE
;
177 std::vector
<EGLint
> pbattrs
;
178 pbattrs
.push_back(LOCAL_EGL_WIDTH
);
179 pbattrs
.push_back(1);
180 pbattrs
.push_back(LOCAL_EGL_HEIGHT
);
181 pbattrs
.push_back(1);
183 for (const auto& cur
: kTerminationAttribs
) {
184 pbattrs
.push_back(cur
);
187 EGLSurface surface
= egl
.fCreatePbufferSurface(config
, pbattrs
.data());
189 MOZ_CRASH("Failed to create fallback EGLSurface");
195 static EGLSurface
CreateSurfaceFromNativeWindow(
196 EglDisplay
& egl
, const EGLNativeWindowType window
, const EGLConfig config
) {
198 EGLSurface newSurface
= EGL_NO_SURFACE
;
200 #ifdef MOZ_WIDGET_ANDROID
201 JNIEnv
* const env
= jni::GetEnvForThread();
202 ANativeWindow
* const nativeWindow
=
203 ANativeWindow_fromSurface(env
, reinterpret_cast<jobject
>(window
));
205 return EGL_NO_SURFACE
;
207 const auto& display
= egl
.mLib
->fGetDisplay(EGL_DEFAULT_DISPLAY
);
208 newSurface
= egl
.mLib
->fCreateWindowSurface(display
, config
, nativeWindow
, 0);
209 ANativeWindow_release(nativeWindow
);
211 newSurface
= egl
.fCreateWindowSurface(config
, window
, 0);
213 const auto err
= egl
.mLib
->fGetError();
214 gfxCriticalNote
<< "Failed to create EGLSurface!: " << gfx::hexa(err
);
220 /* GLContextEGLFactory class was added as a friend of GLContextEGL
221 * so that it could access GLContextEGL::CreateGLContext. This was
222 * done so that a new function would not need to be added to the shared
223 * GLContextProvider interface.
225 class GLContextEGLFactory
{
227 static already_AddRefed
<GLContext
> Create(EGLNativeWindowType aWindow
,
228 bool aHardwareWebRender
);
229 static already_AddRefed
<GLContext
> CreateImpl(EGLNativeWindowType aWindow
,
230 bool aHardwareWebRender
,
234 GLContextEGLFactory() = default;
235 ~GLContextEGLFactory() = default;
238 already_AddRefed
<GLContext
> GLContextEGLFactory::CreateImpl(
239 EGLNativeWindowType aWindow
, bool aHardwareWebRender
, bool aUseGles
) {
241 const auto lib
= gl::DefaultEglLibrary(&failureId
);
243 gfxCriticalNote
<< "Failed[3] to load EGL library: " << failureId
.get();
246 const auto egl
= lib
->CreateDisplay(true, &failureId
);
248 gfxCriticalNote
<< "Failed[3] to create EGL library display: "
255 GdkDisplay
* gdkDisplay
= gdk_display_get_default();
256 if (GdkIsX11Display(gdkDisplay
) && aWindow
) {
257 auto* display
= GDK_DISPLAY_XDISPLAY(gdkDisplay
);
259 XWindowAttributes windowAttrs
;
260 if (!XGetWindowAttributes(display
, (Window
)aWindow
, &windowAttrs
)) {
261 NS_WARNING("[EGL] XGetWindowAttributes() failed");
264 visualID
= XVisualIDFromVisual(windowAttrs
.visual
);
269 bool doubleBuffered
= true;
272 if (aHardwareWebRender
&& egl
->mLib
->IsANGLE()) {
273 // Force enable alpha channel to make sure ANGLE use correct framebuffer
276 if (!CreateConfig(*egl
, &config
, bpp
, false, aUseGles
)) {
277 gfxCriticalNote
<< "Failed to create EGLConfig for WebRender ANGLE!";
280 } else if (aHardwareWebRender
&& (kIsWayland
|| kIsX11
)) {
282 const bool enableDepthBuffer
= gfx::gfxVars::UseWebRenderCompositor();
283 if (!CreateConfig(*egl
, &config
, bpp
, enableDepthBuffer
, aUseGles
,
285 gfxCriticalNote
<< "Failed to create EGLConfig for WebRender!";
289 if (!CreateConfigScreen(*egl
, &config
,
290 /* aEnableDepthBuffer */ false, aUseGles
,
292 gfxCriticalNote
<< "Failed to create EGLConfig!";
297 EGLSurface surface
= EGL_NO_SURFACE
;
299 surface
= mozilla::gl::CreateSurfaceFromNativeWindow(*egl
, aWindow
, config
);
305 CreateContextFlags flags
= CreateContextFlags::NONE
;
306 if (aHardwareWebRender
&&
307 StaticPrefs::gfx_webrender_prefer_robustness_AtStartup()) {
308 flags
|= CreateContextFlags::PREFER_ROBUSTNESS
;
310 if (aHardwareWebRender
&& aUseGles
) {
311 flags
|= CreateContextFlags::PREFER_ES3
;
313 if (!aHardwareWebRender
) {
314 flags
|= CreateContextFlags::REQUIRE_COMPAT_PROFILE
;
317 const auto desc
= GLContextDesc
{{flags
}, false};
318 RefPtr
<GLContextEGL
> gl
= GLContextEGL::CreateGLContext(
319 egl
, desc
, config
, surface
, aUseGles
, &failureId
);
321 const auto err
= egl
->mLib
->fGetError();
322 gfxCriticalNote
<< "Failed to create EGLContext!: " << gfx::hexa(err
);
323 mozilla::gl::DestroySurface(*egl
, surface
);
328 gl
->SetIsDoubleBuffered(doubleBuffered
);
330 #ifdef MOZ_GTK_WAYLAND
331 if (surface
&& GdkIsWaylandDisplay()) {
332 // Make eglSwapBuffers() non-blocking on wayland
333 egl
->fSwapInterval(0);
336 if (aHardwareWebRender
&& egl
->mLib
->IsANGLE()) {
337 MOZ_ASSERT(doubleBuffered
);
338 egl
->fSwapInterval(0);
343 already_AddRefed
<GLContext
> GLContextEGLFactory::Create(
344 EGLNativeWindowType aWindow
, bool aHardwareWebRender
) {
345 RefPtr
<GLContext
> glContext
;
346 #if !defined(MOZ_WIDGET_ANDROID)
347 glContext
= CreateImpl(aWindow
, aHardwareWebRender
, /* aUseGles */ false);
348 #endif // !defined(MOZ_WIDGET_ANDROID)
351 glContext
= CreateImpl(aWindow
, aHardwareWebRender
, /* aUseGles */ true);
353 return glContext
.forget();
357 EGLSurface
GLContextEGL::CreateEGLSurfaceForCompositorWidget(
358 widget::CompositorWidget
* aCompositorWidget
, const EGLConfig aConfig
) {
359 nsCString discardFailureId
;
360 const auto egl
= DefaultEglDisplay(&discardFailureId
);
362 gfxCriticalNote
<< "Failed to load EGL library 6!";
363 return EGL_NO_SURFACE
;
366 MOZ_ASSERT(aCompositorWidget
);
367 EGLNativeWindowType window
=
368 GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget
);
370 gfxCriticalNote
<< "window is null";
371 return EGL_NO_SURFACE
;
374 return mozilla::gl::CreateSurfaceFromNativeWindow(*egl
, window
, aConfig
);
377 GLContextEGL::GLContextEGL(const std::shared_ptr
<EglDisplay
> egl
,
378 const GLContextDesc
& desc
, EGLConfig config
,
379 EGLSurface surface
, EGLContext context
)
380 : GLContext(desc
, nullptr, false),
385 mFallbackSurface(CreateFallbackSurface(*mEgl
, mConfig
)) {
387 printf_stderr("Initializing context %p surface %p on display %p\n", mContext
,
388 mSurface
, mEgl
->mDisplay
);
392 void GLContextEGL::OnMarkDestroyed() {
393 if (mSurfaceOverride
!= EGL_NO_SURFACE
) {
394 SetEGLSurfaceOverride(EGL_NO_SURFACE
);
398 GLContextEGL::~GLContextEGL() {
401 // Wrapped context should not destroy eglContext/Surface
407 printf_stderr("Destroying context %p surface %p on display %p\n", mContext
,
408 mSurface
, mEgl
->mDisplay
);
411 mEgl
->fDestroyContext(mContext
);
413 mozilla::gl::DestroySurface(*mEgl
, mSurface
);
414 mozilla::gl::DestroySurface(*mEgl
, mFallbackSurface
);
417 bool GLContextEGL::Init() {
418 if (!GLContext::Init()) return false;
420 bool current
= MakeCurrent();
422 gfx::LogFailure("Couldn't get device attachments for device."_ns
);
427 mEgl
->HasKHRImageBase() &&
428 mEgl
->IsExtensionSupported(EGLExtension::KHR_gl_texture_2D_image
) &&
429 IsExtensionSupported(OES_EGL_image
);
434 bool GLContextEGL::BindTexImage() {
435 if (!mSurface
) return false;
437 if (mBound
&& !ReleaseTexImage()) return false;
440 mEgl
->fBindTexImage((EGLSurface
)mSurface
, LOCAL_EGL_BACK_BUFFER
);
441 if (success
== LOCAL_EGL_FALSE
) return false;
447 bool GLContextEGL::ReleaseTexImage() {
448 if (!mBound
) return true;
450 if (!mSurface
) return false;
453 success
= mEgl
->fReleaseTexImage((EGLSurface
)mSurface
, LOCAL_EGL_BACK_BUFFER
);
454 if (success
== LOCAL_EGL_FALSE
) return false;
460 void GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf
) {
461 mSurfaceOverride
= surf
;
462 DebugOnly
<bool> ok
= MakeCurrent(true);
466 bool GLContextEGL::MakeCurrentImpl() const {
468 (mSurfaceOverride
!= EGL_NO_SURFACE
) ? mSurfaceOverride
: mSurface
;
470 surface
= mFallbackSurface
;
473 const bool succeeded
= mEgl
->fMakeCurrent(surface
, surface
, mContext
);
475 const auto eglError
= mEgl
->mLib
->fGetError();
476 if (eglError
== LOCAL_EGL_CONTEXT_LOST
) {
477 OnContextLostError();
479 NS_WARNING("Failed to make GL context current!");
481 printf_stderr("EGL Error: 0x%04x\n", eglError
);
489 bool GLContextEGL::IsCurrentImpl() const {
490 return mEgl
->mLib
->fGetCurrentContext() == mContext
;
493 bool GLContextEGL::RenewSurface(CompositorWidget
* aWidget
) {
497 // unconditionally release the surface and create a new one. Don't try to
498 // optimize this away. If we get here, then by definition we know that we want
499 // to get a new surface.
503 EGLNativeWindowType nativeWindow
=
504 GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget
);
506 mSurface
= mozilla::gl::CreateSurfaceFromNativeWindow(*mEgl
, nativeWindow
,
509 NS_WARNING("Failed to create EGLSurface from native window");
513 const bool ok
= MakeCurrent(true);
515 #ifdef MOZ_GTK_WAYLAND
516 if (mSurface
&& GdkIsWaylandDisplay()) {
517 // Make eglSwapBuffers() non-blocking on wayland
518 mEgl
->fSwapInterval(0);
524 void GLContextEGL::ReleaseSurface() {
526 mozilla::gl::DestroySurface(*mEgl
, mSurface
);
528 if (mSurface
== mSurfaceOverride
) {
529 mSurfaceOverride
= EGL_NO_SURFACE
;
531 mSurface
= EGL_NO_SURFACE
;
534 Maybe
<SymbolLoader
> GLContextEGL::GetSymbolLoader() const {
535 return mEgl
->mLib
->GetSymbolLoader();
538 bool GLContextEGL::SwapBuffers() {
540 mSurfaceOverride
!= EGL_NO_SURFACE
? mSurfaceOverride
: mSurface
;
542 if ((mEgl
->IsExtensionSupported(
543 EGLExtension::EXT_swap_buffers_with_damage
) ||
544 mEgl
->IsExtensionSupported(
545 EGLExtension::KHR_swap_buffers_with_damage
))) {
546 std::vector
<EGLint
> rects
;
547 for (auto iter
= mDamageRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
548 const IntRect
& r
= iter
.Get();
549 rects
.push_back(r
.X());
550 rects
.push_back(r
.Y());
551 rects
.push_back(r
.Width());
552 rects
.push_back(r
.Height());
554 mDamageRegion
.SetEmpty();
555 return mEgl
->fSwapBuffersWithDamage(surface
, rects
.data(),
558 return mEgl
->fSwapBuffers(surface
);
564 void GLContextEGL::SetDamage(const nsIntRegion
& aDamageRegion
) {
565 mDamageRegion
= aDamageRegion
;
568 void GLContextEGL::GetWSIInfo(nsCString
* const out
) const {
569 out
->AppendLiteral("EGL_VENDOR: ");
571 (const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
, LOCAL_EGL_VENDOR
));
573 out
->AppendLiteral("\nEGL_VERSION: ");
575 (const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
, LOCAL_EGL_VERSION
));
577 out
->AppendLiteral("\nEGL_EXTENSIONS: ");
578 out
->Append((const char*)mEgl
->mLib
->fQueryString(mEgl
->mDisplay
,
579 LOCAL_EGL_EXTENSIONS
));
581 #ifndef ANDROID // This query will crash some old android.
582 out
->AppendLiteral("\nEGL_EXTENSIONS(nullptr): ");
584 (const char*)mEgl
->mLib
->fQueryString(nullptr, LOCAL_EGL_EXTENSIONS
));
588 // hold a reference to the given surface
589 // for the lifetime of this context.
590 void GLContextEGL::HoldSurface(gfxASurface
* aSurf
) { mThebesSurface
= aSurf
; }
592 bool GLContextEGL::HasExtBufferAge() const {
593 return mEgl
->IsExtensionSupported(EGLExtension::EXT_buffer_age
);
596 bool GLContextEGL::HasKhrPartialUpdate() const {
597 return mEgl
->IsExtensionSupported(EGLExtension::KHR_partial_update
);
600 GLint
GLContextEGL::GetBufferAge() const {
602 mSurfaceOverride
!= EGL_NO_SURFACE
? mSurfaceOverride
: mSurface
;
604 if (surface
&& (HasExtBufferAge() || HasKhrPartialUpdate())) {
606 mEgl
->fQuerySurface(surface
, LOCAL_EGL_BUFFER_AGE_EXT
, &result
);
613 #define LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ 0x6000
615 RefPtr
<GLContextEGL
> GLContextEGL::CreateGLContext(
616 const std::shared_ptr
<EglDisplay
> egl
, const GLContextDesc
& desc
,
617 EGLConfig config
, EGLSurface surface
, const bool useGles
,
618 nsACString
* const out_failureId
) {
619 const auto& flags
= desc
.flags
;
621 std::vector
<EGLint
> required_attribs
;
624 // TODO: This fBindAPI could be more thread-safe
625 if (egl
->mLib
->fBindAPI(LOCAL_EGL_OPENGL_ES_API
) == LOCAL_EGL_FALSE
) {
626 *out_failureId
= "FEATURE_FAILURE_EGL_ES"_ns
;
627 NS_WARNING("Failed to bind API to GLES!");
630 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
631 if (flags
& CreateContextFlags::PREFER_ES3
) {
632 required_attribs
.push_back(3);
634 required_attribs
.push_back(2);
637 if (egl
->mLib
->fBindAPI(LOCAL_EGL_OPENGL_API
) == LOCAL_EGL_FALSE
) {
638 *out_failureId
= "FEATURE_FAILURE_EGL"_ns
;
639 NS_WARNING("Failed to bind API to GL!");
642 if (flags
& CreateContextFlags::REQUIRE_COMPAT_PROFILE
) {
643 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK
);
644 required_attribs
.push_back(
645 LOCAL_EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT
);
646 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
647 required_attribs
.push_back(2);
649 // !REQUIRE_COMPAT_PROFILE means core profle.
650 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK
);
651 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT
);
652 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION
);
653 required_attribs
.push_back(3);
654 required_attribs
.push_back(LOCAL_EGL_CONTEXT_MINOR_VERSION
);
655 required_attribs
.push_back(2);
659 if ((flags
& CreateContextFlags::PREFER_EXACT_VERSION
) &&
660 egl
->mLib
->IsANGLE()) {
661 required_attribs
.push_back(
662 LOCAL_EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE
);
663 required_attribs
.push_back(LOCAL_EGL_FALSE
);
666 const auto debugFlags
= GLContext::ChooseDebugFlags(flags
);
667 if (!debugFlags
&& flags
& CreateContextFlags::NO_VALIDATION
&&
668 egl
->IsExtensionSupported(EGLExtension::KHR_create_context_no_error
)) {
669 required_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_NO_ERROR_KHR
);
670 required_attribs
.push_back(LOCAL_EGL_TRUE
);
673 if (flags
& CreateContextFlags::PROVOKING_VERTEX_DONT_CARE
&&
674 egl
->IsExtensionSupported(
675 EGLExtension::MOZ_create_context_provoking_vertex_dont_care
)) {
676 required_attribs
.push_back(
677 LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ
);
678 required_attribs
.push_back(LOCAL_EGL_TRUE
);
681 std::vector
<EGLint
> ext_robustness_attribs
;
682 std::vector
<EGLint
> ext_rbab_attribs
; // RBAB: Robust Buffer Access Behavior
683 std::vector
<EGLint
> khr_robustness_attribs
;
684 std::vector
<EGLint
> khr_rbab_attribs
; // RBAB: Robust Buffer Access Behavior
685 if (flags
& CreateContextFlags::PREFER_ROBUSTNESS
) {
686 std::vector
<EGLint
> base_robustness_attribs
= required_attribs
;
687 if (egl
->IsExtensionSupported(
688 EGLExtension::NV_robustness_video_memory_purge
)) {
689 base_robustness_attribs
.push_back(
690 LOCAL_EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
);
691 base_robustness_attribs
.push_back(LOCAL_EGL_TRUE
);
694 if (egl
->IsExtensionSupported(
695 EGLExtension::EXT_create_context_robustness
)) {
696 ext_robustness_attribs
= base_robustness_attribs
;
697 ext_robustness_attribs
.push_back(
698 LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT
);
699 ext_robustness_attribs
.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT
);
701 if (gfxVars::AllowEglRbab()) {
702 ext_rbab_attribs
= ext_robustness_attribs
;
703 ext_rbab_attribs
.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT
);
704 ext_rbab_attribs
.push_back(LOCAL_EGL_TRUE
);
708 if (egl
->IsExtensionSupported(EGLExtension::KHR_create_context
)) {
709 khr_robustness_attribs
= base_robustness_attribs
;
710 khr_robustness_attribs
.push_back(
711 LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR
);
712 khr_robustness_attribs
.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_KHR
);
714 khr_rbab_attribs
= khr_robustness_attribs
;
715 khr_rbab_attribs
.push_back(LOCAL_EGL_CONTEXT_FLAGS_KHR
);
716 khr_rbab_attribs
.push_back(
717 LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR
);
721 const auto fnCreate
= [&](const std::vector
<EGLint
>& attribs
) {
722 auto terminated_attribs
= attribs
;
724 for (const auto& cur
: kTerminationAttribs
) {
725 terminated_attribs
.push_back(cur
);
728 return egl
->fCreateContext(config
, EGL_NO_CONTEXT
,
729 terminated_attribs
.data());
734 if (!khr_rbab_attribs
.empty()) {
735 context
= fnCreate(khr_rbab_attribs
);
737 NS_WARNING("Failed to create EGLContext with khr_rbab_attribs");
740 if (!ext_rbab_attribs
.empty()) {
741 context
= fnCreate(ext_rbab_attribs
);
743 NS_WARNING("Failed to create EGLContext with ext_rbab_attribs");
746 if (!khr_robustness_attribs
.empty()) {
747 context
= fnCreate(khr_robustness_attribs
);
749 NS_WARNING("Failed to create EGLContext with khr_robustness_attribs");
752 if (!ext_robustness_attribs
.empty()) {
753 context
= fnCreate(ext_robustness_attribs
);
755 NS_WARNING("Failed to create EGLContext with ext_robustness_attribs");
758 context
= fnCreate(required_attribs
);
760 NS_WARNING("Failed to create EGLContext with required_attribs");
762 *out_failureId
= "FEATURE_FAILURE_EGL_CREATE"_ns
;
767 RefPtr
<GLContextEGL
> glContext
=
768 new GLContextEGL(egl
, desc
, config
, surface
, context
);
769 if (!glContext
->Init()) {
770 *out_failureId
= "FEATURE_FAILURE_EGL_INIT"_ns
;
774 if (GLContext::ShouldSpew()) {
775 printf_stderr("new GLContextEGL %p on EGLDisplay %p\n", glContext
.get(),
783 EGLSurface
GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
784 EglDisplay
& egl
, EGLConfig config
, EGLenum bindToTextureFormat
,
785 mozilla::gfx::IntSize
& pbsize
) {
786 nsTArray
<EGLint
> pbattrs(16);
787 EGLSurface surface
= nullptr;
789 TRY_AGAIN_POWER_OF_TWO
:
791 pbattrs
.AppendElement(LOCAL_EGL_WIDTH
);
792 pbattrs
.AppendElement(pbsize
.width
);
793 pbattrs
.AppendElement(LOCAL_EGL_HEIGHT
);
794 pbattrs
.AppendElement(pbsize
.height
);
796 if (bindToTextureFormat
!= LOCAL_EGL_NONE
) {
797 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_TARGET
);
798 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_2D
);
800 pbattrs
.AppendElement(LOCAL_EGL_TEXTURE_FORMAT
);
801 pbattrs
.AppendElement(bindToTextureFormat
);
804 for (const auto& cur
: kTerminationAttribs
) {
805 pbattrs
.AppendElement(cur
);
808 surface
= egl
.fCreatePbufferSurface(config
, &pbattrs
[0]);
810 if (!is_power_of_two(pbsize
.width
) || !is_power_of_two(pbsize
.height
)) {
811 if (!is_power_of_two(pbsize
.width
))
812 pbsize
.width
= next_power_of_two(pbsize
.width
);
813 if (!is_power_of_two(pbsize
.height
))
814 pbsize
.height
= next_power_of_two(pbsize
.height
);
816 NS_WARNING("Failed to create pbuffer, trying power of two dims");
817 goto TRY_AGAIN_POWER_OF_TWO
;
820 NS_WARNING("Failed to create pbuffer surface");
827 #if defined(MOZ_WAYLAND)
828 WaylandGLSurface::WaylandGLSurface(struct wl_surface
* aWaylandSurface
,
829 struct wl_egl_window
* aEGLWindow
)
830 : mWaylandSurface(aWaylandSurface
), mEGLWindow(aEGLWindow
) {}
832 WaylandGLSurface::~WaylandGLSurface() {
833 wl_egl_window_destroy(mEGLWindow
);
834 wl_surface_destroy(mWaylandSurface
);
839 EGLSurface
GLContextEGL::CreateWaylandBufferSurface(
840 EglDisplay
& egl
, EGLConfig config
, mozilla::gfx::IntSize
& pbsize
) {
841 wl_egl_window
* eglwindow
= nullptr;
843 #ifdef MOZ_GTK_WAYLAND
844 struct wl_compositor
* compositor
=
845 gdk_wayland_display_get_wl_compositor(gdk_display_get_default());
846 struct wl_surface
* wlsurface
= wl_compositor_create_surface(compositor
);
847 eglwindow
= wl_egl_window_create(wlsurface
, pbsize
.width
, pbsize
.height
);
849 if (!eglwindow
) return nullptr;
851 const auto surface
= egl
.fCreateWindowSurface(
852 config
, reinterpret_cast<EGLNativeWindowType
>(eglwindow
), 0);
854 #ifdef MOZ_GTK_WAYLAND
855 MOZ_ASSERT(!sWaylandGLSurface
.Contains(surface
));
856 sWaylandGLSurface
.LookupOrInsert(
857 surface
, new WaylandGLSurface(wlsurface
, eglwindow
));
864 static const EGLint kEGLConfigAttribsRGB16
[] = {
865 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
866 LOCAL_EGL_RED_SIZE
, 5,
867 LOCAL_EGL_GREEN_SIZE
, 6,
868 LOCAL_EGL_BLUE_SIZE
, 5,
869 LOCAL_EGL_ALPHA_SIZE
, 0};
871 static const EGLint kEGLConfigAttribsRGB24
[] = {
872 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
873 LOCAL_EGL_RED_SIZE
, 8,
874 LOCAL_EGL_GREEN_SIZE
, 8,
875 LOCAL_EGL_BLUE_SIZE
, 8,
876 LOCAL_EGL_ALPHA_SIZE
, 0};
878 static const EGLint kEGLConfigAttribsRGBA32
[] = {
879 LOCAL_EGL_SURFACE_TYPE
, LOCAL_EGL_WINDOW_BIT
,
880 LOCAL_EGL_RED_SIZE
, 8,
881 LOCAL_EGL_GREEN_SIZE
, 8,
882 LOCAL_EGL_BLUE_SIZE
, 8,
883 LOCAL_EGL_ALPHA_SIZE
, 8};
885 bool CreateConfig(EglDisplay
& egl
, EGLConfig
* aConfig
, int32_t depth
,
886 bool aEnableDepthBuffer
, bool aUseGles
, int aVisual
) {
887 EGLConfig configs
[64];
888 std::vector
<EGLint
> attribs
;
889 EGLint ncfg
= ArrayLength(configs
);
893 for (const auto& cur
: kEGLConfigAttribsRGB16
) {
894 attribs
.push_back(cur
);
898 for (const auto& cur
: kEGLConfigAttribsRGB24
) {
899 attribs
.push_back(cur
);
903 for (const auto& cur
: kEGLConfigAttribsRGBA32
) {
904 attribs
.push_back(cur
);
908 NS_ERROR("Unknown pixel depth");
913 attribs
.push_back(LOCAL_EGL_RENDERABLE_TYPE
);
914 attribs
.push_back(LOCAL_EGL_OPENGL_ES2_BIT
);
916 for (const auto& cur
: kTerminationAttribs
) {
917 attribs
.push_back(cur
);
920 if (!egl
.fChooseConfig(attribs
.data(), configs
, ncfg
, &ncfg
) || ncfg
< 1) {
924 Maybe
<EGLConfig
> fallbackConfig
;
926 for (int j
= 0; j
< ncfg
; ++j
) {
927 EGLConfig config
= configs
[j
];
929 if (egl
.fGetConfigAttrib(config
, LOCAL_EGL_RED_SIZE
, &r
) &&
930 egl
.fGetConfigAttrib(config
, LOCAL_EGL_GREEN_SIZE
, &g
) &&
931 egl
.fGetConfigAttrib(config
, LOCAL_EGL_BLUE_SIZE
, &b
) &&
932 egl
.fGetConfigAttrib(config
, LOCAL_EGL_ALPHA_SIZE
, &a
) &&
933 ((depth
== 16 && r
== 5 && g
== 6 && b
== 5) ||
934 (depth
== 24 && r
== 8 && g
== 8 && b
== 8) ||
935 (depth
== 32 && r
== 8 && g
== 8 && b
== 8 && a
== 8))) {
937 if (aEnableDepthBuffer
) {
938 if (!egl
.fGetConfigAttrib(config
, LOCAL_EGL_DEPTH_SIZE
, &z
) ||
943 if (kIsX11
&& aVisual
) {
945 if (!egl
.fGetConfigAttrib(config
, LOCAL_EGL_NATIVE_VISUAL_ID
, &vis
) ||
947 if (!fallbackConfig
) {
948 fallbackConfig
= Some(config
);
958 // We don't have a frame buffer X11 visual which matches the EGL visual
959 // from GLContextEGL::FindVisual(). Let's try to use the fallback one and hope
960 // we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there.
961 if (kIsX11
&& 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 // aVisual is used in Linux only.
975 static bool CreateConfigScreen(EglDisplay
& egl
, EGLConfig
* const aConfig
,
976 const bool aEnableDepthBuffer
,
977 const bool aUseGles
, int aVisual
) {
978 int32_t depth
= gfxVars::ScreenDepth();
979 if (CreateConfig(egl
, aConfig
, depth
, aEnableDepthBuffer
, aUseGles
,
983 #ifdef MOZ_WIDGET_ANDROID
985 // Android doesn't always support 16 bit so also try 24 bit
987 return CreateConfig(egl
, aConfig
, 24, aEnableDepthBuffer
, aUseGles
);
990 // Some devices that have 24 bit screens only support 16 bit OpenGL?
992 return CreateConfig(egl
, aConfig
, 16, aEnableDepthBuffer
, aUseGles
);
998 already_AddRefed
<GLContext
> GLContextProviderEGL::CreateForCompositorWidget(
999 CompositorWidget
* aCompositorWidget
, bool aHardwareWebRender
,
1000 bool /*aForceAccelerated*/) {
1001 EGLNativeWindowType window
= nullptr;
1002 if (aCompositorWidget
) {
1003 window
= GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget
);
1005 return GLContextEGLFactory::Create(window
, aHardwareWebRender
);
1008 EGLSurface
GLContextEGL::CreateCompatibleSurface(void* aWindow
) const {
1009 MOZ_ASSERT(aWindow
);
1010 if (mConfig
== EGL_NO_CONFIG
) {
1011 MOZ_CRASH("GFX: Failed with invalid EGLConfig 2!");
1014 const auto fnCreate
= [&](const bool useGles
) -> EGLSurface
{
1015 // NOTE: aWindow is an ANativeWindow
1016 auto config
= mConfig
;
1017 if (!config
&& !CreateConfigScreen(*mEgl
, &config
,
1018 /* aEnableDepthBuffer */ false,
1019 /* useGles */ useGles
)) {
1023 return mEgl
->fCreateWindowSurface(
1024 config
, reinterpret_cast<EGLNativeWindowType
>(aWindow
), 0);
1027 auto surface
= fnCreate(false);
1029 surface
= fnCreate(true);
1032 MOZ_CRASH("GFX: Failed to create EGLSurface 2!");
1037 static void FillContextAttribs(bool es3
, bool useGles
, nsTArray
<EGLint
>* out
) {
1038 out
->AppendElement(LOCAL_EGL_SURFACE_TYPE
);
1039 #ifdef MOZ_GTK_WAYLAND
1040 if (GdkIsWaylandDisplay()) {
1041 // Wayland on desktop does not support PBuffer or FBO.
1042 // We create a dummy wl_egl_window instead.
1043 out
->AppendElement(LOCAL_EGL_WINDOW_BIT
);
1047 out
->AppendElement(LOCAL_EGL_PBUFFER_BIT
);
1051 out
->AppendElement(LOCAL_EGL_RENDERABLE_TYPE
);
1053 out
->AppendElement(LOCAL_EGL_OPENGL_ES3_BIT_KHR
);
1055 out
->AppendElement(LOCAL_EGL_OPENGL_ES2_BIT
);
1059 out
->AppendElement(LOCAL_EGL_RED_SIZE
);
1060 out
->AppendElement(8);
1062 out
->AppendElement(LOCAL_EGL_GREEN_SIZE
);
1063 out
->AppendElement(8);
1065 out
->AppendElement(LOCAL_EGL_BLUE_SIZE
);
1066 out
->AppendElement(8);
1068 out
->AppendElement(LOCAL_EGL_ALPHA_SIZE
);
1069 out
->AppendElement(8);
1071 out
->AppendElement(LOCAL_EGL_DEPTH_SIZE
);
1072 out
->AppendElement(0);
1074 out
->AppendElement(LOCAL_EGL_STENCIL_SIZE
);
1075 out
->AppendElement(0);
1077 // EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
1078 out
->AppendElement(LOCAL_EGL_NONE
);
1079 out
->AppendElement(0);
1081 out
->AppendElement(0);
1082 out
->AppendElement(0);
1086 /// Useful for debugging, but normally unused.
1087 static GLint GetAttrib(GLLibraryEGL* egl, EGLConfig config, EGLint attrib) {
1089 egl->fGetConfigAttrib(config, attrib, &bits);
1090 MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
1096 static EGLConfig
ChooseConfig(EglDisplay
& egl
, const GLContextCreateDesc
& desc
,
1097 const bool useGles
) {
1098 nsTArray
<EGLint
> configAttribList
;
1099 FillContextAttribs(bool(desc
.flags
& CreateContextFlags::PREFER_ES3
), useGles
,
1102 const EGLint
* configAttribs
= configAttribList
.Elements();
1104 // The sorting dictated by the spec for eglChooseConfig reasonably assures
1105 // that a reasonable 'best' config is on top.
1106 const EGLint kMaxConfigs
= 1;
1107 EGLConfig configs
[kMaxConfigs
];
1108 EGLint foundConfigs
= 0;
1109 if (!egl
.fChooseConfig(configAttribs
, configs
, kMaxConfigs
, &foundConfigs
) ||
1110 foundConfigs
== 0) {
1111 return EGL_NO_CONFIG
;
1114 EGLConfig config
= configs
[0];
1120 bool GLContextEGL::FindVisual(bool aUseWebRender
, bool useAlpha
,
1121 int* const out_visualId
) {
1122 nsCString discardFailureId
;
1123 const auto egl
= DefaultEglDisplay(&discardFailureId
);
1126 << "GLContextEGL::FindVisual(): Failed to load EGL library!";
1131 const int bpp
= useAlpha
? 32 : 24;
1132 if (!CreateConfig(*egl
, &config
, bpp
, aUseWebRender
, /* aUseGles */ false)) {
1134 << "GLContextEGL::FindVisual(): Failed to create EGLConfig!";
1137 if (egl
->fGetConfigAttrib(config
, LOCAL_EGL_NATIVE_VISUAL_ID
, out_visualId
)) {
1145 RefPtr
<GLContextEGL
> GLContextEGL::CreateEGLPBufferOffscreenContextImpl(
1146 const std::shared_ptr
<EglDisplay
> egl
, const GLContextCreateDesc
& desc
,
1147 const mozilla::gfx::IntSize
& size
, const bool useGles
,
1148 nsACString
* const out_failureId
) {
1149 const EGLConfig config
= ChooseConfig(*egl
, desc
, useGles
);
1150 if (config
== EGL_NO_CONFIG
) {
1151 *out_failureId
= "FEATURE_FAILURE_EGL_NO_CONFIG"_ns
;
1152 NS_WARNING("Failed to find a compatible config.");
1156 if (GLContext::ShouldSpew()) {
1157 egl
->DumpEGLConfig(config
);
1160 mozilla::gfx::IntSize
pbSize(size
);
1161 EGLSurface surface
= nullptr;
1162 #ifdef MOZ_GTK_WAYLAND
1163 if (GdkIsWaylandDisplay()) {
1164 surface
= GLContextEGL::CreateWaylandBufferSurface(*egl
, config
, pbSize
);
1168 surface
= GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
1169 *egl
, config
, LOCAL_EGL_NONE
, pbSize
);
1172 *out_failureId
= "FEATURE_FAILURE_EGL_POT"_ns
;
1173 NS_WARNING("Failed to create PBuffer for context!");
1177 auto fullDesc
= GLContextDesc
{desc
};
1178 fullDesc
.isOffscreen
= true;
1179 RefPtr
<GLContextEGL
> gl
= GLContextEGL::CreateGLContext(
1180 egl
, fullDesc
, config
, surface
, useGles
, out_failureId
);
1182 NS_WARNING("Failed to create GLContext from PBuffer");
1183 egl
->fDestroySurface(surface
);
1184 #if defined(MOZ_WAYLAND)
1185 DeleteWaylandGLSurface(surface
);
1194 RefPtr
<GLContextEGL
> GLContextEGL::CreateEGLPBufferOffscreenContext(
1195 const std::shared_ptr
<EglDisplay
> display
, const GLContextCreateDesc
& desc
,
1196 const mozilla::gfx::IntSize
& size
, nsACString
* const out_failureId
) {
1197 RefPtr
<GLContextEGL
> gl
= CreateEGLPBufferOffscreenContextImpl(
1198 display
, desc
, size
, /* useGles */ false, out_failureId
);
1200 gl
= CreateEGLPBufferOffscreenContextImpl(
1201 display
, desc
, size
, /* useGles */ true, out_failureId
);
1207 already_AddRefed
<GLContext
> GLContextProviderEGL::CreateHeadless(
1208 const GLContextCreateDesc
& desc
, nsACString
* const out_failureId
) {
1209 const auto display
= DefaultEglDisplay(out_failureId
);
1213 mozilla::gfx::IntSize dummySize
= mozilla::gfx::IntSize(16, 16);
1214 auto ret
= GLContextEGL::CreateEGLPBufferOffscreenContext(
1215 display
, desc
, dummySize
, out_failureId
);
1216 return ret
.forget();
1219 // Don't want a global context on Android as 1) share groups across 2 threads
1220 // fail on many Tegra drivers (bug 759225) and 2) some mobile devices have a
1221 // very strict limit on global number of GL contexts (bug 754257) and 3) each
1222 // EGL context eats 750k on B2G (bug 813783)
1224 GLContext
* GLContextProviderEGL::GetGlobalContext() { return nullptr; }
1228 static StaticMutex sMutex
;
1229 static StaticRefPtr
<GLLibraryEGL
> gDefaultEglLibrary
;
1231 RefPtr
<GLLibraryEGL
> DefaultEglLibrary(nsACString
* const out_failureId
) {
1232 StaticMutexAutoLock
lock(sMutex
);
1233 if (!gDefaultEglLibrary
) {
1234 gDefaultEglLibrary
= GLLibraryEGL::Create(out_failureId
);
1235 if (!gDefaultEglLibrary
) {
1236 NS_WARNING("GLLibraryEGL::Create failed");
1239 return gDefaultEglLibrary
.get();
1245 void GLContextProviderEGL::Shutdown() {
1246 StaticMutexAutoLock
lock(sMutex
);
1247 if (!gDefaultEglLibrary
) {
1250 gDefaultEglLibrary
= nullptr;
1253 } /* namespace gl */
1254 } /* namespace mozilla */
1256 #undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS