diff -r 000000000000 -r 6474c204b198 gfx/gl/GLContextProviderEGL.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/gl/GLContextProviderEGL.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,894 @@ +/* -*- 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/. */ + +#include "mozilla/ArrayUtils.h" + +#include "GLContextEGL.h" + +#if defined(XP_UNIX) + +#ifdef MOZ_WIDGET_GTK +#include +// we're using default display for now +#define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)GDK_WINDOW_XID((GdkWindow *) aWidget->GetNativeData(NS_NATIVE_WINDOW)) +#elif defined(MOZ_WIDGET_QT) +#define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)(aWidget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW)) +#elif defined(MOZ_WIDGET_GONK) +#define GET_NATIVE_WINDOW(aWidget) ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW)) +#include "HwcComposer2D.h" +#include "libdisplay/GonkDisplay.h" +#endif + +#if defined(ANDROID) +/* from widget */ +#if defined(MOZ_WIDGET_ANDROID) +#include "AndroidBridge.h" +#include "nsSurfaceTexture.h" +#endif + +#include +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) + +# if defined(MOZ_WIDGET_GONK) +# include "cutils/properties.h" +# include + +using namespace android; +# endif + +#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 + +// 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 "mozilla/Preferences.h" +#include "gfxUtils.h" +#include "gfxFailure.h" +#include "gfxASurface.h" +#include "gfxPlatform.h" +#include "GLContextProvider.h" +#include "GLLibraryEGL.h" +#include "TextureImageEGL.h" +#include "nsDebug.h" +#include "nsThreadUtils.h" + +#include "nsIWidget.h" + +#include "gfxCrashReporterUtils.h" + +#include "ScopedGLHelpers.h" +#include "GLBlitHelper.h" + +using namespace mozilla::gfx; + +#ifdef MOZ_WIDGET_GONK +extern nsIntRect gScreenBounds; +#endif + +namespace mozilla { +namespace gl { + +#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); + +// 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 EGLint gContextAttribs[] = { + LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS +}; + +static EGLint gContextAttribsRobustness[] = { + LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2, + //LOCAL_EGL_CONTEXT_ROBUST_ACCESS_EXT, LOCAL_EGL_TRUE, + LOCAL_EGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_EXT, LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT, + 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 = EGL_NO_SURFACE; + + #ifdef MOZ_ANDROID_OMTC + mozilla::AndroidBridge::Bridge()->RegisterCompositor(); + newSurface = mozilla::AndroidBridge::Bridge()->CreateEGLSurfaceForCompositor(); + if (newSurface == EGL_NO_SURFACE) { + return EGL_NO_SURFACE; + } + #else + MOZ_ASSERT(widget != nullptr); + newSurface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, GET_NATIVE_WINDOW(widget), 0); + #ifdef MOZ_WIDGET_GONK + gScreenBounds.x = 0; + gScreenBounds.y = 0; + sEGLLibrary.fQuerySurface(EGL_DISPLAY(), newSurface, LOCAL_EGL_WIDTH, &gScreenBounds.width); + sEGLLibrary.fQuerySurface(EGL_DISPLAY(), newSurface, LOCAL_EGL_HEIGHT, &gScreenBounds.height); + #endif + #endif + return newSurface; +} + +GLContextEGL::GLContextEGL( + const SurfaceCaps& caps, + GLContext* shareContext, + bool isOffscreen, + EGLConfig config, + EGLSurface surface, + EGLContext context) + : GLContext(caps, shareContext, isOffscreen) + , mConfig(config) + , mSurface(surface) + , mSurfaceOverride(EGL_NO_SURFACE) + , mContext(context) + , 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 +#if defined(MOZ_WIDGET_GONK) + if (!mIsOffscreen) { + mHwc = HwcComposer2D::GetInstance(); + MOZ_ASSERT(!mHwc->Initialized()); + + if (mHwc->Init(EGL_DISPLAY(), mSurface)) { + NS_WARNING("HWComposer initialization failed!"); + mHwc = nullptr; + } + } +#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); + +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION < 17 + if (!mIsOffscreen) { + // In ICS, SurfaceFlinger's DisplayHardware::fini() does not destroy the EGLSurface associated with the + // native framebuffer. Destroying it causes crashes in the ICS emulator + // EGL implementation, specifically because the egl_window_surface_t dtor + // calls nativeWindow->cancelBuffer and FramebufferNativeWindow does not initialize + // the cancelBuffer function pointer, see bug 986836 + return; + } +#endif + + 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; + } + + PR_STATIC_ASSERT(sizeof(GLint) >= sizeof(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 ? (EGLSurface) surf : mSurface; + MakeCurrent(true); +} + +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. + if (aForce || sEGLLibrary.fGetCurrentContext() != mContext) { + 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 + } + } + } + + return succeeded; +} + +bool +GLContextEGL::IsCurrent() { + return sEGLLibrary.fGetCurrentContext() == mContext; +} + +bool +GLContextEGL::RenewSurface() { + if (!mOwnsContext) { + return false; + } +#ifndef MOZ_WIDGET_ANDROID + MOZ_CRASH("unimplemented"); + // to support this on non-Android platforms, need to keep track of the nsIWidget that + // this GLContext was created for (with CreateForWindow) so that we know what to + // pass again to CreateSurfaceForWindow below. + // The reason why Android doesn't need this is that it delegates EGLSurface creation to + // Java code which is the only thing that knows about our actual widget. +#endif + // 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(nullptr, mConfig); // the nullptr here is where we assume Android. + if (mSurface == EGL_NO_SURFACE) { + return false; + } + return MakeCurrent(true); +} + +void +GLContextEGL::ReleaseSurface() { + if (mOwnsContext) { + DestroySurface(mSurface); + } + mSurface = EGL_NO_SURFACE; +} + +bool +GLContextEGL::SetupLookupFunction() +{ + mLookupFunc = (PlatformLookupFunction)sEGLLibrary.mSymbols.fGetProcAddress; + return true; +} + +bool +GLContextEGL::SwapBuffers() +{ + if (mSurface) { +#ifdef MOZ_WIDGET_GONK + if (!mIsOffscreen) { + if (mHwc) { + return mHwc->Render(EGL_DISPLAY(), mSurface); + } else { + return GetGonkDisplay()->SwapBuffers(EGL_DISPLAY(), mSurface); + } + } else +#endif + return sEGLLibrary.fSwapBuffers(EGL_DISPLAY(), mSurface); + } else { + return false; + } +} + +// hold a reference to the given surface +// for the lifetime of this context. +void +GLContextEGL::HoldSurface(gfxASurface *aSurf) { + mThebesSurface = aSurf; +} + +already_AddRefed +GLContextEGL::CreateGLContext(const SurfaceCaps& caps, + GLContextEGL *shareContext, + bool isOffscreen, + EGLConfig config, + EGLSurface surface) +{ + if (sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) { + NS_WARNING("Failed to bind API to GLES!"); + return nullptr; + } + + EGLContext eglShareContext = shareContext ? shareContext->mContext + : EGL_NO_CONTEXT; + EGLint* attribs = sEGLLibrary.HasRobustness() ? gContextAttribsRobustness + : gContextAttribs; + + EGLContext context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), + config, + eglShareContext, + attribs); + if (!context && shareContext) { + shareContext = nullptr; + context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), + config, + EGL_NO_CONTEXT, + attribs); + } + if (!context) { + NS_WARNING("Failed to create EGLContext!"); + return nullptr; + } + + nsRefPtr glContext = new GLContextEGL(caps, + shareContext, + isOffscreen, + config, + surface, + context); + + if (!glContext->Init()) + return nullptr; + + return glContext.forget(); +} + +EGLSurface +GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config, + EGLenum bindToTextureFormat, + gfxIntSize& pbsize) +{ + nsTArray 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, +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 + LOCAL_EGL_FRAMEBUFFER_TARGET_ANDROID, LOCAL_EGL_TRUE, +#endif + EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS +}; + +static bool +CreateConfig(EGLConfig* aConfig, int32_t depth) +{ + 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) +{ + int32_t depth = gfxPlatform::GetPlatform()->GetScreenDepth(); + if (!CreateConfig(aConfig, depth)) { +#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); + } + // Bug 970096 + // Some devices that have 24 bit screens only support 16 bit OpenGL? + if (depth == 24) { + return CreateConfig(aConfig, 16); + } +#endif + return false; + } else { + return true; + } +} + +already_AddRefed +GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface) +{ + if (!sEGLLibrary.EnsureInitialized()) { + MOZ_CRASH("Failed to load EGL library!\n"); + return nullptr; + } + + if (aContext && aSurface) { + SurfaceCaps caps = SurfaceCaps::Any(); + EGLConfig config = EGL_NO_CONFIG; + nsRefPtr glContext = + new GLContextEGL(caps, + nullptr, false, + config, (EGLSurface)aSurface, (EGLContext)aContext); + + glContext->SetIsDoubleBuffered(true); + glContext->mOwnsContext = false; + + return glContext.forget(); + } + + return nullptr; +} + +already_AddRefed +GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget) +{ + if (!sEGLLibrary.EnsureInitialized()) { + MOZ_CRASH("Failed to load EGL library!\n"); + return nullptr; + } + + bool doubleBuffered = true; + + EGLConfig config; + if (!CreateConfig(&config)) { + MOZ_CRASH("Failed to create EGLConfig!\n"); + return nullptr; + } + + EGLSurface surface = mozilla::gl::CreateSurfaceForWindow(aWidget, config); + + if (surface == EGL_NO_SURFACE) { + MOZ_CRASH("Failed to create EGLSurface!\n"); + return nullptr; + } + + SurfaceCaps caps = SurfaceCaps::Any(); + nsRefPtr glContext = + GLContextEGL::CreateGLContext(caps, + nullptr, false, + config, surface); + + if (!glContext) { + MOZ_CRASH("Failed to create EGLContext!\n"); + DestroySurface(surface); + return nullptr; + } + + glContext->MakeCurrent(); + glContext->SetIsDoubleBuffered(doubleBuffered); + + return glContext.forget(); +} + +already_AddRefed +GLContextEGL::CreateEGLPBufferOffscreenContext(const gfxIntSize& size) +{ + EGLConfig config; + EGLSurface surface; + + const EGLint numConfigs = 1; // We only need one. + EGLConfig configs[numConfigs]; + EGLint foundConfigs = 0; + if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), + kEGLConfigAttribsOffscreenPBuffer, + configs, numConfigs, + &foundConfigs) + || foundConfigs == 0) + { + NS_WARNING("No EGL Config for minimal PBuffer!"); + return nullptr; + } + + // We absolutely don't care, so just pick the first one. + config = configs[0]; + if (GLContext::DebugMode()) + sEGLLibrary.DumpEGLConfig(config); + + gfxIntSize pbSize(size); + surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config, + LOCAL_EGL_NONE, + pbSize); + if (!surface) { + NS_WARNING("Failed to create PBuffer for context!"); + return nullptr; + } + + SurfaceCaps dummyCaps = SurfaceCaps::Any(); + nsRefPtr glContext = + GLContextEGL::CreateGLContext(dummyCaps, + nullptr, true, + config, surface); + if (!glContext) { + NS_WARNING("Failed to create GLContext from PBuffer"); + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); + return nullptr; + } + + if (!glContext->Init()) { + NS_WARNING("Failed to initialize GLContext!"); + // GLContextEGL::dtor will destroy |surface| for us. + return nullptr; + } + + return glContext.forget(); +} + +already_AddRefed +GLContextEGL::CreateEGLPixmapOffscreenContext(const gfxIntSize& size) +{ + gfxASurface *thebesSurface = nullptr; + EGLNativePixmapType pixmap = 0; + + if (!pixmap) { + return nullptr; + } + + EGLSurface surface = 0; + EGLConfig config = 0; + + if (!config) { + return nullptr; + } + MOZ_ASSERT(surface); + + SurfaceCaps dummyCaps = SurfaceCaps::Any(); + nsRefPtr glContext = + GLContextEGL::CreateGLContext(dummyCaps, + nullptr, true, + config, surface); + if (!glContext) { + NS_WARNING("Failed to create GLContext from XSurface"); + sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); + return nullptr; + } + + if (!glContext->Init()) { + NS_WARNING("Failed to initialize GLContext!"); + // GLContextEGL::dtor will destroy |surface| for us. + return nullptr; + } + + glContext->HoldSurface(thebesSurface); + + return glContext.forget(); +} + +// Under EGL, on Android, pbuffers are supported fine, though +// often without the ability to texture from them directly. +already_AddRefed +GLContextProviderEGL::CreateOffscreen(const gfxIntSize& size, + const SurfaceCaps& caps) +{ + if (!sEGLLibrary.EnsureInitialized()) { + return nullptr; + } + + gfxIntSize dummySize = gfxIntSize(16, 16); + nsRefPtr glContext; + glContext = GLContextEGL::CreateEGLPBufferOffscreenContext(dummySize); + + if (!glContext) + return nullptr; + + if (!glContext->InitOffscreen(ToIntSize(size), caps)) + return nullptr; + + return glContext.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) +GLContext * +GLContextProviderEGL::GetGlobalContext() +{ + return nullptr; +} + +void +GLContextProviderEGL::Shutdown() +{ +} + +} /* namespace gl */ +} /* namespace mozilla */ + +#undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS