diff --git a/MobileGL/MG_Remote/Client/ClientSession.cpp b/MobileGL/MG_Remote/Client/ClientSession.cpp index 0c7e1bdc..e0dd62f5 100644 --- a/MobileGL/MG_Remote/Client/ClientSession.cpp +++ b/MobileGL/MG_Remote/Client/ClientSession.cpp @@ -629,6 +629,8 @@ namespace MobileGL::MG_Remote::Client { if (session == nullptr) return false; if (session->m_deviceLost.load(std::memory_order_acquire)) return true; if (tl_currentSessionWork != 0) return false; + // A thread that released a lost context and has bound none since (MG_State's marker). + if (MG_State::tThreadReleasedLostContext) return true; // A thread still on a context from before a recovered loss: lost for good, whatever the // session it would now be writing into. const SharedPtr& context = MG_State::pGLContext; diff --git a/MobileGL/MG_State/EGLState/Core.cpp b/MobileGL/MG_State/EGLState/Core.cpp index d1820b86..606abc23 100644 --- a/MobileGL/MG_State/EGLState/Core.cpp +++ b/MobileGL/MG_State/EGLState/Core.cpp @@ -257,6 +257,14 @@ namespace MobileGL { // context's objects ended in the thread-exit destructor of that TLS: outside every // entry point and its lock, writing their records into the middle of another // thread's - torn records, a reply read from the wrong slot, a lost session. +#if MOBILEGL_BUILD_DISAGGREGATED + { + const auto released = context != nullptr ? m_contexts.find(context) : m_contexts.end(); + tThreadReleasedLostContext = !currentIt->second.RestoreThreadGLState && released != m_contexts.end() && + released->second.GLStateObject && + released->second.GLStateObject->IsFromEndedWireSession(); + } +#endif pGLContext = currentIt->second.RestoreThreadGLState ? Move(currentIt->second.RestoreThreadGLState) : ProcessDefaultGLContext(); #if MOBILEGL_BUILD_DISAGGREGATED @@ -1513,6 +1521,9 @@ namespace MobileGL { if (const auto* contextObject = TryGetContext(context); contextObject != nullptr) { pGLContext = contextObject->GLStateObject; } +#if MOBILEGL_BUILD_DISAGGREGATED + tThreadReleasedLostContext = false; +#endif } return true; } diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 720d1779..18af6010 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -1665,4 +1665,7 @@ namespace MobileGL::MG_State { // Leak-at-exit storage; see GlobalObjects.cpp. thread_local SharedPtr pGLContext; +#if MOBILEGL_BUILD_DISAGGREGATED + thread_local Bool tThreadReleasedLostContext = false; +#endif } // namespace MobileGL::MG_State diff --git a/MobileGL/MG_State/GLState/Core.h b/MobileGL/MG_State/GLState/Core.h index be2a1bb9..f84b4adf 100644 --- a/MobileGL/MG_State/GLState/Core.h +++ b/MobileGL/MG_State/GLState/Core.h @@ -762,6 +762,15 @@ namespace MobileGL { // gave it. NULL on a thread for which neither has happened, the same answer a process // that never reached MG_State::Init() (the spawn server image) has always given. extern thread_local SharedPtr pGLContext; +#if MOBILEGL_BUILD_DISAGGREGATED + // True on a thread whose last EGL release let go of a context lost with an ended server + // session, until it makes a context current again. The release hands such a thread the + // process default rather than the lost context's GL state (which would make the thread's + // TLS that state's last owner), so this is what keeps the thread answering as lost: an + // application that releases its context before asking glGetGraphicsResetStatus - Qt's + // render threads do - still learns that the context is gone and rebuilds it. + extern thread_local Bool tThreadReleasedLostContext; +#endif // The fallback pGLContext starts at and an EGL release edge restores to: the process's // one context for every thread that never bound an EGL context to itself, created on diff --git a/MobileGL/MG_Test/Wire/ClientRecoveryTest.cpp b/MobileGL/MG_Test/Wire/ClientRecoveryTest.cpp index b915a56c..df5b49fe 100644 --- a/MobileGL/MG_Test/Wire/ClientRecoveryTest.cpp +++ b/MobileGL/MG_Test/Wire/ClientRecoveryTest.cpp @@ -567,6 +567,7 @@ namespace { Int32 staleSwap = -1; Int32 staleSwapError = 0; Int32 staleRelease = 0; + Uint32 resetStatusAfterRelease = 0; Int64 recordsByStaleThread = -1; Uint32 pixelAfterStale = 0; Uint32 resetStatusAfterStale = 0xFFFFFFFFu; @@ -621,6 +622,9 @@ namespace { r.staleSwap = EGL::SwapBuffers(egl.dpy, egl.surface); r.staleSwapError = EGL::GetError(); r.staleRelease = EGL::MakeCurrent(egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_TRUE; + // Released, the thread still answers as lost until it binds a context again: Qt's + // render threads release before they ask, and rebuild only when told. + r.resetStatusAfterRelease = glGetGraphicsResetStatus(); }); r.recordsByStaleThread = static_cast(PublishedSeq()) - static_cast(before); r.destroyOldOk = EGL::DestroyContext(egl.dpy, lost) == EGL_TRUE; @@ -1236,6 +1240,8 @@ TEST_F(ClientRecovery, AThreadStillOnTheLostContextReachesNothingOfTheFreshSessi EXPECT_EQ(r.staleSwap, EGL_FALSE); EXPECT_EQ(r.staleSwapError, EGL_CONTEXT_LOST) << std::hex << r.staleSwapError; EXPECT_EQ(r.staleRelease, 1) << "the second thread could not release the lost context"; + EXPECT_NE(r.resetStatusAfterRelease, static_cast(GL_NO_ERROR)) + << "the thread that released the lost context no longer reports the reset"; EXPECT_EQ(r.recordsByStaleThread, 0) << "the thread on the lost context published records on the fresh session"; EXPECT_EQ(r.destroyOldOk, 1); EXPECT_EQ(r.pixelAfterStale, kRed) << "the fresh context stopped rendering: " << std::hex << r.pixelAfterStale