mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 12:48:32 +09:00
[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.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<std::thread::id, Bool> m_eglCurrentThreads;
|
||||
UnorderedMap<std::thread::id, EGLCurrentState> m_eglCurrentThreads;
|
||||
};
|
||||
} // namespace MG_Backend
|
||||
} // namespace MobileGL
|
||||
|
||||
@@ -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<std::recursive_mutex> 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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "../GetProcAddress.h"
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
#include <MG_State/EGLState/Core.h>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
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<std::recursive_mutex> 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<std::recursive_mutex> 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<Uint32>(std::max<EGLint>(GetAttribValue(attrib_list, EGL_WIDTH, 0), 0)),
|
||||
.Height = static_cast<Uint32>(std::max<EGLint>(GetAttribValue(attrib_list, EGL_HEIGHT, 0), 0)),
|
||||
.Width = static_cast<Uint32>(std::max<EGLint>(GetAttribValueAttrib(attrib_list, EGL_WIDTH, 0), 0)),
|
||||
.Height = static_cast<Uint32>(std::max<EGLint>(GetAttribValueAttrib(attrib_list, EGL_HEIGHT, 0), 0)),
|
||||
};
|
||||
if (!backendObject->CreateEGLWindowSurface(windowHandle)) {
|
||||
state->SetError(EGL_BAD_NATIVE_WINDOW);
|
||||
|
||||
@@ -1054,9 +1054,20 @@ namespace MobileGL {
|
||||
EGLContextHandle context) {
|
||||
const std::lock_guard<std::recursive_mutex> 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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <MG_State/EGLState/Core.h>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
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<EGLFixture> CreateFixture() {
|
||||
auto fixture = std::make_unique<EGLFixture>();
|
||||
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<void> threadAReady;
|
||||
std::promise<void> threadBAttempted;
|
||||
std::promise<void> 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);
|
||||
}
|
||||
+9
-5
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user