Bug 1492580 - Restore CONTEXT_LOST handling. (minimal) r=lsalzman a=jcristau
authorJeff Gilbert <jgilbert@mozilla.com>
Wed, 05 Dec 2018 16:10:07 -0500
changeset 501453 2360b266d799
parent 501450 626d7b63b958
child 501454 c0b34e542973
push id1870
push userjcristau@mozilla.com
push dateWed, 05 Dec 2018 21:35:46 +0000
treeherdermozilla-release@319052b37637 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman, jcristau
bugs1492580
milestone64.0
Bug 1492580 - Restore CONTEXT_LOST handling. (minimal) r=lsalzman a=jcristau Similar to the full patch, but about 30% smaller: Contains all fixes to dom/canvas. Contains context loss functional fixes for GLContext. We don't update gfx/gl to handle context loss in its asserts. DEBUG builds will probably assert, crash, and burn when trying to deal with context loss, but that's fine for Beta.
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextValidate.cpp
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextProviderEGL.cpp
gfx/thebes/gfxPrefs.h
modules/libpref/init/all.js
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -135,23 +135,19 @@ WebGLContext::WebGLContext()
     mGeneration = 0;
     mInvalidated = false;
     mCapturedFrameInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
     mDisableExtensions = false;
     mIsMesa = false;
-    mEmitContextLostErrorOnce = false;
     mWebGLError = 0;
-    mUnderlyingGLError = 0;
     mVRReady = false;
 
-    mContextLostErrorSet = false;
-
     mViewportX = 0;
     mViewportY = 0;
     mViewportWidth = 0;
     mViewportHeight = 0;
 
     mDitherEnabled = 1;
     mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
     mScissorTestEnabled = 0;
@@ -161,17 +157,16 @@ WebGLContext::WebGLContext()
         // XXX mtseng: bug 709490, not thread safe
         WebGLMemoryTracker::AddWebGLContext(this);
     }
 
     mAllowContextRestore = true;
     mLastLossWasSimulated = false;
     mLoseContextOnMemoryPressure = false;
     mCanLoseContextInForeground = true;
-    mRestoreWhenVisible = false;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
     mAlreadyWarnedAboutViewportLargerThanDest = false;
 
     mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
     if (mMaxWarnings < -1) {
         GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
@@ -314,29 +309,16 @@ WebGLContext::Invalidate()
 
     SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
 
     mInvalidated = true;
     mCanvasElement->InvalidateCanvasContent(nullptr);
 }
 
 void
-WebGLContext::OnVisibilityChange()
-{
-    if (gl) // Context not lost.
-        return;
-
-    if (!mRestoreWhenVisible || mLastLossWasSimulated) {
-        return;
-    }
-
-    ForceRestoreContext();
-}
-
-void
 WebGLContext::OnMemoryPressure()
 {
     bool shouldLoseContext = mLoseContextOnMemoryPressure;
 
     if (!mCanLoseContextInForeground &&
         ProcessPriorityManager::CurrentProcessIsForeground())
     {
         shouldLoseContext = false;
@@ -1526,78 +1508,57 @@ WebGLContext::Has64BitTimestamps() const
     // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
     return gl->IsSupported(GLFeature::sync);
 }
 
 static bool
 CheckContextLost(GLContext* gl, bool* const out_isGuilty)
 {
     MOZ_ASSERT(gl);
-    MOZ_ASSERT(out_isGuilty);
-
-    bool isEGL = gl->GetContextType() == gl::GLContextType::EGL;
-
-    GLenum resetStatus = LOCAL_GL_NO_ERROR;
-    if (gl->IsSupported(GLFeature::robustness)) {
-        gl->MakeCurrent();
-        resetStatus = gl->fGetGraphicsResetStatus();
-    } else if (isEGL) {
-        // Simulate a ARB_robustness guilty context loss for when we
-        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
-        // but we can't make any distinction.
-        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
-            resetStatus = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
-        }
-    }
-
+
+    const auto resetStatus = gl->fGetGraphicsResetStatus();
     if (resetStatus == LOCAL_GL_NO_ERROR) {
         *out_isGuilty = false;
         return false;
     }
 
     // Assume guilty unless we find otherwise!
     bool isGuilty = true;
     switch (resetStatus) {
     case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
+    case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
         // Either nothing wrong, or not our fault.
         isGuilty = false;
         break;
     case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
         NS_WARNING("WebGL content on the page definitely caused the graphics"
                    " card to reset.");
         break;
     case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
         NS_WARNING("WebGL content on the page might have caused the graphics"
                    " card to reset");
-        // If we can't tell, assume guilty.
+        // If we can't tell, assume not-guilty.
+        // Todo: Implement max number of "unknown" resets per document or time.
+        isGuilty = false;
         break;
     default:
-        MOZ_ASSERT(false, "Unreachable.");
-        // If we do get here, let's pretend to be guilty as an escape plan.
+        gfxCriticalError() << "Unexpected glGetGraphicsResetStatus: "
+                           << gfx::hexa(resetStatus);
         break;
     }
 
     if (isGuilty) {
         NS_WARNING("WebGL context on this page is considered guilty, and will"
                    " not be restored.");
     }
 
     *out_isGuilty = isGuilty;
     return true;
 }
 
-bool
-WebGLContext::TryToRestoreContext()
-{
-    if (NS_FAILED(SetDimensions(mRequestedSize.width, mRequestedSize.height)))
-        return false;
-
-    return true;
-}
-
 void
 WebGLContext::RunContextLossTimer()
 {
     mContextLossHandler.RunTimer();
 }
 
 class UpdateContextLossStatusTask : public CancelableRunnable
 {
@@ -1716,40 +1677,38 @@ WebGLContext::UpdateContextLossStatus()
         if (!mAllowContextRestore)
             return;
 
         // If we're only simulated-lost, we shouldn't auto-restore, and
         // instead we should wait for restoreContext() to be called.
         if (mLastLossWasSimulated)
             return;
 
-        // Restore when the app is visible
-        if (mRestoreWhenVisible)
-            return;
-
         ForceRestoreContext();
         return;
     }
 
     if (mContextStatus == ContextStatus::LostAwaitingRestore) {
         // Context is lost, but we should try to restore it.
 
+        if (mAllowContextRestore) {
+            if (NS_FAILED(SetDimensions(mRequestedSize.width,
+                                        mRequestedSize.height)))
+            {
+                // Assume broken forever.
+                mAllowContextRestore = false;
+            }
+        }
         if (!mAllowContextRestore) {
             // We might decide this after thinking we'd be OK restoring
             // the context, so downgrade.
             mContextStatus = ContextStatus::Lost;
             return;
         }
 
-        if (!TryToRestoreContext()) {
-            // Failed to restore. Try again later.
-            mContextLossHandler.RunTimer();
-            return;
-        }
-
         // Revival!
         mContextStatus = ContextStatus::NotLost;
 
         if (mCanvasElement) {
             nsContentUtils::DispatchTrustedEvent(
                 mCanvasElement->OwnerDoc(),
                 static_cast<nsIContent*>(mCanvasElement),
                 NS_LITERAL_STRING("webglcontextrestored"),
@@ -1759,28 +1718,27 @@ WebGLContext::UpdateContextLossStatus()
             RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
             event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"),
                              CanBubble::eYes,
                              Cancelable::eYes);
             event->SetTrusted(true);
             mOffscreenCanvas->DispatchEvent(*event);
         }
 
-        mEmitContextLostErrorOnce = true;
         return;
     }
 }
 
 void
 WebGLContext::ForceLoseContext(bool simulateLosing)
 {
     printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
     MOZ_ASSERT(gl);
     mContextStatus = ContextStatus::LostAwaitingEvent;
-    mContextLostErrorSet = false;
+    mWebGLError = LOCAL_GL_CONTEXT_LOST_WEBGL;
 
     // Burn it all!
     DestroyResourcesAndContext();
     mLastLossWasSimulated = simulateLosing;
 
     // Queue up a task, since we know the status changed.
     EnqueueUpdateContextLossStatus();
 }
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -348,17 +348,16 @@ protected:
 public:
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext,
                                                            nsICanvasRenderingContextInternal)
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
-    virtual void OnVisibilityChange() override;
     virtual void OnMemoryPressure() override;
 
     // nsICanvasRenderingContextInternal
     virtual int32_t GetWidth() override { return DrawingBufferWidth(); }
     virtual int32_t GetHeight() override { return DrawingBufferHeight(); }
 
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) override;
     NS_IMETHOD InitializeWithDrawTarget(nsIDocShell*,
@@ -1479,33 +1478,28 @@ protected:
     bool mInvalidated;
     bool mCapturedFrameInvalidated;
     bool mResetLayer;
     bool mOptionsFrozen;
     bool mDisableExtensions;
     bool mIsMesa;
     bool mLoseContextOnMemoryPressure;
     bool mCanLoseContextInForeground;
-    bool mRestoreWhenVisible;
     bool mShouldPresent;
     bool mDisableFragHighP;
     bool mVRReady;
 
     template<typename WebGLObjectType>
     void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
 
     GLuint mActiveTexture = 0;
     GLenum mDefaultFB_DrawBuffer0 = 0;
     GLenum mDefaultFB_ReadBuffer = 0;
 
-    // glGetError sources:
-    bool mEmitContextLostErrorOnce;
     mutable GLenum mWebGLError;
-    mutable GLenum mUnderlyingGLError;
-    GLenum GetAndFlushUnderlyingGLErrors() const;
 
     bool mBypassShaderValidation;
 
     webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const;
 
     // some GL constants
     uint32_t mGLMaxTextureUnits = 0;
 
@@ -1921,17 +1915,16 @@ protected:
     bool mAlreadyWarnedAboutViewportLargerThanDest;
 
     GLfloat mLineWidth = 0.0;
 
     WebGLContextLossHandler mContextLossHandler;
     bool mAllowContextRestore;
     bool mLastLossWasSimulated;
     ContextStatus mContextStatus = ContextStatus::NotLost;
-    bool mContextLostErrorSet;
 
     // Used for some hardware (particularly Tegra 2 and 4) that likes to
     // be Flushed while doing hundreds of draw calls.
     int mDrawCallsSinceLastFlush;
 
     mutable int mAlreadyGeneratedWarnings;
     int mMaxWarnings;
     bool mAlreadyWarnedAboutFakeVertexAttrib0;
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -225,18 +225,16 @@ DoSetsIntersect(const std::set<T>& a, co
                           std::back_inserter(intersection));
     return bool(intersection.size());
 }
 
 const webgl::CachedDrawFetchLimits*
 ValidateDraw(WebGLContext* const webgl, const GLenum mode,
              const uint32_t instanceCount)
 {
-    MOZ_ASSERT(webgl->gl->IsCurrent());
-
     if (!webgl->BindCurFBForDraw())
         return nullptr;
 
     switch (mode) {
     case LOCAL_GL_TRIANGLES:
     case LOCAL_GL_TRIANGLE_STRIP:
     case LOCAL_GL_TRIANGLE_FAN:
     case LOCAL_GL_POINTS:
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -905,63 +905,53 @@ WebGLContext::CreateTexture()
 
     GLuint tex = 0;
     gl->fGenTextures(1, &tex);
 
     RefPtr<WebGLTexture> globj = new WebGLTexture(this, tex);
     return globj.forget();
 }
 
-static GLenum
-GetAndClearError(GLenum* errorVar)
-{
-    MOZ_ASSERT(errorVar);
-    GLenum ret = *errorVar;
-    *errorVar = LOCAL_GL_NO_ERROR;
-    return ret;
-}
-
 GLenum
 WebGLContext::GetError()
 {
     const FuncScope funcScope(*this, "getError");
+
     /* WebGL 1.0: Section 5.14.3: Setting and getting state:
      *   If the context's webgl context lost flag is set, returns
      *   CONTEXT_LOST_WEBGL the first time this method is called.
      *   Afterward, returns NO_ERROR until the context has been
      *   restored.
      *
      * WEBGL_lose_context:
      *   [When this extension is enabled: ] loseContext and
      *   restoreContext are allowed to generate INVALID_OPERATION errors
      *   even when the context is lost.
      */
 
-    if (IsContextLost()) {
-        if (mEmitContextLostErrorOnce) {
-            mEmitContextLostErrorOnce = false;
-            return LOCAL_GL_CONTEXT_LOST_WEBGL;
-        }
-        // Don't return yet, since WEBGL_lose_contexts contradicts the
-        // original spec, and allows error generation while lost.
-    }
-
-    GLenum err = GetAndClearError(&mWebGLError);
-    if (err != LOCAL_GL_NO_ERROR)
+    auto err = mWebGLError;
+    mWebGLError = 0;
+    if (IsContextLost() || err) // Must check IsContextLost in all flow paths.
         return err;
 
-    if (IsContextLost())
-        return LOCAL_GL_NO_ERROR;
-
     // Either no WebGL-side error, or it's already been cleared.
     // UnderlyingGL-side errors, now.
-
-    GetAndFlushUnderlyingGLErrors();
-
-    err = GetAndClearError(&mUnderlyingGLError);
+    err = gl->fGetError();
+    if (gl->IsContextLost()) {
+        UpdateContextLossStatus();
+        return GetError();
+    }
+    MOZ_ASSERT(err != LOCAL_GL_CONTEXT_LOST);
+
+    if (err) {
+        GenerateWarning("Driver error unexpected by WebGL: 0x%04x", err);
+        // This might be:
+        // - INVALID_OPERATION from ANGLE due to incomplete RBAB implementation for DrawElements
+        //   with DYNAMIC_DRAW index buffer.
+    }
     return err;
 }
 
 JS::Value
 WebGLContext::GetProgramParameter(const WebGLProgram& prog, GLenum pname)
 {
     const FuncScope funcScope(*this, "getProgramParameter");
     if (IsContextLost())
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -627,30 +627,16 @@ EnumString(const GLenum val)
 void
 WebGLContext::ErrorInvalidEnumArg(const char* argName, GLenum val) const
 {
     nsCString enumName;
     EnumName(val, &enumName);
     ErrorInvalidEnum("Bad `%s`: %s", argName, enumName.BeginReading());
 }
 
-GLenum
-WebGLContext::GetAndFlushUnderlyingGLErrors() const
-{
-    // Get and clear GL error in ALL cases.
-    GLenum error = gl->fGetError();
-
-    // Only store in mUnderlyingGLError if is hasn't already recorded an
-    // error.
-    if (!mUnderlyingGLError)
-        mUnderlyingGLError = error;
-
-    return error;
-}
-
 #ifdef DEBUG
 // For NaNs, etc.
 static bool
 IsCacheCorrect(float cached, float actual)
 {
     if (IsNaN(cached)) {
         // GL is allowed to do anything it wants for NaNs, so if we're shadowing
         // a NaN, then whatever `actual` is might be correct.
@@ -694,17 +680,17 @@ AssertUintParamCorrect(gl::GLContext*, G
 {
 }
 #endif
 
 void
 WebGLContext::AssertCachedBindings() const
 {
 #ifdef DEBUG
-    GetAndFlushUnderlyingGLErrors();
+    gl::GLContext::LocalErrorScope errorScope(*gl);
 
     if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
         GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound);
     }
 
     GLint stencilBits = 0;
     if (GetStencilBits(&stencilBits)) { // Depends on current draw framebuffer.
@@ -734,27 +720,27 @@ WebGLContext::AssertCachedBindings() con
     bound = mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_ARRAY_BUFFER_BINDING, bound);
 
     MOZ_ASSERT(mBoundVertexArray);
     WebGLBuffer* curBuff = mBoundVertexArray->mElementArrayBuffer;
     bound = curBuff ? curBuff->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING, bound);
 
-    MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
+    MOZ_ASSERT(!gl::GLContext::IsBadCallError(errorScope.GetError()));
 #endif
 
     // We do not check the renderbuffer binding, because we never rely on it matching.
 }
 
 void
 WebGLContext::AssertCachedGlobalState() const
 {
 #ifdef DEBUG
-    GetAndFlushUnderlyingGLErrors();
+    gl::GLContext::LocalErrorScope errorScope(*gl);
 
     ////////////////
 
     // Draw state
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DITHER) == mDitherEnabled);
     MOZ_ASSERT_IF(IsWebGL2(),
                   gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled);
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
@@ -802,17 +788,17 @@ WebGLContext::AssertCachedGlobalState() 
         AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ROW_LENGTH  , mPixelStore_UnpackRowLength);
         AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_ROWS   , mPixelStore_UnpackSkipRows);
         AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_PIXELS , mPixelStore_UnpackSkipPixels);
         AssertUintParamCorrect(gl, LOCAL_GL_PACK_ROW_LENGTH    , mPixelStore_PackRowLength);
         AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_ROWS     , mPixelStore_PackSkipRows);
         AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_PIXELS   , mPixelStore_PackSkipPixels);
     }
 
-    MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
+    MOZ_ASSERT(!gl::GLContext::IsBadCallError(errorScope.GetError()));
 #endif
 }
 
 const char*
 InfoFrom(WebGLTexImageFunc func, WebGLTexDimensions dims)
 {
     switch (dims) {
     case WebGLTexDimensions::Tex2D:
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -322,17 +322,16 @@ WebGLContext::InitAndValidateGL(FailureR
                                      error);
         *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_1", reason };
         return false;
     }
 
     mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
     mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
     mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
-    mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
 
     // These are the default values, see 6.2 State tables in the
     // OpenGL ES 2.0.25 spec.
     mColorWriteMask = 0x0f;
     mDriverColorMask = mColorWriteMask;
     mColorClearValue[0] = 0.f;
     mColorClearValue[1] = 0.f;
     mColorClearValue[2] = 0.f;
@@ -378,19 +377,17 @@ WebGLContext::InitAndValidateGL(FailureR
 
     mGenerateMipmapHint = LOCAL_GL_DONT_CARE;
 
     // Bindings, etc.
     mActiveTexture = 0;
     mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;
     mDefaultFB_ReadBuffer = LOCAL_GL_BACK;
 
-    mEmitContextLostErrorOnce = true;
     mWebGLError = LOCAL_GL_NO_ERROR;
-    mUnderlyingGLError = LOCAL_GL_NO_ERROR;
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBound3DTextures.Clear();
     mBound2DArrayTextures.Clear();
     mBoundSamplers.Clear();
 
     mBoundArrayBuffer = nullptr;
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -374,16 +374,18 @@ GLContext::LoadFeatureSymbols(const char
     }
     return true;
 };
 
 bool
 GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
 {
     mWorkAroundDriverBugs = gfxPrefs::WorkAroundDriverBugs();
+    if (!MakeCurrent(true))
+        return false;
 
     const SymLoadStruct coreSymbols[] = {
         { (PRFuncPtr*) &mSymbols.fActiveTexture, { "ActiveTexture", "ActiveTextureARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fAttachShader, { "AttachShader", "AttachShaderARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fBindAttribLocation, { "BindAttribLocation", "BindAttribLocationARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fBindBuffer, { "BindBuffer", "BindBufferARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fBindTexture, { "BindTexture", "BindTextureARB", nullptr } },
         { (PRFuncPtr*) &mSymbols.fBlendColor, { "BlendColor", nullptr } },
@@ -507,22 +509,45 @@ GLContext::InitWithPrefixImpl(const char
         { (PRFuncPtr*) &mSymbols.fDeleteShader, { "DeleteShader", "DeleteShaderARB", nullptr } },
 
         END_SYMBOLS
     };
 
     if (!LoadGLSymbols(this, prefix, trygl, coreSymbols, "GL"))
         return false;
 
+    {
+        const SymLoadStruct symbols[] = {
+            { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatus",
+                                                                "GetGraphicsResetStatusARB",
+                                                                "GetGraphicsResetStatusKHR",
+                                                                "GetGraphicsResetStatusEXT",
+                                                                nullptr } },
+            END_SYMBOLS
+        };
+        (void)LoadGLSymbols(this, prefix, trygl, symbols, nullptr);
+
+        auto err = fGetError();
+        if (err == LOCAL_GL_CONTEXT_LOST) {
+            MOZ_ASSERT(mSymbols.fGetGraphicsResetStatus);
+            const auto status = fGetGraphicsResetStatus();
+            if (status) {
+                printf_stderr("Unflushed glGetGraphicsResetStatus: 0x%04x\n", status);
+            }
+            err = fGetError();
+            MOZ_ASSERT(!err);
+        }
+        if (err) {
+            MOZ_ASSERT(false);
+            return false;
+        }
+    }
+
     ////////////////
 
-    if (!MakeCurrent()) {
-        return false;
-    }
-
     const std::string versionStr = (const char*)fGetString(LOCAL_GL_VERSION);
     if (versionStr.find("OpenGL ES") == 0) {
         mProfile = ContextProfile::OpenGLES;
     }
 
     uint32_t majorVer, minorVer;
     if (!ParseVersion(versionStr, &majorVer, &minorVer)) {
         MOZ_ASSERT(false, "Failed to parse GL_VERSION");
@@ -1006,33 +1031,16 @@ GLContext::LoadMoreSymbols(const char* p
                 const bool isDisabled = (resetStrategy == LOCAL_GL_NO_RESET_NOTIFICATION);
                 printf_stderr("Strategy: %s (0x%04x)",
                               (isDisabled ? "disabled" : "unrecognized"),
                               resetStrategy);
             }
             MarkUnsupported(GLFeature::robustness);
         }
     }
-    if (IsSupported(GLFeature::robustness)) {
-        const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatus",
-                                                                "GetGraphicsResetStatusARB",
-                                                                "GetGraphicsResetStatusKHR",
-                                                                "GetGraphicsResetStatusEXT",
-                                                                nullptr } },
-            END_SYMBOLS
-        };
-        if (fnLoadForFeature(symbols, GLFeature::robustness)) {
-            const auto status = mSymbols.fGetGraphicsResetStatus();
-            MOZ_ALWAYS_TRUE(!status);
-
-            const auto err = mSymbols.fGetError();
-            MOZ_ALWAYS_TRUE(!err);
-        }
-    }
 
     if (IsSupported(GLFeature::sync)) {
         const SymLoadStruct symbols[] = {
             { (PRFuncPtr*) &mSymbols.fFenceSync,      { "FenceSync",      nullptr } },
             { (PRFuncPtr*) &mSymbols.fIsSync,         { "IsSync",         nullptr } },
             { (PRFuncPtr*) &mSymbols.fDeleteSync,     { "DeleteSync",     nullptr } },
             { (PRFuncPtr*) &mSymbols.fClientWaitSync, { "ClientWaitSync", nullptr } },
             { (PRFuncPtr*) &mSymbols.fWaitSync,       { "WaitSync",       nullptr } },
@@ -2046,20 +2054,17 @@ GLContext::MarkDestroyed()
         return;
 
     // Null these before they're naturally nulled after dtor, as we want GLContext to
     // still be alive in *their* dtors.
     mScreen = nullptr;
     mBlitHelper = nullptr;
     mReadTexImageHelper = nullptr;
 
-    if (!MakeCurrent()) {
-        NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
-    }
-
+    mContextLost = true;
     mSymbols = {};
 }
 
 #ifdef MOZ_GL_DEBUG
 /* static */ void
 GLContext::AssertNotPassingStackBufferToTheGL(const void* ptr)
 {
   int somethingOnTheStack;
@@ -2938,28 +2943,28 @@ GetBytesPerTexel(GLenum format, GLenum t
 
     gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
     return 0;
 }
 
 bool
 GLContext::MakeCurrent(bool aForce) const
 {
-    if (MOZ_UNLIKELY( IsDestroyed() ))
+    if (MOZ_UNLIKELY( IsContextLost() ))
         return false;
 
     if (MOZ_LIKELY( !aForce )) {
         bool isCurrent;
         if (mUseTLSIsCurrent) {
             isCurrent = (sCurrentContext.get() == reinterpret_cast<uintptr_t>(this));
         } else {
             isCurrent = IsCurrentImpl();
         }
         if (MOZ_LIKELY( isCurrent )) {
-            MOZ_ASSERT(IsCurrentImpl());
+            MOZ_ASSERT(IsCurrentImpl() || !MakeCurrentImpl()); // Might have lost context.
             return true;
         }
     }
 
     if (!MakeCurrentImpl())
         return false;
 
     sCurrentContext.set(reinterpret_cast<uintptr_t>(this));
@@ -2972,52 +2977,169 @@ GLContext::ResetSyncCallCount(const char
     if (ShouldSpew()) {
         printf_stderr("On %s, mSyncGLCallCount = %" PRIu64 "\n",
                        resetReason, mSyncGLCallCount);
     }
 
     mSyncGLCallCount = 0;
 }
 
+// -
+
+GLenum
+GLContext::GetError() const
+{
+    if (mContextLost)
+        return LOCAL_GL_CONTEXT_LOST;
+
+    if (mImplicitMakeCurrent) {
+        (void)MakeCurrent();
+    }
+
+    const auto fnGetError = [&]() {
+        const auto ret = mSymbols.fGetError();
+        if (ret == LOCAL_GL_CONTEXT_LOST) {
+            OnContextLostError();
+            mTopError = ret; // Promote to top!
+        }
+        return ret;
+    };
+
+    auto ret = fnGetError();
+
+    {
+        auto flushedErr = ret;
+        uint32_t i = 1;
+        while (flushedErr && !mContextLost) {
+            if (i == 100) {
+                gfxCriticalError() << "Flushing glGetError still "
+                                   << gfx::hexa(flushedErr) << " after " << i
+                                   << " calls.";
+                break;
+            }
+            flushedErr = fnGetError();
+            i += 1;
+        }
+    }
+
+    if (mTopError) {
+        ret = mTopError;
+        mTopError = 0;
+    }
+
+    if (mDebugFlags & DebugFlagTrace) {
+        const auto errStr = GLErrorToString(ret);
+        printf_stderr("[gl:%p] GetError() -> %s\n", this, errStr);
+    }
+    return ret;
+}
+
+GLenum
+GLContext::fGetGraphicsResetStatus() const
+{
+    OnSyncCall();
+
+    GLenum ret = 0;
+    if (mSymbols.fGetGraphicsResetStatus) {
+        if (mImplicitMakeCurrent) {
+            (void)MakeCurrent();
+        }
+        ret = mSymbols.fGetGraphicsResetStatus();
+    } else {
+        if (!MakeCurrent(true)) {
+            ret = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
+        }
+    }
+
+    if (mDebugFlags & DebugFlagTrace) {
+        printf_stderr("[gl:%p] GetGraphicsResetStatus() -> 0x%04x\n", this, ret);
+    }
+
+    return ret;
+}
+
+void
+GLContext::OnContextLostError() const
+{
+    if (mDebugFlags & DebugFlagTrace) {
+        printf_stderr("[gl:%p] CONTEXT_LOST\n", this);
+    }
+    mContextLost = true;
+}
+
+// --
+
+/*static*/ const char*
+GLContext::GLErrorToString(const GLenum err)
+{
+    switch (err) {
+    case LOCAL_GL_NO_ERROR:
+        return "GL_NO_ERROR";
+    case LOCAL_GL_INVALID_ENUM:
+        return "GL_INVALID_ENUM";
+    case LOCAL_GL_INVALID_VALUE:
+        return "GL_INVALID_VALUE";
+    case LOCAL_GL_INVALID_OPERATION:
+        return "GL_INVALID_OPERATION";
+    case LOCAL_GL_STACK_OVERFLOW:
+        return "GL_STACK_OVERFLOW";
+    case LOCAL_GL_STACK_UNDERFLOW:
+        return "GL_STACK_UNDERFLOW";
+    case LOCAL_GL_OUT_OF_MEMORY:
+        return "GL_OUT_OF_MEMORY";
+    case LOCAL_GL_TABLE_TOO_LARGE:
+        return "GL_TABLE_TOO_LARGE";
+    case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION:
+        return "GL_INVALID_FRAMEBUFFER_OPERATION";
+    case LOCAL_GL_CONTEXT_LOST:
+        return "GL_CONTEXT_LOST";
+    }
+
+    return "<unknown>";
+}
+
 // --
 
 void
 GLContext::BeforeGLCall_Debug(const char* const funcName) const
 {
     MOZ_ASSERT(mDebugFlags);
 
-    FlushErrors();
-
     if (mDebugFlags & DebugFlagTrace) {
         printf_stderr("[gl:%p] > %s\n", this, funcName);
     }
+
+    MOZ_ASSERT(!mDebugErrorScope);
+    mDebugErrorScope.reset(new LocalErrorScope(*this));
 }
 
 void
 GLContext::AfterGLCall_Debug(const char* const funcName) const
 {
     MOZ_ASSERT(mDebugFlags);
 
     // calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
     // the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
     // tend to be meaningless
     mSymbols.fFinish();
-    GLenum err = FlushErrors();
+
+    const auto err = mDebugErrorScope->GetError();
+    mDebugErrorScope = nullptr;
+    if (!mTopError) {
+        mTopError = err;
+    }
 
     if (mDebugFlags & DebugFlagTrace) {
-        printf_stderr("[gl:%p] < %s [%s (0x%04x)]\n", this, funcName,
-                      GLErrorToString(err), err);
+        printf_stderr("[gl:%p] < %s [%s]\n", this, funcName,
+                      GLErrorToString(err));
     }
 
-    if (err != LOCAL_GL_NO_ERROR &&
-        !mLocalErrorScopeStack.size())
-    {
-        printf_stderr("[gl:%p] %s: Generated unexpected %s error."
-                      " (0x%04x)\n", this, funcName,
-                      GLErrorToString(err), err);
+    if (err && !mLocalErrorScopeStack.size()) {
+        printf_stderr("[gl:%p] %s: Generated unexpected %s error.\n", this, funcName,
+                      GLErrorToString(err));
 
         if (mDebugFlags & DebugFlagAbortOnError) {
             MOZ_CRASH("Unexpected error with MOZ_GL_DEBUG_ABORT_ON_ERROR. (Run"
                       " with MOZ_GL_DEBUG_ABORT_ON_ERROR=0 to disable)");
         }
     }
 }
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -335,17 +335,16 @@ public:
      * Get the default framebuffer for this context.
      */
     virtual GLuint GetDefaultFramebuffer() {
         return 0;
     }
 
 protected:
     bool mIsOffscreen;
-    mutable bool mContextLost = false;
 
     /**
      * mVersion store the OpenGL's version, multiplied by 100. For example, if
      * the context is an OpenGL 2.1 context, mVersion value will be 210.
      */
     uint32_t mVersion = 0;
     ContextProfile mProfile = ContextProfile::Unknown;
 
@@ -548,113 +547,79 @@ private:
     /**
      * Is this feature supported using the core (unsuffixed) symbols?
      */
     bool IsFeatureProvidedByCoreSymbols(GLFeature feature);
 
 public:
 // -----------------------------------------------------------------------------
 // Error handling
-    static const char* GLErrorToString(GLenum aError) {
-        switch (aError) {
-            case LOCAL_GL_INVALID_ENUM:
-                return "GL_INVALID_ENUM";
-            case LOCAL_GL_INVALID_VALUE:
-                return "GL_INVALID_VALUE";
-            case LOCAL_GL_INVALID_OPERATION:
-                return "GL_INVALID_OPERATION";
-            case LOCAL_GL_STACK_OVERFLOW:
-                return "GL_STACK_OVERFLOW";
-            case LOCAL_GL_STACK_UNDERFLOW:
-                return "GL_STACK_UNDERFLOW";
-            case LOCAL_GL_OUT_OF_MEMORY:
-                return "GL_OUT_OF_MEMORY";
-            case LOCAL_GL_TABLE_TOO_LARGE:
-                return "GL_TABLE_TOO_LARGE";
-            case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION:
-                return "GL_INVALID_FRAMEBUFFER_OPERATION";
-            default:
-                return "";
-        }
-    }
-
 private:
+    mutable bool mContextLost = false;
     mutable GLenum mTopError = 0;
 
-    GLenum RawGetError() const {
-        return mSymbols.fGetError();
-    }
-
-    GLenum RawGetErrorAndClear() const {
-        GLenum err = RawGetError();
-
-        if (err)
-            while (RawGetError()) {}
-
-        return err;
-    }
-
-    GLenum FlushErrors() const {
-        GLenum err = RawGetErrorAndClear();
-        if (!mTopError)
-            mTopError = err;
-        return err;
-    }
+protected:
+    void OnContextLostError() const;
+
+public:
+    static const char* GLErrorToString(GLenum aError);
+
+    static bool IsBadCallError(const GLenum err) {
+        return !(err == 0 ||
+                 err == LOCAL_GL_CONTEXT_LOST);
+    }
+
+    class LocalErrorScope;
+private:
+    mutable std::stack<const LocalErrorScope*> mLocalErrorScopeStack;
+    mutable UniquePtr<LocalErrorScope> mDebugErrorScope;
 
     ////////////////////////////////////
     // Use this safer option.
 
 public:
-    class LocalErrorScope;
-
-private:
-    std::stack<const LocalErrorScope*> mLocalErrorScopeStack;
-
-public:
     class LocalErrorScope {
-        GLContext& mGL;
+        const GLContext& mGL;
         GLenum mOldTop;
         bool mHasBeenChecked;
 
     public:
-        explicit LocalErrorScope(GLContext& gl)
+        explicit LocalErrorScope(const GLContext& gl)
             : mGL(gl)
             , mHasBeenChecked(false)
         {
             mGL.mLocalErrorScopeStack.push(this);
-
-            mGL.FlushErrors();
-
-            mOldTop = mGL.mTopError;
-            mGL.mTopError = LOCAL_GL_NO_ERROR;
+            mOldTop = mGL.GetError();
         }
 
+        /// Never returns CONTEXT_LOST.
         GLenum GetError() {
             MOZ_ASSERT(!mHasBeenChecked);
             mHasBeenChecked = true;
 
-            const GLenum ret = mGL.fGetError();
-
-            while (mGL.fGetError()) {}
-
+            const auto ret = mGL.GetError();
+            if (ret == LOCAL_GL_CONTEXT_LOST)
+                return 0;
             return ret;
         }
 
         ~LocalErrorScope() {
             MOZ_ASSERT(mHasBeenChecked);
 
-            MOZ_ASSERT(mGL.fGetError() == LOCAL_GL_NO_ERROR);
+            MOZ_ASSERT(!IsBadCallError(mGL.GetError()));
 
             MOZ_ASSERT(mGL.mLocalErrorScopeStack.top() == this);
             mGL.mLocalErrorScopeStack.pop();
 
             mGL.mTopError = mOldTop;
         }
     };
 
+    // -
+
     bool GetPotentialInteger(GLenum pname, GLint* param) {
         LocalErrorScope localError(*this);
 
         fGetIntegerv(pname, param);
 
         GLenum err = localError.GetError();
         MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_INVALID_ENUM);
         return err == LOCAL_GL_NO_ERROR;
@@ -709,30 +674,32 @@ private:
 
     void BeforeGLCall_Debug(const char* funcName) const;
     void AfterGLCall_Debug(const char* funcName) const;
     static void OnImplicitMakeCurrentFailure(const char* funcName);
 
     bool BeforeGLCall(const char* const funcName) const {
         if (mImplicitMakeCurrent) {
             if (MOZ_UNLIKELY( !MakeCurrent() )) {
-                OnImplicitMakeCurrentFailure(funcName);
+                if (!mContextLost) {
+                    OnImplicitMakeCurrentFailure(funcName);
+                }
                 return false;
             }
         }
         MOZ_ASSERT(IsCurrentImpl());
 
-        if (mDebugFlags) {
+        if (MOZ_UNLIKELY( mDebugFlags )) {
             BeforeGLCall_Debug(funcName);
         }
         return true;
     }
 
     void AfterGLCall(const char* const funcName) const {
-        if (mDebugFlags) {
+        if (MOZ_UNLIKELY( mDebugFlags )) {
             AfterGLCall_Debug(funcName);
         }
     }
 
     GLContext* TrackingContext()
     {
         GLContext* tip = this;
         while (tip->mSharedContext)
@@ -796,27 +763,27 @@ public:
 
     void ResetSyncCallCount(const char* resetReason) const;
 
 // -----------------------------------------------------------------------------
 // GL official entry points
 public:
     // We smash all errors together, so you never have to loop on this. We
     // guarantee that immediately after this call, there are no errors left.
+    // Always returns the top-most error, except if followed by CONTEXT_LOST, then return
+    // that instead.
+    GLenum GetError() const;
+
     GLenum fGetError() {
-        GLenum err = LOCAL_GL_CONTEXT_LOST;
-        BEFORE_GL_CALL;
-
-        FlushErrors();
-        err = mTopError;
-        mTopError = LOCAL_GL_NO_ERROR;
-
-        AFTER_GL_CALL;
-        return err;
-    }
+        return GetError();
+    }
+
+    GLenum fGetGraphicsResetStatus() const;
+
+    // -
 
     void fActiveTexture(GLenum texture) {
         BEFORE_GL_CALL;
         mSymbols.fActiveTexture(texture);
         AFTER_GL_CALL;
     }
 
     void fAttachShader(GLuint program, GLuint shader) {
@@ -2295,26 +2262,16 @@ public:
         TRACKING_CONTEXT(DeletedRenderbuffers(this, n, names));
     }
 
     void fDeleteTextures(GLsizei n, const GLuint* names) {
         raw_fDeleteTextures(n, names);
         TRACKING_CONTEXT(DeletedTextures(this, n, names));
     }
 
-    GLenum fGetGraphicsResetStatus() {
-        GLenum ret = 0;
-        BEFORE_GL_CALL;
-        ASSERT_SYMBOL_PRESENT(fGetGraphicsResetStatus);
-        ret = mSymbols.fGetGraphicsResetStatus();
-        OnSyncCall();
-        AFTER_GL_CALL;
-        return ret;
-    }
-
 
 // -----------------------------------------------------------------------------
 // Extension ARB_sync (GL)
 public:
     GLsync fFenceSync(GLenum condition, GLbitfield flags) {
         GLsync ret = 0;
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fFenceSync);
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -432,18 +432,17 @@ GLContextEGL::MakeCurrentImpl() const
         surface = mFallbackSurface;
     }
 
     const bool succeeded = mEgl->fMakeCurrent(EGL_DISPLAY(), surface, surface,
                                               mContext);
     if (!succeeded) {
         const auto eglError = mEgl->fGetError();
         if (eglError == LOCAL_EGL_CONTEXT_LOST) {
-            mContextLost = true;
-            NS_WARNING("EGL context has been lost.");
+            OnContextLostError();
         } else {
             NS_WARNING("Failed to make GL context current!");
 #ifdef DEBUG
             printf_stderr("EGL Error: 0x%04x\n", eglError);
 #endif
         }
     }
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -783,17 +783,16 @@ private:
   DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
   DECL_GFX_PREF(Live, "webgl.max-contexts",                    WebGLMaxContexts, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.max-contexts-per-principal",      WebGLMaxContextsPerPrincipal, uint32_t, 16);
   DECL_GFX_PREF(Live, "webgl.max-warnings-per-context",        WebGLMaxWarningsPerContext, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.min_capability_mode",             WebGLMinCapabilityMode, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-force",                      WebGLForceMSAA, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-samples",                    WebGLMsaaSamples, uint32_t, 4);
   DECL_GFX_PREF(Live, "webgl.prefer-16bpp",                    WebGLPrefer16bpp, bool, false);
-  DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
   DECL_GFX_PREF(Live, "webgl.allow-immediate-queries",         WebGLImmediateQueries, bool, false);
   DECL_GFX_PREF(Live, "webgl.allow-fb-invalidation",           WebGLFBInvalidation, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.perf.max-warnings",                    WebGLMaxPerfWarnings, int32_t, 0);
   DECL_GFX_PREF(Live, "webgl.perf.max-acceptable-fb-status-invals", WebGLMaxAcceptableFBStatusInvals, int32_t, 0);
   DECL_GFX_PREF(Live, "webgl.perf.spew-frame-allocs",          WebGLSpewFrameAllocs, bool, true);
 
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4775,17 +4775,16 @@ pref("webgl.min_capability_mode", false)
 pref("webgl.disable-extensions", false);
 pref("webgl.msaa-force", false);
 pref("webgl.prefer-16bpp", false);
 pref("webgl.default-no-alpha", false);
 pref("webgl.force-layers-readback", false);
 pref("webgl.force-index-validation", 0);
 pref("webgl.lose-context-on-memory-pressure", false);
 pref("webgl.can-lose-context-in-foreground", true);
-pref("webgl.restore-context-when-visible", true);
 #ifdef ANDROID
 pref("webgl.max-contexts", 16);
 pref("webgl.max-contexts-per-principal", 8);
 #else
 pref("webgl.max-contexts", 32);
 pref("webgl.max-contexts-per-principal", 16);
 #endif
 pref("webgl.max-warnings-per-context", 32);