Bug 1492580 - Repair CONTEXT_LOST handling. r=lsalzman
authorJeff Gilbert <jgilbert@mozilla.com>
Tue, 27 Nov 2018 04:23:19 +0000
changeset 504631 ff40e8ca1e42bc69b512e1e3e063ebc2ed394eb3
parent 504619 b0e69b1368832a3846c0940a6fb8bb834bdc62a1
child 504632 093ef9c7cb94cd97d45bc3b453f62128114dbb7a
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1492580
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1492580 - Repair CONTEXT_LOST handling. r=lsalzman Simplify error handling in GLContext. Modernize context loss handling in GLContext. Remove various unused parts. Fix WebGLContext's context loss/restoration. MozReview-Commit-ID: Lu2hi5HnP8x Differential Revision: https://phabricator.services.mozilla.com/D12496
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/GLBlitHelper.cpp
gfx/gl/GLBlitHelperD3D.cpp
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextGLX.h
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLDefs.h
gfx/gl/GLReadTexImageHelper.cpp
gfx/gl/GLScreenBuffer.cpp
gfx/gl/GLXLibrary.h
gfx/gl/ScopedGLHelpers.cpp
gfx/gl/ScopedGLHelpers.h
gfx/layers/client/CanvasClient.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;
@@ -1535,78 +1517,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
 {
@@ -1725,40 +1686,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"),
@@ -1768,28 +1727,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
@@ -359,17 +359,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*,
@@ -1488,33 +1487,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;
 
@@ -1930,17 +1924,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
@@ -903,63 +903,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)) {
         AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING,
                                mBoundVertexArray->mGLName);
     }
 
     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/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -373,37 +373,37 @@ public:
 DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog)
     : mParent(*parent)
     , mProg(prog)
     , mLoc_uDestMatrix(mParent.mGL->fGetUniformLocation(mProg, "uDestMatrix"))
     , mLoc_uTexMatrix0(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix0"))
     , mLoc_uTexMatrix1(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix1"))
     , mLoc_uColorMatrix(mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix"))
 {
-    MOZ_ASSERT(mLoc_uDestMatrix != -1);
-    MOZ_ASSERT(mLoc_uTexMatrix0 != -1);
+    const auto& gl = mParent.mGL;
+    MOZ_GL_ASSERT(gl, mLoc_uDestMatrix != -1);
+    MOZ_GL_ASSERT(gl, mLoc_uTexMatrix0 != -1);
     if (mLoc_uColorMatrix != -1) {
-        MOZ_ASSERT(mLoc_uTexMatrix1 != -1);
+        MOZ_GL_ASSERT(gl, mLoc_uTexMatrix1 != -1);
 
-        const auto& gl = mParent.mGL;
         int32_t numActiveUniforms = 0;
         gl->fGetProgramiv(mProg, LOCAL_GL_ACTIVE_UNIFORMS, &numActiveUniforms);
 
         const size_t kMaxNameSize = 32;
         char name[kMaxNameSize] = {0};
         GLint size = 0;
         GLenum type = 0;
         for (int32_t i = 0; i < numActiveUniforms; i++) {
             gl->fGetActiveUniform(mProg, i, kMaxNameSize, nullptr, &size, &type, name);
             if (strcmp("uColorMatrix", name) == 0) {
                 mType_uColorMatrix = type;
                 break;
             }
         }
-        MOZ_ASSERT(mType_uColorMatrix);
+        MOZ_GL_ASSERT(gl, mType_uColorMatrix);
     }
 }
 
 DrawBlitProg::~DrawBlitProg()
 {
     const auto& gl = mParent.mGL;
     if (!gl->MakeCurrent())
         return;
@@ -665,17 +665,17 @@ GLBlitHelper::CreateDrawBlitProg(const D
     mGL->fAttachShader(prog, mDrawBlitProg_VertShader);
     mGL->fAttachShader(prog, fs);
 
     mGL->fBindAttribLocation(prog, 0, "aPosition");
     mGL->fLinkProgram(prog);
 
     GLenum status = 0;
     mGL->fGetProgramiv(prog, LOCAL_GL_LINK_STATUS, (GLint*)&status);
-    if (status == LOCAL_GL_TRUE) {
+    if (status == LOCAL_GL_TRUE || !mGL->CheckContextLost()) {
         const SaveRestoreCurrentProgram oldProg(mGL);
         mGL->fUseProgram(prog);
         const char* samplerNames[] = {
             "uTex0",
             "uTex1",
             "uTex2"
         };
         for (int i = 0; i < 3; i++) {
@@ -706,17 +706,17 @@ GLBlitHelper::CreateDrawBlitProg(const D
     const UniquePtr<char[]> fsLog(new char[fsLogLen+1]);
     mGL->fGetShaderInfoLog(fs, fsLogLen, nullptr, fsLog.get());
     fsLog[fsLogLen] = 0;
 
     gfxCriticalError() << "DrawBlitProg link failed:\n"
                        << "progLog: " << progLog.get() << "\n"
                        << "vsLog: " << vsLog.get() << "\n"
                        << "fsLog: " << fsLog.get() << "\n";
-    return nullptr;
+    MOZ_CRASH();
 }
 
 // -----------------------------------------------------------------------------
 
 bool
 GLBlitHelper::BlitImageToFramebuffer(layers::Image* const srcImage,
                                      const gfx::IntSize& destSize,
                                      const OriginPos destOrigin)
@@ -803,17 +803,16 @@ GLBlitHelper::BlitImage(layers::SurfaceT
     const auto& srcOrigin = srcImage->GetOriginPos();
 
     // I honestly have no idea why this logic is flipped, but changing the
     // source origin would mean we'd have to flip it in the compositor
     // which makes just as little sense as this.
     const bool yFlip = (srcOrigin == destOrigin);
 
     const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_RGBA});
-    MOZ_RELEASE_ASSERT(prog);
 
     // There is no padding on these images, so we can use the GetTransformMatrix directly.
     const DrawBlitProg::BaseArgs baseArgs = { transform3, yFlip, destSize, Nothing() };
     prog->Draw(baseArgs, nullptr);
 
     if (surfaceTexture->IsSingleBuffer()) {
         surfaceTexture->ReleaseTexImage();
     }
@@ -839,17 +838,16 @@ GuessDivisors(const gfx::IntSize& ySize,
     return true;
 }
 
 bool
 GLBlitHelper::BlitImage(layers::PlanarYCbCrImage* const yuvImage,
                         const gfx::IntSize& destSize, const OriginPos destOrigin)
 {
     const auto& prog = GetDrawBlitProg({kFragHeader_Tex2D, kFragBody_PlanarYUV});
-    MOZ_RELEASE_ASSERT(prog);
 
     if (!mYuvUploads[0]) {
         mGL->fGenTextures(3, mYuvUploads);
         const ScopedBindTexture bindTex(mGL, mYuvUploads[0]);
         mGL->TexParams_SetClampNoMips();
         mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
         mGL->TexParams_SetClampNoMips();
         mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
@@ -1103,19 +1101,16 @@ GLBlitHelper::BlitImage(layers::MacIOSur
 
         if (p == 0) {
             baseArgs.texMatrix0 = SubRectMat3(0, 0, width, height);
             yuvArgs.texMatrix1 = SubRectMat3(0, 0, width / 2.0, height / 2.0);
         }
     }
 
     const auto& prog = GetDrawBlitProg({fragHeader, fragBody});
-    if (!prog)
-        return false;
-
     prog->Draw(baseArgs, pYuvArgs);
     return true;
 }
 #endif
 
 // -----------------------------------------------------------------------------
 
 void
@@ -1135,17 +1130,16 @@ GLBlitHelper::DrawBlitTextureToFramebuff
         fragHeader = kFragHeader_Tex2DRect;
         texMatrix0 = SubRectMat3(0, 0, srcSize.width, srcSize.height);
         break;
     default:
         gfxCriticalError() << "Unexpected srcTarget: " << srcTarget;
         return;
     }
     const auto& prog = GetDrawBlitProg({ fragHeader, kFragBody_RGBA});
-    MOZ_ASSERT(prog);
 
     const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget);
     mGL->fBindTexture(srcTarget, srcTex);
 
     const bool yFlip = false;
     const DrawBlitProg::BaseArgs baseArgs = { texMatrix0, yFlip, destSize, Nothing() };
     prog->Draw(baseArgs);
 }
@@ -1170,32 +1164,32 @@ GLBlitHelper::BlitFramebuffer(const gfx:
 
 void
 GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB, const GLuint destFB,
                                            const gfx::IntSize& srcSize,
                                            const gfx::IntSize& destSize,
                                            GLuint filter) const
 {
     MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
-    MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
-    MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
+    MOZ_GL_ASSERT(mGL, !srcFB || mGL->fIsFramebuffer(srcFB));
+    MOZ_GL_ASSERT(mGL, !destFB || mGL->fIsFramebuffer(destFB));
 
     const ScopedBindFramebuffer boundFB(mGL);
     mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcFB);
     mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFB);
 
     BlitFramebuffer(srcSize, destSize, filter);
 }
 
 void
 GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, const gfx::IntSize& srcSize,
                                        const gfx::IntSize& destSize,
                                        GLenum srcTarget) const
 {
-    MOZ_ASSERT(mGL->fIsTexture(srcTex));
+    MOZ_GL_ASSERT(mGL, mGL->fIsTexture(srcTex));
 
     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
         const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
         const ScopedBindFramebuffer bindFB(mGL);
         mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcWrapper.FB());
         BlitFramebuffer(srcSize, destSize);
         return;
     }
@@ -1204,17 +1198,17 @@ GLBlitHelper::BlitTextureToFramebuffer(G
 }
 
 void
 GLBlitHelper::BlitFramebufferToTexture(GLuint destTex,
                                        const gfx::IntSize& srcSize,
                                        const gfx::IntSize& destSize,
                                        GLenum destTarget) const
 {
-    MOZ_ASSERT(mGL->fIsTexture(destTex));
+    MOZ_GL_ASSERT(mGL, mGL->fIsTexture(destTex));
 
     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
         const ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget);
         const ScopedBindFramebuffer bindFB(mGL);
         mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destWrapper.FB());
         BlitFramebuffer(srcSize, destSize);
         return;
     }
@@ -1228,18 +1222,18 @@ GLBlitHelper::BlitFramebufferToTexture(G
 }
 
 void
 GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex,
                                    const gfx::IntSize& srcSize,
                                    const gfx::IntSize& destSize,
                                    GLenum srcTarget, GLenum destTarget) const
 {
-    MOZ_ASSERT(mGL->fIsTexture(srcTex));
-    MOZ_ASSERT(mGL->fIsTexture(destTex));
+    MOZ_GL_ASSERT(mGL, mGL->fIsTexture(srcTex));
+    MOZ_GL_ASSERT(mGL, mGL->fIsTexture(destTex));
 
     // Start down the CopyTexSubImage path, not the DrawBlit path.
     const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
     const ScopedBindFramebuffer bindFB(mGL, srcWrapper.FB());
     BlitFramebufferToTexture(destTex, srcSize, destSize, destTarget);
 }
 
 } // namespace gl
--- a/gfx/gl/GLBlitHelperD3D.cpp
+++ b/gfx/gl/GLBlitHelperD3D.cpp
@@ -258,17 +258,17 @@ GLBlitHelper::BlitDescriptor(const layer
         format != gfx::SurfaceFormat::P016) {
         gfxCriticalError() << "Non-NV12 format for SurfaceDescriptorD3D10: "
                            << uint32_t(format);
       return false;
     }
 
     const auto tex = OpenSharedTexture(d3d, handle);
     if (!tex) {
-        MOZ_ASSERT(false, "Get a nullptr from OpenSharedResource.");
+        MOZ_GL_ASSERT(mGL, false); // Get a nullptr from OpenSharedResource.
         return false;
     }
     const RefPtr<ID3D11Texture2D> texList[2] = { tex, tex };
     const EGLAttrib postAttribs0[] = {
         LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 0,
         LOCAL_EGL_NONE
     };
     const EGLAttrib postAttribs1[] = {
@@ -276,17 +276,17 @@ GLBlitHelper::BlitDescriptor(const layer
         LOCAL_EGL_NONE
     };
     const EGLAttrib* const postAttribsList[2] = { postAttribs0, postAttribs1 };
     // /layers/d3d11/CompositorD3D11.cpp uses bt601 for EffectTypes::NV12.
     //return BlitAngleNv12(tex, YUVColorSpace::BT601, destSize, destOrigin);
 
     const BindAnglePlanes bindPlanes(this, 2, texList, postAttribsList);
     if (!bindPlanes.Success()) {
-        MOZ_ASSERT(false, "BindAnglePlanes failed.");
+        MOZ_GL_ASSERT(mGL, false); // BindAnglePlanes failed.
         return false;
     }
 
     D3D11_TEXTURE2D_DESC texDesc = {0};
     tex->GetDesc(&texDesc);
 
     const gfx::IntSize ySize(texDesc.Width, texDesc.Height);
     const gfx::IntSize divisors(2, 2);
@@ -301,17 +301,16 @@ GLBlitHelper::BlitDescriptor(const layer
         yFlip, destSize, Nothing()
     };
     const DrawBlitProg::YUVArgs yuvArgs = {
         SubRectMat3(clipRect, uvSize, divisors),
         colorSpace
     };
 
     const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_NV12});
-    MOZ_RELEASE_ASSERT(prog);
     prog->Draw(baseArgs, &yuvArgs);
     return true;
 }
 
 // --
 
 bool
 GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3],
@@ -343,15 +342,14 @@ GLBlitHelper::BlitAngleYCbCr(const Windo
         yFlip, destSize, Nothing()
     };
     const DrawBlitProg::YUVArgs yuvArgs = {
         SubRectMat3(clipRect, uvSize, divisors),
         colorSpace
     };
 
     const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_PlanarYUV});
-    MOZ_RELEASE_ASSERT(prog);
     prog->Draw(baseArgs, &yuvArgs);
     return true;
 }
 
 } // namespace gl
 } // namespace mozilla
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -268,17 +268,18 @@ GLContext::ChooseDebugFlags(const Create
 }
 
 GLContext::GLContext(CreateContextFlags flags, const SurfaceCaps& caps,
                      GLContext* sharedContext, bool isOffscreen, bool useTLSIsCurrent)
   : mUseTLSIsCurrent(ShouldUseTLSIsCurrent(useTLSIsCurrent)),
     mIsOffscreen(isOffscreen),
     mDebugFlags(ChooseDebugFlags(flags)),
     mSharedContext(sharedContext),
-    mCaps(caps)
+    mCaps(caps),
+    mWorkAroundDriverBugs(gfxPrefs::WorkAroundDriverBugs())
 {
     mOwningThreadId = PlatformThread::CurrentId();
     MOZ_ALWAYS_TRUE( sCurrentContext.init() );
     sCurrentContext.set(0);
 }
 
 GLContext::~GLContext() {
     NS_ASSERTION(IsDestroyed(), "GLContext implementation must call MarkDestroyed in destructor!");
@@ -374,17 +375,18 @@ GLContext::LoadFeatureSymbols(const char
         return false;
     }
     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 } },
@@ -508,22 +510,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");
@@ -845,18 +870,18 @@ GLContext::InitWithPrefixImpl(const char
     {
         NS_ERROR("GLContext requires support for framebuffer objects.");
         return false;
     }
     MOZ_RELEASE_ASSERT(mSymbols.fBindFramebuffer, "GFX: mSymbols.fBindFramebuffer zero or not set.");
 
     ////////////////
 
-    const auto err = mSymbols.fGetError();
-    MOZ_RELEASE_ASSERT(!err);
+    const auto err = fGetError();
+    MOZ_RELEASE_ASSERT(!IsBadCallError(err));
     if (err)
         return false;
 
     LoadMoreSymbols(prefix, trygl);
 
     ////////////////////////////////////////////////////////////////////////////
 
     raw_fGetIntegerv(LOCAL_GL_VIEWPORT, mViewportRect);
@@ -954,17 +979,17 @@ GLContext::InitWithPrefixImpl(const char
 
     // TODO: Remove SurfaceCaps::any.
     if (mCaps.any) {
         mCaps.any = false;
         mCaps.color = true;
         mCaps.alpha = false;
     }
 
-    MOZ_ASSERT(IsCurrent());
+    MOZ_GL_ASSERT(this, IsCurrent());
 
     if (ShouldSpew() && IsExtensionSupported(KHR_debug)) {
         fEnable(LOCAL_GL_DEBUG_OUTPUT);
         fDisable(LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS);
         fDebugMessageCallback(&StaticDebugCallback, (void*)this);
         fDebugMessageControl(LOCAL_GL_DONT_CARE,
                              LOCAL_GL_DONT_CARE,
                              LOCAL_GL_DONT_CARE,
@@ -1007,33 +1032,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 } },
@@ -1619,17 +1627,17 @@ GLContext::DebugCallback(GLenum source,
                   typeStr.BeginReading(),
                   sevStr.BeginReading(),
                   message);
 }
 
 void
 GLContext::InitExtensions()
 {
-    MOZ_ASSERT(IsCurrent());
+    MOZ_GL_ASSERT(this, IsCurrent());
 
     std::vector<nsCString> driverExtensionList;
 
     [&]() {
         if (mSymbols.fGetStringi) {
             GLuint count = 0;
             if (GetPotentialInteger(LOCAL_GL_NUM_EXTENSIONS, (GLint*)&count)) {
                 for (GLuint i = 0; i < count; i++) {
@@ -1647,17 +1655,17 @@ GLContext::InitExtensions()
 
         const char* rawExts = (const char*)fGetString(LOCAL_GL_EXTENSIONS);
         if (rawExts) {
             nsDependentCString exts(rawExts);
             SplitByChar(exts, ' ', &driverExtensionList);
         }
     }();
     const auto err = fGetError();
-    MOZ_ALWAYS_TRUE(!err);
+    MOZ_ALWAYS_TRUE(!IsBadCallError(err));
 
     const bool shouldDumpExts = ShouldDumpExts();
     if (shouldDumpExts) {
         printf_stderr("%i GL driver extensions: (*: recognized)\n",
                       (uint32_t)driverExtensionList.size());
     }
 
     MarkBitfieldByStrings(driverExtensionList, shouldDumpExts, sExtensionNames,
@@ -1879,17 +1887,17 @@ GLContext::ChooseGLFormats(const Surface
 }
 
 bool
 GLContext::IsFramebufferComplete(GLuint fb, GLenum* pStatus)
 {
     MOZ_ASSERT(fb);
 
     ScopedBindFramebuffer autoFB(this, fb);
-    MOZ_ASSERT(fIsFramebuffer(fb));
+    MOZ_GL_ASSERT(this, fIsFramebuffer(fb));
 
     GLenum status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     if (pStatus)
         *pStatus = status;
 
     return status == LOCAL_GL_FRAMEBUFFER_COMPLETE;
 }
 
@@ -1897,46 +1905,52 @@ void
 GLContext::AttachBuffersToFB(GLuint colorTex, GLuint colorRB,
                              GLuint depthRB, GLuint stencilRB,
                              GLuint fb, GLenum target)
 {
     MOZ_ASSERT(fb);
     MOZ_ASSERT( !(colorTex && colorRB) );
 
     ScopedBindFramebuffer autoFB(this, fb);
-    MOZ_ASSERT(fIsFramebuffer(fb)); // It only counts after being bound.
+    MOZ_GL_ASSERT(this, fIsFramebuffer(fb)); // It only counts after being bound.
 
     if (colorTex) {
-        MOZ_ASSERT(fIsTexture(colorTex));
+        MOZ_GL_ASSERT(this, fIsTexture(colorTex));
         MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D ||
                    target == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
         fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                               LOCAL_GL_COLOR_ATTACHMENT0,
                               target,
                               colorTex,
                               0);
     } else if (colorRB) {
         // On the Android 4.3 emulator, IsRenderbuffer may return false incorrectly.
-        MOZ_ASSERT_IF(Renderer() != GLRenderer::AndroidEmulator, fIsRenderbuffer(colorRB));
+        MOZ_GL_ASSERT(this,
+                      fIsRenderbuffer(colorRB) ||
+                      Renderer() == GLRenderer::AndroidEmulator);
         fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_COLOR_ATTACHMENT0,
                                  LOCAL_GL_RENDERBUFFER,
                                  colorRB);
     }
 
     if (depthRB) {
-        MOZ_ASSERT_IF(Renderer() != GLRenderer::AndroidEmulator, fIsRenderbuffer(depthRB));
+        MOZ_GL_ASSERT(this,
+                      fIsRenderbuffer(depthRB) ||
+                      Renderer() == GLRenderer::AndroidEmulator);
         fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_DEPTH_ATTACHMENT,
                                  LOCAL_GL_RENDERBUFFER,
                                  depthRB);
     }
 
     if (stencilRB) {
-        MOZ_ASSERT_IF(Renderer() != GLRenderer::AndroidEmulator, fIsRenderbuffer(stencilRB));
+        MOZ_GL_ASSERT(this,
+                      fIsRenderbuffer(stencilRB) ||
+                      Renderer() == GLRenderer::AndroidEmulator);
         fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_STENCIL_ATTACHMENT,
                                  LOCAL_GL_RENDERBUFFER,
                                  stencilRB);
     }
 }
 
 bool
@@ -2047,47 +2061,22 @@ 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 = {};
 }
 
 // -
 
-GLenum
-GLContext::RawGetErrorAndClear() const
-{
-    const GLenum ret = mSymbols.fGetError();
-
-    auto flushedErr = ret;
-    uint32_t i = 1;
-    while (flushedErr && flushedErr != LOCAL_GL_CONTEXT_LOST) {
-        if (i == 100) {
-            gfxCriticalError() << "Flushing glGetError still " << gfx::hexa(flushedErr)
-                               << " after " << i << " calls.";
-            break;
-        }
-        flushedErr = mSymbols.fGetError();
-        i += 1;
-    }
-
-    return ret;
-}
-
-// -
-
 #ifdef MOZ_GL_DEBUG
 /* static */ void
 GLContext::AssertNotPassingStackBufferToTheGL(const void* ptr)
 {
   int somethingOnTheStack;
   const void* someStackPtr = &somethingOnTheStack;
   const int page_bits = 12;
   intptr_t page = reinterpret_cast<uintptr_t>(ptr) >> page_bits;
@@ -2501,17 +2490,17 @@ GLContext::Readback(SharedSurface* src, 
                 fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                          LOCAL_GL_RENDERBUFFER, src->ProdRenderbuffer());
                 break;
             default:
                 MOZ_CRASH("GFX: bad `src->mAttachType`.");
             }
 
             DebugOnly<GLenum> status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
-            MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
+            MOZ_GL_ASSERT(this, status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
         }
 
         if (src->NeedsIndirectReads()) {
             fGenTextures(1, &tempTex);
             {
                 ScopedBindTexture autoTex(this, tempTex);
 
                 GLenum format = src->mHasAlpha ? LOCAL_GL_RGBA
@@ -2963,28 +2952,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));
@@ -2997,52 +2986,178 @@ GLContext::ResetSyncCallCount(const char
     if (ShouldSpew()) {
         printf_stderr("On %s, mSyncGLCallCount = %" PRIu64 "\n",
                        resetReason, mSyncGLCallCount);
     }
 
     mSyncGLCallCount = 0;
 }
 
+// -
+
+bool
+CheckContextLost(const GLContext* const gl)
+{
+    return gl->CheckContextLost();
+}
+
+// -
+
+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.c_str());
+    }
+    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*/ std::string
+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";
+    }
+
+    const nsPrintfCString hex("<enum 0x%04x>", err);
+    return hex.BeginReading();
+}
+
 // --
 
 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).c_str());
     }
 
-    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).c_str());
 
         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
@@ -301,16 +301,21 @@ public:
     GLRenderer Renderer() const {
         return mRenderer;
     }
 
     bool IsContextLost() const {
         return mContextLost;
     }
 
+    bool CheckContextLost() const {
+        mTopError = GetError();
+        return IsContextLost();
+    }
+
     bool HasPBOState() const {
         return (!IsGLES() || Version() >= 300);
     }
 
     /**
      * If this context is double-buffered, returns TRUE.
      */
     virtual bool IsDoubleBuffered() const {
@@ -335,17 +340,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;
 
@@ -546,105 +550,82 @@ private:
      */
     void MarkUnsupported(GLFeature feature);
 
     /**
      * 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 RawGetErrorAndClear() const;
-
-    GLenum FlushErrors() const {
-        GLenum err = RawGetErrorAndClear();
-        if (!mTopError)
-            mTopError = err;
-        return err;
-    }
+protected:
+    void OnContextLostError() const;
+
+public:
+    static std::string 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;
@@ -696,33 +677,36 @@ private:
             AfterGLCall(MOZ_FUNCTION_NAME); \
         } \
         do { } while (0)
 
     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 {
+    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) {
+        MOZ_GL_ASSERT(this, IsCurrentImpl());
+
+        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)
@@ -786,27 +770,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) {
@@ -2285,27 +2269,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);
         ret = mSymbols.fFenceSync(condition, flags);
@@ -3360,17 +3333,17 @@ public:
     virtual bool Init() = 0;
 
     virtual bool SetupLookupFunction() = 0;
 
     virtual void ReleaseSurface() {}
 
     bool IsDestroyed() const {
         // MarkDestroyed will mark all these as null.
-        return mSymbols.fUseProgram == nullptr;
+        return mContextLost && mSymbols.fUseProgram == nullptr;
     }
 
     GLContext* GetSharedContext() { return mSharedContext; }
 
     /**
      * Returns true if the thread on which this context was created is the currently
      * executing thread.
      */
@@ -3414,17 +3387,17 @@ public:
      * Return size of this offscreen context.
      *
      * Only valid if IsOffscreen() returns true.
      */
     const gfx::IntSize& OffscreenSize() const;
 
     void BindFB(GLuint fb) {
         fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
-        MOZ_ASSERT(!fb || fIsFramebuffer(fb));
+        MOZ_GL_ASSERT(this, !fb || fIsFramebuffer(fb));
     }
 
     void BindDrawFB(GLuint fb) {
         fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, fb);
     }
 
     void BindReadFB(GLuint fb) {
         fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, fb);
@@ -3624,17 +3597,17 @@ protected:
     GLint mMaxTextureImageSize = 0;
     GLint mMaxRenderbufferSize = 0;
     GLint mMaxViewportDims[2] = {};
     GLsizei mMaxSamples = 0;
     bool mNeedsTextureSizeChecks = false;
     bool mNeedsFlushBeforeDeleteFB = false;
     bool mTextureAllocCrashesOnMapFailure = false;
     bool mNeedsCheckAfterAttachTextureToFb = false;
-    bool mWorkAroundDriverBugs = true;
+    const bool mWorkAroundDriverBugs;
     mutable uint64_t mSyncGLCallCount = 0;
 
     bool IsTextureSizeSafeToPassToDriver(GLenum target, GLsizei width, GLsizei height) const {
         if (mNeedsTextureSizeChecks) {
             // some drivers incorrectly handle some large texture sizes that are below the
             // max texture size that they report. So we check ourselves against our own values
             // (mMax[CubeMap]TextureSize).
             // see bug 737182 for Mac Intel 2D textures
--- a/gfx/gl/GLContextGLX.h
+++ b/gfx/gl/GLContextGLX.h
@@ -36,17 +36,17 @@ public:
 
     // Finds a GLXFBConfig compatible with the provided window.
     static bool
     FindFBConfigForWindow(Display* display, int screen, Window window,
                           ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
                           GLXFBConfig* const out_config, int* const out_visid,
                           bool aWebRender);
 
-    ~GLContextGLX();
+    ~GLContextGLX() override;
 
     virtual GLContextType GetContextType() const override { return GLContextType::GLX; }
 
     static GLContextGLX* Cast(GLContext* gl) {
         MOZ_ASSERT(gl->GetContextType() == GLContextType::GLX);
         return static_cast<GLContextGLX*>(gl);
     }
 
--- 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/gl/GLDefs.h
+++ b/gfx/gl/GLDefs.h
@@ -3,16 +3,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(LOCALGL_H_)
 #define LOCALGL_H_
 
 #include "GLTypes.h"
 #include "GLConsts.h"
 
+namespace mozilla {
+namespace gl {
+class GLContext;
+bool CheckContextLost(const GLContext* gl);
+}
+}
+
+#define MOZ_GL_ASSERT(glContext,expr) \
+  MOZ_ASSERT((expr) || mozilla::gl::CheckContextLost(glContext))
+
+// -
+
 // TODO: use official constant names instead of followed ones.
 
 // IMG_texture_compression_pvrtc
 #define LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1            0x8C00
 #define LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1            0x8C01
 #define LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1           0x8C02
 #define LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1           0x8C03
 
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -146,18 +146,18 @@ GLReadTexImageHelper::TextureImageProgra
     return mPrograms[variant];
 }
 
 bool
 GLReadTexImageHelper::DidGLErrorOccur(const char* str)
 {
     GLenum error = mGL->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
-        printf_stderr("GL ERROR: %s (0x%04x) %s\n",
-                      GLContext::GLErrorToString(error), error, str);
+        printf_stderr("GL ERROR: %s %s\n",
+                      GLContext::GLErrorToString(error).c_str(), str);
         return true;
     }
 
     return false;
 }
 
 bool
 GetActualReadFormats(GLContext* gl,
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -287,54 +287,52 @@ GLScreenBuffer::BindReadFB_Internal(GLui
 #endif
 }
 
 
 GLuint
 GLScreenBuffer::GetDrawFB() const
 {
 #ifdef DEBUG
-    MOZ_ASSERT(mGL->IsCurrent());
     MOZ_ASSERT(!mInInternalMode_DrawFB);
 
     // Don't need a branch here, because:
     // LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT == LOCAL_GL_FRAMEBUFFER_BINDING == 0x8CA6
     // We use raw_ here because this is debug code and we need to see what
     // the driver thinks.
     GLuint actual = 0;
     mGL->raw_fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, (GLint*)&actual);
 
     GLuint predicted = mInternalDrawFB;
-    if (predicted != actual) {
+    if (predicted != actual && !mGL->CheckContextLost()) {
         printf_stderr("Misprediction: Bound draw FB predicted: %d. Was: %d.\n",
                       predicted, actual);
         MOZ_ASSERT(false, "Draw FB binding misprediction!");
     }
 #endif
 
     return mUserDrawFB;
 }
 
 GLuint
 GLScreenBuffer::GetReadFB() const
 {
 #ifdef DEBUG
-    MOZ_ASSERT(mGL->IsCurrent());
     MOZ_ASSERT(!mInInternalMode_ReadFB);
 
     // We use raw_ here because this is debug code and we need to see what
     // the driver thinks.
     GLuint actual = 0;
     if (mGL->IsSupported(GLFeature::split_framebuffer))
         mGL->raw_fGetIntegerv(LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT, (GLint*)&actual);
     else
         mGL->raw_fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&actual);
 
     GLuint predicted = mInternalReadFB;
-    if (predicted != actual) {
+    if (predicted != actual && !mGL->CheckContextLost()) {
         printf_stderr("Misprediction: Bound read FB predicted: %d. Was: %d.\n",
                       predicted, actual);
         MOZ_ASSERT(false, "Read FB binding misprediction!");
     }
 #endif
 
     return mUserReadFB;
 }
@@ -995,17 +993,17 @@ ReadBuffer::Attach(SharedSurface* surf)
             colorRB = surf->ProdRenderbuffer();
             break;
         default:
             MOZ_CRASH("GFX: Unknown attachment type, attach?");
         }
 
         mGL->AttachBuffersToFB(colorTex, colorRB, 0, 0, mFB, target);
         mGL->mFBOMapping[mFB] = surf;
-        MOZ_ASSERT(mGL->IsFramebufferComplete(mFB));
+        MOZ_GL_ASSERT(mGL, mGL->IsFramebufferComplete(mFB));
     }
 
     mSurf = surf;
 }
 
 const gfx::IntSize&
 ReadBuffer::Size() const
 {
--- a/gfx/gl/GLXLibrary.h
+++ b/gfx/gl/GLXLibrary.h
@@ -23,16 +23,18 @@ typedef struct __GLXFBConfigRec* GLXFBCo
 #include "prenv.h"
 
 struct PRLibrary;
 class gfxASurface;
 
 namespace mozilla {
 namespace gl {
 
+class GLContextGLX;
+
 class GLXLibrary
 {
 public:
     bool EnsureInitialized();
 
 private:
     void BeforeGLXCall() const;
     void AfterGLXCall() const;
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -6,24 +6,16 @@
 #include "mozilla/UniquePtr.h"
 
 #include "GLContext.h"
 #include "ScopedGLHelpers.h"
 
 namespace mozilla {
 namespace gl {
 
-#ifdef DEBUG
-bool
-IsContextCurrent(GLContext* gl)
-{
-    return gl->IsCurrent();
-}
-#endif
-
 /* ScopedGLState - Wraps glEnable/glDisable. **********************************/
 
 // Use |newState = true| to enable, |false| to disable.
 ScopedGLState::ScopedGLState(GLContext* aGL, GLenum aCapability, bool aNewState)
     : ScopedGLWrapper<ScopedGLState>(aGL)
     , mCapability(aCapability)
 {
     mOldState = mGL->fIsEnabled(mCapability);
--- a/gfx/gl/ScopedGLHelpers.h
+++ b/gfx/gl/ScopedGLHelpers.h
@@ -29,28 +29,26 @@ protected:
     GLContext* const mGL;
 
     explicit ScopedGLWrapper(GLContext* gl)
         : mIsUnwrapped(false)
         , mGL(gl)
     {
         MOZ_ASSERT(&ScopedGLWrapper<Derived>::Unwrap == &Derived::Unwrap);
         MOZ_ASSERT(&Derived::UnwrapImpl);
-        MOZ_ASSERT(IsContextCurrent(mGL));
     }
 
     virtual ~ScopedGLWrapper() {
         if (!mIsUnwrapped)
             Unwrap();
     }
 
 public:
     void Unwrap() {
         MOZ_ASSERT(!mIsUnwrapped);
-        MOZ_ASSERT(IsContextCurrent(mGL));
 
         Derived* derived = static_cast<Derived*>(this);
         derived->UnwrapImpl();
 
         mIsUnwrapped = true;
     }
 };
 
@@ -217,17 +215,17 @@ public:
     ScopedFramebufferForTexture(GLContext* aGL, GLuint aTexture,
                                 GLenum aTarget = LOCAL_GL_TEXTURE_2D);
 
     bool IsComplete() const {
         return mComplete;
     }
 
     GLuint FB() const {
-        MOZ_ASSERT(IsComplete());
+        MOZ_GL_ASSERT(mGL, IsComplete());
         return mFB;
     }
 
 protected:
     void UnwrapImpl();
 };
 
 struct ScopedFramebufferForRenderbuffer
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -396,17 +396,18 @@ CanvasClientSharedSurface::UpdateRendere
   AsyncCanvasRenderer* asyncRenderer = nullptr;
   if (aRenderer.constructed<ShareableCanvasRenderer*>()) {
     canvasRenderer = aRenderer.ref<ShareableCanvasRenderer*>();
     gl = canvasRenderer->mGLContext;
   } else {
     asyncRenderer = aRenderer.ref<AsyncCanvasRenderer*>();
     gl = asyncRenderer->mGLContext;
   }
-  gl->MakeCurrent();
+  if (!gl->MakeCurrent())
+    return;
 
   RefPtr<TextureClient> newFront;
 
   mShSurfClient = nullptr;
   if (canvasRenderer && canvasRenderer->mGLFrontbuffer) {
     mShSurfClient = CloneSurface(canvasRenderer->mGLFrontbuffer.get(), canvasRenderer->mFactory.get());
     if (!mShSurfClient) {
       gfxCriticalError() << "Invalid canvas front buffer";
@@ -428,17 +429,17 @@ CanvasClientSharedSurface::UpdateRendere
   newFront = mShSurfClient;
 
   SharedSurface* surf = mShSurfClient->Surf();
 
   if (!surf->IsBufferAvailable()) {
     NS_WARNING("SharedSurface buffer not available, skip update");
     return;
   }
-  
+
   // Readback if needed.
   mReadbackClient = nullptr;
 
   auto forwarder = GetForwarder();
 
   bool needsReadback = (surf->mType == SharedSurfaceType::Basic);
   if (needsReadback) {
     TextureFlags flags = TextureFlags::IMMUTABLE;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -798,17 +798,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
@@ -4857,17 +4857,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);