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