From 6418561d3c1df90adc4e89a2c472214e4ecb0b2e Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 3 Jul 2026 10:14:26 +0800 Subject: [PATCH] [Fix] (MG_State/EGLState, MG_Backend): defer current EGL surface destruction --- MobileGL/MG_Backend/BackendObject.cpp | 65 ++++++++++++++++--- MobileGL/MG_Backend/BackendObject.h | 7 ++ .../DirectGLES/BackendObject_DirectGLES.cpp | 8 ++- .../DirectGLES/BackendObject_DirectGLES.h | 1 + .../BackendObject_DirectVulkan.cpp | 8 ++- .../DirectVulkan/BackendObject_DirectVulkan.h | 1 + .../MG_Backend/DirectVulkan/DirectVulkan.cpp | 29 +++------ MobileGL/MG_State/EGLState/Core.cpp | 44 ++++++++----- MobileGL/MG_State/EGLState/Core.h | 3 + 9 files changed, 114 insertions(+), 52 deletions(-) diff --git a/MobileGL/MG_Backend/BackendObject.cpp b/MobileGL/MG_Backend/BackendObject.cpp index d8502838..94da30bb 100644 --- a/MobileGL/MG_Backend/BackendObject.cpp +++ b/MobileGL/MG_Backend/BackendObject.cpp @@ -319,7 +319,7 @@ namespace MobileGL::MG_Backend { const std::lock_guard lock(m_eglStateMutex); const auto threadKey = CurrentThreadKey(); if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) { - m_eglCurrentThreads.erase(threadKey); + ReleaseEGLCurrentThread(threadKey); return true; } @@ -358,6 +358,7 @@ namespace MobileGL::MG_Backend { m_backendCapabilitiesInitialized = true; } + ReleaseEGLCurrentThread(threadKey); m_eglCurrentThreads[threadKey] = EGLCurrentState{ .Display = dpy, .DrawSurface = draw, @@ -408,18 +409,60 @@ namespace MobileGL::MG_Backend { return true; } - void BackendObject::ReleaseEGLSurface(EGLSurface surface) { - const std::lock_guard lock(m_eglStateMutex); - for (auto currentIt = m_eglCurrentThreads.begin(); currentIt != m_eglCurrentThreads.end();) { - if (currentIt->second.DrawSurface == surface || currentIt->second.ReadSurface == surface) { - currentIt = m_eglCurrentThreads.erase(currentIt); - continue; + Bool BackendObject::IsEGLSurfaceCurrent(EGLSurface surface) const { + if (surface == EGL_NO_SURFACE) { + return false; + } + for (const auto& current : m_eglCurrentThreads) { + if (current.second.DrawSurface == surface || current.second.ReadSurface == surface) { + return true; } - ++currentIt; + } + return false; + } + + void BackendObject::DestroyPendingEGLSurfaceIfUnused(EGLSurface surface) { + auto surfaceIt = m_eglSurfaces.find(surface); + if (surfaceIt == m_eglSurfaces.end() || !surfaceIt->second.DestroyPending || + IsEGLSurfaceCurrent(surface)) { + return; } - m_eglSurfaces.erase(surface); + m_eglSurfaces.erase(surfaceIt); if (m_eglSurface == surface) { + OnEGLSurfaceReleased(surface); + ResetEGLRuntimeState(); + } + } + + void BackendObject::ReleaseEGLCurrentThread(const std::thread::id& threadKey) { + auto currentIt = m_eglCurrentThreads.find(threadKey); + if (currentIt == m_eglCurrentThreads.end()) { + return; + } + + const EGLSurface drawSurface = currentIt->second.DrawSurface; + const EGLSurface readSurface = currentIt->second.ReadSurface; + m_eglCurrentThreads.erase(currentIt); + DestroyPendingEGLSurfaceIfUnused(drawSurface); + DestroyPendingEGLSurfaceIfUnused(readSurface); + } + + void BackendObject::ReleaseEGLSurface(EGLSurface surface) { + const std::lock_guard lock(m_eglStateMutex); + auto surfaceIt = m_eglSurfaces.find(surface); + if (surfaceIt == m_eglSurfaces.end()) { + return; + } + + if (IsEGLSurfaceCurrent(surface)) { + surfaceIt->second.DestroyPending = true; + return; + } + + m_eglSurfaces.erase(surfaceIt); + if (m_eglSurface == surface) { + OnEGLSurfaceReleased(surface); ResetEGLRuntimeState(); } } @@ -450,4 +493,8 @@ namespace MobileGL::MG_Backend { (void)height; return false; } + + void BackendObject::OnEGLSurfaceReleased(EGLSurface surface) { + (void)surface; + } } // namespace MobileGL::MG_Backend diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 8af4ab9f..5cddaa23 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -278,6 +278,7 @@ namespace MobileGL { struct EGLSurfaceState { SurfaceKind Kind = SurfaceKind::None; + Bool DestroyPending = false; WindowHandle Window; EGLint Width = 1; EGLint Height = 1; @@ -289,6 +290,7 @@ namespace MobileGL { const EGLSurfaceState* GetRegisteredEGLSurface(EGLSurface surface) const; Bool ActivateEGLSurface(EGLSurface surface); virtual Bool InitPbufferSurface(EGLint width, EGLint height); + virtual void OnEGLSurfaceReleased(EGLSurface surface); FormatCapabilityCache& MutableFormatCapabilities(); mutable std::recursive_mutex m_eglStateMutex; @@ -302,6 +304,11 @@ namespace MobileGL { SurfaceKind m_eglSurfaceKind = SurfaceKind::None; UnorderedMap m_eglCurrentThreads; UnorderedMap m_eglSurfaces; + + private: + Bool IsEGLSurfaceCurrent(EGLSurface surface) const; + void DestroyPendingEGLSurfaceIfUnused(EGLSurface surface); + void ReleaseEGLCurrentThread(const std::thread::id& threadKey); }; } // namespace MG_Backend } // namespace MobileGL diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index efe62139..a94ddcf8 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -756,9 +756,6 @@ namespace MobileGL::MG_Backend::DirectGLES { void BackendObject_DirectGLES::ReleaseEGLSurface(EGLSurface surface) { const std::lock_guard lock(m_eglStateMutex); - if (m_eglSurface == surface) { - DestroyEGLContext(); - } BackendObject::ReleaseEGLSurface(surface); } @@ -768,6 +765,11 @@ namespace MobileGL::MG_Backend::DirectGLES { BackendObject::ReleaseEGLResources(); } + void BackendObject_DirectGLES::OnEGLSurfaceReleased(EGLSurface surface) { + (void)surface; + DestroyEGLContext(); + } + const RendererInfo& BackendObject_DirectGLES::GetRendererInfo() const { static RendererInfo RendererInfo = { .RendererName = "Espryt", // Renderer Name diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h index 219019ba..53e14ff5 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h @@ -39,6 +39,7 @@ namespace MobileGL::MG_Backend::DirectGLES { private: void UpdateDynamicBackendParameters(); Bool InitPbufferSurface(EGLint width, EGLint height) override; + void OnEGLSurfaceReleased(EGLSurface surface) override; Bool m_initialized = false; MG_External::EGLFunctionsTable m_EGLFunctions; diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index aac88071..996167e0 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -463,9 +463,6 @@ namespace MobileGL::MG_Backend::DirectVulkan { void BackendObject_DirectVulkan::ReleaseEGLSurface(EGLSurface surface) { const std::lock_guard lock(m_eglStateMutex); - if (m_eglSurface == surface) { - pVulkanRenderer.reset(); - } BackendObject::ReleaseEGLSurface(surface); } @@ -475,6 +472,11 @@ namespace MobileGL::MG_Backend::DirectVulkan { BackendObject::ReleaseEGLResources(); } + void BackendObject_DirectVulkan::OnEGLSurfaceReleased(EGLSurface surface) { + (void)surface; + pVulkanRenderer.reset(); + } + const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const { return m_rendererInfo; } diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h index 1d89828b..cb0e4008 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h @@ -38,6 +38,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { private: Bool InitPbufferSurface(EGLint width, EGLint height) override; + void OnEGLSurfaceReleased(EGLSurface surface) override; void UpdateAdvertisedExtensions(); void UpdateDynamicBackendParameters(); diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index fc1bf54d..b01c94e4 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -637,11 +637,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) { - if (!pVulkanRenderer || !MG_State::pGLContext) { - // TODO: Keep DirectVulkan renderer/context lifetime valid across failed compute image-load-store programs. - MGLOG_E("DirectVulkan::DispatchCompute skipped: renderer or GL context is null"); - return; - } + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DispatchCompute called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DispatchCompute called with null GL context"); pVulkanRenderer->DispatchCompute(numGroupsX, numGroupsY, numGroupsZ); } @@ -652,11 +649,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void MemoryBarrier(GLbitfield barriers) { - if (!pVulkanRenderer || !MG_State::pGLContext) { - // TODO: Preserve or replay memory barriers when DirectVulkan renderer/context recovery is implemented. - MGLOG_E("DirectVulkan::MemoryBarrier skipped: renderer or GL context is null"); - return; - } + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MemoryBarrier called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MemoryBarrier called with null GL context"); pVulkanRenderer->MemoryBarrier(barriers); } @@ -1175,12 +1169,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { pVulkanRenderer->ReadPixels(x, y, width, height, format, type, pixels); } void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) { - if (!pVulkanRenderer || !MG_State::pGLContext) { - // TODO: Keep DirectVulkan renderer/context lifetime valid across failed image-load-store readbacks. - ClearReadPixelsOutput(1, 1, format, type, pixels); - MGLOG_E("DirectVulkan::GetTexImage skipped: renderer or GL context is null"); - return; - } + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::GetTexImage called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::GetTexImage called with null GL context"); pVulkanRenderer->GetTexImage(target, level, format, type, pixels); } void GetTextureImage(const SharedPtr& texture, TextureUploadTarget uploadTarget, @@ -1199,11 +1189,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void DrawArrays(GLenum mode, GLint first, GLsizei count) { - if (!pVulkanRenderer || !MG_State::pGLContext) { - // TODO: Keep DirectVulkan renderer/context lifetime valid across failed image-load-store programs. - MGLOG_E("DirectVulkan::DrawArrays skipped: renderer or GL context is null"); - return; - } + MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer"); + MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArrays called with null GL context"); DrawCmd payload{}; payload.mode = mode; diff --git a/MobileGL/MG_State/EGLState/Core.cpp b/MobileGL/MG_State/EGLState/Core.cpp index e960c33a..b24bec5b 100644 --- a/MobileGL/MG_State/EGLState/Core.cpp +++ b/MobileGL/MG_State/EGLState/Core.cpp @@ -224,6 +224,8 @@ namespace MobileGL { return; } + const EGLSurfaceHandle drawSurface = currentIt->second.DrawSurface; + const EGLSurfaceHandle readSurface = currentIt->second.ReadSurface; const EGLContextHandle context = currentIt->second.Context; if (context != nullptr) { auto ownerIt = m_contextOwners.find(context); @@ -232,6 +234,29 @@ namespace MobileGL { } } m_threadCurrents.erase(currentIt); + DestroyPendingSurfaceIfUnused(drawSurface); + DestroyPendingSurfaceIfUnused(readSurface); + } + + Bool EGLContext::IsSurfaceCurrentUnlocked(EGLSurfaceHandle surface) const { + if (surface == EGL_NO_SURFACE) { + return false; + } + for (const auto& current : m_threadCurrents) { + if (current.second.DrawSurface == surface || current.second.ReadSurface == surface) { + return true; + } + } + return false; + } + + void EGLContext::DestroyPendingSurfaceIfUnused(EGLSurfaceHandle surface) { + auto surfaceIt = m_surfaces.find(surface); + if (surfaceIt == m_surfaces.end() || !surfaceIt->second.DestroyPending || + IsSurfaceCurrentUnlocked(surface)) { + return; + } + m_surfaces.erase(surfaceIt); } void EGLContext::ReleaseDisplayObjects(EGLDisplayHandle display) { @@ -972,22 +997,9 @@ namespace MobileGL { return false; } - for (auto currentIt = m_threadCurrents.begin(); currentIt != m_threadCurrents.end();) { - if (currentIt->second.DrawSurface == surface) { - currentIt->second.DrawSurface = EGL_NO_SURFACE; - } - if (currentIt->second.ReadSurface == surface) { - currentIt->second.ReadSurface = EGL_NO_SURFACE; - } - - const Bool emptyCurrent = currentIt->second.Context == nullptr && - currentIt->second.DrawSurface == EGL_NO_SURFACE && - currentIt->second.ReadSurface == EGL_NO_SURFACE; - if (emptyCurrent) { - currentIt = m_threadCurrents.erase(currentIt); - continue; - } - ++currentIt; + if (IsSurfaceCurrentUnlocked(surface)) { + surfaceIt->second.DestroyPending = true; + return true; } m_surfaces.erase(surfaceIt); return true; diff --git a/MobileGL/MG_State/EGLState/Core.h b/MobileGL/MG_State/EGLState/Core.h index bcedafb7..8c42c813 100644 --- a/MobileGL/MG_State/EGLState/Core.h +++ b/MobileGL/MG_State/EGLState/Core.h @@ -169,6 +169,7 @@ namespace MobileGL { EGLDisplayHandle Display = EGL_NO_DISPLAY; EGLConfigHandle Config = nullptr; SurfaceType Type = SurfaceType::Window; + Bool DestroyPending = false; Uint64 NativeHandleKey = 0; EGLClientBuffer ClientBuffer = nullptr; EGLenum BufferType = EGL_NONE; @@ -232,6 +233,8 @@ namespace MobileGL { void ReleaseDisplayObjects(EGLDisplayHandle display); void ReleaseThreadUnlocked(const std::thread::id& threadKey); + Bool IsSurfaceCurrentUnlocked(EGLSurfaceHandle surface) const; + void DestroyPendingSurfaceIfUnused(EGLSurfaceHandle surface); private: mutable std::recursive_mutex m_mutex;