summaryrefslogtreecommitdiffstats
path: root/gfx/gl/GLContextProviderEGL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/gl/GLContextProviderEGL.cpp')
-rw-r--r--gfx/gl/GLContextProviderEGL.cpp1025
1 files changed, 1025 insertions, 0 deletions
diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp
new file mode 100644
index 000000000..ca972e0f3
--- /dev/null
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -0,0 +1,1025 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if defined(MOZ_WIDGET_GTK)
+ #include <gdk/gdkx.h>
+ // we're using default display for now
+ #define GET_NATIVE_WINDOW(aWidget) ((EGLNativeWindowType)GDK_WINDOW_XID((GdkWindow*)aWidget->GetNativeData(NS_NATIVE_WINDOW)))
+#else
+ #define GET_NATIVE_WINDOW(aWidget) ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW))
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+ #define GET_JAVA_SURFACE(aWidget) (aWidget->GetNativeData(NS_JAVA_SURFACE))
+#endif
+
+#if defined(XP_UNIX)
+ #ifdef MOZ_WIDGET_ANDROID
+ #include <android/native_window.h>
+ #include <android/native_window_jni.h>
+ #endif
+
+ #ifdef ANDROID
+ #include <android/log.h>
+ #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
+ #endif
+
+ #define GLES2_LIB "libGLESv2.so"
+ #define GLES2_LIB2 "libGLESv2.so.2"
+
+#elif defined(XP_WIN)
+ #include "nsIFile.h"
+
+ #define GLES2_LIB "libGLESv2.dll"
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN 1
+ #endif
+
+ #include <windows.h>
+
+ // a little helper
+ class AutoDestroyHWND {
+ public:
+ AutoDestroyHWND(HWND aWnd = nullptr)
+ : mWnd(aWnd)
+ {
+ }
+
+ ~AutoDestroyHWND() {
+ if (mWnd) {
+ ::DestroyWindow(mWnd);
+ }
+ }
+
+ operator HWND() {
+ return mWnd;
+ }
+
+ HWND forget() {
+ HWND w = mWnd;
+ mWnd = nullptr;
+ return w;
+ }
+
+ HWND operator=(HWND aWnd) {
+ if (mWnd && mWnd != aWnd) {
+ ::DestroyWindow(mWnd);
+ }
+ mWnd = aWnd;
+ return mWnd;
+ }
+
+ HWND mWnd;
+ };
+#else
+ #error "Platform not recognized"
+#endif
+
+#include "gfxASurface.h"
+#include "gfxCrashReporterUtils.h"
+#include "gfxFailure.h"
+#include "gfxPlatform.h"
+#include "gfxUtils.h"
+#include "GLBlitHelper.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "GLLibraryEGL.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "nsDebug.h"
+#include "nsIWidget.h"
+#include "nsThreadUtils.h"
+#include "ScopedGLHelpers.h"
+#include "TextureImageEGL.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::widget;
+
+#define ADD_ATTR_2(_array, _k, _v) do { \
+ (_array).AppendElement(_k); \
+ (_array).AppendElement(_v); \
+} while (0)
+
+#define ADD_ATTR_1(_array, _k) do { \
+ (_array).AppendElement(_k); \
+} while (0)
+
+static bool
+CreateConfig(EGLConfig* aConfig, nsIWidget* aWidget);
+
+// append three zeros at the end of attribs list to work around
+// EGL implementation bugs that iterate until they find 0, instead of
+// EGL_NONE. See bug 948406.
+#define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
+ LOCAL_EGL_NONE, 0, 0, 0
+
+static EGLint gTerminationAttribs[] = {
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static int
+next_power_of_two(int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+
+ return v;
+}
+
+static bool
+is_power_of_two(int v)
+{
+ NS_ASSERTION(v >= 0, "bad value");
+
+ if (v == 0)
+ return true;
+
+ return (v & (v-1)) == 0;
+}
+
+static void
+DestroySurface(EGLSurface oldSurface) {
+ if (oldSurface != EGL_NO_SURFACE) {
+ sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
+ EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ sEGLLibrary.fDestroySurface(EGL_DISPLAY(), oldSurface);
+ }
+}
+
+static EGLSurface
+CreateSurfaceForWindow(nsIWidget* widget, const EGLConfig& config) {
+ EGLSurface newSurface = nullptr;
+
+ MOZ_ASSERT(widget);
+#ifdef MOZ_WIDGET_ANDROID
+ void* javaSurface = GET_JAVA_SURFACE(widget);
+ if (!javaSurface) {
+ MOZ_CRASH("GFX: Failed to get Java surface.\n");
+ }
+ JNIEnv* const env = jni::GetEnvForThread();
+ ANativeWindow* const nativeWindow = ANativeWindow_fromSurface(
+ env, reinterpret_cast<jobject>(javaSurface));
+ newSurface = sEGLLibrary.fCreateWindowSurface(
+ sEGLLibrary.fGetDisplay(EGL_DEFAULT_DISPLAY),
+ config, nativeWindow, 0);
+ ANativeWindow_release(nativeWindow);
+#else
+ newSurface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config,
+ GET_NATIVE_WINDOW(widget), 0);
+#endif
+ return newSurface;
+}
+
+GLContextEGL::GLContextEGL(CreateContextFlags flags, const SurfaceCaps& caps,
+ GLContext* shareContext, bool isOffscreen, EGLConfig config,
+ EGLSurface surface, EGLContext context)
+ : GLContext(flags, caps, shareContext, isOffscreen)
+ , mConfig(config)
+ , mSurface(surface)
+ , mContext(context)
+ , mSurfaceOverride(EGL_NO_SURFACE)
+ , mThebesSurface(nullptr)
+ , mBound(false)
+ , mIsPBuffer(false)
+ , mIsDoubleBuffered(false)
+ , mCanBindToTexture(false)
+ , mShareWithEGLImage(false)
+ , mOwnsContext(true)
+{
+ // any EGL contexts will always be GLESv2
+ SetProfileVersion(ContextProfile::OpenGLES, 200);
+
+#ifdef DEBUG
+ printf_stderr("Initializing context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
+#endif
+}
+
+GLContextEGL::~GLContextEGL()
+{
+ MarkDestroyed();
+
+ // Wrapped context should not destroy eglContext/Surface
+ if (!mOwnsContext) {
+ return;
+ }
+
+#ifdef DEBUG
+ printf_stderr("Destroying context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
+#endif
+
+ sEGLLibrary.fDestroyContext(EGL_DISPLAY(), mContext);
+ sEGLLibrary.UnsetCachedCurrentContext();
+
+ mozilla::gl::DestroySurface(mSurface);
+}
+
+bool
+GLContextEGL::Init()
+{
+#if defined(ANDROID)
+ // We can't use LoadApitraceLibrary here because the GLContext
+ // expects its own handle to the GL library
+ if (!OpenLibrary(APITRACE_LIB))
+#endif
+ if (!OpenLibrary(GLES2_LIB)) {
+#if defined(XP_UNIX)
+ if (!OpenLibrary(GLES2_LIB2)) {
+ NS_WARNING("Couldn't load GLES2 LIB.");
+ return false;
+ }
+#endif
+ }
+
+ SetupLookupFunction();
+ if (!InitWithPrefix("gl", true))
+ return false;
+
+ bool current = MakeCurrent();
+ if (!current) {
+ gfx::LogFailure(NS_LITERAL_CSTRING(
+ "Couldn't get device attachments for device."));
+ return false;
+ }
+
+ static_assert(sizeof(GLint) >= sizeof(int32_t), "GLint is smaller than int32_t");
+ mMaxTextureImageSize = INT32_MAX;
+
+ mShareWithEGLImage = sEGLLibrary.HasKHRImageBase() &&
+ sEGLLibrary.HasKHRImageTexture2D() &&
+ IsExtensionSupported(OES_EGL_image);
+
+ return true;
+}
+
+bool
+GLContextEGL::BindTexImage()
+{
+ if (!mSurface)
+ return false;
+
+ if (mBound && !ReleaseTexImage())
+ return false;
+
+ EGLBoolean success = sEGLLibrary.fBindTexImage(EGL_DISPLAY(),
+ (EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER);
+ if (success == LOCAL_EGL_FALSE)
+ return false;
+
+ mBound = true;
+ return true;
+}
+
+bool
+GLContextEGL::ReleaseTexImage()
+{
+ if (!mBound)
+ return true;
+
+ if (!mSurface)
+ return false;
+
+ EGLBoolean success;
+ success = sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(),
+ (EGLSurface)mSurface,
+ LOCAL_EGL_BACK_BUFFER);
+ if (success == LOCAL_EGL_FALSE)
+ return false;
+
+ mBound = false;
+ return true;
+}
+
+void
+GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf) {
+ if (Screen()) {
+ /* Blit `draw` to `read` if we need to, before we potentially juggle
+ * `read` around. If we don't, we might attach a different `read`,
+ * and *then* hit AssureBlitted, which will blit a dirty `draw` onto
+ * the wrong `read`!
+ */
+ Screen()->AssureBlitted();
+ }
+
+ mSurfaceOverride = surf;
+ DebugOnly<bool> ok = MakeCurrent(true);
+ MOZ_ASSERT(ok);
+}
+
+bool
+GLContextEGL::MakeCurrentImpl(bool aForce) {
+ bool succeeded = true;
+
+ // Assume that EGL has the same problem as WGL does,
+ // where MakeCurrent with an already-current context is
+ // still expensive.
+ bool hasDifferentContext = false;
+ if (sEGLLibrary.CachedCurrentContext() != mContext) {
+ // even if the cached context doesn't match the current one
+ // might still
+ if (sEGLLibrary.fGetCurrentContext() != mContext) {
+ hasDifferentContext = true;
+ } else {
+ sEGLLibrary.SetCachedCurrentContext(mContext);
+ }
+ }
+
+ if (aForce || hasDifferentContext) {
+ EGLSurface surface = mSurfaceOverride != EGL_NO_SURFACE
+ ? mSurfaceOverride
+ : mSurface;
+ if (surface == EGL_NO_SURFACE) {
+ return false;
+ }
+ succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
+ surface, surface,
+ mContext);
+ if (!succeeded) {
+ int eglError = sEGLLibrary.fGetError();
+ if (eglError == LOCAL_EGL_CONTEXT_LOST) {
+ mContextLost = true;
+ NS_WARNING("EGL context has been lost.");
+ } else {
+ NS_WARNING("Failed to make GL context current!");
+#ifdef DEBUG
+ printf_stderr("EGL Error: 0x%04x\n", eglError);
+#endif
+ }
+ } else {
+ sEGLLibrary.SetCachedCurrentContext(mContext);
+ }
+ } else {
+ MOZ_ASSERT(sEGLLibrary.CachedCurrentContextMatches());
+ }
+
+ return succeeded;
+}
+
+bool
+GLContextEGL::IsCurrent() {
+ return sEGLLibrary.fGetCurrentContext() == mContext;
+}
+
+bool
+GLContextEGL::RenewSurface(nsIWidget* aWidget) {
+ if (!mOwnsContext) {
+ return false;
+ }
+ // unconditionally release the surface and create a new one. Don't try to optimize this away.
+ // If we get here, then by definition we know that we want to get a new surface.
+ ReleaseSurface();
+ mSurface = mozilla::gl::CreateSurfaceForWindow(aWidget, mConfig);
+ if (!mSurface) {
+ return false;
+ }
+ return MakeCurrent(true);
+}
+
+void
+GLContextEGL::ReleaseSurface() {
+ if (mOwnsContext) {
+ mozilla::gl::DestroySurface(mSurface);
+ }
+ if (mSurface == mSurfaceOverride) {
+ mSurfaceOverride = EGL_NO_SURFACE;
+ }
+ mSurface = EGL_NO_SURFACE;
+}
+
+bool
+GLContextEGL::SetupLookupFunction()
+{
+ mLookupFunc = (PlatformLookupFunction)sEGLLibrary.mSymbols.fGetProcAddress;
+ return true;
+}
+
+bool
+GLContextEGL::SwapBuffers()
+{
+ EGLSurface surface = mSurfaceOverride != EGL_NO_SURFACE
+ ? mSurfaceOverride
+ : mSurface;
+ if (surface) {
+ return sEGLLibrary.fSwapBuffers(EGL_DISPLAY(), surface);
+ } else {
+ return false;
+ }
+}
+
+// hold a reference to the given surface
+// for the lifetime of this context.
+void
+GLContextEGL::HoldSurface(gfxASurface* aSurf) {
+ mThebesSurface = aSurf;
+}
+
+/* static */ EGLSurface
+GLContextEGL::CreateSurfaceForWindow(nsIWidget* aWidget)
+{
+ nsCString discardFailureId;
+ if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+ MOZ_CRASH("GFX: Failed to load EGL library!\n");
+ return nullptr;
+ }
+
+ EGLConfig config;
+ if (!CreateConfig(&config, aWidget)) {
+ MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
+ return nullptr;
+ }
+
+ EGLSurface surface = mozilla::gl::CreateSurfaceForWindow(aWidget, config);
+ if (!surface) {
+ MOZ_CRASH("GFX: Failed to create EGLSurface for window!\n");
+ return nullptr;
+ }
+ return surface;
+}
+
+/* static */ void
+GLContextEGL::DestroySurface(EGLSurface aSurface)
+{
+ if (aSurface != EGL_NO_SURFACE) {
+ sEGLLibrary.fDestroySurface(EGL_DISPLAY(), aSurface);
+ }
+}
+
+already_AddRefed<GLContextEGL>
+GLContextEGL::CreateGLContext(CreateContextFlags flags,
+ const SurfaceCaps& caps,
+ GLContextEGL* shareContext,
+ bool isOffscreen,
+ EGLConfig config,
+ EGLSurface surface,
+ nsACString* const out_failureId)
+{
+ if (sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_ES");
+ NS_WARNING("Failed to bind API to GLES!");
+ return nullptr;
+ }
+
+ EGLContext eglShareContext = shareContext ? shareContext->mContext
+ : EGL_NO_CONTEXT;
+
+ nsTArray<EGLint> contextAttribs;
+
+ contextAttribs.AppendElement(LOCAL_EGL_CONTEXT_CLIENT_VERSION);
+ if (flags & CreateContextFlags::PREFER_ES3)
+ contextAttribs.AppendElement(3);
+ else
+ contextAttribs.AppendElement(2);
+
+ if (sEGLLibrary.HasRobustness()) {
+// contextAttribs.AppendElement(LOCAL_EGL_CONTEXT_ROBUST_ACCESS_EXT);
+// contextAttribs.AppendElement(LOCAL_EGL_TRUE);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); i++) {
+ contextAttribs.AppendElement(gTerminationAttribs[i]);
+ }
+
+ EGLContext context = sEGLLibrary.fCreateContext(EGL_DISPLAY(),
+ config,
+ eglShareContext,
+ contextAttribs.Elements());
+ if (!context && shareContext) {
+ shareContext = nullptr;
+ context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), config, EGL_NO_CONTEXT,
+ contextAttribs.Elements());
+ }
+ if (!context) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_CREATE");
+ NS_WARNING("Failed to create EGLContext!");
+ return nullptr;
+ }
+
+ RefPtr<GLContextEGL> glContext = new GLContextEGL(flags, caps, shareContext,
+ isOffscreen, config, surface,
+ context);
+
+ if (!glContext->Init()) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_INIT");
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+EGLSurface
+GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
+ EGLenum bindToTextureFormat,
+ mozilla::gfx::IntSize& pbsize)
+{
+ nsTArray<EGLint> pbattrs(16);
+ EGLSurface surface = nullptr;
+
+TRY_AGAIN_POWER_OF_TWO:
+ pbattrs.Clear();
+ pbattrs.AppendElement(LOCAL_EGL_WIDTH); pbattrs.AppendElement(pbsize.width);
+ pbattrs.AppendElement(LOCAL_EGL_HEIGHT); pbattrs.AppendElement(pbsize.height);
+
+ if (bindToTextureFormat != LOCAL_EGL_NONE) {
+ pbattrs.AppendElement(LOCAL_EGL_TEXTURE_TARGET);
+ pbattrs.AppendElement(LOCAL_EGL_TEXTURE_2D);
+
+ pbattrs.AppendElement(LOCAL_EGL_TEXTURE_FORMAT);
+ pbattrs.AppendElement(bindToTextureFormat);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); i++) {
+ pbattrs.AppendElement(gTerminationAttribs[i]);
+ }
+
+ surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, &pbattrs[0]);
+ if (!surface) {
+ if (!is_power_of_two(pbsize.width) ||
+ !is_power_of_two(pbsize.height))
+ {
+ if (!is_power_of_two(pbsize.width))
+ pbsize.width = next_power_of_two(pbsize.width);
+ if (!is_power_of_two(pbsize.height))
+ pbsize.height = next_power_of_two(pbsize.height);
+
+ NS_WARNING("Failed to create pbuffer, trying power of two dims");
+ goto TRY_AGAIN_POWER_OF_TWO;
+ }
+
+ NS_WARNING("Failed to create pbuffer surface");
+ return nullptr;
+ }
+
+ return surface;
+}
+
+static const EGLint kEGLConfigAttribsOffscreenPBuffer[] = {
+ LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PBUFFER_BIT,
+ LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+ // Old versions of llvmpipe seem to need this to properly create the pbuffer (bug 981856)
+ LOCAL_EGL_RED_SIZE, 8,
+ LOCAL_EGL_GREEN_SIZE, 8,
+ LOCAL_EGL_BLUE_SIZE, 8,
+ LOCAL_EGL_ALPHA_SIZE, 0,
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static const EGLint kEGLConfigAttribsRGB16[] = {
+ LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
+ LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+ LOCAL_EGL_RED_SIZE, 5,
+ LOCAL_EGL_GREEN_SIZE, 6,
+ LOCAL_EGL_BLUE_SIZE, 5,
+ LOCAL_EGL_ALPHA_SIZE, 0,
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static const EGLint kEGLConfigAttribsRGB24[] = {
+ LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
+ LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+ LOCAL_EGL_RED_SIZE, 8,
+ LOCAL_EGL_GREEN_SIZE, 8,
+ LOCAL_EGL_BLUE_SIZE, 8,
+ LOCAL_EGL_ALPHA_SIZE, 0,
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static const EGLint kEGLConfigAttribsRGBA32[] = {
+ LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
+ LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+ LOCAL_EGL_RED_SIZE, 8,
+ LOCAL_EGL_GREEN_SIZE, 8,
+ LOCAL_EGL_BLUE_SIZE, 8,
+ LOCAL_EGL_ALPHA_SIZE, 8,
+ EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+};
+
+static bool
+CreateConfig(EGLConfig* aConfig, int32_t depth, nsIWidget* aWidget)
+{
+ EGLConfig configs[64];
+ const EGLint* attribs;
+ EGLint ncfg = ArrayLength(configs);
+
+ switch (depth) {
+ case 16:
+ attribs = kEGLConfigAttribsRGB16;
+ break;
+ case 24:
+ attribs = kEGLConfigAttribsRGB24;
+ break;
+ case 32:
+ attribs = kEGLConfigAttribsRGBA32;
+ break;
+ default:
+ NS_ERROR("Unknown pixel depth");
+ return false;
+ }
+
+ if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs,
+ configs, ncfg, &ncfg) ||
+ ncfg < 1) {
+ return false;
+ }
+
+ for (int j = 0; j < ncfg; ++j) {
+ EGLConfig config = configs[j];
+ EGLint r, g, b, a;
+ if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+ LOCAL_EGL_RED_SIZE, &r) &&
+ sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+ LOCAL_EGL_GREEN_SIZE, &g) &&
+ sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+ LOCAL_EGL_BLUE_SIZE, &b) &&
+ sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+ LOCAL_EGL_ALPHA_SIZE, &a) &&
+ ((depth == 16 && r == 5 && g == 6 && b == 5) ||
+ (depth == 24 && r == 8 && g == 8 && b == 8) ||
+ (depth == 32 && r == 8 && g == 8 && b == 8 && a == 8)))
+ {
+ *aConfig = config;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Return true if a suitable EGLConfig was found and pass it out
+// through aConfig. Return false otherwise.
+//
+// NB: It's entirely legal for the returned EGLConfig to be valid yet
+// have the value null.
+static bool
+CreateConfig(EGLConfig* aConfig, nsIWidget* aWidget)
+{
+ int32_t depth = gfxPlatform::GetPlatform()->GetScreenDepth();
+ if (!CreateConfig(aConfig, depth, aWidget)) {
+#ifdef MOZ_WIDGET_ANDROID
+ // Bug 736005
+ // Android doesn't always support 16 bit so also try 24 bit
+ if (depth == 16) {
+ return CreateConfig(aConfig, 24, aWidget);
+ }
+ // Bug 970096
+ // Some devices that have 24 bit screens only support 16 bit OpenGL?
+ if (depth == 24) {
+ return CreateConfig(aConfig, 16, aWidget);
+ }
+#endif
+ return false;
+ } else {
+ return true;
+ }
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface)
+{
+ nsCString discardFailureId;
+ if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+ MOZ_CRASH("GFX: Failed to load EGL library 2!\n");
+ return nullptr;
+ }
+
+ if (!aContext || !aSurface)
+ return nullptr;
+
+ SurfaceCaps caps = SurfaceCaps::Any();
+ EGLConfig config = EGL_NO_CONFIG;
+ RefPtr<GLContextEGL> gl = new GLContextEGL(CreateContextFlags::NONE, caps, nullptr,
+ false, config, (EGLSurface)aSurface,
+ (EGLContext)aContext);
+ gl->SetIsDoubleBuffered(true);
+ gl->mOwnsContext = false;
+
+ return gl.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+ return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+{
+ nsCString discardFailureId;
+ if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+ MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
+ return nullptr;
+ }
+
+ bool doubleBuffered = true;
+
+ EGLConfig config;
+ if (!CreateConfig(&config, aWidget)) {
+ MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
+ return nullptr;
+ }
+
+ EGLSurface surface = mozilla::gl::CreateSurfaceForWindow(aWidget, config);
+ if (!surface) {
+ MOZ_CRASH("GFX: Failed to create EGLSurface!\n");
+ return nullptr;
+ }
+
+ SurfaceCaps caps = SurfaceCaps::Any();
+ RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(CreateContextFlags::NONE,
+ caps, nullptr, false, config,
+ surface, &discardFailureId);
+ if (!gl) {
+ MOZ_CRASH("GFX: Failed to create EGLContext!\n");
+ mozilla::gl::DestroySurface(surface);
+ return nullptr;
+ }
+
+ gl->MakeCurrent();
+ gl->SetIsDoubleBuffered(doubleBuffered);
+
+ return gl.forget();
+}
+
+#if defined(ANDROID)
+EGLSurface
+GLContextProviderEGL::CreateEGLSurface(void* aWindow)
+{
+ nsCString discardFailureId;
+ if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+ MOZ_CRASH("GFX: Failed to load EGL library 4!\n");
+ }
+
+ EGLConfig config;
+ if (!CreateConfig(&config, static_cast<nsIWidget*>(aWindow))) {
+ MOZ_CRASH("GFX: Failed to create EGLConfig 2!\n");
+ }
+
+ MOZ_ASSERT(aWindow);
+
+ EGLSurface surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, aWindow,
+ 0);
+ if (surface == EGL_NO_SURFACE) {
+ MOZ_CRASH("GFX: Failed to create EGLSurface 2!\n");
+ }
+
+ return surface;
+}
+
+void
+GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface)
+{
+ nsCString discardFailureId;
+ if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
+ MOZ_CRASH("GFX: Failed to load EGL library 5!\n");
+ }
+
+ sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface);
+}
+#endif // defined(ANDROID)
+
+static void
+FillContextAttribs(bool alpha, bool depth, bool stencil, bool bpp16,
+ bool es3, nsTArray<EGLint>* out)
+{
+ out->AppendElement(LOCAL_EGL_SURFACE_TYPE);
+ out->AppendElement(LOCAL_EGL_PBUFFER_BIT);
+
+ out->AppendElement(LOCAL_EGL_RENDERABLE_TYPE);
+ if (es3) {
+ out->AppendElement(LOCAL_EGL_OPENGL_ES3_BIT_KHR);
+ } else {
+ out->AppendElement(LOCAL_EGL_OPENGL_ES2_BIT);
+ }
+
+ out->AppendElement(LOCAL_EGL_RED_SIZE);
+ if (bpp16) {
+ out->AppendElement(alpha ? 4 : 5);
+ } else {
+ out->AppendElement(8);
+ }
+
+ out->AppendElement(LOCAL_EGL_GREEN_SIZE);
+ if (bpp16) {
+ out->AppendElement(alpha ? 4 : 6);
+ } else {
+ out->AppendElement(8);
+ }
+
+ out->AppendElement(LOCAL_EGL_BLUE_SIZE);
+ if (bpp16) {
+ out->AppendElement(alpha ? 4 : 5);
+ } else {
+ out->AppendElement(8);
+ }
+
+ out->AppendElement(LOCAL_EGL_ALPHA_SIZE);
+ if (alpha) {
+ out->AppendElement(bpp16 ? 4 : 8);
+ } else {
+ out->AppendElement(0);
+ }
+
+ out->AppendElement(LOCAL_EGL_DEPTH_SIZE);
+ out->AppendElement(depth ? 16 : 0);
+
+ out->AppendElement(LOCAL_EGL_STENCIL_SIZE);
+ out->AppendElement(stencil ? 8 : 0);
+
+ // EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
+ out->AppendElement(LOCAL_EGL_NONE);
+ out->AppendElement(0);
+
+ out->AppendElement(0);
+ out->AppendElement(0);
+}
+
+static GLint
+GetAttrib(GLLibraryEGL* egl, EGLConfig config, EGLint attrib)
+{
+ EGLint bits = 0;
+ egl->fGetConfigAttrib(egl->Display(), config, attrib, &bits);
+ MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
+
+ return bits;
+}
+
+static EGLConfig
+ChooseConfig(GLLibraryEGL* egl, CreateContextFlags flags, const SurfaceCaps& minCaps,
+ SurfaceCaps* const out_configCaps)
+{
+ nsTArray<EGLint> configAttribList;
+ FillContextAttribs(minCaps.alpha, minCaps.depth, minCaps.stencil, minCaps.bpp16,
+ bool(flags & CreateContextFlags::PREFER_ES3), &configAttribList);
+
+ const EGLint* configAttribs = configAttribList.Elements();
+
+ // We're guaranteed to get at least minCaps, and the sorting dictated by the spec for
+ // eglChooseConfig reasonably assures that a reasonable 'best' config is on top.
+ const EGLint kMaxConfigs = 1;
+ EGLConfig configs[kMaxConfigs];
+ EGLint foundConfigs = 0;
+ if (!egl->fChooseConfig(egl->Display(), configAttribs, configs, kMaxConfigs,
+ &foundConfigs)
+ || foundConfigs == 0)
+ {
+ return EGL_NO_CONFIG;
+ }
+
+ EGLConfig config = configs[0];
+
+ *out_configCaps = minCaps; // Pick up any preserve, etc.
+ out_configCaps->color = true;
+ out_configCaps->alpha = bool(GetAttrib(egl, config, LOCAL_EGL_ALPHA_SIZE));
+ out_configCaps->depth = bool(GetAttrib(egl, config, LOCAL_EGL_DEPTH_SIZE));
+ out_configCaps->stencil = bool(GetAttrib(egl, config, LOCAL_EGL_STENCIL_SIZE));
+ out_configCaps->bpp16 = (GetAttrib(egl, config, LOCAL_EGL_RED_SIZE) < 8);
+
+ return config;
+}
+
+/*static*/ already_AddRefed<GLContextEGL>
+GLContextEGL::CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
+ const mozilla::gfx::IntSize& size,
+ const SurfaceCaps& minCaps,
+ nsACString* const out_failureId)
+{
+ bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
+ if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, out_failureId)) {
+ return nullptr;
+ }
+
+ SurfaceCaps configCaps;
+ EGLConfig config = ChooseConfig(&sEGLLibrary, flags, minCaps, &configCaps);
+ if (config == EGL_NO_CONFIG) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_NO_CONFIG");
+ NS_WARNING("Failed to find a compatible config.");
+ return nullptr;
+ }
+
+ if (GLContext::ShouldSpew()) {
+ sEGLLibrary.DumpEGLConfig(config);
+ }
+
+ mozilla::gfx::IntSize pbSize(size);
+ EGLSurface surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config,
+ LOCAL_EGL_NONE,
+ pbSize);
+ if (!surface) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_POT");
+ NS_WARNING("Failed to create PBuffer for context!");
+ return nullptr;
+ }
+
+ RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, configCaps, nullptr,
+ true, config, surface,
+ out_failureId);
+ if (!gl) {
+ NS_WARNING("Failed to create GLContext from PBuffer");
+ sEGLLibrary.fDestroySurface(sEGLLibrary.Display(), surface);
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+
+/*static*/ already_AddRefed<GLContext>
+GLContextProviderEGL::CreateHeadless(CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ mozilla::gfx::IntSize dummySize = mozilla::gfx::IntSize(16, 16);
+ SurfaceCaps dummyCaps = SurfaceCaps::Any();
+ return GLContextEGL::CreateEGLPBufferOffscreenContext(flags, dummySize, dummyCaps,
+ out_failureId);
+}
+
+// Under EGL, on Android, pbuffers are supported fine, though
+// often without the ability to texture from them directly.
+/*static*/ already_AddRefed<GLContext>
+GLContextProviderEGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
+ const SurfaceCaps& minCaps,
+ CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
+ if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, out_failureId)) { // Needed for IsANGLE().
+ return nullptr;
+ }
+
+ bool canOffscreenUseHeadless = true;
+ if (sEGLLibrary.IsANGLE()) {
+ // ANGLE needs to use PBuffers.
+ canOffscreenUseHeadless = false;
+ }
+
+ RefPtr<GLContext> gl;
+ SurfaceCaps minOffscreenCaps = minCaps;
+
+ if (canOffscreenUseHeadless) {
+ gl = CreateHeadless(flags, out_failureId);
+ if (!gl) {
+ return nullptr;
+ }
+ } else {
+ SurfaceCaps minBackbufferCaps = minOffscreenCaps;
+ if (minOffscreenCaps.antialias) {
+ minBackbufferCaps.antialias = false;
+ minBackbufferCaps.depth = false;
+ minBackbufferCaps.stencil = false;
+ }
+
+ gl = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, size,
+ minBackbufferCaps,
+ out_failureId);
+ if (!gl)
+ return nullptr;
+
+ // Pull the actual resulting caps to ensure that our offscreen matches our
+ // backbuffer.
+ minOffscreenCaps.alpha = gl->Caps().alpha;
+ if (!minOffscreenCaps.antialias) {
+ // Only update these if we don't have AA. If we do have AA, we ignore
+ // backbuffer depth/stencil.
+ minOffscreenCaps.depth = gl->Caps().depth;
+ minOffscreenCaps.stencil = gl->Caps().stencil;
+ }
+ }
+
+ // Init the offscreen with the updated offscreen caps.
+ if (!gl->InitOffscreen(size, minOffscreenCaps)) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_OFFSCREEN");
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+
+// Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
+// and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
+// and 3) each EGL context eats 750k on B2G (bug 813783)
+/*static*/ GLContext*
+GLContextProviderEGL::GetGlobalContext()
+{
+ return nullptr;
+}
+
+/*static*/ void
+GLContextProviderEGL::Shutdown()
+{
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS