michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include "GLContextEGL.h" michael@0: michael@0: #if defined(XP_UNIX) michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include michael@0: // we're using default display for now michael@0: #define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)GDK_WINDOW_XID((GdkWindow *) aWidget->GetNativeData(NS_NATIVE_WINDOW)) michael@0: #elif defined(MOZ_WIDGET_QT) michael@0: #define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)(aWidget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW)) michael@0: #elif defined(MOZ_WIDGET_GONK) michael@0: #define GET_NATIVE_WINDOW(aWidget) ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW)) michael@0: #include "HwcComposer2D.h" michael@0: #include "libdisplay/GonkDisplay.h" michael@0: #endif michael@0: michael@0: #if defined(ANDROID) michael@0: /* from widget */ michael@0: #if defined(MOZ_WIDGET_ANDROID) michael@0: #include "AndroidBridge.h" michael@0: #include "nsSurfaceTexture.h" michael@0: #endif michael@0: michael@0: #include michael@0: #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) michael@0: michael@0: # if defined(MOZ_WIDGET_GONK) michael@0: # include "cutils/properties.h" michael@0: # include michael@0: michael@0: using namespace android; michael@0: # endif michael@0: michael@0: #endif michael@0: michael@0: #define GLES2_LIB "libGLESv2.so" michael@0: #define GLES2_LIB2 "libGLESv2.so.2" michael@0: michael@0: #elif defined(XP_WIN) michael@0: michael@0: #include "nsIFile.h" michael@0: michael@0: #define GLES2_LIB "libGLESv2.dll" michael@0: michael@0: #ifndef WIN32_LEAN_AND_MEAN michael@0: #define WIN32_LEAN_AND_MEAN 1 michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: // a little helper michael@0: class AutoDestroyHWND { michael@0: public: michael@0: AutoDestroyHWND(HWND aWnd = nullptr) michael@0: : mWnd(aWnd) michael@0: { michael@0: } michael@0: michael@0: ~AutoDestroyHWND() { michael@0: if (mWnd) { michael@0: ::DestroyWindow(mWnd); michael@0: } michael@0: } michael@0: michael@0: operator HWND() { michael@0: return mWnd; michael@0: } michael@0: michael@0: HWND forget() { michael@0: HWND w = mWnd; michael@0: mWnd = nullptr; michael@0: return w; michael@0: } michael@0: michael@0: HWND operator=(HWND aWnd) { michael@0: if (mWnd && mWnd != aWnd) { michael@0: ::DestroyWindow(mWnd); michael@0: } michael@0: mWnd = aWnd; michael@0: return mWnd; michael@0: } michael@0: michael@0: HWND mWnd; michael@0: }; michael@0: michael@0: #else michael@0: michael@0: #error "Platform not recognized" michael@0: michael@0: #endif michael@0: michael@0: #include "mozilla/Preferences.h" michael@0: #include "gfxUtils.h" michael@0: #include "gfxFailure.h" michael@0: #include "gfxASurface.h" michael@0: #include "gfxPlatform.h" michael@0: #include "GLContextProvider.h" michael@0: #include "GLLibraryEGL.h" michael@0: #include "TextureImageEGL.h" michael@0: #include "nsDebug.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: #include "nsIWidget.h" michael@0: michael@0: #include "gfxCrashReporterUtils.h" michael@0: michael@0: #include "ScopedGLHelpers.h" michael@0: #include "GLBlitHelper.h" michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: extern nsIntRect gScreenBounds; michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace gl { michael@0: michael@0: #define ADD_ATTR_2(_array, _k, _v) do { \ michael@0: (_array).AppendElement(_k); \ michael@0: (_array).AppendElement(_v); \ michael@0: } while (0) michael@0: michael@0: #define ADD_ATTR_1(_array, _k) do { \ michael@0: (_array).AppendElement(_k); \ michael@0: } while (0) michael@0: michael@0: static bool michael@0: CreateConfig(EGLConfig* aConfig); michael@0: michael@0: // append three zeros at the end of attribs list to work around michael@0: // EGL implementation bugs that iterate until they find 0, instead of michael@0: // EGL_NONE. See bug 948406. michael@0: #define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \ michael@0: LOCAL_EGL_NONE, 0, 0, 0 michael@0: michael@0: static EGLint gTerminationAttribs[] = { michael@0: EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS michael@0: }; michael@0: michael@0: static EGLint gContextAttribs[] = { michael@0: LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2, michael@0: EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS michael@0: }; michael@0: michael@0: static EGLint gContextAttribsRobustness[] = { michael@0: LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2, michael@0: //LOCAL_EGL_CONTEXT_ROBUST_ACCESS_EXT, LOCAL_EGL_TRUE, michael@0: LOCAL_EGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_EXT, LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT, michael@0: EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS michael@0: }; michael@0: michael@0: static int michael@0: next_power_of_two(int v) michael@0: { michael@0: v--; michael@0: v |= v >> 1; michael@0: v |= v >> 2; michael@0: v |= v >> 4; michael@0: v |= v >> 8; michael@0: v |= v >> 16; michael@0: v++; michael@0: michael@0: return v; michael@0: } michael@0: michael@0: static bool michael@0: is_power_of_two(int v) michael@0: { michael@0: NS_ASSERTION(v >= 0, "bad value"); michael@0: michael@0: if (v == 0) michael@0: return true; michael@0: michael@0: return (v & (v-1)) == 0; michael@0: } michael@0: michael@0: static void michael@0: DestroySurface(EGLSurface oldSurface) { michael@0: if (oldSurface != EGL_NO_SURFACE) { michael@0: sEGLLibrary.fMakeCurrent(EGL_DISPLAY(), michael@0: EGL_NO_SURFACE, EGL_NO_SURFACE, michael@0: EGL_NO_CONTEXT); michael@0: sEGLLibrary.fDestroySurface(EGL_DISPLAY(), oldSurface); michael@0: } michael@0: } michael@0: michael@0: static EGLSurface michael@0: CreateSurfaceForWindow(nsIWidget* widget, const EGLConfig& config) { michael@0: EGLSurface newSurface = EGL_NO_SURFACE; michael@0: michael@0: #ifdef MOZ_ANDROID_OMTC michael@0: mozilla::AndroidBridge::Bridge()->RegisterCompositor(); michael@0: newSurface = mozilla::AndroidBridge::Bridge()->CreateEGLSurfaceForCompositor(); michael@0: if (newSurface == EGL_NO_SURFACE) { michael@0: return EGL_NO_SURFACE; michael@0: } michael@0: #else michael@0: MOZ_ASSERT(widget != nullptr); michael@0: newSurface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, GET_NATIVE_WINDOW(widget), 0); michael@0: #ifdef MOZ_WIDGET_GONK michael@0: gScreenBounds.x = 0; michael@0: gScreenBounds.y = 0; michael@0: sEGLLibrary.fQuerySurface(EGL_DISPLAY(), newSurface, LOCAL_EGL_WIDTH, &gScreenBounds.width); michael@0: sEGLLibrary.fQuerySurface(EGL_DISPLAY(), newSurface, LOCAL_EGL_HEIGHT, &gScreenBounds.height); michael@0: #endif michael@0: #endif michael@0: return newSurface; michael@0: } michael@0: michael@0: GLContextEGL::GLContextEGL( michael@0: const SurfaceCaps& caps, michael@0: GLContext* shareContext, michael@0: bool isOffscreen, michael@0: EGLConfig config, michael@0: EGLSurface surface, michael@0: EGLContext context) michael@0: : GLContext(caps, shareContext, isOffscreen) michael@0: , mConfig(config) michael@0: , mSurface(surface) michael@0: , mSurfaceOverride(EGL_NO_SURFACE) michael@0: , mContext(context) michael@0: , mThebesSurface(nullptr) michael@0: , mBound(false) michael@0: , mIsPBuffer(false) michael@0: , mIsDoubleBuffered(false) michael@0: , mCanBindToTexture(false) michael@0: , mShareWithEGLImage(false) michael@0: , mOwnsContext(true) michael@0: { michael@0: // any EGL contexts will always be GLESv2 michael@0: SetProfileVersion(ContextProfile::OpenGLES, 200); michael@0: michael@0: #ifdef DEBUG michael@0: printf_stderr("Initializing context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY()); michael@0: #endif michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: if (!mIsOffscreen) { michael@0: mHwc = HwcComposer2D::GetInstance(); michael@0: MOZ_ASSERT(!mHwc->Initialized()); michael@0: michael@0: if (mHwc->Init(EGL_DISPLAY(), mSurface)) { michael@0: NS_WARNING("HWComposer initialization failed!"); michael@0: mHwc = nullptr; michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: GLContextEGL::~GLContextEGL() michael@0: { michael@0: MarkDestroyed(); michael@0: michael@0: // Wrapped context should not destroy eglContext/Surface michael@0: if (!mOwnsContext) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: printf_stderr("Destroying context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY()); michael@0: #endif michael@0: michael@0: sEGLLibrary.fDestroyContext(EGL_DISPLAY(), mContext); michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION < 17 michael@0: if (!mIsOffscreen) { michael@0: // In ICS, SurfaceFlinger's DisplayHardware::fini() does not destroy the EGLSurface associated with the michael@0: // native framebuffer. Destroying it causes crashes in the ICS emulator michael@0: // EGL implementation, specifically because the egl_window_surface_t dtor michael@0: // calls nativeWindow->cancelBuffer and FramebufferNativeWindow does not initialize michael@0: // the cancelBuffer function pointer, see bug 986836 michael@0: return; michael@0: } michael@0: #endif michael@0: michael@0: mozilla::gl::DestroySurface(mSurface); michael@0: } michael@0: michael@0: bool michael@0: GLContextEGL::Init() michael@0: { michael@0: #if defined(ANDROID) michael@0: // We can't use LoadApitraceLibrary here because the GLContext michael@0: // expects its own handle to the GL library michael@0: if (!OpenLibrary(APITRACE_LIB)) michael@0: #endif michael@0: if (!OpenLibrary(GLES2_LIB)) { michael@0: #if defined(XP_UNIX) michael@0: if (!OpenLibrary(GLES2_LIB2)) { michael@0: NS_WARNING("Couldn't load GLES2 LIB."); michael@0: return false; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: SetupLookupFunction(); michael@0: if (!InitWithPrefix("gl", true)) michael@0: return false; michael@0: michael@0: bool current = MakeCurrent(); michael@0: if (!current) { michael@0: gfx::LogFailure(NS_LITERAL_CSTRING( michael@0: "Couldn't get device attachments for device.")); michael@0: return false; michael@0: } michael@0: michael@0: PR_STATIC_ASSERT(sizeof(GLint) >= sizeof(int32_t)); michael@0: mMaxTextureImageSize = INT32_MAX; michael@0: michael@0: mShareWithEGLImage = sEGLLibrary.HasKHRImageBase() && michael@0: sEGLLibrary.HasKHRImageTexture2D() && michael@0: IsExtensionSupported(OES_EGL_image); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: GLContextEGL::BindTexImage() michael@0: { michael@0: if (!mSurface) michael@0: return false; michael@0: michael@0: if (mBound && !ReleaseTexImage()) michael@0: return false; michael@0: michael@0: EGLBoolean success = sEGLLibrary.fBindTexImage(EGL_DISPLAY(), michael@0: (EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER); michael@0: if (success == LOCAL_EGL_FALSE) michael@0: return false; michael@0: michael@0: mBound = true; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: GLContextEGL::ReleaseTexImage() michael@0: { michael@0: if (!mBound) michael@0: return true; michael@0: michael@0: if (!mSurface) michael@0: return false; michael@0: michael@0: EGLBoolean success; michael@0: success = sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(), michael@0: (EGLSurface)mSurface, michael@0: LOCAL_EGL_BACK_BUFFER); michael@0: if (success == LOCAL_EGL_FALSE) michael@0: return false; michael@0: michael@0: mBound = false; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf) { michael@0: if (Screen()) { michael@0: /* Blit `draw` to `read` if we need to, before we potentially juggle michael@0: * `read` around. If we don't, we might attach a different `read`, michael@0: * and *then* hit AssureBlitted, which will blit a dirty `draw` onto michael@0: * the wrong `read`! michael@0: */ michael@0: Screen()->AssureBlitted(); michael@0: } michael@0: michael@0: mSurfaceOverride = surf ? (EGLSurface) surf : mSurface; michael@0: MakeCurrent(true); michael@0: } michael@0: michael@0: bool michael@0: GLContextEGL::MakeCurrentImpl(bool aForce) { michael@0: bool succeeded = true; michael@0: michael@0: // Assume that EGL has the same problem as WGL does, michael@0: // where MakeCurrent with an already-current context is michael@0: // still expensive. michael@0: if (aForce || sEGLLibrary.fGetCurrentContext() != mContext) { michael@0: EGLSurface surface = mSurfaceOverride != EGL_NO_SURFACE michael@0: ? mSurfaceOverride michael@0: : mSurface; michael@0: if (surface == EGL_NO_SURFACE) { michael@0: return false; michael@0: } michael@0: succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(), michael@0: surface, surface, michael@0: mContext); michael@0: if (!succeeded) { michael@0: int eglError = sEGLLibrary.fGetError(); michael@0: if (eglError == LOCAL_EGL_CONTEXT_LOST) { michael@0: mContextLost = true; michael@0: NS_WARNING("EGL context has been lost."); michael@0: } else { michael@0: NS_WARNING("Failed to make GL context current!"); michael@0: #ifdef DEBUG michael@0: printf_stderr("EGL Error: 0x%04x\n", eglError); michael@0: #endif michael@0: } michael@0: } michael@0: } michael@0: michael@0: return succeeded; michael@0: } michael@0: michael@0: bool michael@0: GLContextEGL::IsCurrent() { michael@0: return sEGLLibrary.fGetCurrentContext() == mContext; michael@0: } michael@0: michael@0: bool michael@0: GLContextEGL::RenewSurface() { michael@0: if (!mOwnsContext) { michael@0: return false; michael@0: } michael@0: #ifndef MOZ_WIDGET_ANDROID michael@0: MOZ_CRASH("unimplemented"); michael@0: // to support this on non-Android platforms, need to keep track of the nsIWidget that michael@0: // this GLContext was created for (with CreateForWindow) so that we know what to michael@0: // pass again to CreateSurfaceForWindow below. michael@0: // The reason why Android doesn't need this is that it delegates EGLSurface creation to michael@0: // Java code which is the only thing that knows about our actual widget. michael@0: #endif michael@0: // unconditionally release the surface and create a new one. Don't try to optimize this away. michael@0: // If we get here, then by definition we know that we want to get a new surface. michael@0: ReleaseSurface(); michael@0: mSurface = mozilla::gl::CreateSurfaceForWindow(nullptr, mConfig); // the nullptr here is where we assume Android. michael@0: if (mSurface == EGL_NO_SURFACE) { michael@0: return false; michael@0: } michael@0: return MakeCurrent(true); michael@0: } michael@0: michael@0: void michael@0: GLContextEGL::ReleaseSurface() { michael@0: if (mOwnsContext) { michael@0: DestroySurface(mSurface); michael@0: } michael@0: mSurface = EGL_NO_SURFACE; michael@0: } michael@0: michael@0: bool michael@0: GLContextEGL::SetupLookupFunction() michael@0: { michael@0: mLookupFunc = (PlatformLookupFunction)sEGLLibrary.mSymbols.fGetProcAddress; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: GLContextEGL::SwapBuffers() michael@0: { michael@0: if (mSurface) { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: if (!mIsOffscreen) { michael@0: if (mHwc) { michael@0: return mHwc->Render(EGL_DISPLAY(), mSurface); michael@0: } else { michael@0: return GetGonkDisplay()->SwapBuffers(EGL_DISPLAY(), mSurface); michael@0: } michael@0: } else michael@0: #endif michael@0: return sEGLLibrary.fSwapBuffers(EGL_DISPLAY(), mSurface); michael@0: } else { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // hold a reference to the given surface michael@0: // for the lifetime of this context. michael@0: void michael@0: GLContextEGL::HoldSurface(gfxASurface *aSurf) { michael@0: mThebesSurface = aSurf; michael@0: } michael@0: michael@0: already_AddRefed michael@0: GLContextEGL::CreateGLContext(const SurfaceCaps& caps, michael@0: GLContextEGL *shareContext, michael@0: bool isOffscreen, michael@0: EGLConfig config, michael@0: EGLSurface surface) michael@0: { michael@0: if (sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) { michael@0: NS_WARNING("Failed to bind API to GLES!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: EGLContext eglShareContext = shareContext ? shareContext->mContext michael@0: : EGL_NO_CONTEXT; michael@0: EGLint* attribs = sEGLLibrary.HasRobustness() ? gContextAttribsRobustness michael@0: : gContextAttribs; michael@0: michael@0: EGLContext context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), michael@0: config, michael@0: eglShareContext, michael@0: attribs); michael@0: if (!context && shareContext) { michael@0: shareContext = nullptr; michael@0: context = sEGLLibrary.fCreateContext(EGL_DISPLAY(), michael@0: config, michael@0: EGL_NO_CONTEXT, michael@0: attribs); michael@0: } michael@0: if (!context) { michael@0: NS_WARNING("Failed to create EGLContext!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr glContext = new GLContextEGL(caps, michael@0: shareContext, michael@0: isOffscreen, michael@0: config, michael@0: surface, michael@0: context); michael@0: michael@0: if (!glContext->Init()) michael@0: return nullptr; michael@0: michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: EGLSurface michael@0: GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config, michael@0: EGLenum bindToTextureFormat, michael@0: gfxIntSize& pbsize) michael@0: { michael@0: nsTArray pbattrs(16); michael@0: EGLSurface surface = nullptr; michael@0: michael@0: TRY_AGAIN_POWER_OF_TWO: michael@0: pbattrs.Clear(); michael@0: pbattrs.AppendElement(LOCAL_EGL_WIDTH); pbattrs.AppendElement(pbsize.width); michael@0: pbattrs.AppendElement(LOCAL_EGL_HEIGHT); pbattrs.AppendElement(pbsize.height); michael@0: michael@0: if (bindToTextureFormat != LOCAL_EGL_NONE) { michael@0: pbattrs.AppendElement(LOCAL_EGL_TEXTURE_TARGET); michael@0: pbattrs.AppendElement(LOCAL_EGL_TEXTURE_2D); michael@0: michael@0: pbattrs.AppendElement(LOCAL_EGL_TEXTURE_FORMAT); michael@0: pbattrs.AppendElement(bindToTextureFormat); michael@0: } michael@0: michael@0: for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); i++) { michael@0: pbattrs.AppendElement(gTerminationAttribs[i]); michael@0: } michael@0: michael@0: surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, &pbattrs[0]); michael@0: if (!surface) { michael@0: if (!is_power_of_two(pbsize.width) || michael@0: !is_power_of_two(pbsize.height)) michael@0: { michael@0: if (!is_power_of_two(pbsize.width)) michael@0: pbsize.width = next_power_of_two(pbsize.width); michael@0: if (!is_power_of_two(pbsize.height)) michael@0: pbsize.height = next_power_of_two(pbsize.height); michael@0: michael@0: NS_WARNING("Failed to create pbuffer, trying power of two dims"); michael@0: goto TRY_AGAIN_POWER_OF_TWO; michael@0: } michael@0: michael@0: NS_WARNING("Failed to create pbuffer surface"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return surface; michael@0: } michael@0: michael@0: static const EGLint kEGLConfigAttribsOffscreenPBuffer[] = { michael@0: LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PBUFFER_BIT, michael@0: LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, michael@0: // Old versions of llvmpipe seem to need this to properly create the pbuffer (bug 981856) michael@0: LOCAL_EGL_RED_SIZE, 8, michael@0: LOCAL_EGL_GREEN_SIZE, 8, michael@0: LOCAL_EGL_BLUE_SIZE, 8, michael@0: LOCAL_EGL_ALPHA_SIZE, 0, michael@0: EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS michael@0: }; michael@0: michael@0: static const EGLint kEGLConfigAttribsRGB16[] = { michael@0: LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT, michael@0: LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, michael@0: LOCAL_EGL_RED_SIZE, 5, michael@0: LOCAL_EGL_GREEN_SIZE, 6, michael@0: LOCAL_EGL_BLUE_SIZE, 5, michael@0: LOCAL_EGL_ALPHA_SIZE, 0, michael@0: EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS michael@0: }; michael@0: michael@0: static const EGLint kEGLConfigAttribsRGB24[] = { michael@0: LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT, michael@0: LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, michael@0: LOCAL_EGL_RED_SIZE, 8, michael@0: LOCAL_EGL_GREEN_SIZE, 8, michael@0: LOCAL_EGL_BLUE_SIZE, 8, michael@0: LOCAL_EGL_ALPHA_SIZE, 0, michael@0: EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS michael@0: }; michael@0: michael@0: static const EGLint kEGLConfigAttribsRGBA32[] = { michael@0: LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT, michael@0: LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, michael@0: LOCAL_EGL_RED_SIZE, 8, michael@0: LOCAL_EGL_GREEN_SIZE, 8, michael@0: LOCAL_EGL_BLUE_SIZE, 8, michael@0: LOCAL_EGL_ALPHA_SIZE, 8, michael@0: #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 michael@0: LOCAL_EGL_FRAMEBUFFER_TARGET_ANDROID, LOCAL_EGL_TRUE, michael@0: #endif michael@0: EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS michael@0: }; michael@0: michael@0: static bool michael@0: CreateConfig(EGLConfig* aConfig, int32_t depth) michael@0: { michael@0: EGLConfig configs[64]; michael@0: const EGLint* attribs; michael@0: EGLint ncfg = ArrayLength(configs); michael@0: michael@0: switch (depth) { michael@0: case 16: michael@0: attribs = kEGLConfigAttribsRGB16; michael@0: break; michael@0: case 24: michael@0: attribs = kEGLConfigAttribsRGB24; michael@0: break; michael@0: case 32: michael@0: attribs = kEGLConfigAttribsRGBA32; michael@0: break; michael@0: default: michael@0: NS_ERROR("Unknown pixel depth"); michael@0: return false; michael@0: } michael@0: michael@0: if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs, michael@0: configs, ncfg, &ncfg) || michael@0: ncfg < 1) { michael@0: return false; michael@0: } michael@0: michael@0: for (int j = 0; j < ncfg; ++j) { michael@0: EGLConfig config = configs[j]; michael@0: EGLint r, g, b, a; michael@0: michael@0: if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, michael@0: LOCAL_EGL_RED_SIZE, &r) && michael@0: sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, michael@0: LOCAL_EGL_GREEN_SIZE, &g) && michael@0: sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, michael@0: LOCAL_EGL_BLUE_SIZE, &b) && michael@0: sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, michael@0: LOCAL_EGL_ALPHA_SIZE, &a) && michael@0: ((depth == 16 && r == 5 && g == 6 && b == 5) || michael@0: (depth == 24 && r == 8 && g == 8 && b == 8) || michael@0: (depth == 32 && r == 8 && g == 8 && b == 8 && a == 8))) michael@0: { michael@0: *aConfig = config; michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Return true if a suitable EGLConfig was found and pass it out michael@0: // through aConfig. Return false otherwise. michael@0: // michael@0: // NB: It's entirely legal for the returned EGLConfig to be valid yet michael@0: // have the value null. michael@0: static bool michael@0: CreateConfig(EGLConfig* aConfig) michael@0: { michael@0: int32_t depth = gfxPlatform::GetPlatform()->GetScreenDepth(); michael@0: if (!CreateConfig(aConfig, depth)) { michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: // Bug 736005 michael@0: // Android doesn't always support 16 bit so also try 24 bit michael@0: if (depth == 16) { michael@0: return CreateConfig(aConfig, 24); michael@0: } michael@0: // Bug 970096 michael@0: // Some devices that have 24 bit screens only support 16 bit OpenGL? michael@0: if (depth == 24) { michael@0: return CreateConfig(aConfig, 16); michael@0: } michael@0: #endif michael@0: return false; michael@0: } else { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface) michael@0: { michael@0: if (!sEGLLibrary.EnsureInitialized()) { michael@0: MOZ_CRASH("Failed to load EGL library!\n"); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aContext && aSurface) { michael@0: SurfaceCaps caps = SurfaceCaps::Any(); michael@0: EGLConfig config = EGL_NO_CONFIG; michael@0: nsRefPtr glContext = michael@0: new GLContextEGL(caps, michael@0: nullptr, false, michael@0: config, (EGLSurface)aSurface, (EGLContext)aContext); michael@0: michael@0: glContext->SetIsDoubleBuffered(true); michael@0: glContext->mOwnsContext = false; michael@0: michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget) michael@0: { michael@0: if (!sEGLLibrary.EnsureInitialized()) { michael@0: MOZ_CRASH("Failed to load EGL library!\n"); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool doubleBuffered = true; michael@0: michael@0: EGLConfig config; michael@0: if (!CreateConfig(&config)) { michael@0: MOZ_CRASH("Failed to create EGLConfig!\n"); michael@0: return nullptr; michael@0: } michael@0: michael@0: EGLSurface surface = mozilla::gl::CreateSurfaceForWindow(aWidget, config); michael@0: michael@0: if (surface == EGL_NO_SURFACE) { michael@0: MOZ_CRASH("Failed to create EGLSurface!\n"); michael@0: return nullptr; michael@0: } michael@0: michael@0: SurfaceCaps caps = SurfaceCaps::Any(); michael@0: nsRefPtr glContext = michael@0: GLContextEGL::CreateGLContext(caps, michael@0: nullptr, false, michael@0: config, surface); michael@0: michael@0: if (!glContext) { michael@0: MOZ_CRASH("Failed to create EGLContext!\n"); michael@0: DestroySurface(surface); michael@0: return nullptr; michael@0: } michael@0: michael@0: glContext->MakeCurrent(); michael@0: glContext->SetIsDoubleBuffered(doubleBuffered); michael@0: michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: GLContextEGL::CreateEGLPBufferOffscreenContext(const gfxIntSize& size) michael@0: { michael@0: EGLConfig config; michael@0: EGLSurface surface; michael@0: michael@0: const EGLint numConfigs = 1; // We only need one. michael@0: EGLConfig configs[numConfigs]; michael@0: EGLint foundConfigs = 0; michael@0: if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), michael@0: kEGLConfigAttribsOffscreenPBuffer, michael@0: configs, numConfigs, michael@0: &foundConfigs) michael@0: || foundConfigs == 0) michael@0: { michael@0: NS_WARNING("No EGL Config for minimal PBuffer!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // We absolutely don't care, so just pick the first one. michael@0: config = configs[0]; michael@0: if (GLContext::DebugMode()) michael@0: sEGLLibrary.DumpEGLConfig(config); michael@0: michael@0: gfxIntSize pbSize(size); michael@0: surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config, michael@0: LOCAL_EGL_NONE, michael@0: pbSize); michael@0: if (!surface) { michael@0: NS_WARNING("Failed to create PBuffer for context!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: SurfaceCaps dummyCaps = SurfaceCaps::Any(); michael@0: nsRefPtr glContext = michael@0: GLContextEGL::CreateGLContext(dummyCaps, michael@0: nullptr, true, michael@0: config, surface); michael@0: if (!glContext) { michael@0: NS_WARNING("Failed to create GLContext from PBuffer"); michael@0: sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!glContext->Init()) { michael@0: NS_WARNING("Failed to initialize GLContext!"); michael@0: // GLContextEGL::dtor will destroy |surface| for us. michael@0: return nullptr; michael@0: } michael@0: michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: GLContextEGL::CreateEGLPixmapOffscreenContext(const gfxIntSize& size) michael@0: { michael@0: gfxASurface *thebesSurface = nullptr; michael@0: EGLNativePixmapType pixmap = 0; michael@0: michael@0: if (!pixmap) { michael@0: return nullptr; michael@0: } michael@0: michael@0: EGLSurface surface = 0; michael@0: EGLConfig config = 0; michael@0: michael@0: if (!config) { michael@0: return nullptr; michael@0: } michael@0: MOZ_ASSERT(surface); michael@0: michael@0: SurfaceCaps dummyCaps = SurfaceCaps::Any(); michael@0: nsRefPtr glContext = michael@0: GLContextEGL::CreateGLContext(dummyCaps, michael@0: nullptr, true, michael@0: config, surface); michael@0: if (!glContext) { michael@0: NS_WARNING("Failed to create GLContext from XSurface"); michael@0: sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!glContext->Init()) { michael@0: NS_WARNING("Failed to initialize GLContext!"); michael@0: // GLContextEGL::dtor will destroy |surface| for us. michael@0: return nullptr; michael@0: } michael@0: michael@0: glContext->HoldSurface(thebesSurface); michael@0: michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: // Under EGL, on Android, pbuffers are supported fine, though michael@0: // often without the ability to texture from them directly. michael@0: already_AddRefed michael@0: GLContextProviderEGL::CreateOffscreen(const gfxIntSize& size, michael@0: const SurfaceCaps& caps) michael@0: { michael@0: if (!sEGLLibrary.EnsureInitialized()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: gfxIntSize dummySize = gfxIntSize(16, 16); michael@0: nsRefPtr glContext; michael@0: glContext = GLContextEGL::CreateEGLPBufferOffscreenContext(dummySize); michael@0: michael@0: if (!glContext) michael@0: return nullptr; michael@0: michael@0: if (!glContext->InitOffscreen(ToIntSize(size), caps)) michael@0: return nullptr; michael@0: michael@0: return glContext.forget(); michael@0: } michael@0: michael@0: // Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225) michael@0: // and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257) michael@0: // and 3) each EGL context eats 750k on B2G (bug 813783) michael@0: GLContext * michael@0: GLContextProviderEGL::GetGlobalContext() michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: GLContextProviderEGL::Shutdown() michael@0: { michael@0: } michael@0: michael@0: } /* namespace gl */ michael@0: } /* namespace mozilla */ michael@0: michael@0: #undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS