diff --git a/dom/url/URL.cpp b/dom/url/URL.cpp index 5acaeb46fb..0681fd8ee6 100644 --- a/dom/url/URL.cpp +++ b/dom/url/URL.cpp @@ -1802,11 +1802,11 @@ URL::CanParse(const GlobalObject& aGlobal, const nsAString& aURL, return true; } -URLSearchParams* +already_AddRefed URL::SearchParams() { CreateSearchParamsIfNeeded(); - return mSearchParams; + return do_AddRef(mSearchParams); } bool IsChromeURI(nsIURI* aURI) diff --git a/dom/url/URL.h b/dom/url/URL.h index 14ca6fa2a1..8db4cd5154 100644 --- a/dom/url/URL.h +++ b/dom/url/URL.h @@ -149,7 +149,7 @@ public: virtual void SetSearch(const nsAString& aSearch, ErrorResult& aRv); - URLSearchParams* SearchParams(); + already_AddRefed SearchParams(); virtual void GetHash(nsAString& aHost, ErrorResult& aRv) const = 0; diff --git a/gfx/angle/src/libANGLE/State.cpp b/gfx/angle/src/libANGLE/State.cpp index 6c3f04dede..67746ca07a 100644 --- a/gfx/angle/src/libANGLE/State.cpp +++ b/gfx/angle/src/libANGLE/State.cpp @@ -940,7 +940,7 @@ bool State::removeReadFramebufferBinding(GLuint framebuffer) bool State::removeDrawFramebufferBinding(GLuint framebuffer) { - if (mReadFramebuffer != nullptr && + if (mDrawFramebuffer != nullptr && mDrawFramebuffer->id() == framebuffer) { setDrawFramebufferBinding(nullptr); diff --git a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp index 107a577e44..a32529c0e9 100644 --- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp +++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp @@ -711,7 +711,8 @@ gl::ErrorOrResult Buffer11::getConstantBufferRangeSto return a.second.lruCount < b.second.lruCount; }); - ASSERT(iter->second.storage != newStorage); + if (iter->second.storage == newStorage) + break; ASSERT(mConstantBufferStorageAdditionalSize >= iter->second.storage->getSize()); mConstantBufferStorageAdditionalSize -= iter->second.storage->getSize(); @@ -1140,8 +1141,15 @@ gl::ErrorOrResult Buffer11::EmulatedIndexedStorage::getNativeSto ANGLE_TRY_RESULT(attribute.computeOffset(startVertex), offset); // Expand the memory storage upon request and cache the results. - unsigned int expandedDataSize = - static_cast((indexInfo->srcCount * attribute.stride) + offset); + angle::CheckedNumeric checkedExpandedDataSize(indexInfo->srcCount); + checkedExpandedDataSize *= attribute.stride; + checkedExpandedDataSize += offset; + if (!checkedExpandedDataSize.IsValid()) { + return gl::Error( + GL_OUT_OF_MEMORY, + "Error resizing buffer in Buffer11::EmulatedIndexedStorage::getNativeStorage"); + } + unsigned int expandedDataSize = checkedExpandedDataSize.ValueOrDie(); MemoryBuffer expandedData; if (!expandedData.resize(expandedDataSize)) { diff --git a/gfx/cairo/cairo/src/cairo-pdf-surface.c b/gfx/cairo/cairo/src/cairo-pdf-surface.c index 3dcf58859d..d317d9feb4 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-surface.c +++ b/gfx/cairo/cairo/src/cairo-pdf-surface.c @@ -1743,7 +1743,8 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, unsigned long alpha_size; uint32_t *pixel32; uint8_t *pixel8; - int i, x, y; + int x, y; + unsigned long i; cairo_bool_t opaque; uint8_t a; @@ -1755,10 +1756,10 @@ _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, stream_ret->id = 0; if (image->format == CAIRO_FORMAT_A1) { - alpha_size = (image->width + 7) / 8 * image->height; + alpha_size = (unsigned long) ((image->width + 7) / 8) * image->height; alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height); } else { - alpha_size = image->height * image->width; + alpha_size = (unsigned long) image->height * image->width; alpha = _cairo_malloc_ab (image->height, image->width); } @@ -1841,7 +1842,8 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, char *rgb; unsigned long rgb_size; uint32_t *pixel; - int i, x, y; + int x, y; + unsigned long i; cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ cairo_bool_t need_smask; const char *interpolate = "true"; @@ -1856,7 +1858,7 @@ _cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, image->format == CAIRO_FORMAT_A8 || image->format == CAIRO_FORMAT_A1); - rgb_size = image->height * image->width * 3; + rgb_size = (unsigned long) image->height * image->width * 3; rgb = _cairo_malloc_abc (image->width, image->height, 3); if (unlikely (rgb == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); diff --git a/gfx/gl/GLContextProvider.h b/gfx/gl/GLContextProvider.h index 6e096c1f7e..0d16e33cde 100644 --- a/gfx/gl/GLContextProvider.h +++ b/gfx/gl/GLContextProvider.h @@ -11,6 +11,10 @@ #include "nsSize.h" // for gfx::IntSize (needed by GLContextProviderImpl.h below) +#if defined(MOZ_X11) +#include "GLContextProviderX11.h" +#endif + class nsIWidget; namespace mozilla { @@ -45,13 +49,15 @@ namespace gl { #define GL_CONTEXT_PROVIDER_NAME GLContextProviderGLX #include "GLContextProviderImpl.h" #undef GL_CONTEXT_PROVIDER_NAME - #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderGLX #endif #define GL_CONTEXT_PROVIDER_NAME GLContextProviderEGL #include "GLContextProviderImpl.h" #undef GL_CONTEXT_PROVIDER_NAME -#ifndef GL_CONTEXT_PROVIDER_DEFAULT + +#if defined(MOZ_X11) + #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderX11 +#elif !defined(GL_CONTEXT_PROVIDER_DEFAULT) #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderEGL #endif diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index ad515e6040..068146ac54 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -13,13 +13,23 @@ #if defined(XP_UNIX) - #define GLES2_LIB "libGLESv2.so" - #define GLES2_LIB2 "libGLESv2.so.2" + #if defined(MOZ_X11) + #if defined(__OpenBSD__) || defined(__NetBSD__) + #define EGL_OPENGL_LIB "libGL.so" + #define EGL_OPENGL_LIB2 "libGL.so.1" + #else + #define EGL_OPENGL_LIB "libGL.so.1" + #define EGL_OPENGL_LIB2 "libGL.so" + #endif + #else + #define EGL_OPENGL_LIB "libGLESv2.so" + #define EGL_OPENGL_LIB2 "libGLESv2.so.2" + #endif #elif defined(XP_WIN) #include "nsIFile.h" - #define GLES2_LIB "libGLESv2.dll" + #define EGL_OPENGL_LIB "libGLESv2.dll" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 @@ -102,6 +112,45 @@ using namespace mozilla::widget; static bool CreateConfig(EGLConfig* aConfig, nsIWidget* aWidget); +static bool +UseDesktopOpenGLForEGL() +{ +#ifdef MOZ_X11 + return true; +#else + return false; +#endif +} + +static EGLint +GetEGLRenderableType(CreateContextFlags flags) +{ + if (UseDesktopOpenGLForEGL()) { + return LOCAL_EGL_OPENGL_BIT; + } + + return bool(flags & CreateContextFlags::PREFER_ES3) + ? LOCAL_EGL_OPENGL_ES3_BIT_KHR + : LOCAL_EGL_OPENGL_ES2_BIT; +} + +static bool +BindContextAPI(nsACString* const out_failureId) +{ + const EGLenum api = UseDesktopOpenGLForEGL() + ? LOCAL_EGL_OPENGL_API + : LOCAL_EGL_OPENGL_ES_API; + if (sEGLLibrary.fBindAPI(api) == LOCAL_EGL_FALSE) { + *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_BIND_API"); + NS_WARNING(UseDesktopOpenGLForEGL() + ? "Failed to bind API to OpenGL!" + : "Failed to bind API to GLES!"); + return false; + } + + return true; +} + // 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. @@ -172,8 +221,12 @@ GLContextEGL::GLContextEGL(CreateContextFlags flags, const SurfaceCaps& caps, , mShareWithEGLImage(false) , mOwnsContext(true) { - // any EGL contexts will always be GLESv2 - SetProfileVersion(ContextProfile::OpenGLES, 200); + // X11 uses desktop OpenGL-over-EGL. Other EGL paths still use GLES/ANGLE. + if (UseDesktopOpenGLForEGL()) { + SetProfileVersion(ContextProfile::OpenGLCompatibility, 200); + } else { + SetProfileVersion(ContextProfile::OpenGLES, 200); + } #ifdef DEBUG printf_stderr("Initializing context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY()); @@ -202,10 +255,10 @@ GLContextEGL::~GLContextEGL() bool GLContextEGL::Init() { - if (!OpenLibrary(GLES2_LIB)) { + if (!OpenLibrary(EGL_OPENGL_LIB)) { #if defined(XP_UNIX) - if (!OpenLibrary(GLES2_LIB2)) { - NS_WARNING("Couldn't load GLES2 LIB."); + if (!OpenLibrary(EGL_OPENGL_LIB2)) { + NS_WARNING("Couldn't load EGL GL library."); return false; } #endif @@ -451,9 +504,7 @@ GLContextEGL::CreateGLContext(CreateContextFlags flags, 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!"); + if (!BindContextAPI(out_failureId)) { return nullptr; } @@ -462,11 +513,14 @@ GLContextEGL::CreateGLContext(CreateContextFlags flags, nsTArray contextAttribs; - contextAttribs.AppendElement(LOCAL_EGL_CONTEXT_CLIENT_VERSION); - if (flags & CreateContextFlags::PREFER_ES3) - contextAttribs.AppendElement(3); - else - contextAttribs.AppendElement(2); + if (!UseDesktopOpenGLForEGL()) { + 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); @@ -550,70 +604,52 @@ 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 void +AppendWindowConfigAttribs(int32_t depth, nsTArray* out) +{ + out->AppendElement(LOCAL_EGL_SURFACE_TYPE); + out->AppendElement(LOCAL_EGL_WINDOW_BIT); -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 -}; + out->AppendElement(LOCAL_EGL_RENDERABLE_TYPE); + out->AppendElement(GetEGLRenderableType(CreateContextFlags::NONE)); -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 -}; + out->AppendElement(LOCAL_EGL_RED_SIZE); + out->AppendElement(depth == 16 ? 5 : 8); -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 -}; + out->AppendElement(LOCAL_EGL_GREEN_SIZE); + out->AppendElement(depth == 16 ? 6 : 8); + + out->AppendElement(LOCAL_EGL_BLUE_SIZE); + out->AppendElement(depth == 16 ? 5 : 8); + + out->AppendElement(LOCAL_EGL_ALPHA_SIZE); + out->AppendElement(depth == 32 ? 8 : 0); + + for (size_t i = 0; i < MOZ_ARRAY_LENGTH(gTerminationAttribs); ++i) { + out->AppendElement(gTerminationAttribs[i]); + } +} static bool CreateConfig(EGLConfig* aConfig, int32_t depth, nsIWidget* aWidget) { EGLConfig configs[64]; - const EGLint* attribs; EGLint ncfg = ArrayLength(configs); + nsTArray attribs; 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, + AppendWindowConfigAttribs(depth, &attribs); + + if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs.Elements(), configs, ncfg, &ncfg) || ncfg < 1) { return false; @@ -727,17 +763,13 @@ GLContextProviderEGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated static void FillContextAttribs(bool alpha, bool depth, bool stencil, bool bpp16, - bool es3, nsTArray* out) + CreateContextFlags flags, nsTArray* 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(GetEGLRenderableType(flags)); out->AppendElement(LOCAL_EGL_RED_SIZE); if (bpp16) { @@ -797,7 +829,7 @@ ChooseConfig(GLLibraryEGL* egl, CreateContextFlags flags, const SurfaceCaps& min { nsTArray configAttribList; FillContextAttribs(minCaps.alpha, minCaps.depth, minCaps.stencil, minCaps.bpp16, - bool(flags & CreateContextFlags::PREFER_ES3), &configAttribList); + flags, &configAttribList); const EGLint* configAttribs = configAttribList.Elements(); diff --git a/gfx/gl/GLContextProviderX11.cpp b/gfx/gl/GLContextProviderX11.cpp new file mode 100644 index 0000000000..e54b303589 --- /dev/null +++ b/gfx/gl/GLContextProviderX11.cpp @@ -0,0 +1,262 @@ +/* -*- 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 "GLContextProviderX11.h" + +#include "GLContextProvider.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticMutex.h" +#include "nsDebug.h" +#include "nsString.h" +#include "plstr.h" +#include "prenv.h" + +namespace mozilla { +namespace gl { + +namespace { + +enum class X11ProviderKind : uint8_t { + Unresolved, + EGL, + GLX +}; + +enum class X11ProviderSelection : uint8_t { + Auto, + EGL, + GLX +}; + +StaticMutex sProviderMutex; +X11ProviderKind sProviderKind = X11ProviderKind::Unresolved; + +static X11ProviderSelection +ParseProviderSelection(const char* aValue) +{ + if (!aValue || !*aValue) { + return X11ProviderSelection::Auto; + } + + if (!PL_strcasecmp(aValue, "egl")) { + return X11ProviderSelection::EGL; + } + + if (!PL_strcasecmp(aValue, "glx")) { + return X11ProviderSelection::GLX; + } + + return X11ProviderSelection::Auto; +} + +static X11ProviderSelection +GetProviderSelection() +{ + const char* envValue = PR_GetEnv("MOZ_X11_GL_PROVIDER"); + if (envValue && *envValue) { + return ParseProviderSelection(envValue); + } + + nsAdoptingCString prefValue = + Preferences::GetCString("gfx.x11.gl-provider"); + if (prefValue && !prefValue.IsEmpty()) { + return ParseProviderSelection(prefValue.get()); + } + + return X11ProviderSelection::Auto; +} + +template +static already_AddRefed +CreateWithFallback(const char* aOperation, + EGLFn&& aCreateEGL, + GLXFn&& aCreateGLX) +{ + RefPtr context; + X11ProviderSelection selection = GetProviderSelection(); + + { + StaticMutexAutoLock lock(sProviderMutex); + if (selection == X11ProviderSelection::GLX || + sProviderKind == X11ProviderKind::GLX) + { + sProviderKind = X11ProviderKind::GLX; + context = aCreateGLX(); + return context.forget(); + } + } + + context = aCreateEGL(); + if (context) { + StaticMutexAutoLock lock(sProviderMutex); + sProviderKind = X11ProviderKind::EGL; + return context.forget(); + } + + if (selection == X11ProviderSelection::EGL) { + printf_stderr("GLContextProviderX11: EGL %s failed with provider forced to EGL.\n", + aOperation); + return nullptr; + } + + printf_stderr("GLContextProviderX11: EGL %s failed, falling back to GLX.\n", + aOperation); + + { + StaticMutexAutoLock lock(sProviderMutex); + sProviderKind = X11ProviderKind::GLX; + } + + context = aCreateGLX(); + return context.forget(); +} + +} // anonymous namespace + +already_AddRefed +GLContextProviderX11::CreateForCompositorWidget(widget::CompositorWidget* aCompositorWidget, + bool aForceAccelerated) +{ + return CreateWithFallback( + "CreateForCompositorWidget", + [&]() { + return RefPtr( + GLContextProviderEGL::CreateForCompositorWidget(aCompositorWidget, + aForceAccelerated)); + }, + [&]() { + return RefPtr( + GLContextProviderGLX::CreateForCompositorWidget(aCompositorWidget, + aForceAccelerated)); + }); +} + +already_AddRefed +GLContextProviderX11::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated) +{ + return CreateWithFallback( + "CreateForWindow", + [&]() { + return RefPtr( + GLContextProviderEGL::CreateForWindow(aWidget, aForceAccelerated)); + }, + [&]() { + return RefPtr( + GLContextProviderGLX::CreateForWindow(aWidget, aForceAccelerated)); + }); +} + +already_AddRefed +GLContextProviderX11::CreateOffscreen(const gfx::IntSize& aSize, + const SurfaceCaps& aMinCaps, + CreateContextFlags aFlags, + nsACString* const aOutFailureId) +{ + return CreateWithFallback( + "CreateOffscreen", + [&]() { + nsAutoCString eglFailureId; + RefPtr context = + GLContextProviderEGL::CreateOffscreen(aSize, aMinCaps, aFlags, + &eglFailureId); + if (!context && aOutFailureId) { + aOutFailureId->Assign(eglFailureId); + } + return context; + }, + [&]() { + return RefPtr( + GLContextProviderGLX::CreateOffscreen(aSize, aMinCaps, aFlags, + aOutFailureId)); + }); +} + +already_AddRefed +GLContextProviderX11::CreateHeadless(CreateContextFlags aFlags, + nsACString* const aOutFailureId) +{ + return CreateWithFallback( + "CreateHeadless", + [&]() { + nsAutoCString eglFailureId; + RefPtr context = + GLContextProviderEGL::CreateHeadless(aFlags, &eglFailureId); + if (!context && aOutFailureId) { + aOutFailureId->Assign(eglFailureId); + } + return context; + }, + [&]() { + return RefPtr( + GLContextProviderGLX::CreateHeadless(aFlags, aOutFailureId)); + }); +} + +already_AddRefed +GLContextProviderX11::CreateWrappingExisting(void* aContext, void* aSurface) +{ + return CreateWithFallback( + "CreateWrappingExisting", + [&]() { + return RefPtr( + GLContextProviderEGL::CreateWrappingExisting(aContext, aSurface)); + }, + [&]() { + return RefPtr( + GLContextProviderGLX::CreateWrappingExisting(aContext, aSurface)); + }); +} + +GLContext* +GLContextProviderX11::GetGlobalContext() +{ + X11ProviderSelection selection = GetProviderSelection(); + + { + StaticMutexAutoLock lock(sProviderMutex); + if (selection == X11ProviderSelection::GLX || + sProviderKind == X11ProviderKind::GLX) + { + sProviderKind = X11ProviderKind::GLX; + return GLContextProviderGLX::GetGlobalContext(); + } + } + + GLContext* context = GLContextProviderEGL::GetGlobalContext(); + if (context) { + StaticMutexAutoLock lock(sProviderMutex); + sProviderKind = X11ProviderKind::EGL; + return context; + } + + if (selection == X11ProviderSelection::EGL) { + printf_stderr("GLContextProviderX11: EGL GetGlobalContext failed with provider forced to EGL.\n"); + return nullptr; + } + + printf_stderr("GLContextProviderX11: EGL GetGlobalContext failed, falling back to GLX.\n"); + + { + StaticMutexAutoLock lock(sProviderMutex); + sProviderKind = X11ProviderKind::GLX; + } + + return GLContextProviderGLX::GetGlobalContext(); +} + +void +GLContextProviderX11::Shutdown() +{ + { + StaticMutexAutoLock lock(sProviderMutex); + sProviderKind = X11ProviderKind::Unresolved; + } + + GLContextProviderEGL::Shutdown(); + GLContextProviderGLX::Shutdown(); +} + +} // namespace gl +} // namespace mozilla diff --git a/gfx/gl/GLContextProviderX11.h b/gfx/gl/GLContextProviderX11.h new file mode 100644 index 0000000000..c2cb848cd4 --- /dev/null +++ b/gfx/gl/GLContextProviderX11.h @@ -0,0 +1,54 @@ +/* -*- 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/. */ + +#ifndef GLCONTEXTPROVIDER_X11_H_ +#define GLCONTEXTPROVIDER_X11_H_ + +#include "GLContextTypes.h" +#include "SurfaceTypes.h" +#include "nsSize.h" + +class nsIWidget; + +namespace mozilla { +namespace widget { +class CompositorWidget; +} +namespace gl { + +class GLContextProviderX11 +{ +public: + static already_AddRefed + CreateForCompositorWidget(widget::CompositorWidget* aCompositorWidget, + bool aForceAccelerated); + + static already_AddRefed + CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated); + + static already_AddRefed + CreateOffscreen(const gfx::IntSize& aSize, + const SurfaceCaps& aMinCaps, + CreateContextFlags aFlags, + nsACString* const aOutFailureId); + + static already_AddRefed + CreateHeadless(CreateContextFlags aFlags, + nsACString* const aOutFailureId); + + static already_AddRefed + CreateWrappingExisting(void* aContext, void* aSurface); + + static GLContext* + GetGlobalContext(); + + static void + Shutdown(); +}; + +} // namespace gl +} // namespace mozilla + +#endif /* GLCONTEXTPROVIDER_X11_H_ */ diff --git a/gfx/gl/GLLibraryEGL.cpp b/gfx/gl/GLLibraryEGL.cpp index 5b95d83b2b..80ff5feaf4 100644 --- a/gfx/gl/GLLibraryEGL.cpp +++ b/gfx/gl/GLLibraryEGL.cpp @@ -312,7 +312,6 @@ GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString* const out_failureId // libEGL.so and libEGL.so.1 in that order. if (!mEGLLibrary) { - printf_stderr("Attempting load of libEGL.so\n"); mEGLLibrary = PR_LoadLibrary("libEGL.so"); } #if defined(XP_UNIX) @@ -705,4 +704,3 @@ GLLibraryEGL::AfterGLCall(const char* glFunction) } /* namespace gl */ } /* namespace mozilla */ - diff --git a/gfx/gl/moz.build b/gfx/gl/moz.build index f114559d76..70155fb0f5 100644 --- a/gfx/gl/moz.build +++ b/gfx/gl/moz.build @@ -53,8 +53,19 @@ EXPORTS += [ if CONFIG['MOZ_X11']: EXPORTS += [ 'GLContextGLX.h', + 'GLContextProviderX11.h', 'GLXLibrary.h', ] + # GLContextProviderX11.cpp falls back to GLX, so keep the GLX provider + # available on all X11 builds even when EGL is the preferred provider. + # GLContextProviderGLX.cpp must stay out of UNIFIED_SOURCES because it + # includes X11 headers that cause conflicts there. + SOURCES += [ + 'GLContextProviderGLX.cpp', + ] + UNIFIED_SOURCES += [ + 'GLContextProviderX11.cpp', + ] # Win32 is a special snowflake, for ANGLE if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': @@ -103,11 +114,8 @@ elif gl_provider == 'EAGL': 'GLContextEAGL.h', ] -elif gl_provider == 'GLX': - # GLContextProviderGLX.cpp needs to be kept out of UNIFIED_SOURCES - # as it includes X11 headers which cause conflicts. +if CONFIG['MOZ_X11'] and gl_provider == 'GLX': SOURCES += [ - 'GLContextProviderGLX.cpp', 'SharedSurfaceGLX.cpp' ] EXPORTS += [ diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index a07fb749a9..8ecf6d2fdf 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -7242,7 +7242,7 @@ nsTextFrame::GetCharacterOffsetAtFramePointInternal(nsPoint aPoint, gfxFontUtils::IsRegionalIndicator (SURROGATE_TO_UCS4(frag->CharAt(offs), frag->CharAt(offs + 1)))) { allowSplitLigature = false; - if (extraCluster.GetSkippedOffset() > 1 && + if (extraCluster.GetSkippedOffset() >= skippedRange.start + 2 && !mTextRun->IsLigatureGroupStart(extraCluster.GetSkippedOffset())) { // CountCharsFit() left us in the middle of the flag; back up over the // first character of the ligature, and adjust fitWidth accordingly. diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 9536425b46..52a821ea42 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4458,6 +4458,10 @@ pref("layers.acceleration.enabled", true); // This requires layers.acceleration.enabled to be set to true pref("layers.acceleration.force", false); +// Preferred X11 GL context provider. "auto" prefers EGL and falls back to GLX, +// "egl" forces EGL, and "glx" forces the legacy GLX path. Restart required. +pref("gfx.x11.gl-provider", "auto"); + // Preference that when switched at runtime will run a series of benchmarks // and output the result to stderr. pref("layers.bench.enabled", false); diff --git a/toolkit/xre/glxtest.cpp b/toolkit/xre/glxtest.cpp index 714b039e50..6024bf03c8 100644 --- a/toolkit/xre/glxtest.cpp +++ b/toolkit/xre/glxtest.cpp @@ -13,14 +13,17 @@ // This file implements the idea to do that in a separate process. // // The only non-static function here is fire_glxtest_process(). It creates a pipe, publishes its 'read' end as the -// mozilla::widget::glxtest_pipe global variable, forks, and runs that GLX probe in the child process, -// which runs the glxtest() static function. This creates a X connection, a GLX context, calls glGetString, and writes that +// mozilla::widget::glxtest_pipe global variable, forks, and runs that X11 GL probe in the child process, +// which runs the glxtest() static function. This creates a X connection, a GL context, calls glGetString, and writes that // to the 'write' end of the pipe. #include #include +#include +#include #include #include +#include #include "nscore.h" #include #include "stdint.h" @@ -36,8 +39,18 @@ #include "X11/Xlib.h" #include "X11/Xutil.h" +#include "EGL/egl.h" + #include "mozilla/Unused.h" +#if defined(__OpenBSD__) || defined(__NetBSD__) +# define LIBGL_FILENAME "libGL.so" +# define LIBGL_FILENAME_FALLBACK "libGL.so.1" +#else +# define LIBGL_FILENAME "libGL.so.1" +# define LIBGL_FILENAME_FALLBACK "libGL.so" +#endif + // stuff from glx.h typedef struct __GLXcontextRec *GLXContext; typedef XID GLXPixmap; @@ -71,6 +84,7 @@ extern pid_t glxtest_pid; // the write end of the pipe, which we're going to write to static int write_end_of_the_pipe = -1; +static std::string sAutoFallbackMessage; #if MOZ_WIDGET_GTK == 2 static int gtk_write_end_of_the_pipe = -1; @@ -88,28 +102,300 @@ static func_ptr_type cast(void *ptr) ); } +static void +write_to_pipe(const char* str, size_t len) +{ + if (str && len) { + mozilla::Unused << write(write_end_of_the_pipe, str, len); + } +} + +static void +write_to_pipe(const std::string& str) +{ + write_to_pipe(str.c_str(), str.size()); +} + static void fatal_error(const char *str) { - mozilla::Unused << write(write_end_of_the_pipe, str, strlen(str)); - mozilla::Unused << write(write_end_of_the_pipe, "\n", 1); + if (!sAutoFallbackMessage.empty()) { + write_to_pipe(sAutoFallbackMessage); + } + write_to_pipe(str, strlen(str)); + write_to_pipe("\n", 1); _exit(EXIT_FAILURE); } +enum class ProbeMode { + Auto, + EGL, + GLX +}; + +static ProbeMode +get_probe_mode() +{ + const char* envValue = getenv("MOZ_X11_GL_PROVIDER"); + if (!envValue || !*envValue) { + return ProbeMode::Auto; + } + + if (!strcasecmp(envValue, "egl")) { + return ProbeMode::EGL; + } + + if (!strcasecmp(envValue, "glx")) { + return ProbeMode::GLX; + } + + return ProbeMode::Auto; +} + +static bool +load_library(const char* aPrimary, + const char* aSecondary, + void** aOutLibrary, + std::string* aError) +{ + *aOutLibrary = dlopen(aPrimary, RTLD_LAZY); + if (!*aOutLibrary && aSecondary) { + *aOutLibrary = dlopen(aSecondary, RTLD_LAZY); + } + + if (*aOutLibrary) { + return true; + } + + *aError = std::string("Unable to load ") + aPrimary; + if (aSecondary) { + *aError += " or "; + *aError += aSecondary; + } + return false; +} + static int x_error_handler(Display *, XErrorEvent *ev) { enum { bufsize = 1024 }; char buf[bufsize]; int length = snprintf(buf, bufsize, - "X error occurred in GLX probe, error_code=%d, request_code=%d, minor_code=%d\n", + "X error occurred in X11 GL probe, error_code=%d, request_code=%d, minor_code=%d\n", ev->error_code, ev->request_code, ev->minor_code); - mozilla::Unused << write(write_end_of_the_pipe, buf, length); + if (!sAutoFallbackMessage.empty()) { + write_to_pipe(sAutoFallbackMessage); + } + write_to_pipe(buf, length); _exit(EXIT_FAILURE); return 0; } +static bool +try_egltest(std::string* aError) +{ + static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_NONE + }; + static const EGLint surfaceAttribs[] = { + EGL_WIDTH, 16, + EGL_HEIGHT, 16, + EGL_NONE + }; + static const EGLint contextAttribs[] = { + EGL_NONE + }; + + void* libegl = nullptr; + if (!load_library("libEGL.so", "libEGL.so.1", &libegl, aError)) { + return false; + } + + void* libgl = nullptr; + if (!load_library(LIBGL_FILENAME, LIBGL_FILENAME_FALLBACK, &libgl, aError)) { + dlclose(libegl); + return false; + } + + typedef EGLDisplay (* PFNEGLGETDISPLAY)(EGLNativeDisplayType); + typedef EGLBoolean (* PFNEGLINITIALIZE)(EGLDisplay, EGLint*, EGLint*); + typedef EGLBoolean (* PFNEGLBINDAPI)(EGLenum); + typedef EGLBoolean (* PFNEGLCHOOSECONFIG)(EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*); + typedef EGLContext (* PFNEGLCREATECONTEXT)(EGLDisplay, EGLConfig, EGLContext, const EGLint*); + typedef EGLSurface (* PFNEGLCREATEPBUFFERSURFACE)(EGLDisplay, EGLConfig, const EGLint*); + typedef EGLBoolean (* PFNEGLMAKECURRENT)(EGLDisplay, EGLSurface, EGLSurface, EGLContext); + typedef EGLBoolean (* PFNEGLDESTROYCONTEXT)(EGLDisplay, EGLContext); + typedef EGLBoolean (* PFNEGLDESTROYSURFACE)(EGLDisplay, EGLSurface); + typedef EGLBoolean (* PFNEGLTERMINATE)(EGLDisplay); + typedef EGLint (* PFNEGLGETERROR)(void); + typedef const GLubyte* (* PFNGLGETSTRING)(GLenum); + + PFNEGLGETDISPLAY eglGetDisplay = cast(dlsym(libegl, "eglGetDisplay")); + PFNEGLINITIALIZE eglInitialize = cast(dlsym(libegl, "eglInitialize")); + PFNEGLBINDAPI eglBindAPI = cast(dlsym(libegl, "eglBindAPI")); + PFNEGLCHOOSECONFIG eglChooseConfig = cast(dlsym(libegl, "eglChooseConfig")); + PFNEGLCREATECONTEXT eglCreateContext = cast(dlsym(libegl, "eglCreateContext")); + PFNEGLCREATEPBUFFERSURFACE eglCreatePbufferSurface = + cast(dlsym(libegl, "eglCreatePbufferSurface")); + PFNEGLMAKECURRENT eglMakeCurrent = cast(dlsym(libegl, "eglMakeCurrent")); + PFNEGLDESTROYCONTEXT eglDestroyContext = cast(dlsym(libegl, "eglDestroyContext")); + PFNEGLDESTROYSURFACE eglDestroySurface = cast(dlsym(libegl, "eglDestroySurface")); + PFNEGLTERMINATE eglTerminate = cast(dlsym(libegl, "eglTerminate")); + PFNEGLGETERROR eglGetError = cast(dlsym(libegl, "eglGetError")); + PFNGLGETSTRING glGetString = cast(dlsym(libgl, "glGetString")); + + if (!eglGetDisplay || + !eglInitialize || + !eglBindAPI || + !eglChooseConfig || + !eglCreateContext || + !eglCreatePbufferSurface || + !eglMakeCurrent || + !eglDestroyContext || + !eglDestroySurface || + !eglTerminate || + !eglGetError || + !glGetString) + { + *aError = "EGL probe couldn't find required EGL/OpenGL symbols"; + dlclose(libgl); + dlclose(libegl); + return false; + } + + Display* dpy = XOpenDisplay(nullptr); + if (!dpy) { + *aError = "Unable to open a connection to the X server"; + dlclose(libgl); + dlclose(libegl); + return false; + } + + EGLDisplay eglDisplay = EGL_NO_DISPLAY; + EGLSurface surface = EGL_NO_SURFACE; + EGLContext context = EGL_NO_CONTEXT; + EGLConfig config = nullptr; + EGLint major = 0; + EGLint minor = 0; + EGLint configCount = 0; + const GLubyte* vendorString = nullptr; + const GLubyte* rendererString = nullptr; + const GLubyte* versionString = nullptr; + enum { bufsize = 1024 }; + char buf[bufsize]; + int length = 0; + + eglDisplay = eglGetDisplay((EGLNativeDisplayType)dpy); + if (eglDisplay == EGL_NO_DISPLAY) { + *aError = "EGL probe could not get an EGL display"; + goto egltest_error; + } + + if (!eglInitialize(eglDisplay, &major, &minor)) { + *aError = "EGL probe could not initialize EGL"; + goto egltest_error; + } + + if (!eglBindAPI(EGL_OPENGL_API)) { + *aError = "EGL probe could not bind the OpenGL API"; + goto egltest_error; + } + + if (!eglChooseConfig(eglDisplay, configAttribs, &config, 1, &configCount) || + configCount < 1) + { + *aError = "EGL probe could not find a suitable EGLConfig"; + goto egltest_error; + } + + surface = eglCreatePbufferSurface(eglDisplay, config, surfaceAttribs); + if (surface == EGL_NO_SURFACE) { + *aError = "EGL probe could not create a pbuffer surface"; + goto egltest_error; + } + + context = eglCreateContext(eglDisplay, config, EGL_NO_CONTEXT, contextAttribs); + if (context == EGL_NO_CONTEXT) { + eglDestroySurface(eglDisplay, surface); + *aError = "EGL probe could not create an EGL context"; + goto egltest_error; + } + + if (!eglMakeCurrent(eglDisplay, surface, surface, context)) { + eglDestroyContext(eglDisplay, context); + eglDestroySurface(eglDisplay, surface); + *aError = "EGL probe could not make the EGL context current"; + goto egltest_error; + } + + vendorString = glGetString(GL_VENDOR); + rendererString = glGetString(GL_RENDERER); + versionString = glGetString(GL_VERSION); + + if (!vendorString || !rendererString || !versionString) { + eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(eglDisplay, context); + eglDestroySurface(eglDisplay, surface); + *aError = "EGL probe glGetString returned null"; + goto egltest_error; + } + + length = snprintf(buf, bufsize, + "PROVIDER\nEGL\nVENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\nFALSE\n", + vendorString, + rendererString, + versionString); + if (length >= bufsize) { + eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(eglDisplay, context); + eglDestroySurface(eglDisplay, surface); + *aError = "EGL probe GL strings length too large for buffer size"; + goto egltest_error; + } + + eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(eglDisplay, context); + eglDestroySurface(eglDisplay, surface); + eglTerminate(eglDisplay); + +#ifdef NS_FREE_PERMANENT_DATA + XCloseDisplay(dpy); +#else + XSync(dpy, False); +#endif + + dlclose(libgl); + dlclose(libegl); + + write_to_pipe(buf, length); + return true; + +egltest_error: + if (!aError->empty() && eglGetError) { + char eglErrorBuf[128]; + int length = snprintf(eglErrorBuf, sizeof(eglErrorBuf), + " (EGL error 0x%04x)", eglGetError()); + if (length > 0 && size_t(length) < sizeof(eglErrorBuf)) { + aError->append(eglErrorBuf); + } + } +#ifdef NS_FREE_PERMANENT_DATA + XCloseDisplay(dpy); +#else + XSync(dpy, False); +#endif + dlclose(libgl); + dlclose(libegl); + return false; +} + // glxtest is declared inside extern "C" so that the name is not mangled. // The name is used in build/valgrind/x86_64-redhat-linux-gnu.sup to suppress @@ -160,15 +446,25 @@ void glxtest() if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) fatal_error("The MOZ_AVOID_OPENGL_ALTOGETHER environment variable is defined"); + ProbeMode probeMode = get_probe_mode(); + if (probeMode != ProbeMode::GLX) { + std::string eglError; + if (try_egltest(&eglError)) { + return; + } + + if (probeMode == ProbeMode::EGL) { + fatal_error(eglError.c_str()); + } + + sAutoFallbackMessage = "EGL probe failed, falling back to GLX: " + eglError + "\n"; + } + ///// Open libGL and load needed symbols ///// -#if defined(__OpenBSD__) || defined(__NetBSD__) - #define LIBGL_FILENAME "libGL.so" -#else - #define LIBGL_FILENAME "libGL.so.1" -#endif - void *libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY); - if (!libgl) - fatal_error("Unable to load " LIBGL_FILENAME); + void* libgl = nullptr; + std::string libglError; + if (!load_library(LIBGL_FILENAME, LIBGL_FILENAME_FALLBACK, &libgl, &libglError)) + fatal_error(libglError.c_str()); typedef void* (* PFNGLXGETPROCADDRESS) (const char *); PFNGLXGETPROCADDRESS glXGetProcAddress = cast(dlsym(libgl, "glXGetProcAddress")); @@ -260,7 +556,7 @@ void glxtest() fatal_error("glGetString returned null"); int length = snprintf(buf, bufsize, - "VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n", + "PROVIDER\nGLX\nVENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n", vendorString, rendererString, versionString, diff --git a/toolkit/xre/moz.build b/toolkit/xre/moz.build index 216d866317..9523bf8ab0 100644 --- a/toolkit/xre/moz.build +++ b/toolkit/xre/moz.build @@ -43,7 +43,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3': if CONFIG['MOZ_X11']: EXPORTS += ['nsX11ErrorHandler.h'] - UNIFIED_SOURCES += ['nsX11ErrorHandler.cpp'] + UNIFIED_SOURCES += [ + 'glxtest.cpp', + 'nsX11ErrorHandler.cpp', + ] UNIFIED_SOURCES += [ 'CreateAppData.cpp', @@ -62,11 +65,6 @@ SOURCES += [ 'ProfileReset.cpp', ] -if CONFIG['MOZ_GL_DEFAULT_PROVIDER'] == 'GLX': - UNIFIED_SOURCES += [ - 'glxtest.cpp', - ] - if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']: UNIFIED_SOURCES += [ 'EventTracer.cpp', @@ -82,7 +80,7 @@ include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' -if CONFIG['MOZ_GL_DEFAULT_PROVIDER'] == 'GLX': +if CONFIG['MOZ_X11']: DEFINES['USE_GLX_TEST'] = True for var in ('MOZ_APP_NAME', @@ -132,6 +130,11 @@ if CONFIG['MOZ_ENABLE_XREMOTE']: '/widget/xremoteclient', ] +if CONFIG['MOZ_X11']: + LOCAL_INCLUDES += [ + '/gfx/angle/include', + ] + CXXFLAGS += CONFIG['TK_CFLAGS'] CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS'] diff --git a/widget/GfxInfoX11.cpp b/widget/GfxInfoX11.cpp index f87fc06e18..8501871843 100644 --- a/widget/GfxInfoX11.cpp +++ b/widget/GfxInfoX11.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include "nsCRTGlue.h" @@ -27,6 +28,7 @@ pid_t glxtest_pid = 0; nsresult GfxInfo::Init() { + mGLImplementation.Truncate(); mGLMajorVersion = 0; mMajorVersion = 0; mMinorVersion = 0; @@ -111,6 +113,8 @@ GfxInfo::GetData() stringToFill->Assign(line); stringToFill = nullptr; } + else if(!strcmp(line, "PROVIDER")) + stringToFill = &mGLImplementation; else if(!strcmp(line, "VENDOR")) stringToFill = &mVendor; else if(!strcmp(line, "RENDERER")) @@ -125,6 +129,10 @@ GfxInfo::GetData() if (!strcmp(textureFromPixmap.get(), "TRUE")) mHasTextureFromPixmap = true; + if (mGLImplementation.IsEmpty()) { + mGLImplementation.AssignLiteral("GLX"); + } + // only useful for Linux kernel version check for FGLRX driver. // assumes X client == X server, which is sad. struct utsname unameobj; @@ -151,13 +159,14 @@ GfxInfo::GetData() mOSRelease.Assign(spoofedOSRelease); if (error || + mGLImplementation.IsEmpty() || mVendor.IsEmpty() || mRenderer.IsEmpty() || mVersion.IsEmpty() || mOS.IsEmpty() || mOSRelease.IsEmpty()) { - mAdapterDescription.AppendLiteral("GLXtest process failed"); + mAdapterDescription.AppendLiteral("X11 GL probe failed"); if (waiting_for_glxtest_process_failed) mAdapterDescription.AppendPrintf(" (waitpid failed with errno=%d for pid %d)", waitpid_errno, glxtest_pid); if (exited_with_error_code) @@ -175,9 +184,14 @@ GfxInfo::GetData() mAdapterDescription.Append(mVendor); mAdapterDescription.AppendLiteral(" -- "); mAdapterDescription.Append(mRenderer); + mAdapterDescription.AppendLiteral(" ("); + mAdapterDescription.Append(mGLImplementation); + mAdapterDescription.Append(')'); nsAutoCString note; - note.AppendLiteral("OpenGL: "); + note.AppendLiteral("OpenGL ("); + note.Append(mGLImplementation); + note.AppendLiteral("): "); note.Append(mAdapterDescription); note.AppendLiteral(" -- "); note.Append(mVersion); @@ -194,8 +208,13 @@ GfxInfo::GetData() #define strcasestr PL_strcasestr #endif - // determine the major OpenGL version. That's the first integer in the version string. - mGLMajorVersion = strtol(mVersion.get(), 0, 10); + // Determine the major GL version from the first digit sequence in the + // version string. EGL/GLES strings commonly begin with "OpenGL ES ". + const char* versionString = mVersion.get(); + while (*versionString && !isdigit(*versionString)) { + versionString++; + } + mGLMajorVersion = strtol(versionString, nullptr, 10); // determine driver type (vendor) and where in the version string // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers) diff --git a/widget/GfxInfoX11.h b/widget/GfxInfoX11.h index 63447c9169..3a6854ea6f 100644 --- a/widget/GfxInfoX11.h +++ b/widget/GfxInfoX11.h @@ -64,6 +64,7 @@ protected: virtual const nsTArray& GetGfxDriverInfo() override; private: + nsCString mGLImplementation; nsCString mVendor; nsCString mRenderer; nsCString mVersion;