mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Fix] (MG_State/EGLState, MG_Backend): defer current EGL surface destruction
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user