Bug 1276732 - Report the GLContext error to WebGL for telemetry. r=jrmuizel draft
authorBenoit Girard <b56girard@gmail.com>
Mon, 06 Jun 2016 16:52:42 -0400
changeset 375875 391e44f4d4fdcf5782053fc6c3e9b17e651a9a1f
parent 375744 d98f20c25feeac4dd7ebbd1c022957df1ef58af4
child 522995 c9e6965fad2e540601ac089752faa987f6a4db95
push id20409
push userb56girard@gmail.com
push dateMon, 06 Jun 2016 20:52:53 +0000
reviewersjrmuizel
bugs1276732
milestone49.0a1
Bug 1276732 - Report the GLContext error to WebGL for telemetry. r=jrmuizel MozReview-Commit-ID: 24eb4FXMOiI
dom/canvas/WebGLContext.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
gfx/gl/GLContextEGL.h
gfx/gl/GLContextProviderCGL.mm
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/gl/GLContextProviderImpl.h
gfx/gl/GLContextProviderNull.cpp
gfx/gl/GLContextProviderWGL.cpp
gfx/gl/GLLibraryEGL.cpp
gfx/gl/GLLibraryEGL.h
gfx/layers/GLImages.cpp
gfx/layers/opengl/CompositorOGL.cpp
gfx/tests/gtest/TestCompositor.cpp
gfx/thebes/gfxPlatform.cpp
widget/android/GfxInfo.cpp
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -582,17 +582,17 @@ BaseCaps(const WebGLContextOptions& opti
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                 WebGLContext* webgl, nsACString* const out_failReason,
                 nsACString* const out_failureId)
 {
     const gfx::IntSize dummySize(16, 16);
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
-                                                                     flags);
+                                                                     flags, *out_failureId);
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         if (out_failReason->Length()) {
             out_failReason->AppendLiteral("\n");
         }
@@ -606,17 +606,17 @@ CreateGLWithEGL(const gl::SurfaceCaps& c
 
 static already_AddRefed<GLContext>
 CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                   WebGLContext* webgl, nsACString* const out_failReason,
                   nsACString* const out_failureId)
 {
     const gfx::IntSize dummySize(16, 16);
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
-                                                                     flags);
+                                                                     flags, *out_failureId);
     if (gl && !gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         if (out_failReason->Length()) {
             out_failReason->AppendLiteral("\n");
         }
@@ -642,17 +642,18 @@ CreateGLWithDefault(const gl::SurfaceCap
             out_failReason->AppendASCII("\n");
         }
         out_failReason->AppendASCII("Refused to create native OpenGL context because of"
                                     " blacklisting.");
         return nullptr;
     }
 
     const gfx::IntSize dummySize(16, 16);
-    RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps, flags);
+    RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps,
+                                                                  flags, *out_failureId);
 
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         if (out_failReason->Length()) {
             out_failReason->AppendASCII("\n");
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -87,17 +87,18 @@ private:
   bool mCanceled;
 };
 
 static RefPtr<GLContext> sPluginContext = nullptr;
 
 static bool EnsureGLContext()
 {
   if (!sPluginContext) {
-    sPluginContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE);
+    nsCString failureId;
+    sPluginContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE, failureId);
   }
 
   return sPluginContext != nullptr;
 }
 
 static std::map<NPP, nsNPAPIPluginInstance*> sPluginNPPMap;
 
 #endif
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -18,17 +18,18 @@ class GLContextEGL : public GLContext
     friend class TextureImageEGL;
 
     static already_AddRefed<GLContextEGL>
     CreateGLContext(CreateContextFlags flags,
                     const SurfaceCaps& caps,
                     GLContextEGL *shareContext,
                     bool isOffscreen,
                     EGLConfig config,
-                    EGLSurface surface);
+                    EGLSurface surface,
+                    nsACString& aFailureId);
 
 public:
     MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextEGL, override)
     GLContextEGL(const SurfaceCaps& caps,
                  GLContext* shareContext,
                  bool isOffscreen,
                  EGLConfig config,
                  EGLSurface surface,
@@ -101,17 +102,18 @@ public:
 
     bool BindTex2DOffscreen(GLContext *aOffscreen);
     void UnbindTex2DOffscreen(GLContext *aOffscreen);
     void BindOffscreenFramebuffer();
 
     static already_AddRefed<GLContextEGL>
     CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
                                      const gfx::IntSize& size,
-                                     const SurfaceCaps& minCaps);
+                                     const SurfaceCaps& minCaps,
+                                     nsACString& aFailureId);
 
 protected:
     friend class GLContextProviderEGL;
 
 public:
     const EGLConfig  mConfig;
 protected:
     EGLSurface mSurface;
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -316,57 +316,66 @@ CreateOffscreenFBOContext(CreateContextF
 
     if (gfxPrefs::GLMultithreaded()) {
         CGLEnable(glContext->GetCGLContext(), kCGLCEMPEngine);
     }
     return glContext.forget();
 }
 
 already_AddRefed<GLContext>
-GLContextProviderCGL::CreateHeadless(CreateContextFlags flags)
+GLContextProviderCGL::CreateHeadless(CreateContextFlags flags, nsACString& aFailureId)
 {
     RefPtr<GLContextCGL> gl;
     gl = CreateOffscreenFBOContext(flags);
-    if (!gl)
+    if (!gl) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_FBO");
         return nullptr;
+    }
 
     if (!gl->Init()) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_INIT");
         NS_WARNING("Failed during Init.");
         return nullptr;
     }
 
     return gl.forget();
 }
 
 already_AddRefed<GLContext>
 GLContextProviderCGL::CreateOffscreen(const IntSize& size,
                                       const SurfaceCaps& minCaps,
-                                      CreateContextFlags flags)
+                                      CreateContextFlags flags,
+                                      nsACString& aFailureId)
 {
-    RefPtr<GLContext> gl = CreateHeadless(flags);
-    if (!gl)
+    RefPtr<GLContext> gl = CreateHeadless(flags, aFailureId);
+    if (!gl) {
         return nullptr;
+    }
 
-    if (!gl->InitOffscreen(size, minCaps))
+    if (!gl->InitOffscreen(size, minCaps)) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_INIT");
         return nullptr;
+    }
 
     return gl.forget();
 }
 
 static RefPtr<GLContext> gGlobalContext;
 
 GLContext*
 GLContextProviderCGL::GetGlobalContext()
 {
     static bool triedToCreateContext = false;
     if (!triedToCreateContext) {
         triedToCreateContext = true;
 
         MOZ_RELEASE_ASSERT(!gGlobalContext);
-        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE);
+        nsCString discardFailureId;
+        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE,
+                                                discardFailureId);
         gGlobalContext = temp;
 
         if (!gGlobalContext) {
             NS_WARNING("Couldn't init gGlobalContext.");
         }
     }
 
     return gGlobalContext;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -450,17 +450,18 @@ GLContextEGL::SwapBuffers()
 void
 GLContextEGL::HoldSurface(gfxASurface *aSurf) {
     mThebesSurface = aSurf;
 }
 
 /* static */ EGLSurface
 GLContextEGL::CreateSurfaceForWindow(nsIWidget* aWidget)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library!\n");
         return nullptr;
     }
 
     EGLConfig config;
     if (!CreateConfig(&config, aWidget)) {
         MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
         return nullptr;
@@ -483,19 +484,21 @@ GLContextEGL::DestroySurface(EGLSurface 
 }
 
 already_AddRefed<GLContextEGL>
 GLContextEGL::CreateGLContext(CreateContextFlags flags,
                 const SurfaceCaps& caps,
                 GLContextEGL *shareContext,
                 bool isOffscreen,
                 EGLConfig config,
-                EGLSurface surface)
+                EGLSurface surface,
+                nsACString& aFailureId)
 {
     if (sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_ES");
         NS_WARNING("Failed to bind API to GLES!");
         return nullptr;
     }
 
     EGLContext eglShareContext = shareContext ? shareContext->mContext
                                               : EGL_NO_CONTEXT;
 
     nsTArray<EGLint> contextAttribs;
@@ -522,29 +525,32 @@ GLContextEGL::CreateGLContext(CreateCont
     if (!context && shareContext) {
         shareContext = nullptr;
         context = sEGLLibrary.fCreateContext(EGL_DISPLAY(),
                                               config,
                                               EGL_NO_CONTEXT,
                                               contextAttribs.Elements());
     }
     if (!context) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_CREATE");
         NS_WARNING("Failed to create EGLContext!");
         return nullptr;
     }
 
     RefPtr<GLContextEGL> glContext = new GLContextEGL(caps,
                                                         shareContext,
                                                         isOffscreen,
                                                         config,
                                                         surface,
                                                         context);
 
-    if (!glContext->Init())
+    if (!glContext->Init()) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_INIT");
         return nullptr;
+    }
 
     return glContext.forget();
 }
 
 EGLSurface
 GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
                                                    EGLenum bindToTextureFormat,
                                                    mozilla::gfx::IntSize& pbsize)
@@ -733,17 +739,18 @@ CreateConfig(EGLConfig* aConfig, nsIWidg
     } else {
         return true;
     }
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 2!\n");
         return nullptr;
     }
 
     if (aContext && aSurface) {
         SurfaceCaps caps = SurfaceCaps::Any();
         EGLConfig config = EGL_NO_CONFIG;
         RefPtr<GLContextEGL> glContext =
@@ -758,17 +765,18 @@ GLContextProviderEGL::CreateWrappingExis
     }
 
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
         return nullptr;
     }
 
     bool doubleBuffered = true;
 
     EGLConfig config;
     if (!CreateConfig(&config, aWidget)) {
@@ -781,17 +789,17 @@ GLContextProviderEGL::CreateForWindow(ns
         MOZ_CRASH("GFX: Failed to create EGLSurface!\n");
         return nullptr;
     }
 
     SurfaceCaps caps = SurfaceCaps::Any();
     RefPtr<GLContextEGL> glContext =
         GLContextEGL::CreateGLContext(CreateContextFlags::NONE, caps,
                                       nullptr, false,
-                                      config, surface);
+                                      config, surface, discardFailureId);
 
     if (!glContext) {
         MOZ_CRASH("GFX: Failed to create EGLContext!\n");
         mozilla::gl::DestroySurface(surface);
         return nullptr;
     }
 
     glContext->MakeCurrent();
@@ -799,17 +807,18 @@ GLContextProviderEGL::CreateForWindow(ns
 
     return glContext.forget();
 }
 
 #if defined(ANDROID)
 EGLSurface
 GLContextProviderEGL::CreateEGLSurface(void* aWindow)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 4!\n");
     }
 
     EGLConfig config;
     if (!CreateConfig(&config, static_cast<nsIWidget*>(aWindow))) {
         MOZ_CRASH("GFX: Failed to create EGLConfig 2!\n");
     }
 
@@ -822,17 +831,18 @@ GLContextProviderEGL::CreateEGLSurface(v
     }
 
     return surface;
 }
 
 void
 GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 5!\n");
     }
 
     sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface);
 }
 #endif // defined(ANDROID)
 
 static void
@@ -933,111 +943,124 @@ ChooseConfig(GLLibraryEGL* egl, CreateCo
     out_configCaps->bpp16 = (GetAttrib(egl, config, LOCAL_EGL_RED_SIZE) < 8);
 
     return config;
 }
 
 /*static*/ already_AddRefed<GLContextEGL>
 GLContextEGL::CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
                                                const mozilla::gfx::IntSize& size,
-                                               const SurfaceCaps& minCaps)
+                                               const SurfaceCaps& minCaps,
+                                               nsACString& aFailureId)
 {
     bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
-    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware))
+    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, aFailureId)) {
         return nullptr;
+    }
 
     SurfaceCaps configCaps;
     EGLConfig config = ChooseConfig(&sEGLLibrary, flags, minCaps, &configCaps);
     if (config == EGL_NO_CONFIG) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_NO_CONFIG");
         NS_WARNING("Failed to find a compatible config.");
         return nullptr;
     }
 
     if (GLContext::ShouldSpew()) {
         sEGLLibrary.DumpEGLConfig(config);
     }
 
     mozilla::gfx::IntSize pbSize(size);
     EGLSurface surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config,
                                                                             LOCAL_EGL_NONE,
                                                                             pbSize);
     if (!surface) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_POT");
         NS_WARNING("Failed to create PBuffer for context!");
         return nullptr;
     }
 
     RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, configCaps, nullptr, true,
-                                                            config, surface);
+                                                            config, surface, aFailureId);
     if (!gl) {
         NS_WARNING("Failed to create GLContext from PBuffer");
         sEGLLibrary.fDestroySurface(sEGLLibrary.Display(), surface);
         return nullptr;
     }
 
     return gl.forget();
 }
 
 /*static*/ already_AddRefed<GLContext>
-GLContextProviderEGL::CreateHeadless(CreateContextFlags flags)
+GLContextProviderEGL::CreateHeadless(CreateContextFlags flags, nsACString& aFailureId)
 {
     mozilla::gfx::IntSize dummySize = mozilla::gfx::IntSize(16, 16);
     SurfaceCaps dummyCaps = SurfaceCaps::Any();
-    return GLContextEGL::CreateEGLPBufferOffscreenContext(flags, dummySize, dummyCaps);
+    return GLContextEGL::CreateEGLPBufferOffscreenContext(flags, dummySize, dummyCaps,
+                                                          aFailureId);
 }
 
 // Under EGL, on Android, pbuffers are supported fine, though
 // often without the ability to texture from them directly.
 /*static*/ already_AddRefed<GLContext>
 GLContextProviderEGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
                                       const SurfaceCaps& minCaps,
-                                      CreateContextFlags flags)
+                                      CreateContextFlags flags,
+                                      nsACString& aFailureId)
 {
     bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
-    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware)) // Needed for IsANGLE().
+    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, aFailureId)) { // Needed for IsANGLE().
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LIB_INIT");
         return nullptr;
+    }
 
     bool canOffscreenUseHeadless = true;
     if (sEGLLibrary.IsANGLE()) {
         // ANGLE needs to use PBuffers.
         canOffscreenUseHeadless = false;
     }
 
     RefPtr<GLContext> gl;
     SurfaceCaps minOffscreenCaps = minCaps;
 
     if (canOffscreenUseHeadless) {
-        gl = CreateHeadless(flags);
-        if (!gl)
+        gl = CreateHeadless(flags, aFailureId);
+        if (!gl) {
             return nullptr;
+        }
     } else {
         SurfaceCaps minBackbufferCaps = minOffscreenCaps;
         if (minOffscreenCaps.antialias) {
             minBackbufferCaps.antialias = false;
             minBackbufferCaps.depth = false;
             minBackbufferCaps.stencil = false;
         }
 
-        gl = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, size, minBackbufferCaps);
-        if (!gl)
+        gl = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, size, minBackbufferCaps,
+                                                            aFailureId);
+        if (!gl) {
             return nullptr;
+        }
 
         // Pull the actual resulting caps to ensure that our offscreen matches our
         // backbuffer.
         minOffscreenCaps.alpha = gl->Caps().alpha;
         if (!minOffscreenCaps.antialias) {
             // Only update these if we don't have AA. If we do have AA, we ignore
             // backbuffer depth/stencil.
             minOffscreenCaps.depth = gl->Caps().depth;
             minOffscreenCaps.stencil = gl->Caps().stencil;
         }
     }
 
     // Init the offscreen with the updated offscreen caps.
-    if (!gl->InitOffscreen(size, minOffscreenCaps))
+    if (!gl->InitOffscreen(size, minOffscreenCaps)) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_OFFSCREEN");
         return nullptr;
+    }
 
     return gl.forget();
 }
 
 // Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
 // and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
 // and 3) each EGL context eats 750k on B2G (bug 813783)
 /*static*/ GLContext*
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1218,17 +1218,18 @@ GLContextGLX::FindFBConfigForWindow(Disp
         }
     }
 
     NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
     return false;
 }
 
 static already_AddRefed<GLContextGLX>
-CreateOffscreenPixmapContext(const IntSize& size, const SurfaceCaps& minCaps, ContextProfile profile = ContextProfile::OpenGLCompatibility)
+CreateOffscreenPixmapContext(const IntSize& size, const SurfaceCaps& minCaps, nsACString& aFailureId,
+                             ContextProfile profile = ContextProfile::OpenGLCompatibility)
 {
     GLXLibrary* glx = &sGLXLibrary;
     if (!glx->EnsureInitialized())
         return nullptr;
 
     Display* display = DefaultXDisplay();
     int screen = DefaultScreen(display);
 
@@ -1280,64 +1281,68 @@ DONE_CREATING_PIXMAP:
         return nullptr;
 
     GLContextGLX* shareContext = GetGlobalContextGLX();
     return GLContextGLX::CreateGLContext(minCaps, shareContext, true, display, pixmap,
                                          config, true, surface, profile);
 }
 
 /*static*/ already_AddRefed<GLContext>
-GLContextProviderGLX::CreateHeadless(CreateContextFlags)
+GLContextProviderGLX::CreateHeadless(CreateContextFlags, nsACString& aFailureId)
 {
     IntSize dummySize = IntSize(16, 16);
     SurfaceCaps dummyCaps = SurfaceCaps::Any();
-    return CreateOffscreenPixmapContext(dummySize, dummyCaps);
+    return CreateOffscreenPixmapContext(dummySize, dummyCaps, aFailureId);
 }
 
 /*static*/ already_AddRefed<GLContext>
 GLContextProviderGLX::CreateOffscreen(const IntSize& size,
                                       const SurfaceCaps& minCaps,
-                                      CreateContextFlags flags)
+                                      CreateContextFlags flags,
+                                      nsACString& aFailureId)
 {
     SurfaceCaps minBackbufferCaps = minCaps;
     if (minCaps.antialias) {
         minBackbufferCaps.antialias = false;
         minBackbufferCaps.depth = false;
         minBackbufferCaps.stencil = false;
     }
 
     ContextProfile profile = ContextProfile::OpenGLCore;
     if (flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE) {
         profile = ContextProfile::OpenGLCompatibility;
     }
 
     RefPtr<GLContext> gl;
-    gl = CreateOffscreenPixmapContext(size, minBackbufferCaps, profile);
+    gl = CreateOffscreenPixmapContext(size, minBackbufferCaps, aFailureId, profile);
     if (!gl)
         return nullptr;
 
-    if (!gl->InitOffscreen(size, minCaps))
+    if (!gl->InitOffscreen(size, minCaps)) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_GLX_INIT");
         return nullptr;
+    }
 
     return gl.forget();
 }
 
 /*static*/ GLContext*
 GLContextProviderGLX::GetGlobalContext()
 {
     // TODO: get GLX context sharing to work well with multiple threads
     if (gfxEnv::DisableContextSharingGlx())
         return nullptr;
 
     static bool triedToCreateContext = false;
     if (!triedToCreateContext) {
         triedToCreateContext = true;
 
         MOZ_RELEASE_ASSERT(!gGlobalContext);
-        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE);
+        nsCString discardFailureId;
+        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE, discardFailureId);
         gGlobalContext = temp;
     }
 
     return gGlobalContext;
 }
 
 /*static*/ void
 GLContextProviderGLX::Shutdown()
--- a/gfx/gl/GLContextProviderImpl.h
+++ b/gfx/gl/GLContextProviderImpl.h
@@ -62,21 +62,22 @@ public:
      * @param flags   The set of CreateContextFlags to be used for this
      *                offscreen context.
      *
      * @return Context to use for offscreen rendering
      */
     static already_AddRefed<GLContext>
     CreateOffscreen(const mozilla::gfx::IntSize& size,
                     const SurfaceCaps& minCaps,
-                    CreateContextFlags flags);
+                    CreateContextFlags flags,
+                    nsACString& failureId);
 
     // Just create a context. We'll add offscreen stuff ourselves.
     static already_AddRefed<GLContext>
-    CreateHeadless(CreateContextFlags flags);
+    CreateHeadless(CreateContextFlags flags, nsACString& aFailureId);
 
     /**
      * Create wrapping Gecko GLContext for external gl context.
      *
      * @param aContext External context which will be wrapped by Gecko GLContext.
      * @param aSurface External surface which is used for external context.
      *
      * @return Wrapping Context to use for rendering
--- a/gfx/gl/GLContextProviderNull.cpp
+++ b/gfx/gl/GLContextProviderNull.cpp
@@ -18,18 +18,20 @@ already_AddRefed<GLContext>
 GLContextProviderNull::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateOffscreen(const gfx::IntSize&,
                                        const SurfaceCaps&,
-                                       CreateContextFlags)
+                                       CreateContextFlags,
+                                       nsACString& aFailureId)
 {
+    aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_NULL");
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateHeadless(CreateContextFlags)
 {
     return nullptr;
 }
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -636,17 +636,17 @@ CreateWindowOffscreenContext()
     RefPtr<GLContextWGL> glContext = new GLContextWGL(caps,
                                                         shareContext, true,
                                                         dc, context, win);
 
     return glContext.forget();
 }
 
 /*static*/ already_AddRefed<GLContext>
-GLContextProviderWGL::CreateHeadless(CreateContextFlags)
+GLContextProviderWGL::CreateHeadless(CreateContextFlags, nsACString& aFailureId)
 {
     if (!sWGLLib.EnsureInitialized()) {
         return nullptr;
     }
 
     RefPtr<GLContextWGL> glContext;
 
     // Always try to create a pbuffer context first, because we
@@ -671,39 +671,43 @@ GLContextProviderWGL::CreateHeadless(Cre
 
     RefPtr<GLContext> retGL = glContext.get();
     return retGL.forget();
 }
 
 /*static*/ already_AddRefed<GLContext>
 GLContextProviderWGL::CreateOffscreen(const IntSize& size,
                                       const SurfaceCaps& minCaps,
-                                      CreateContextFlags flags)
+                                      CreateContextFlags flags,
+                                      nsACString& aFailureId)
 {
-    RefPtr<GLContext> gl = CreateHeadless(flags);
+    RefPtr<GLContext> gl = CreateHeadless(flags, aFailureId);
     if (!gl)
         return nullptr;
 
-    if (!gl->InitOffscreen(size, minCaps))
+    if (!gl->InitOffscreen(size, minCaps)) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WGL_INIT");
         return nullptr;
+    }
 
     return gl.forget();
 }
 
 static StaticRefPtr<GLContext> gGlobalContext;
 
 /*static*/ GLContext*
 GLContextProviderWGL::GetGlobalContext()
 {
     static bool triedToCreateContext = false;
     if (!triedToCreateContext) {
         triedToCreateContext = true;
 
         MOZ_RELEASE_ASSERT(!gGlobalContext);
-        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE);
+        nsCString discardFailureId;
+        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE, discardFailureId);
         gGlobalContext = temp;
     }
 
     return static_cast<GLContext*>(gGlobalContext);
 }
 
 /*static*/ void
 GLContextProviderWGL::Shutdown()
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -137,26 +137,29 @@ GetAndInitWARPDisplay(GLLibraryEGL& egl,
 
     if (!egl.fInitialize(display, nullptr, nullptr))
         return EGL_NO_DISPLAY;
 
     return display;
 }
 
 static bool
-IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
+IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo, nsACString& aFailureId)
 {
     int32_t angleSupport;
     nsCString failureId;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_WEBGL_ANGLE,
                                          failureId,
                                          &angleSupport);
     Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID,
                           failureId);
+    if (failureId.IsEmpty()) {
+      aFailureId = failureId;
+    }
     return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK);
 }
 
 static EGLDisplay
 GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
 {
     EGLDisplay display = egl.fGetDisplay(displayType);
     if (display == EGL_NO_DISPLAY)
@@ -196,17 +199,19 @@ GetAndInitDisplayForAccelANGLE(GLLibrary
     return ret;
 }
 
 bool
 GLLibraryEGL::ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface)
 {
     StaticMutexAutoUnlock lock(sMutex);
     if (!mReadbackGL) {
-        mReadbackGL = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::NONE);
+        nsCString discardFailureId;
+        mReadbackGL = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::NONE,
+                                                            discardFailureId);
     }
 
     ScopedTexture destTex(mReadbackGL);
     const GLuint target = LOCAL_GL_TEXTURE_EXTERNAL;
     ScopedBindTexture autoTex(mReadbackGL, destTex.Texture(), target);
     mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
     mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
     mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
@@ -218,17 +223,17 @@ GLLibraryEGL::ReadbackEGLImage(EGLImage 
     int shaderConfig = config.mFeatures;
     mReadbackGL->ReadTexImageHelper()->ReadTexImage(out_surface, 0, target,
                                                     out_surface->GetSize(), shaderConfig);
 
     return true;
 }
 
 bool
-GLLibraryEGL::EnsureInitialized(bool forceAccel)
+GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString& aFailureId)
 {
     if (mInitialized) {
         return true;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("EGL");
 
 #ifdef MOZ_B2G
@@ -384,17 +389,17 @@ GLLibraryEGL::EnsureInitialized(bool for
 
     // Check the ANGLE support the system has
     nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
     mIsANGLE = IsExtensionSupported(ANGLE_platform_angle);
 
     EGLDisplay chosenDisplay = nullptr;
 
     if (IsExtensionSupported(ANGLE_platform_angle_d3d)) {
-        bool accelAngleSupport = IsAccelAngleSupported(gfxInfo);
+        bool accelAngleSupport = IsAccelAngleSupported(gfxInfo, aFailureId);
 
         bool shouldTryAccel = forceAccel || accelAngleSupport;
         bool shouldTryWARP = !shouldTryAccel;
         if (gfxPrefs::WebGLANGLEForceWARP()) {
             shouldTryWARP = true;
             shouldTryAccel = false;
         }
 
@@ -405,28 +410,34 @@ GLLibraryEGL::EnsureInitialized(bool for
                 mIsWARP = true;
             }
         }
 
         if (!chosenDisplay) {
             // If falling back to WARP did not work and we don't want to try
             // using HW accelerated ANGLE, then fail.
             if (!shouldTryAccel) {
+                if (aFailureId.IsEmpty()) {
+                    aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WARP_FALLBACK");
+                }
                 NS_ERROR("Fallback WARP ANGLE context failed to initialize.");
                 return false;
             }
 
             // Hardware accelerated ANGLE path
             chosenDisplay = GetAndInitDisplayForAccelANGLE(*this);
         }
     } else {
         chosenDisplay = GetAndInitDisplay(*this, EGL_DEFAULT_DISPLAY);
     }
 
     if (!chosenDisplay) {
+        if (aFailureId.IsEmpty()) {
+            aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_DISPLAY");
+        }
         NS_WARNING("Failed to initialize a display.");
         return false;
     }
     mEGLDisplay = chosenDisplay;
 
     InitDisplayExtensions();
 
     ////////////////////////////////////
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -530,17 +530,17 @@ public:
     }
 
     bool HasRobustness() const {
         return IsExtensionSupported(EXT_create_context_robustness);
     }
 
     bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);
 
-    bool EnsureInitialized(bool forceAccel = false);
+    bool EnsureInitialized(bool forceAccel, nsACString& aFailureId);
 
     void DumpEGLConfig(EGLConfig cfg);
     void DumpEGLConfigs();
 
     struct {
         typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void *display_id);
         pfnGetDisplay fGetDisplay;
         typedef EGLDisplay(GLAPIENTRY * pfnGetPlatformDisplayEXT)(EGLenum platform, void *native_display, const EGLint *attrib_list);
--- a/gfx/layers/GLImages.cpp
+++ b/gfx/layers/GLImages.cpp
@@ -46,17 +46,19 @@ EGLImageImage::~EGLImageImage()
 }
 
 already_AddRefed<gfx::SourceSurface>
 GLImage::GetAsSourceSurface()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread");
 
   if (!sSnapshotContext) {
-    sSnapshotContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE);
+    nsCString discardFailureId;
+    sSnapshotContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE,
+                                                         discardFailureId);
     if (!sSnapshotContext) {
       NS_WARNING("Failed to create snapshot GLContext");
       return nullptr;
     }
   }
 
   sSnapshotContext->MakeCurrent();
   ScopedTexture scopedTex(sSnapshotContext);
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -126,18 +126,20 @@ CompositorOGL::CreateContext()
 #endif
 
   // Allow to create offscreen GL context for main Layer Manager
   if (!context && gfxEnv::LayersPreferOffscreen()) {
     SurfaceCaps caps = SurfaceCaps::ForRGB();
     caps.preserve = false;
     caps.bpp16 = gfxPlatform::GetPlatform()->GetOffscreenFormat() == SurfaceFormat::R5G6B5_UINT16;
 
+    nsCString discardFailureId;
     context = GLContextProvider::CreateOffscreen(mSurfaceSize,
-                                                 caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE);
+                                                 caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE,
+                                                 discardFailureId);
   }
 
   if (!context) {
     context = gl::GLContextProvider::CreateForWindow(mWidget->RealWidget(),
                 gfxPlatform::GetPlatform()->RequiresAcceleratedGLContextForCompositorOGL());
   }
 
   if (!context) {
--- a/gfx/tests/gtest/TestCompositor.cpp
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -43,19 +43,21 @@ public:
     return GetClientBounds(aRect);
   }
 
   void* GetNativeData(uint32_t aDataType) override {
     if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
       mozilla::gl::SurfaceCaps caps = mozilla::gl::SurfaceCaps::ForRGB();
       caps.preserve = false;
       caps.bpp16 = false;
+      nsCString discardFailureId;
       RefPtr<GLContext> context = GLContextProvider::CreateOffscreen(
         IntSize(gCompWidth, gCompHeight), caps,
-        CreateContextFlags::REQUIRE_COMPAT_PROFILE);
+        CreateContextFlags::REQUIRE_COMPAT_PROFILE,
+        discardFailureId);
       return context.forget().take();
     }
     return nullptr;
   }
 
   NS_IMETHOD              Create(nsIWidget* aParent,
                                  nsNativeWidget aNativeParent,
                                  const LayoutDeviceIntRect& aRect,
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1316,18 +1316,20 @@ gfxPlatform::GetSkiaGLGlue()
 
   if (!mSkiaGlue) {
     /* Dummy context. We always draw into a FBO.
      *
      * FIXME: This should be stored in TLS or something, since there needs to be one for each thread using it. As it
      * stands, this only works on the main thread.
      */
     RefPtr<GLContext> glContext;
+    nsCString discardFailureId;
     glContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE |
-                                                  CreateContextFlags::ALLOW_OFFLINE_RENDERER);
+                                                  CreateContextFlags::ALLOW_OFFLINE_RENDERER,
+                                                  discardFailureId);
     if (!glContext) {
       printf_stderr("Failed to create GLContext for SkiaGL!\n");
       return nullptr;
     }
     mSkiaGlue = new SkiaGLGlue(glContext);
     MOZ_ASSERT(mSkiaGlue->GetGrContext(), "No GrContext");
     InitializeSkiaCacheLimits();
   }
--- a/widget/android/GfxInfo.cpp
+++ b/widget/android/GfxInfo.cpp
@@ -70,17 +70,19 @@ public:
   }
 
   void EnsureInitialized() {
     if (mReady) {
       return;
     }
 
     RefPtr<gl::GLContext> gl;
-    gl = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE);
+    nsCString discardFailureId;
+    gl = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE,
+                                               discardFailureId);
 
     if (!gl) {
       // Setting mReady to true here means that we won't retry. Everything will
       // remain blacklisted forever. Ideally, we would like to update that once
       // any GLContext is successfully created, like the compositor's GLContext.
       mReady = true;
       return;
     }