diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 9464fa48..c4c838ea 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -17110,7 +17110,14 @@ namespace MobileGL::MG_Backend::DirectGLES { tuple->Read = EGL_NO_SURFACE; g_nativeRegistryEpoch.fetch_add(1, std::memory_order_acq_rel); if (g_displayUsers > 0) --g_displayUsers; - if (g_displayUsers == 0 && g_Display != EGL_NO_DISPLAY && g_EGLFuncs.eglTerminate) { + // NOT WHILE A SESSION STILL HOLDS SURFACES. eglTerminate destroys them with the display, and + // the session keeps naming them: the next context made current on one (an application that + // destroys its only context and creates another on the same surface, as eglinfo does with + // its surfaceless stand-in) bound a surface the driver no longer had. The session's own + // teardown terminates the display once its surfaces are gone (DestroyNativeStateOfSession). + Bool surfacesRemain = false; + for (const auto* session : NativeSessions()) surfacesRemain = surfacesRemain || !session->Surfaces.empty(); + if (g_displayUsers == 0 && !surfacesRemain && g_Display != EGL_NO_DISPLAY && g_EGLFuncs.eglTerminate) { g_EGLFuncs.eglTerminate(g_Display); NoteDisplayTerminated(); g_Display = EGL_NO_DISPLAY; @@ -17139,9 +17146,11 @@ namespace MobileGL::MG_Backend::DirectGLES { // surface drew one window's frame into the other. NativeSessionState& session = ActiveNativeSession(); // Nothing to make it current on yet: a context bound for the records of its own creation - // (EGLImpl's CreateContext) before the session has any surface. Its first make-current - // gives it one. - if (tuple.Draw == EGL_NO_SURFACE && session.Draw == EGL_NO_SURFACE) return true; + // (EGLImpl's CreateContext), which no make-current has given a surface (or whose surface + // is gone). Binding it to whichever surface the session holds would hand it another + // context's surface - one the driver may not even have any more. Its first make-current + // binds it natively. + if (tuple.Draw == EGL_NO_SURFACE) return true; if (tuple.Draw != session.Draw && SessionOwnsSurface(session, tuple.Draw)) { session.Draw = tuple.Draw; session.Read = SessionOwnsSurface(session, tuple.Read) ? tuple.Read : tuple.Draw; diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index 00f3b7d7..094f0399 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -938,6 +938,9 @@ namespace MobileGL::MG_Impl::EGLImpl { // knows it. Read here because `state->MakeCurrent` below has already committed the // new one by the time the emission points are reached. const Uint64 oldContextToken = state->GetContextClientToken(oldContext); + // Read before the release too: a thread letting go of a context lost with an ended session + // says nothing to the fresh one (its binding never named that context). + const Bool oldContextLost = ContextIsLost(state, oldContext); #endif const String threadId = CurrentThreadIdString(); @@ -981,7 +984,11 @@ namespace MobileGL::MG_Impl::EGLImpl { } MGLOG_D("eglMakeCurrent release succeeded thread=%s", threadId.c_str()); #if MOBILEGL_BUILD_DISAGGREGATED - EmitContextBinding(state, EGL_NO_CONTEXT, oldContextToken); + { + std::optional silence; + if (oldContextLost) silence.emplace(); + EmitContextBinding(state, EGL_NO_CONTEXT, oldContextToken); + } #endif return EGL_TRUE; }