diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -351,11 +351,10 @@ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext, nsICanvasRenderingContextInternal) virtual JSObject* WrapObject(JSContext* cx, JS::Handle 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(); } @@ -1482,11 +1481,10 @@ bool mOptionsFrozen; bool mDisableExtensions; bool mIsMesa; bool mLoseContextOnMemoryPressure; bool mCanLoseContextInForeground; - bool mRestoreWhenVisible; bool mShouldPresent; bool mDisableFragHighP; bool mVRReady; template @@ -1494,15 +1492,11 @@ 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; @@ -1924,11 +1918,10 @@ 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; diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -138,17 +138,13 @@ 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; @@ -164,11 +160,10 @@ mAllowContextRestore = true; mLastLossWasSimulated = false; mLoseContextOnMemoryPressure = false; mCanLoseContextInForeground = true; - mRestoreWhenVisible = false; mAlreadyGeneratedWarnings = 0; mAlreadyWarnedAboutFakeVertexAttrib0 = false; mAlreadyWarnedAboutViewportLargerThanDest = false; @@ -316,23 +311,10 @@ 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; @@ -1529,51 +1511,39 @@ 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" @@ -1582,19 +1552,10 @@ *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(); } @@ -1719,34 +1680,32 @@ // 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( @@ -1762,22 +1721,21 @@ 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; diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -228,12 +228,10 @@ 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: diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -908,23 +908,15 @@ RefPtr 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. @@ -933,32 +925,30 @@ * [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. + err = gl->fGetError(); + if (gl->IsContextLost()) { + UpdateContextLossStatus(); + return GetError(); + } + MOZ_ASSERT(err != LOCAL_GL_CONTEXT_LOST); - GetAndFlushUnderlyingGLErrors(); - - err = GetAndClearError(&mUnderlyingGLError); + 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) diff --git a/dom/canvas/WebGLContextUtils.cpp b/dom/canvas/WebGLContextUtils.cpp --- a/dom/canvas/WebGLContextUtils.cpp +++ b/dom/canvas/WebGLContextUtils.cpp @@ -630,24 +630,10 @@ 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) { @@ -697,11 +683,11 @@ 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); } @@ -737,21 +723,21 @@ 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); @@ -805,11 +791,11 @@ 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) diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp --- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -325,11 +325,10 @@ } 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; @@ -381,13 +380,11 @@ // 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(); diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -338,11 +338,10 @@ 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. */ @@ -551,107 +550,73 @@ 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(); +protected: + void OnContextLostError() const; - if (err) - while (RawGetError()) {} +public: + static const char* GLErrorToString(GLenum aError); - return err; + static bool IsBadCallError(const GLenum err) { + return !(err == 0 || + err == LOCAL_GL_CONTEXT_LOST); } - GLenum FlushErrors() const { - GLenum err = RawGetErrorAndClear(); - if (!mTopError) - mTopError = err; - return err; - } + class LocalErrorScope; +private: + mutable std::stack mLocalErrorScopeStack; + mutable UniquePtr mDebugErrorScope; //////////////////////////////////// // Use this safer option. -public: - class LocalErrorScope; - -private: - std::stack 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); @@ -712,24 +677,26 @@ 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() @@ -799,21 +766,21 @@ // ----------------------------------------------------------------------------- // 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; + return GetError(); + } - FlushErrors(); - err = mTopError; - mTopError = LOCAL_GL_NO_ERROR; + GLenum fGetGraphicsResetStatus() const; - AFTER_GL_CALL; - return err; - } + // - void fActiveTexture(GLenum texture) { BEFORE_GL_CALL; mSymbols.fActiveTexture(texture); AFTER_GL_CALL; @@ -2298,20 +2265,10 @@ 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) { diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -377,10 +377,12 @@ 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 } }, @@ -510,16 +512,39 @@ }; if (!LoadGLSymbols(this, prefix, trygl, coreSymbols, "GL")) return false; - //////////////// - - if (!MakeCurrent()) { - 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; + } } + //////////////// + const std::string versionStr = (const char*)fGetString(LOCAL_GL_VERSION); if (versionStr.find("OpenGL ES") == 0) { mProfile = ContextProfile::OpenGLES; } @@ -1009,27 +1034,10 @@ 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 } }, @@ -2049,14 +2057,11 @@ // 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 @@ -2941,22 +2946,22 @@ } 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(this)); } else { isCurrent = IsCurrentImpl(); } if (MOZ_LIKELY( isCurrent )) { - MOZ_ASSERT(IsCurrentImpl()); + MOZ_ASSERT(IsCurrentImpl() || !MakeCurrentImpl()); // Might have lost context. return true; } } if (!MakeCurrentImpl()) @@ -2975,22 +2980,137 @@ } 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 ""; +} + // -- 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 { @@ -2998,23 +3118,25 @@ // 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)"); } diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -435,12 +435,11 @@ 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 diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -786,11 +786,10 @@ 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); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4778,11 +4778,10 @@ 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);