mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +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 std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
||||||
const auto threadKey = CurrentThreadKey();
|
const auto threadKey = CurrentThreadKey();
|
||||||
if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) {
|
if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) {
|
||||||
m_eglCurrentThreads.erase(threadKey);
|
ReleaseEGLCurrentThread(threadKey);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,6 +358,7 @@ namespace MobileGL::MG_Backend {
|
|||||||
m_backendCapabilitiesInitialized = true;
|
m_backendCapabilitiesInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReleaseEGLCurrentThread(threadKey);
|
||||||
m_eglCurrentThreads[threadKey] = EGLCurrentState{
|
m_eglCurrentThreads[threadKey] = EGLCurrentState{
|
||||||
.Display = dpy,
|
.Display = dpy,
|
||||||
.DrawSurface = draw,
|
.DrawSurface = draw,
|
||||||
@@ -408,18 +409,60 @@ namespace MobileGL::MG_Backend {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendObject::ReleaseEGLSurface(EGLSurface surface) {
|
Bool BackendObject::IsEGLSurfaceCurrent(EGLSurface surface) const {
|
||||||
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
if (surface == EGL_NO_SURFACE) {
|
||||||
for (auto currentIt = m_eglCurrentThreads.begin(); currentIt != m_eglCurrentThreads.end();) {
|
return false;
|
||||||
if (currentIt->second.DrawSurface == surface || currentIt->second.ReadSurface == surface) {
|
}
|
||||||
currentIt = m_eglCurrentThreads.erase(currentIt);
|
for (const auto& current : m_eglCurrentThreads) {
|
||||||
continue;
|
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) {
|
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();
|
ResetEGLRuntimeState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,4 +493,8 @@ namespace MobileGL::MG_Backend {
|
|||||||
(void)height;
|
(void)height;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BackendObject::OnEGLSurfaceReleased(EGLSurface surface) {
|
||||||
|
(void)surface;
|
||||||
|
}
|
||||||
} // namespace MobileGL::MG_Backend
|
} // namespace MobileGL::MG_Backend
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ namespace MobileGL {
|
|||||||
|
|
||||||
struct EGLSurfaceState {
|
struct EGLSurfaceState {
|
||||||
SurfaceKind Kind = SurfaceKind::None;
|
SurfaceKind Kind = SurfaceKind::None;
|
||||||
|
Bool DestroyPending = false;
|
||||||
WindowHandle Window;
|
WindowHandle Window;
|
||||||
EGLint Width = 1;
|
EGLint Width = 1;
|
||||||
EGLint Height = 1;
|
EGLint Height = 1;
|
||||||
@@ -289,6 +290,7 @@ namespace MobileGL {
|
|||||||
const EGLSurfaceState* GetRegisteredEGLSurface(EGLSurface surface) const;
|
const EGLSurfaceState* GetRegisteredEGLSurface(EGLSurface surface) const;
|
||||||
Bool ActivateEGLSurface(EGLSurface surface);
|
Bool ActivateEGLSurface(EGLSurface surface);
|
||||||
virtual Bool InitPbufferSurface(EGLint width, EGLint height);
|
virtual Bool InitPbufferSurface(EGLint width, EGLint height);
|
||||||
|
virtual void OnEGLSurfaceReleased(EGLSurface surface);
|
||||||
FormatCapabilityCache& MutableFormatCapabilities();
|
FormatCapabilityCache& MutableFormatCapabilities();
|
||||||
|
|
||||||
mutable std::recursive_mutex m_eglStateMutex;
|
mutable std::recursive_mutex m_eglStateMutex;
|
||||||
@@ -302,6 +304,11 @@ namespace MobileGL {
|
|||||||
SurfaceKind m_eglSurfaceKind = SurfaceKind::None;
|
SurfaceKind m_eglSurfaceKind = SurfaceKind::None;
|
||||||
UnorderedMap<std::thread::id, EGLCurrentState> m_eglCurrentThreads;
|
UnorderedMap<std::thread::id, EGLCurrentState> m_eglCurrentThreads;
|
||||||
UnorderedMap<EGLSurface, EGLSurfaceState> m_eglSurfaces;
|
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 MG_Backend
|
||||||
} // namespace MobileGL
|
} // namespace MobileGL
|
||||||
|
|||||||
@@ -756,9 +756,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
void BackendObject_DirectGLES::ReleaseEGLSurface(EGLSurface surface) {
|
void BackendObject_DirectGLES::ReleaseEGLSurface(EGLSurface surface) {
|
||||||
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
||||||
if (m_eglSurface == surface) {
|
|
||||||
DestroyEGLContext();
|
|
||||||
}
|
|
||||||
BackendObject::ReleaseEGLSurface(surface);
|
BackendObject::ReleaseEGLSurface(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,6 +765,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
BackendObject::ReleaseEGLResources();
|
BackendObject::ReleaseEGLResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BackendObject_DirectGLES::OnEGLSurfaceReleased(EGLSurface surface) {
|
||||||
|
(void)surface;
|
||||||
|
DestroyEGLContext();
|
||||||
|
}
|
||||||
|
|
||||||
const RendererInfo& BackendObject_DirectGLES::GetRendererInfo() const {
|
const RendererInfo& BackendObject_DirectGLES::GetRendererInfo() const {
|
||||||
static RendererInfo RendererInfo = {
|
static RendererInfo RendererInfo = {
|
||||||
.RendererName = "Espryt", // Renderer Name
|
.RendererName = "Espryt", // Renderer Name
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
private:
|
private:
|
||||||
void UpdateDynamicBackendParameters();
|
void UpdateDynamicBackendParameters();
|
||||||
Bool InitPbufferSurface(EGLint width, EGLint height) override;
|
Bool InitPbufferSurface(EGLint width, EGLint height) override;
|
||||||
|
void OnEGLSurfaceReleased(EGLSurface surface) override;
|
||||||
|
|
||||||
Bool m_initialized = false;
|
Bool m_initialized = false;
|
||||||
MG_External::EGLFunctionsTable m_EGLFunctions;
|
MG_External::EGLFunctionsTable m_EGLFunctions;
|
||||||
|
|||||||
@@ -463,9 +463,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
void BackendObject_DirectVulkan::ReleaseEGLSurface(EGLSurface surface) {
|
void BackendObject_DirectVulkan::ReleaseEGLSurface(EGLSurface surface) {
|
||||||
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
||||||
if (m_eglSurface == surface) {
|
|
||||||
pVulkanRenderer.reset();
|
|
||||||
}
|
|
||||||
BackendObject::ReleaseEGLSurface(surface);
|
BackendObject::ReleaseEGLSurface(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,6 +472,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
BackendObject::ReleaseEGLResources();
|
BackendObject::ReleaseEGLResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BackendObject_DirectVulkan::OnEGLSurfaceReleased(EGLSurface surface) {
|
||||||
|
(void)surface;
|
||||||
|
pVulkanRenderer.reset();
|
||||||
|
}
|
||||||
|
|
||||||
const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const {
|
const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const {
|
||||||
return m_rendererInfo;
|
return m_rendererInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Bool InitPbufferSurface(EGLint width, EGLint height) override;
|
Bool InitPbufferSurface(EGLint width, EGLint height) override;
|
||||||
|
void OnEGLSurfaceReleased(EGLSurface surface) override;
|
||||||
void UpdateAdvertisedExtensions();
|
void UpdateAdvertisedExtensions();
|
||||||
void UpdateDynamicBackendParameters();
|
void UpdateDynamicBackendParameters();
|
||||||
|
|
||||||
|
|||||||
@@ -637,11 +637,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) {
|
void DispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) {
|
||||||
if (!pVulkanRenderer || !MG_State::pGLContext) {
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DispatchCompute called with null VulkanRenderer");
|
||||||
// TODO: Keep DirectVulkan renderer/context lifetime valid across failed compute image-load-store programs.
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DispatchCompute called with null GL context");
|
||||||
MGLOG_E("DirectVulkan::DispatchCompute skipped: renderer or GL context is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pVulkanRenderer->DispatchCompute(numGroupsX, numGroupsY, numGroupsZ);
|
pVulkanRenderer->DispatchCompute(numGroupsX, numGroupsY, numGroupsZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,11 +649,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MemoryBarrier(GLbitfield barriers) {
|
void MemoryBarrier(GLbitfield barriers) {
|
||||||
if (!pVulkanRenderer || !MG_State::pGLContext) {
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MemoryBarrier called with null VulkanRenderer");
|
||||||
// TODO: Preserve or replay memory barriers when DirectVulkan renderer/context recovery is implemented.
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MemoryBarrier called with null GL context");
|
||||||
MGLOG_E("DirectVulkan::MemoryBarrier skipped: renderer or GL context is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pVulkanRenderer->MemoryBarrier(barriers);
|
pVulkanRenderer->MemoryBarrier(barriers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1175,12 +1169,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
pVulkanRenderer->ReadPixels(x, y, width, height, format, type, pixels);
|
pVulkanRenderer->ReadPixels(x, y, width, height, format, type, pixels);
|
||||||
}
|
}
|
||||||
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {
|
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {
|
||||||
if (!pVulkanRenderer || !MG_State::pGLContext) {
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::GetTexImage called with null VulkanRenderer");
|
||||||
// TODO: Keep DirectVulkan renderer/context lifetime valid across failed image-load-store readbacks.
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::GetTexImage called with null GL context");
|
||||||
ClearReadPixelsOutput(1, 1, format, type, pixels);
|
|
||||||
MGLOG_E("DirectVulkan::GetTexImage skipped: renderer or GL context is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pVulkanRenderer->GetTexImage(target, level, format, type, pixels);
|
pVulkanRenderer->GetTexImage(target, level, format, type, pixels);
|
||||||
}
|
}
|
||||||
void GetTextureImage(const SharedPtr<MG_State::GLState::ITextureObject>& texture, TextureUploadTarget uploadTarget,
|
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) {
|
void DrawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||||
if (!pVulkanRenderer || !MG_State::pGLContext) {
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawArrays called with null VulkanRenderer");
|
||||||
// TODO: Keep DirectVulkan renderer/context lifetime valid across failed image-load-store programs.
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawArrays called with null GL context");
|
||||||
MGLOG_E("DirectVulkan::DrawArrays skipped: renderer or GL context is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawCmd payload{};
|
DrawCmd payload{};
|
||||||
payload.mode = mode;
|
payload.mode = mode;
|
||||||
|
|||||||
@@ -224,6 +224,8 @@ namespace MobileGL {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EGLSurfaceHandle drawSurface = currentIt->second.DrawSurface;
|
||||||
|
const EGLSurfaceHandle readSurface = currentIt->second.ReadSurface;
|
||||||
const EGLContextHandle context = currentIt->second.Context;
|
const EGLContextHandle context = currentIt->second.Context;
|
||||||
if (context != nullptr) {
|
if (context != nullptr) {
|
||||||
auto ownerIt = m_contextOwners.find(context);
|
auto ownerIt = m_contextOwners.find(context);
|
||||||
@@ -232,6 +234,29 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_threadCurrents.erase(currentIt);
|
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) {
|
void EGLContext::ReleaseDisplayObjects(EGLDisplayHandle display) {
|
||||||
@@ -972,22 +997,9 @@ namespace MobileGL {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto currentIt = m_threadCurrents.begin(); currentIt != m_threadCurrents.end();) {
|
if (IsSurfaceCurrentUnlocked(surface)) {
|
||||||
if (currentIt->second.DrawSurface == surface) {
|
surfaceIt->second.DestroyPending = true;
|
||||||
currentIt->second.DrawSurface = EGL_NO_SURFACE;
|
return true;
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
m_surfaces.erase(surfaceIt);
|
m_surfaces.erase(surfaceIt);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ namespace MobileGL {
|
|||||||
EGLDisplayHandle Display = EGL_NO_DISPLAY;
|
EGLDisplayHandle Display = EGL_NO_DISPLAY;
|
||||||
EGLConfigHandle Config = nullptr;
|
EGLConfigHandle Config = nullptr;
|
||||||
SurfaceType Type = SurfaceType::Window;
|
SurfaceType Type = SurfaceType::Window;
|
||||||
|
Bool DestroyPending = false;
|
||||||
Uint64 NativeHandleKey = 0;
|
Uint64 NativeHandleKey = 0;
|
||||||
EGLClientBuffer ClientBuffer = nullptr;
|
EGLClientBuffer ClientBuffer = nullptr;
|
||||||
EGLenum BufferType = EGL_NONE;
|
EGLenum BufferType = EGL_NONE;
|
||||||
@@ -232,6 +233,8 @@ namespace MobileGL {
|
|||||||
|
|
||||||
void ReleaseDisplayObjects(EGLDisplayHandle display);
|
void ReleaseDisplayObjects(EGLDisplayHandle display);
|
||||||
void ReleaseThreadUnlocked(const std::thread::id& threadKey);
|
void ReleaseThreadUnlocked(const std::thread::id& threadKey);
|
||||||
|
Bool IsSurfaceCurrentUnlocked(EGLSurfaceHandle surface) const;
|
||||||
|
void DestroyPendingSurfaceIfUnused(EGLSurfaceHandle surface);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable std::recursive_mutex m_mutex;
|
mutable std::recursive_mutex m_mutex;
|
||||||
|
|||||||
Reference in New Issue
Block a user