[Fix] (MG_State/EGLState, MG_Backend): defer current EGL surface destruction

This commit is contained in:
2026-07-03 10:14:26 +08:00
parent ce2b5a793f
commit 6418561d3c
9 changed files with 114 additions and 52 deletions
+56 -9
View File
@@ -319,7 +319,7 @@ namespace MobileGL::MG_Backend {
const std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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
+7
View File
@@ -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<std::thread::id, EGLCurrentState> m_eglCurrentThreads;
UnorderedMap<EGLSurface, EGLSurfaceState> m_eglSurfaces;
private:
Bool IsEGLSurfaceCurrent(EGLSurface surface) const;
void DestroyPendingEGLSurfaceIfUnused(EGLSurface surface);
void ReleaseEGLCurrentThread(const std::thread::id& threadKey);
};
} // namespace MG_Backend
} // namespace MobileGL
@@ -756,9 +756,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
void BackendObject_DirectGLES::ReleaseEGLSurface(EGLSurface surface) {
const std::lock_guard<std::recursive_mutex> 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
@@ -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;
@@ -463,9 +463,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void BackendObject_DirectVulkan::ReleaseEGLSurface(EGLSurface surface) {
const std::lock_guard<std::recursive_mutex> 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;
}
@@ -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();
@@ -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<MG_State::GLState::ITextureObject>& 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;
+28 -16
View File
@@ -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;
+3
View File
@@ -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;