From 59e904dc50233c5ee5e861ba1eab79bc6e6c72dd Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 27 Aug 2015 10:38:45 -0400 Subject: [PATCH] Bug 1191042 - Establish correct EGLConfig at GLContext creation. r=jrmuizel --- dom/canvas/WebGLContext.cpp | 28 +++++++ gfx/gl/GLContextEGL.h | 12 ++- gfx/gl/GLContextProvider.h | 11 --- gfx/gl/GLContextProviderEGL.cpp | 113 ++++++++++++++++++++------- gfx/gl/GLContextTypes.h | 17 ++++ gfx/gl/SharedSurfaceANGLE.cpp | 133 +------------------------------- 6 files changed, 141 insertions(+), 173 deletions(-) diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index aec41fc81fd..d3433604a8f 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -571,6 +571,24 @@ CreateHeadlessANGLE(CreateContextFlags flags, const nsCOMPtr& gfxInf { nsRefPtr gl; + // ANGLE doesn't really support non-alpha backbuffers. + flags |= CreateContextFlags::SUPPORT_ALPHA; + + // ANGLE also doesn't really support depth without stencil, and vice-versa. + // While we could fake that we don't have one or the other, instead, we *should* just + // fail to create a context without both. However, since the defaults are depth:true, + // stencil:false, many demos that rely on the implicit depth allocation will fail. + // Thus we must limp along for depth:true, stencil:false, but we shouldn't support + // depth:false, stencil:true. + + // That is, if there's depth, give depth+stencil, else give neither. + if (flags & CreateContextFlags::SUPPORT_DEPTH) { + flags |= CreateContextFlags::SUPPORT_STENCIL; + } else { + flags &= ~CreateContextFlags::SUPPORT_DEPTH; + flags &= ~CreateContextFlags::SUPPORT_STENCIL; + } + #ifdef XP_WIN gl = gl::GLContextProviderEGL::CreateHeadless(flags); if (!gl) { @@ -758,6 +776,16 @@ WebGLContext::CreateOffscreenGL(bool forceEnabled) CreateContextFlags flags = forceEnabled ? CreateContextFlags::FORCE_ENABLE_HARDWARE : CreateContextFlags::NONE; + if (mOptions.alpha) + flags |= CreateContextFlags::SUPPORT_ALPHA; + + if (!mOptions.antialias) { + if (mOptions.depth) + flags |= CreateContextFlags::SUPPORT_DEPTH; + if (mOptions.stencil) + flags |= CreateContextFlags::SUPPORT_STENCIL; + } + gl = CreateHeadlessGL(flags, gfxInfo, this); do { diff --git a/gfx/gl/GLContextEGL.h b/gfx/gl/GLContextEGL.h index 5a4d3b7222d..3296ba3f8c1 100644 --- a/gfx/gl/GLContextEGL.h +++ b/gfx/gl/GLContextEGL.h @@ -92,15 +92,19 @@ public: // for the lifetime of this context. void HoldSurface(gfxASurface *aSurf); - EGLContext GetEGLContext() { + EGLContext GetEGLContext() const { return mContext; } - EGLSurface GetEGLSurface() { + EGLConfig GetEGLConfig() const { + return mConfig; + } + + EGLSurface GetEGLSurface() const { return mSurface; } - EGLDisplay GetEGLDisplay() { + EGLDisplay GetEGLDisplay() const { return EGL_DISPLAY(); } @@ -112,7 +116,7 @@ public: CreateEGLPixmapOffscreenContext(const gfx::IntSize& size); static already_AddRefed - CreateEGLPBufferOffscreenContext(const gfx::IntSize& size); + CreateEGLPBufferOffscreenContext(CreateContextFlags flags, const gfx::IntSize& size); protected: friend class GLContextProviderEGL; diff --git a/gfx/gl/GLContextProvider.h b/gfx/gl/GLContextProvider.h index 2a5d7c32843..d3a34032e64 100644 --- a/gfx/gl/GLContextProvider.h +++ b/gfx/gl/GLContextProvider.h @@ -9,7 +9,6 @@ #include "GLContextTypes.h" #include "nsAutoPtr.h" #include "SurfaceTypes.h" -#include "mozilla/TypedEnumBits.h" #include "nsSize.h" // for gfx::IntSize (needed by GLContextProviderImpl.h below) @@ -18,16 +17,6 @@ class nsIWidget; namespace mozilla { namespace gl { -enum class CreateContextFlags : int8_t { - NONE = 0, - REQUIRE_COMPAT_PROFILE = 1 << 0, - // Force the use of hardware backed GL, don't allow software implementations. - FORCE_ENABLE_HARDWARE = 1 << 1, - /* Don't force discrete GPU to be used (if applicable) */ - ALLOW_OFFLINE_RENDERER = 1 << 2, -}; -MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CreateContextFlags) - #define IN_GL_CONTEXT_PROVIDER_H // Null is always there diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index c42b56272c2..1d1d16e6788 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -593,17 +593,6 @@ TRY_AGAIN_POWER_OF_TWO: 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, @@ -839,18 +828,72 @@ GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface) } #endif // defined(ANDROID) -already_AddRefed -GLContextEGL::CreateEGLPBufferOffscreenContext(const mozilla::gfx::IntSize& size) -{ - EGLConfig config; - EGLSurface surface; - const EGLint numConfigs = 1; // We only need one. - EGLConfig configs[numConfigs]; +static void +FillContextAttribs(bool alpha, bool depth, bool stencil, std::vector* out) +{ + out->push_back(LOCAL_EGL_SURFACE_TYPE); + out->push_back(LOCAL_EGL_PBUFFER_BIT); + + out->push_back(LOCAL_EGL_RENDERABLE_TYPE); + out->push_back(LOCAL_EGL_OPENGL_ES2_BIT); + + out->push_back(LOCAL_EGL_RED_SIZE); + out->push_back(8); + + out->push_back(LOCAL_EGL_GREEN_SIZE); + out->push_back(8); + + out->push_back(LOCAL_EGL_BLUE_SIZE); + out->push_back(8); + + out->push_back(LOCAL_EGL_ALPHA_SIZE); + out->push_back(alpha ? 8 : 0); + + out->push_back(LOCAL_EGL_DEPTH_SIZE); + out->push_back(depth ? 24 : 0); + + out->push_back(LOCAL_EGL_STENCIL_SIZE); + out->push_back(stencil ? 8 : 0); + + // EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS + out->push_back(LOCAL_EGL_NONE); + out->push_back(0); + + out->push_back(0); + out->push_back(0); +} + +static bool +DoesAttribPresenceMatch(GLLibraryEGL& egl, EGLConfig config, EGLint attrib, + bool shouldHaveBits) +{ + EGLint bits = 0; + egl.fGetConfigAttrib(egl.Display(), config, attrib, &bits); + MOZ_ASSERT(egl.fGetError() == LOCAL_EGL_SUCCESS); + + return bool(bits) == shouldHaveBits; +} + +already_AddRefed +GLContextEGL::CreateEGLPBufferOffscreenContext(CreateContextFlags flags, + const mozilla::gfx::IntSize& size) +{ + const bool alpha = (flags & CreateContextFlags::SUPPORT_ALPHA); + const bool depth = (flags & CreateContextFlags::SUPPORT_DEPTH); + const bool stencil = (flags & CreateContextFlags::SUPPORT_STENCIL); + + std::vector configAttribVec; + FillContextAttribs(alpha, depth, stencil, &configAttribVec); + + const EGLint* configAttribs = configAttribVec.data(); + + const EGLint kMaxConfigs = 256; + EGLConfig configs[kMaxConfigs]; EGLint foundConfigs = 0; if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), - kEGLConfigAttribsOffscreenPBuffer, - configs, numConfigs, + configAttribs, + configs, kMaxConfigs, &foundConfigs) || foundConfigs == 0) { @@ -858,15 +901,33 @@ GLContextEGL::CreateEGLPBufferOffscreenContext(const mozilla::gfx::IntSize& size return nullptr; } - // We absolutely don't care, so just pick the first one. - config = configs[0]; + EGLConfig config = EGL_NO_CONFIG; + for (EGLint i = 0; i < foundConfigs; i++) { + EGLConfig& cur = configs[i]; + + bool doesMatch = true; + doesMatch &= DoesAttribPresenceMatch(sEGLLibrary, cur, LOCAL_EGL_ALPHA_SIZE, alpha); + doesMatch &= DoesAttribPresenceMatch(sEGLLibrary, cur, LOCAL_EGL_DEPTH_SIZE, depth); + doesMatch &= DoesAttribPresenceMatch(sEGLLibrary, cur, LOCAL_EGL_STENCIL_SIZE, stencil); + + if (doesMatch) { + config = cur; + break; + } + } + + if (config == EGL_NO_CONFIG) { + NS_WARNING("Failed to find a compatible config."); + return nullptr; + } + if (GLContext::ShouldSpew()) sEGLLibrary.DumpEGLConfig(config); mozilla::gfx::IntSize pbSize(size); - surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config, - LOCAL_EGL_NONE, - pbSize); + EGLSurface surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config, + LOCAL_EGL_NONE, + pbSize); if (!surface) { NS_WARNING("Failed to create PBuffer for context!"); return nullptr; @@ -941,7 +1002,7 @@ GLContextProviderEGL::CreateHeadless(CreateContextFlags flags) mozilla::gfx::IntSize dummySize = mozilla::gfx::IntSize(16, 16); nsRefPtr glContext; - glContext = GLContextEGL::CreateEGLPBufferOffscreenContext(dummySize); + glContext = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, dummySize); if (!glContext) return nullptr; diff --git a/gfx/gl/GLContextTypes.h b/gfx/gl/GLContextTypes.h index 979790bb1e5..fd298506e22 100644 --- a/gfx/gl/GLContextTypes.h +++ b/gfx/gl/GLContextTypes.h @@ -7,6 +7,7 @@ #define GLCONTEXT_TYPES_H_ #include "GLTypes.h" +#include "mozilla/TypedEnumBits.h" namespace mozilla { namespace gl { @@ -43,6 +44,22 @@ struct GLFormats GLsizei samples; }; +enum class CreateContextFlags : int8_t { + NONE = 0, + + REQUIRE_COMPAT_PROFILE = 1 << 0, + // Force the use of hardware backed GL, don't allow software implementations. + FORCE_ENABLE_HARDWARE = 1 << 1, + // Don't force discrete GPU to be used. (if applicable) + ALLOW_OFFLINE_RENDERER = 1 << 2, + + // Ensure that later we'll be able to attach a backbuffer with these properties: + SUPPORT_ALPHA = 1 << 3, + SUPPORT_DEPTH = 1 << 4, + SUPPORT_STENCIL = 1 << 5, +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CreateContextFlags) + } /* namespace gl */ } /* namespace mozilla */ diff --git a/gfx/gl/SharedSurfaceANGLE.cpp b/gfx/gl/SharedSurfaceANGLE.cpp index 9477f0cec4f..4bfd5aa43eb 100644 --- a/gfx/gl/SharedSurfaceANGLE.cpp +++ b/gfx/gl/SharedSurfaceANGLE.cpp @@ -281,137 +281,6 @@ SharedSurface_ANGLEShareHandle::ToSurfaceDescriptor(layers::SurfaceDescriptor* c //////////////////////////////////////////////////////////////////////////////// // Factory -static void -FillPBufferAttribs_ByBits(nsTArray& aAttrs, - int redBits, int greenBits, - int blueBits, int alphaBits, - int depthBits, int stencilBits) -{ - aAttrs.Clear(); - -#if defined(A1) || defined(A2) -#error The temp-macro names we want are already defined. -#endif - -#define A1(_x) do { aAttrs.AppendElement(_x); } while (0) -#define A2(_x,_y) do { A1(_x); A1(_y); } while (0) - - A2(LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT); - A2(LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_PBUFFER_BIT); - - A2(LOCAL_EGL_RED_SIZE, redBits); - A2(LOCAL_EGL_GREEN_SIZE, greenBits); - A2(LOCAL_EGL_BLUE_SIZE, blueBits); - A2(LOCAL_EGL_ALPHA_SIZE, alphaBits); - - A2(LOCAL_EGL_DEPTH_SIZE, depthBits); - A2(LOCAL_EGL_STENCIL_SIZE, stencilBits); - - A1(LOCAL_EGL_NONE); -#undef A1 -#undef A2 -} - -static void -FillPBufferAttribs_BySizes(nsTArray& attribs, - bool bpp16, bool hasAlpha, - int depthBits, int stencilBits) -{ - int red = 0; - int green = 0; - int blue = 0; - int alpha = 0; - - if (bpp16) { - if (hasAlpha) { - red = green = blue = alpha = 4; - } else { - red = 5; - green = 6; - blue = 5; - } - } else { - red = green = blue = 8; - if (hasAlpha) - alpha = 8; - } - - FillPBufferAttribs_ByBits(attribs, red, green, blue, alpha, depthBits, - stencilBits); -} - -static bool -DoesAttribBitsMatchCapBool(GLLibraryEGL* egl, EGLConfig config, EGLint attrib, - bool capBool) -{ - EGLint bits = 0; - egl->fGetConfigAttrib(egl->Display(), config, attrib, &bits); - MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS); - - bool hasBits = !!bits; - - return hasBits == capBool; -} - -static EGLConfig -ChooseConfig(GLContext* gl, GLLibraryEGL* egl, const SurfaceCaps& caps) -{ - MOZ_ASSERT(egl); - MOZ_ASSERT(caps.color); - - // We might want 24-bit depth, but we're only (fairly) sure to get 16-bit. - int depthBits = caps.depth ? 16 : 0; - int stencilBits = caps.stencil ? 8 : 0; - - // Ok, now we have everything. - nsTArray attribs(32); - FillPBufferAttribs_BySizes(attribs, caps.bpp16, caps.alpha, depthBits, - stencilBits); - - // Time to try to get this config: - EGLConfig configs[64]; - int numConfigs = sizeof(configs)/sizeof(EGLConfig); - int foundConfigs = 0; - - if (!egl->fChooseConfig(egl->Display(), attribs.Elements(), configs, - numConfigs, &foundConfigs) || - !foundConfigs) - { - NS_WARNING("No configs found for the requested formats."); - return EGL_NO_CONFIG; - } - - // The requests passed to ChooseConfig are treated as minimums. If you ask - // for 0 bits of alpha, we might still get 8 bits. - EGLConfig config = EGL_NO_CONFIG; - for (int i = 0; i < foundConfigs; i++) { - EGLConfig cur = configs[i]; - if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE, - caps.alpha) || - !DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE, - caps.depth) || - !DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_STENCIL_SIZE, - caps.stencil)) - { - continue; - } - - config = cur; - break; - } - - if (config == EGL_NO_CONFIG) { - NS_WARNING("No acceptable EGLConfig found."); - return EGL_NO_CONFIG; - } - - if (gl->DebugMode()) { - egl->DumpEGLConfig(config); - } - - return config; -} - /*static*/ UniquePtr SurfaceFactory_ANGLEShareHandle::Create(GLContext* gl, const SurfaceCaps& caps, const RefPtr& allocator, @@ -449,7 +318,7 @@ SurfaceFactory_ANGLEShareHandle::SurfaceFactory_ANGLEShareHandle(GLContext* gl, *out_success = false; mContext = GLContextEGL::Cast(mProdGL)->GetEGLContext(); - mConfig = ChooseConfig(mProdGL, mEGL, mReadCaps); + mConfig = GLContextEGL::Cast(mProdGL)->GetEGLConfig(); if (mConfig == EGL_NO_CONFIG) return;