From 5b116696f0eb4d6fc1c1531f831683fbf306cfe9 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 30 Jun 2026 16:44:38 +0800 Subject: [PATCH] [Fix] (MG_Impl/EGLImpl, MG_Backend): handle EGL current handoff for NeoForge earlydisplay Treat valid-display no-surface eglMakeCurrent calls as EGL release requests, keep EGLState and backend current records consistent across threads, and rebind the native DirectGLES EGL context during attach/release. Add EGLState coverage for cross-thread owner transfer and same-thread release/reattach behavior. --- MobileGL/MG_Backend/BackendObject.cpp | 18 ++- MobileGL/MG_Backend/BackendObject.h | 9 +- .../DirectGLES/BackendObject_DirectGLES.cpp | 34 +++++- MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp | 30 ++++- MobileGL/MG_Backend/DirectGLES/DirectGLES.h | 2 + .../BackendObject_DirectVulkan.cpp | 3 +- MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp | 43 +++++++- MobileGL/MG_State/EGLState/Core.cpp | 13 ++- MobileGL/MG_Test/CMakeLists.txt | 1 + MobileGL/MG_Test/EGLState/CMakeLists.txt | 20 ++++ MobileGL/MG_Test/EGLState/EGLStateTest.cpp | 104 ++++++++++++++++++ build.gradle | 14 ++- 12 files changed, 271 insertions(+), 20 deletions(-) create mode 100644 MobileGL/MG_Test/EGLState/CMakeLists.txt create mode 100644 MobileGL/MG_Test/EGLState/EGLStateTest.cpp diff --git a/MobileGL/MG_Backend/BackendObject.cpp b/MobileGL/MG_Backend/BackendObject.cpp index 11118554..40cbd983 100644 --- a/MobileGL/MG_Backend/BackendObject.cpp +++ b/MobileGL/MG_Backend/BackendObject.cpp @@ -33,7 +33,8 @@ namespace MobileGL::MG_Backend { }; Bool IsReleaseCurrentRequest(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { - return dpy == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT; + (void)dpy; + return draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT; } std::thread::id CurrentThreadKey() { @@ -296,7 +297,12 @@ namespace MobileGL::MG_Backend { m_backendCapabilitiesInitialized = true; } - m_eglCurrentThreads[threadKey] = true; + m_eglCurrentThreads[threadKey] = EGLCurrentState{ + .Display = dpy, + .DrawSurface = draw, + .ReadSurface = read, + .Context = ctx, + }; return true; } @@ -314,10 +320,16 @@ namespace MobileGL::MG_Backend { MGLOG_E("SwapEGLBuffers failed: EGL display mismatch or not initialized"); return false; } - if (m_eglCurrentThreads.find(CurrentThreadKey()) == m_eglCurrentThreads.end()) { + const auto currentIt = m_eglCurrentThreads.find(CurrentThreadKey()); + if (currentIt == m_eglCurrentThreads.end()) { MGLOG_E("SwapEGLBuffers failed: no current context attached"); return false; } + if (currentIt->second.Display != dpy || currentIt->second.DrawSurface != draw || + currentIt->second.Context == EGL_NO_CONTEXT) { + MGLOG_E("SwapEGLBuffers failed: draw surface is not current on this thread"); + return false; + } if (!m_eglSurfaceInitialized || draw == EGL_NO_SURFACE) { MGLOG_E("SwapEGLBuffers failed: invalid draw surface"); return false; diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 7e6523f0..f1c77250 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -268,6 +268,13 @@ namespace MobileGL { Pbuffer }; + struct EGLCurrentState { + EGLDisplay Display = EGL_NO_DISPLAY; + EGLSurface DrawSurface = EGL_NO_SURFACE; + EGLSurface ReadSurface = EGL_NO_SURFACE; + EGLContext Context = EGL_NO_CONTEXT; + }; + void ResetEGLRuntimeState(); virtual Bool InitPbufferSurface(EGLint width, EGLint height); FormatCapabilityCache& MutableFormatCapabilities(); @@ -280,7 +287,7 @@ namespace MobileGL { Bool m_eglSurfaceInitialized = false; Bool m_backendCapabilitiesInitialized = false; SurfaceKind m_eglSurfaceKind = SurfaceKind::None; - UnorderedMap m_eglCurrentThreads; + UnorderedMap m_eglCurrentThreads; }; } // namespace MG_Backend } // namespace MobileGL diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index e22648a9..7d7f3dfb 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -22,7 +22,8 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace { Bool IsReleaseCurrentRequest(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { - return dpy == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT; + (void)dpy; + return draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT; } void ClearGLErrors(const MG_External::GLESFunctionsTable& gl) { @@ -708,11 +709,40 @@ namespace MobileGL::MG_Backend::DirectGLES { } Bool BackendObject_DirectGLES::MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { + const std::lock_guard lock(m_eglStateMutex); if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) { + if (!DirectGLES::ReleaseCurrent()) { + return false; + } return BackendObject::MakeEGLCurrent(dpy, draw, read, ctx); } - return BackendObject::MakeEGLCurrent(dpy, draw, read, ctx); + if (!m_initialized) { + MGLOG_E("DirectGLES backend not initialized"); + return false; + } + if (!m_eglDisplayInitialized || m_eglDisplay != dpy) { + MGLOG_E("MakeEGLCurrent failed: EGL display mismatch or not initialized"); + return false; + } + if (!m_eglSurfaceInitialized) { + MGLOG_E("MakeEGLCurrent failed: EGL surface is not initialized"); + return false; + } + if (draw == EGL_NO_SURFACE || read == EGL_NO_SURFACE || ctx == EGL_NO_CONTEXT) { + MGLOG_E("MakeEGLCurrent failed: draw/read/context is invalid"); + return false; + } + + if (!DirectGLES::MakeCurrent()) { + return false; + } + + if (!BackendObject::MakeEGLCurrent(dpy, draw, read, ctx)) { + (void)DirectGLES::ReleaseCurrent(); + return false; + } + return true; } Bool BackendObject_DirectGLES::SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) { diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 664d6600..0bca0537 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -3222,7 +3222,7 @@ namespace MobileGL::MG_Backend::DirectGLES { g_Surface = g_EGLFuncs.eglCreateWindowSurface(g_Display, g_Config, window, nullptr); if (g_Surface == EGL_NO_SURFACE) return false; - if (!g_EGLFuncs.eglMakeCurrent(g_Display, g_Surface, g_Surface, g_Context)) return false; + if (!MakeCurrent()) return false; MGLOG_D("EGL context created successfully: display=%p, surface=%p, context=%p. window=%p", g_Display, g_Surface, g_Context, window); @@ -3237,13 +3237,39 @@ namespace MobileGL::MG_Backend::DirectGLES { g_Surface = g_EGLFuncs.eglCreatePbufferSurface(g_Display, g_Config, surfaceAttribs); if (g_Surface == EGL_NO_SURFACE) return false; - if (!g_EGLFuncs.eglMakeCurrent(g_Display, g_Surface, g_Surface, g_Context)) return false; + if (!MakeCurrent()) return false; MGLOG_D("EGL pbuffer context created successfully: display=%p, surface=%p, context=%p. size=%dx%d", g_Display, g_Surface, g_Context, width, height); return true; } + Bool MakeCurrent() { + if (!g_EGLFuncs.eglMakeCurrent || g_Display == EGL_NO_DISPLAY || g_Surface == EGL_NO_SURFACE || + g_Context == EGL_NO_CONTEXT) { + MGLOG_E("DirectGLES::MakeCurrent failed: EGL display/surface/context is not initialized"); + return false; + } + if (!g_EGLFuncs.eglMakeCurrent(g_Display, g_Surface, g_Surface, g_Context)) { + const EGLint error = g_EGLFuncs.eglGetError ? g_EGLFuncs.eglGetError() : EGL_SUCCESS; + MGLOG_E("DirectGLES::MakeCurrent failed: native eglMakeCurrent returned error 0x%04x", error); + return false; + } + return true; + } + + Bool ReleaseCurrent() { + if (!g_EGLFuncs.eglMakeCurrent || g_Display == EGL_NO_DISPLAY) { + return true; + } + if (!g_EGLFuncs.eglMakeCurrent(g_Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + const EGLint error = g_EGLFuncs.eglGetError ? g_EGLFuncs.eglGetError() : EGL_SUCCESS; + MGLOG_E("DirectGLES::ReleaseCurrent failed: native eglMakeCurrent returned error 0x%04x", error); + return false; + } + return true; + } + void Present() { g_EGLFuncs.eglSwapBuffers(g_Display, g_Surface); } diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h index bd227a5d..053a0376 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h @@ -93,6 +93,8 @@ namespace MobileGL::MG_Backend::DirectGLES { void ShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); Bool InitWindowSurface(NativeWindowType window); Bool InitPbufferSurface(EGLint width, EGLint height); + Bool MakeCurrent(); + Bool ReleaseCurrent(); void Present(); void SetEGLFuncsTable(const MG_External::EGLFunctionsTable& eglFuncs); void SetGLESFuncsTable(const MG_External::GLESFunctionsTable& glesFuncs); diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index 693175e3..1bbda163 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -20,7 +20,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { namespace { Bool IsReleaseCurrentRequest(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { - return dpy == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT; + (void)dpy; + return draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT; } Bool IsFormatIndexValid(TextureInternalFormat format) { diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index 01557cf5..69ccd765 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -10,6 +10,8 @@ #include "../GetProcAddress.h" #include #include +#include +#include #include namespace MobileGL::MG_Impl::EGLImpl { @@ -31,6 +33,17 @@ namespace MobileGL::MG_Impl::EGLImpl { return backendObject; } + std::recursive_mutex& EGLOperationMutex() { + static std::recursive_mutex mutex; + return mutex; + } + + String CurrentThreadIdString() { + std::ostringstream stream; + stream << std::this_thread::get_id(); + return stream.str(); + } + MG_Backend::WindowBackend DetectWindowBackend() { #if defined(ANDROID) || defined(__ANDROID__) return MG_Backend::WindowBackend::Android; @@ -55,7 +68,7 @@ namespace MobileGL::MG_Impl::EGLImpl { return defaultValue; } - EGLint GetAttribValue(const EGLAttrib* attribList, EGLint attrib, EGLint defaultValue) { + EGLint GetAttribValueAttrib(const EGLAttrib* attribList, EGLint attrib, EGLint defaultValue) { if (!attribList) { return defaultValue; } @@ -126,6 +139,7 @@ namespace MobileGL::MG_Impl::EGLImpl { } EGLBoolean SwapBuffers(EGLDisplay dpy, EGLSurface draw) { + const std::lock_guard operationLock(EGLOperationMutex()); auto* state = GetState(); if (!state) { return EGL_FALSE; @@ -141,6 +155,7 @@ namespace MobileGL::MG_Impl::EGLImpl { return EGL_FALSE; } if (!backendObject->SwapEGLBuffers(dpy, draw)) { + MGLOG_E("eglSwapBuffers failed on thread=%s dpy=%p draw=%p", CurrentThreadIdString().c_str(), dpy, draw); state->SetError(EGL_BAD_SURFACE); return EGL_FALSE; } @@ -202,6 +217,7 @@ namespace MobileGL::MG_Impl::EGLImpl { } EGLBoolean MakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { + const std::lock_guard operationLock(EGLOperationMutex()); auto* state = GetState(); if (!state) { return EGL_FALSE; @@ -211,17 +227,30 @@ namespace MobileGL::MG_Impl::EGLImpl { const auto oldDraw = state->GetCurrentSurface(EGL_DRAW); const auto oldRead = state->GetCurrentSurface(EGL_READ); const auto oldContext = state->GetCurrentContext(); + const String threadId = CurrentThreadIdString(); + + MGLOG_D("eglMakeCurrent begin thread=%s dpy=%p draw=%p read=%p ctx=%p oldDpy=%p oldDraw=%p oldRead=%p oldCtx=%p", + threadId.c_str(), dpy, draw, read, ctx, oldDisplay, oldDraw, oldRead, oldContext); if (!state->MakeCurrent(dpy, draw, read, ctx)) { + const EGLint error = state->ConsumeError(); + MGLOG_D("eglMakeCurrent rejected by EGLState thread=%s error=0x%04x", threadId.c_str(), error); + state->SetError(error); return EGL_FALSE; } const Bool releaseCurrentRequest = - dpy == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT; + draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT; if (releaseCurrentRequest) { if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) { - (void)backendObject->MakeEGLCurrent(dpy, draw, read, ctx); + if (!backendObject->MakeEGLCurrent(dpy, draw, read, ctx)) { + MGLOG_E("eglMakeCurrent release failed in backend thread=%s", threadId.c_str()); + state->MakeCurrent(oldDisplay, oldDraw, oldRead, oldContext); + state->SetError(EGL_BAD_ACCESS); + return EGL_FALSE; + } } + MGLOG_D("eglMakeCurrent release succeeded thread=%s", threadId.c_str()); return EGL_TRUE; } @@ -232,10 +261,14 @@ namespace MobileGL::MG_Impl::EGLImpl { return EGL_FALSE; } if (!backendObject->MakeEGLCurrent(dpy, draw, read, ctx)) { + MGLOG_E("eglMakeCurrent backend attach failed thread=%s dpy=%p draw=%p read=%p ctx=%p", threadId.c_str(), + dpy, draw, read, ctx); state->SetError(EGL_BAD_ACCESS); state->MakeCurrent(oldDisplay, oldDraw, oldRead, oldContext); return EGL_FALSE; } + MGLOG_D("eglMakeCurrent attach succeeded thread=%s dpy=%p draw=%p read=%p ctx=%p", threadId.c_str(), dpy, draw, + read, ctx); return EGL_TRUE; } @@ -612,8 +645,8 @@ namespace MobileGL::MG_Impl::EGLImpl { const MG_Backend::WindowHandle windowHandle = { .Backend = DetectWindowBackend(), .Handle = native_window, - .Width = static_cast(std::max(GetAttribValue(attrib_list, EGL_WIDTH, 0), 0)), - .Height = static_cast(std::max(GetAttribValue(attrib_list, EGL_HEIGHT, 0), 0)), + .Width = static_cast(std::max(GetAttribValueAttrib(attrib_list, EGL_WIDTH, 0), 0)), + .Height = static_cast(std::max(GetAttribValueAttrib(attrib_list, EGL_HEIGHT, 0), 0)), }; if (!backendObject->CreateEGLWindowSurface(windowHandle)) { state->SetError(EGL_BAD_NATIVE_WINDOW); diff --git a/MobileGL/MG_State/EGLState/Core.cpp b/MobileGL/MG_State/EGLState/Core.cpp index 2390e488..57c2c7ca 100644 --- a/MobileGL/MG_State/EGLState/Core.cpp +++ b/MobileGL/MG_State/EGLState/Core.cpp @@ -1054,9 +1054,20 @@ namespace MobileGL { EGLContextHandle context) { const std::lock_guard lock(m_mutex); const Bool releaseCurrentRequest = - display == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && context == nullptr; + draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && context == nullptr; const auto threadKey = CurrentThreadKey(); if (releaseCurrentRequest) { + if (display != EGL_NO_DISPLAY) { + auto* displayObject = TryGetDisplay(display); + if (!displayObject) { + SetError(EGL_BAD_DISPLAY); + return false; + } + if (!displayObject->Initialized) { + SetError(EGL_NOT_INITIALIZED); + return false; + } + } ReleaseThreadUnlocked(threadKey); return true; } diff --git a/MobileGL/MG_Test/CMakeLists.txt b/MobileGL/MG_Test/CMakeLists.txt index 814482df..0fee4f8c 100644 --- a/MobileGL/MG_Test/CMakeLists.txt +++ b/MobileGL/MG_Test/CMakeLists.txt @@ -65,6 +65,7 @@ include(GoogleTest) gtest_discover_tests(SanityTest) add_subdirectory(Buffer) +add_subdirectory(EGLState) add_subdirectory(Framebuffer) add_subdirectory(Texture) add_subdirectory(VertexArray) diff --git a/MobileGL/MG_Test/EGLState/CMakeLists.txt b/MobileGL/MG_Test/EGLState/CMakeLists.txt new file mode 100644 index 00000000..030e7db7 --- /dev/null +++ b/MobileGL/MG_Test/EGLState/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.14) + +add_executable( + EGLStateTest + EGLStateTest.cpp +) + +target_include_directories(EGLStateTest PRIVATE + ${MGL_ROOT}/include + ${MGL_ROOT}/MobileGL +) + +target_link_libraries( + EGLStateTest PRIVATE + GTest::gtest_main + ${LINK_LIBRARIES} +) + +include(GoogleTest) +gtest_discover_tests(EGLStateTest) diff --git a/MobileGL/MG_Test/EGLState/EGLStateTest.cpp b/MobileGL/MG_Test/EGLState/EGLStateTest.cpp new file mode 100644 index 00000000..33135715 --- /dev/null +++ b/MobileGL/MG_Test/EGLState/EGLStateTest.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +namespace { + using StateContext = MobileGL::MG_State::EGLState::EGLContext; + + struct EGLFixture { + StateContext State; + EGLDisplay Display = EGL_NO_DISPLAY; + EGLConfig Config = nullptr; + EGLSurface Surface = EGL_NO_SURFACE; + StateContext::EGLContextHandle Context = EGL_NO_CONTEXT; + }; + + std::unique_ptr CreateFixture() { + auto fixture = std::make_unique(); + fixture->Display = fixture->State.GetDisplay(EGL_DEFAULT_DISPLAY); + EXPECT_NE(fixture->Display, EGL_NO_DISPLAY); + EXPECT_TRUE(fixture->State.InitializeDisplay(fixture->Display, nullptr, nullptr)); + + EGLint configCount = 0; + EXPECT_TRUE(fixture->State.ChooseConfig(fixture->Display, nullptr, &fixture->Config, 1, &configCount)); + EXPECT_GE(configCount, 1); + EXPECT_NE(fixture->Config, nullptr); + + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + fixture->Surface = fixture->State.CreatePbufferSurface(fixture->Display, fixture->Config, surfaceAttribs); + EXPECT_NE(fixture->Surface, EGL_NO_SURFACE); + + fixture->Context = fixture->State.CreateContext(fixture->Display, fixture->Config, EGL_NO_CONTEXT, nullptr); + EXPECT_NE(fixture->Context, EGL_NO_CONTEXT); + return fixture; + } +} + +TEST(EGLStateMakeCurrent, ContextCannotBeCurrentOnTwoThreadsAtOnce) { + auto fixture = CreateFixture(); + + std::promise threadAReady; + std::promise threadBAttempted; + std::promise threadAReleased; + + auto threadAReadyFuture = threadAReady.get_future(); + auto threadBAttemptedFuture = threadBAttempted.get_future(); + auto threadAReleasedFuture = threadAReleased.get_future(); + + bool threadAAttachOk = false; + bool threadAReleaseOk = false; + bool threadBDenied = false; + EGLint threadBDeniedError = EGL_SUCCESS; + bool threadBAttachAfterReleaseOk = false; + EGLint threadBAttachAfterReleaseError = EGL_SUCCESS; + + std::thread threadA([&] { + threadAAttachOk = fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, + fixture->Context); + threadAReady.set_value(); + threadBAttemptedFuture.wait(); + threadAReleaseOk = + fixture->State.MakeCurrent(fixture->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + threadAReleased.set_value(); + }); + + std::thread threadB([&] { + threadAReadyFuture.wait(); + threadBDenied = fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, + fixture->Context); + threadBDeniedError = fixture->State.ConsumeError(); + threadBAttempted.set_value(); + threadAReleasedFuture.wait(); + threadBAttachAfterReleaseOk = + fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context); + threadBAttachAfterReleaseError = fixture->State.ConsumeError(); + if (threadBAttachAfterReleaseOk) { + (void)fixture->State.MakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + }); + + threadA.join(); + threadB.join(); + + EXPECT_TRUE(threadAAttachOk); + EXPECT_TRUE(threadAReleaseOk); + EXPECT_FALSE(threadBDenied); + EXPECT_EQ(threadBDeniedError, EGL_BAD_ACCESS); + EXPECT_TRUE(threadBAttachAfterReleaseOk); + EXPECT_EQ(threadBAttachAfterReleaseError, EGL_SUCCESS); +} + +TEST(EGLStateMakeCurrent, SameThreadRepeatedAttachReleaseDoesNotLeaveStaleOwner) { + auto fixture = CreateFixture(); + + EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context)); + EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context)); + EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); + EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context)); + EXPECT_TRUE(fixture->State.MakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); + EXPECT_TRUE(fixture->State.MakeCurrent(fixture->Display, fixture->Surface, fixture->Surface, fixture->Context)); + EXPECT_TRUE(fixture->State.MakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); + EXPECT_EQ(fixture->State.ConsumeError(), EGL_SUCCESS); +} diff --git a/build.gradle b/build.gradle index 8780ca18..87c465aa 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,9 @@ apply plugin: 'com.android.library' +def mobileGlLogActiveLevel = { + (rootProject.findProperty('mobilegl.logLevel') ?: System.getenv('MOBILEGL_LOG_ACTIVE_LEVEL') ?: 'MOBILEGL_LOG_LEVEL_INFO') as String +} + android { namespace 'top.mobilegl.mobilegl' compileSdk 34 @@ -9,11 +13,11 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" -// externalNativeBuild { -// cmake { -// arguments "-DMOBILEGL_ENABLE_TRACY=ON" -// } -// } + externalNativeBuild { + cmake { + arguments "-DMOBILEGL_LOG_ACTIVE_LEVEL=${mobileGlLogActiveLevel()}" + } + } } buildTypes {